diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm
index 57e9d68bfa..548206be92 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm
@@ -99,6 +99,13 @@
Initialized - Indicates whether the block has been initialized with values;
this property applies to Default and Overlay blocks.
+
+ Byte Source - Provides information about the source of the bytes in this
+ block. If the bytes were originally imported from a file, then this will indicate which file
+ and the offset into that file. If the bytes are mapped to another region of memory, it will
+ provide the address for the mapping. Blocks may consist of regions that have different
+ sources. In that case, source information about the first several regions will be d
+ displayed.
Source - The name of the file that produced the bytes that make up this
block as set by the file importer; for Bit Mapped or
Default - A normal memory block within the processor's address
space. These blocks cannot overlap any other default block. Default blocks
- can be either initialized or uninitialized. If you select Initialized you can
- enter a byte value that will be used to fill all the bytes in the new memory
- block.
-
+ can be one of the following types:
+
+ Initialized - Specify a value and a new block will be created
+ using that value for every byte in the block.
+ Uninitialized - An unitialized block will be created.
+ File Bytes - Select from a list of imported files and enter
+ a starting offset for that file. Those bytes will be the initial value for the block.
+ You can use the "Add To Program"
+ using "Binary Import" to create new FileBytes that you can use here.
+
Overlay - An overlay block is used to give an alternative set of
bytes (and related information) for a range in memory. This is achieved by
creating a new address space related to the actual processor address space and placing
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png
index 4272b752fa..e8fa300a22 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png
index d615a569cf..94073d9ac8 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png
index 06218d1ee5..9346e1cc33 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png differ
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png
index 5ba0d563f8..53c86882bf 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png differ
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java
new file mode 100644
index 0000000000..4ddaf1a515
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java
@@ -0,0 +1,135 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.cmd.Command;
+import ghidra.framework.model.DomainObject;
+import ghidra.framework.store.LockException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.mem.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.*;
+
+/**
+ * Base command class for adding memory blocks.
+ */
+abstract class AbstractAddMemoryBlockCmd implements Command {
+ protected String message;
+ protected final String name;
+ protected final String comment;
+ protected final String source;
+ protected final Address start;
+ protected final long length;
+
+ protected final boolean read;
+ protected final boolean write;
+ protected final boolean execute;
+ protected final boolean isVolatile;
+
+ AbstractAddMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile) {
+ this.name = name;
+ this.comment = comment;
+ this.source = source;
+ this.start = start;
+ this.length = length;
+ this.read = read;
+ this.write = write;
+ this.execute = execute;
+ this.isVolatile = isVolatile;
+ }
+
+ @Override
+ public String getStatusMsg() {
+ return message;
+ }
+
+ @Override
+ public String getName() {
+ return "Add Memory Block";
+ }
+
+ protected abstract MemoryBlock createMemoryBlock(Memory memory)
+ throws LockException, MemoryConflictException, AddressOverflowException,
+ DuplicateNameException, CancelledException;
+
+ @Override
+ public boolean applyTo(DomainObject obj) {
+ Program program = (Program) obj;
+ try {
+ Memory memory = program.getMemory();
+ MemoryBlock block = createMemoryBlock(memory);
+ block.setComment(comment);
+ block.setRead(read);
+ block.setWrite(write);
+ block.setExecute(execute);
+ block.setVolatile(isVolatile);
+ block.setSourceName(source);
+ renameFragment(program, block.getStart());
+ return true;
+ }
+ catch (IllegalArgumentException e) {
+ message = e.getMessage();
+ }
+ catch (AddressOverflowException e) {
+ message = e.getMessage();
+ }
+ catch (MemoryConflictException e) {
+ message = e.getMessage();
+ }
+ catch (DuplicateNameException e) {
+ message = "Duplicate Name: " + e.getMessage();
+ }
+ catch (IllegalStateException e) {
+ message = e.getMessage();
+ }
+ catch (Throwable t) {
+ message = "Create block failed";
+ Msg.showError(this, null, "Create Block Failed", t.getMessage(), t);
+ }
+ throw new RollbackException(message);
+ }
+
+ private void renameFragment(Program program, Address blockStartAddr) {
+ Listing listing = program.getListing();
+ String[] treeNames = listing.getTreeNames();
+ for (String treeName : treeNames) {
+ ProgramFragment frag = listing.getFragment(treeName, blockStartAddr);
+ renameFragment(frag, name);
+ }
+ }
+
+ private void renameFragment(ProgramFragment fragment, String fragmentName) {
+ String newName = fragmentName;
+ int count = 1;
+ while (!doRenameFragment(fragment, newName)) {
+ newName = fragmentName + "_" + count;
+ count++;
+ }
+ }
+
+ private boolean doRenameFragment(ProgramFragment fragment, String fragmentName) {
+ try {
+ fragment.setName(fragmentName);
+ return true;
+ }
+ catch (DuplicateNameException e) {
+ return false;
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java
new file mode 100644
index 0000000000..9c00677354
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java
@@ -0,0 +1,57 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.store.LockException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.mem.*;
+
+/**
+ * Command for adding Bit-mapped memory blocks
+ */
+public class AddBitMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
+
+ private final Address mappedAddress;
+
+ /**
+ * Create a new AddBitMappedMemoryBlockCmd
+ * @param name the name for the new memory block.
+ * @param comment the comment for the block
+ * @param source indicates what is creating the block
+ * @param start the start address for the the block
+ * @param length the length of the new block
+ * @param read sets the block's read permission flag
+ * @param write sets the block's write permission flag
+ * @param execute sets the block's execute permission flag
+ * @param isVolatile sets the block's volatile flag
+ * @param mappedAddress the address in memory that will serve as the bytes source for the block
+ */
+ public AddBitMappedMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile,
+ Address mappedAddress) {
+ super(name, comment, source, start, length, read, write, execute, isVolatile);
+ this.mappedAddress = mappedAddress;
+
+ }
+
+ @Override
+ protected MemoryBlock createMemoryBlock(Memory memory)
+ throws LockException, MemoryConflictException, AddressOverflowException {
+ return memory.createBitMappedBlock(name, start, mappedAddress, length);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java
new file mode 100644
index 0000000000..8ebfd7ac39
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java
@@ -0,0 +1,57 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.store.LockException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.mem.*;
+
+/**
+ * Command for adding byte-mapped memory blocks
+ */
+public class AddByteMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
+
+ private final Address mappedAddress;
+
+ /**
+ * Create a new AddByteMappedMemoryBlockCmd
+ * @param name the name for the new memory block.
+ * @param comment the comment for the block
+ * @param source indicates what is creating the block
+ * @param start the start address for the the block
+ * @param length the length of the new block
+ * @param read sets the block's read permission flag
+ * @param write sets the block's write permission flag
+ * @param execute sets the block's execute permission flag
+ * @param isVolatile sets the block's volatile flag
+ * @param mappedAddress the address in memory that will serve as the bytes source for the block
+ */
+ public AddByteMappedMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile,
+ Address mappedAddress) {
+ super(name, comment, source, start, length, read, write, execute, isVolatile);
+ this.mappedAddress = mappedAddress;
+
+ }
+
+ @Override
+ protected MemoryBlock createMemoryBlock(Memory memory)
+ throws LockException, MemoryConflictException, AddressOverflowException {
+ return memory.createByteMappedBlock(name, start, mappedAddress, length);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java
new file mode 100644
index 0000000000..c4b9ff1542
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java
@@ -0,0 +1,65 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.mem.*;
+import ghidra.util.exception.DuplicateNameException;
+
+/**
+ * Command for adding a new memory block using bytes from an imported {@link FileBytes} object.
+ */
+public class AddFileBytesMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
+
+ private final FileBytes fileBytes;
+ private final long offset;
+ private final boolean isOverlay;
+
+ /**
+ * Create a new AddFileBytesMemoryBlockCmd
+ * @param name the name for the new memory block.
+ * @param comment the comment for the block
+ * @param source indicates what is creating the block
+ * @param start the start address for the the block
+ * @param length the length of the new block
+ * @param read sets the block's read permission flag
+ * @param write sets the block's write permission flag
+ * @param execute sets the block's execute permission flag
+ * @param isVolatile sets the block's volatile flag
+ * @param fileBytes the {@link FileBytes} object that provides the byte source for this block.
+ * @param offset the offset into the {@link FileBytes} object for the first byte in this block.
+ * @param isOverlay if true, the block will be created in a new overlay address space.
+ */
+ public AddFileBytesMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile,
+ FileBytes fileBytes, long offset, boolean isOverlay) {
+ super(name, comment, source, start, length, read, write, execute, isVolatile);
+ this.fileBytes = fileBytes;
+ this.offset = offset;
+ this.isOverlay = isOverlay;
+ }
+
+ @Override
+ protected MemoryBlock createMemoryBlock(Memory memory) throws LockException,
+ MemoryConflictException, AddressOverflowException, DuplicateNameException {
+
+ return memory.createInitializedBlock(name, start, fileBytes, offset, length, isOverlay);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java
new file mode 100644
index 0000000000..050aa5bbe5
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java
@@ -0,0 +1,63 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.store.LockException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.mem.*;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.DuplicateNameException;
+
+/**
+ * Command for adding a new memory block initialized with a specific byte.
+ */
+public class AddInitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
+
+ private final byte initialValue;
+ private final boolean isOverlay;
+
+ /**
+ * Create a new AddFileBytesMemoryBlockCmd
+ * @param name the name for the new memory block.
+ * @param comment the comment for the block
+ * @param source indicates what is creating the block
+ * @param start the start address for the the block
+ * @param length the length of the new block
+ * @param read sets the block's read permission flag
+ * @param write sets the block's write permission flag
+ * @param execute sets the block's execute permission flag
+ * @param isVolatile sets the block's volatile flag
+ * @param initialValue the bytes value to use throught the new block.
+ * @param isOverlay if true, the block will be created in a new overlay address space.
+ */
+ public AddInitializedMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile,
+ byte initialValue, boolean isOverlay) {
+ super(name, comment, source, start, length, read, write, execute, isVolatile);
+
+ this.initialValue = initialValue;
+ this.isOverlay = isOverlay;
+ }
+
+ @Override
+ protected MemoryBlock createMemoryBlock(Memory memory)
+ throws LockException, MemoryConflictException, AddressOverflowException,
+ DuplicateNameException, CancelledException {
+ return memory.createInitializedBlock(name, start, length, initialValue, null, isOverlay);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java
deleted file mode 100644
index 9df9c8974c..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java
+++ /dev/null
@@ -1,174 +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.app.cmd.memory;
-
-import ghidra.framework.cmd.Command;
-import ghidra.framework.model.DomainObject;
-import ghidra.program.model.address.Address;
-import ghidra.program.model.address.AddressOverflowException;
-import ghidra.program.model.listing.*;
-import ghidra.program.model.mem.*;
-import ghidra.util.Msg;
-import ghidra.util.exception.DuplicateNameException;
-import ghidra.util.exception.RollbackException;
-
-/**
- *
- * Command to add a memory block.
- *
- *
- */
-public class AddMemoryBlockCmd implements Command {
-
- private String name;
- private String comment;
- private String source;
- private Address start;
- private int length;
- private boolean read;
- private boolean write;
- private boolean execute;
- private boolean isVolatile;
- private byte initialValue;
- private MemoryBlockType blockType;
- private Address baseAddr;
- private Program program;
- private String message;
- private boolean isInitialized;
-
- /**
- *
- * Construct a new AddMemoryBlockCmd
- * @param name block name
- * @param comment block comments
- * @param source block source
- * @param start starting address of the block
- * @param length block length
- * @param read read permissions
- * @param write write permissions
- * @param execute execute permissions
- * @param isVolatile volatile setting
- * @param initialValue initial byte value
- * @param blockType type of block to add: MemoryBlockType.DEFAULT,
- * MemoryBlockType.OVERLAY, or MemoryBlockType.BIT_MAPPED or MemoryBlockType.BYTE_MAPPED
- * @param baseAddr base address for the source address if the block type
- * is TYPE_BIT_MAPPED or TYPE_BYTE_MAPPED; otherwise, null
- */
- public AddMemoryBlockCmd(String name, String comment, String source, Address start, int length,
- boolean read, boolean write, boolean execute, boolean isVolatile, byte initialValue,
- MemoryBlockType blockType, Address baseAddr, boolean isInitialized) {
- this.name = name;
- this.comment = comment;
- this.source = source;
- this.start = start;
- this.length = length;
- this.read = read;
- this.write = write;
- this.execute = execute;
- this.isVolatile = isVolatile;
- this.initialValue = initialValue;
- this.blockType = blockType;
- this.baseAddr = baseAddr;
- this.isInitialized = isInitialized;
-
- }
-
- /**
- * @see ghidra.framework.cmd.Command#applyTo(ghidra.framework.model.DomainObject)
- */
- @Override
- public boolean applyTo(DomainObject obj) {
- program = (Program) obj;
- try {
- Memory memory = program.getMemory();
- MemoryBlock block = null;
- if (isInitialized) {
- block = memory.createInitializedBlock(name, start, length, initialValue, null,
- (blockType == MemoryBlockType.OVERLAY));
- }
- else if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) {
- block = memory.createUninitializedBlock(name, start, length,
- (blockType == MemoryBlockType.OVERLAY));
- }
- else if (blockType == MemoryBlockType.BIT_MAPPED) {
- block = memory.createBitMappedBlock(name, start, baseAddr, length);
- }
- else {
- block = memory.createByteMappedBlock(name, start, baseAddr, length);
- }
- block.setComment(comment);
- block.setRead(read);
- block.setWrite(write);
- block.setExecute(execute);
- block.setVolatile(isVolatile);
- block.setSourceName(source);
- renameFragment(block.getStart());
- return true;
- }
- catch (IllegalArgumentException e) {
- message = e.getMessage();
- }
- catch (AddressOverflowException e) {
- message = e.getMessage();
- }
- catch (MemoryConflictException e) {
- message = e.getMessage();
- }
- catch (OutOfMemoryError e) {
- message = "Not enough memory to create block";
- }
- catch (DuplicateNameException e) {
- message = "Duplicate Name: " + e.getMessage();
- }
- catch (IllegalStateException e) {
- message = e.getMessage();
- }
- catch (Throwable t) {
- message = "Create block failed";
- Msg.showError(this, null, "Create Block Failed", t.getMessage(), t);
- }
- throw new RollbackException(message);
- }
-
- /**
- * @see ghidra.framework.cmd.Command#getName()
- */
- @Override
- public String getName() {
- return "Add Memory Block";
- }
-
- /**
- * @see ghidra.framework.cmd.Command#getStatusMsg()
- */
- @Override
- public String getStatusMsg() {
- return message;
- }
-
- private void renameFragment(Address blockStartAddr) {
- Listing listing = program.getListing();
- String[] treeNames = listing.getTreeNames();
- for (int i = 0; i < treeNames.length; i++) {
- try {
- ProgramFragment frag = listing.getFragment(treeNames[i], blockStartAddr);
- frag.setName(name);
- }
- catch (DuplicateNameException exc) {
- }
- }
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java
new file mode 100644
index 0000000000..2e7b5df4c4
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java
@@ -0,0 +1,58 @@
+/* ###
+ * 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.app.cmd.memory;
+
+import ghidra.framework.store.LockException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.mem.*;
+import ghidra.util.exception.DuplicateNameException;
+
+/**
+ * Command for adding uninitialized memory blocks
+ */
+public class AddUninitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
+
+ private final boolean isOverlay;
+
+ /**
+ * Create a new AddUninitializedMemoryBlockCmd
+ * @param name the name for the new memory block.
+ * @param comment the comment for the block
+ * @param source indicates what is creating the block
+ * @param start the start address for the the block
+ * @param length the length of the new block
+ * @param read sets the block's read permission flag
+ * @param write sets the block's write permission flag
+ * @param execute sets the block's execute permission flag
+ * @param isVolatile sets the block's volatile flag
+ * @param isOverlay if true, the block will be created in a new overlay address space.
+ */
+ public AddUninitializedMemoryBlockCmd(String name, String comment, String source, Address start,
+ long length, boolean read, boolean write, boolean execute, boolean isVolatile,
+ boolean isOverlay) {
+ super(name, comment, source, start, length, read, write, execute, isVolatile);
+
+ this.isOverlay = isOverlay;
+ }
+
+ @Override
+ protected MemoryBlock createMemoryBlock(Memory memory) throws LockException,
+ MemoryConflictException, AddressOverflowException, DuplicateNameException {
+ return memory.createUninitializedBlock(name, start, length, isOverlay);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java
index 00ee426751..8da90fed94 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java
@@ -15,8 +15,8 @@
*/
package ghidra.app.plugin.core.memory;
-import java.awt.BorderLayout;
-import java.awt.CardLayout;
+import java.awt.*;
+import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
@@ -27,14 +27,18 @@ import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel;
+import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
import ghidra.app.plugin.core.misc.RegisterField;
import ghidra.app.util.*;
import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.listing.Program;
+import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.HelpLocation;
+import ghidra.util.layout.HorizontalLayout;
import ghidra.util.layout.PairLayout;
/**
@@ -50,10 +54,9 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
private JTextField commentField;
private JPanel viewPanel;
- private CardLayout cardLayout;
- private JPanel initializedPanel;
- private JPanel bottomPanel;
+ private CardLayout typeCardLayout;
private JRadioButton initializedRB;
+ private GRadioButton initializedFromFileBytesRB;
private JRadioButton uninitializedRB;
private JCheckBox readCB;
@@ -68,13 +71,25 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
private AddBlockModel model;
private GhidraComboBox comboBox;
private boolean updatingInitializedRB;
+ private CardLayout initializedTypeCardLayout;
private final static String MAPPED = "Mapped";
- private final static String OTHER = "Other";
+ private final static String UNMAPPED = "Unmapped";
+ private static final String UNITIALIZED = "UNITIALIZED";
+ private static final String INITIALIZED = "INITIALIZED";
+ private static final String FILE_BYTES = "FILE_BYTES";
+ private JPanel inializedTypePanel;
+ private RegisterField fileOffsetField;
+ private GhidraComboBox fileBytesComboBox;
AddBlockDialog(AddBlockModel model) {
super("Add Memory Block", true, true, true, false);
- init(model);
+ this.model = model;
+ model.setChangeListener(this);
+ setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block"));
+ addWorkPanel(buildWorkPanel());
+ addOKButton();
+ addCancelButton();
}
/**
@@ -84,150 +99,197 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
public void stateChanged(ChangeEvent e) {
setStatusText(model.getMessage());
setOkEnabled(model.isValidInfo());
- readCB.setEnabled(model.isReadEnabled());
- writeCB.setEnabled(model.isWriteEnabled());
- executeCB.setEnabled(model.isExecuteEnabled());
- volatileCB.setEnabled(model.isVolatileEnabled());
- if (initializedRB != null) {
- updatingInitializedRB = true;
- try {
- initializedRB.setSelected(model.getInitializedState());
- }
- finally {
- updatingInitializedRB = false;
- }
- }
- }
-
- private void init(AddBlockModel blockModel) {
- this.model = blockModel;
- blockModel.setChangeListener(this);
- create();
- setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block"));
+ readCB.setSelected(model.isRead());
+ writeCB.setSelected(model.isWrite());
+ executeCB.setSelected(model.isExecute());
+ volatileCB.setSelected(model.isVolatile());
}
/**
* Define the Main panel for the dialog here.
*/
- private void create() {
- cardLayout = new CardLayout();
- viewPanel = new JPanel(cardLayout);
+ private JComponent buildWorkPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
+ panel.add(buildMainPanel(), BorderLayout.NORTH);
+ panel.add(buildVariablePanel(), BorderLayout.CENTER);
+ return panel;
+ }
- nameField = new JTextField();
- nameField.setName("Block Name");
+ private Component buildMainPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(buildBasicInfoPanel(), BorderLayout.NORTH);
+ panel.add(buildPermissionsPanel(), BorderLayout.CENTER);
+ panel.add(buildTypesPanel(), BorderLayout.SOUTH);
- addrField = new AddressInput();
- addrField.setName("Start Addr");
+ return panel;
+ }
- lengthField = new RegisterField(32, null, false);
- lengthField.setName("Length");
+ private Component buildBasicInfoPanel() {
+ JPanel panel = new JPanel(new PairLayout(4, 10, 150));
+ panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
- commentField = new JTextField();
- commentField.setName("Comment");
+ panel.add(new GLabel("Block Name:", SwingConstants.RIGHT));
+ panel.add(buildNameField());
+ panel.add(new GLabel("Start Addr:", SwingConstants.RIGHT));
+ panel.add(buildAddressField());
+ panel.add(new GLabel("Length:", SwingConstants.RIGHT));
+ panel.add(buildLengthField());
+ panel.add(new GLabel("Comment:", SwingConstants.RIGHT));
+ panel.add(buildCommentField());
- addrFactory = model.getProgram().getAddressFactory();
- addrField.setAddressFactory(addrFactory, true);
+ return panel;
+ }
- nameField.getDocument().addDocumentListener(new DocumentListener() {
- @Override
- public void insertUpdate(DocumentEvent e) {
- nameChanged();
- }
+ private Component buildPermissionsPanel() {
- @Override
- public void removeUpdate(DocumentEvent e) {
- nameChanged();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- nameChanged();
- }
- });
-
- lengthField.setChangeListener(e -> lengthChanged());
- addrField.addChangeListener(ev -> addrChanged());
-
- readCB = new GCheckBox();
+ readCB = new GCheckBox("Read");
readCB.setName("Read");
+ readCB.setSelected(model.isRead());
+ readCB.addActionListener(e -> model.setRead(readCB.isSelected()));
- writeCB = new GCheckBox();
+ writeCB = new GCheckBox("Write");
writeCB.setName("Write");
+ writeCB.setSelected(model.isWrite());
+ writeCB.addActionListener(e -> model.setWrite(writeCB.isSelected()));
- executeCB = new GCheckBox();
+ executeCB = new GCheckBox("Execute");
executeCB.setName("Execute");
+ executeCB.setSelected(model.isExecute());
+ executeCB.addActionListener(e -> model.setExecute(executeCB.isSelected()));
- volatileCB = new GCheckBox();
+ volatileCB = new GCheckBox("Volatile");
volatileCB.setName("Volatile");
+ volatileCB.setSelected(model.isVolatile());
+ volatileCB.addActionListener(e -> model.setVolatile(volatileCB.isSelected()));
- JPanel topPanel = new JPanel(new PairLayout(4, 10, 150));
- topPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
- topPanel.add(new GLabel("Block Name:", SwingConstants.RIGHT));
- topPanel.add(nameField);
- topPanel.add(new GLabel("Start Addr:", SwingConstants.RIGHT));
- topPanel.add(addrField);
- topPanel.add(new GLabel("Length:", SwingConstants.RIGHT));
- topPanel.add(lengthField);
- topPanel.add(new GLabel("Comment:", SwingConstants.RIGHT));
- topPanel.add(commentField);
+ JPanel panel = new JPanel(new HorizontalLayout(10));
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 30, 20, 30));
+ panel.add(readCB);
+ panel.add(writeCB);
+ panel.add(executeCB);
+ panel.add(volatileCB);
- JPanel execPanel = new JPanel();
- BoxLayout bl = new BoxLayout(execPanel, BoxLayout.X_AXIS);
- execPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ return panel;
+ }
- execPanel.setLayout(bl);
- execPanel.add(Box.createHorizontalStrut(10));
- execPanel.add(new GLabel("Read"));
- execPanel.add(readCB);
- execPanel.add(Box.createHorizontalStrut(10));
+ private Component buildTypesPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createTitledBorder("Block Types"));
- execPanel.add(new GLabel("Write"));
- execPanel.add(writeCB);
- execPanel.add(Box.createHorizontalStrut(10));
+ MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT,
+ MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
- execPanel.add(new GLabel("Execute"));
- execPanel.add(executeCB);
- execPanel.add(Box.createHorizontalStrut(10));
+ comboBox = new GhidraComboBox<>(items);
+ comboBox.addItemListener(e -> blockTypeSelected());
+ panel.add(comboBox);
+ return panel;
+ }
- execPanel.add(new GLabel("Volatile"));
- execPanel.add(volatileCB);
+ private Component buildVariablePanel() {
+ typeCardLayout = new CardLayout();
+ viewPanel = new JPanel(typeCardLayout);
+ viewPanel.setBorder(BorderFactory.createEtchedBorder());
- JPanel panel = new JPanel();
- panel.add(execPanel);
+ viewPanel.add(buildMappedPanel(), MAPPED);
+ viewPanel.add(buildUnmappedPanel(), UNMAPPED);
+ typeCardLayout.show(viewPanel, UNMAPPED);
+ return viewPanel;
+ }
- JPanel outerTopPanel = new JPanel(new BorderLayout());
- outerTopPanel.add(topPanel, BorderLayout.NORTH);
- outerTopPanel.add(panel, BorderLayout.CENTER);
+ private Component buildUnmappedPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.add(buildInitializedRadioButtonPanel(), BorderLayout.NORTH);
+ panel.add(buildVariableInitializedPanel());
+ return panel;
+ }
- bottomPanel = new JPanel();
- BoxLayout layout = new BoxLayout(bottomPanel, BoxLayout.Y_AXIS);
- bottomPanel.setLayout(layout);
- bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 4, 5));
- bottomPanel.add(createComboBoxPanel());
- bottomPanel.add(viewPanel);
+ private Component buildInitializedRadioButtonPanel() {
+ JPanel panel = new JPanel(new HorizontalLayout(10));
- JPanel mainPanel = new JPanel();
- layout = new BoxLayout(mainPanel, BoxLayout.Y_AXIS);
- mainPanel.setLayout(layout);
- mainPanel.add(outerTopPanel);
- mainPanel.add(bottomPanel);
- mainPanel.validate();
+ ButtonGroup radioGroup = new ButtonGroup();
+ initializedRB = new GRadioButton("Initialized", false);
+ initializedRB.setName(initializedRB.getText());
+ initializedRB.addActionListener(ev -> initializeRBChanged());
- JPanel mainPanel2 = new JPanel(new BorderLayout());
- mainPanel2.add(mainPanel, BorderLayout.NORTH);
- mainPanel2.add(new JPanel(), BorderLayout.CENTER);
+ initializedFromFileBytesRB = new GRadioButton("File Bytes", false);
+ initializedFromFileBytesRB.setName(initializedRB.getText());
+ initializedFromFileBytesRB.addActionListener(ev -> initializeRBChanged());
- createCardPanels();
+ uninitializedRB = new GRadioButton("Uninitialized", true);
+ uninitializedRB.setName(uninitializedRB.getText());
+ uninitializedRB.addActionListener(ev -> initializeRBChanged());
- addWorkPanel(mainPanel2);
- addOKButton();
- addCancelButton();
+ radioGroup.add(initializedRB);
+ radioGroup.add(initializedFromFileBytesRB);
+ radioGroup.add(uninitializedRB);
+
+ panel.add(initializedRB);
+ panel.add(initializedFromFileBytesRB);
+ panel.add(uninitializedRB);
+
+ return panel;
+ }
+
+ private Component buildVariableInitializedPanel() {
+ initializedTypeCardLayout = new CardLayout();
+
+ inializedTypePanel = new JPanel(initializedTypeCardLayout);
+ inializedTypePanel.add(new JPanel(), UNITIALIZED);
+ inializedTypePanel.add(buildInitalValuePanel(), INITIALIZED);
+ inializedTypePanel.add(buildFileBytesPanel(), FILE_BYTES);
+ return inializedTypePanel;
+ }
+
+ private Component buildInitalValuePanel() {
+ initialValueLabel = new GDLabel("Initial Value");
+ initialValueField = new RegisterField(8, null, false);
+ initialValueField.setName("Initial Value");
+
+ initialValueField.setChangeListener(e -> initialValueChanged());
+
+ JPanel panel = new JPanel(new PairLayout(4, 10));
+ panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
+ panel.add(initialValueLabel);
+ panel.add(initialValueField);
+ return panel;
+ }
+
+ private Component buildFileBytesPanel() {
+ JPanel panel = new JPanel(new PairLayout(5, 5));
+ panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
+
+ panel.add(new GLabel("File Bytes:"));
+ panel.add(buildFileBytesCombo());
+ panel.add(new GLabel("File Offset:"));
+ panel.add(buildFileOffsetField());
+
+ return panel;
+ }
+
+ private Component buildFileBytesCombo() {
+ Memory memory = model.getProgram().getMemory();
+ List allFileBytes = memory.getAllFileBytes();
+ FileBytes[] fileBytes = allFileBytes.toArray(new FileBytes[allFileBytes.size()]);
+
+ fileBytesComboBox = new GhidraComboBox<>(fileBytes) {
+ public Dimension getPreferredSize() {
+ Dimension preferredSize = super.getPreferredSize();
+ preferredSize.width = 100;
+ return preferredSize;
+ }
+ };
+ fileBytesComboBox.addItemListener(e -> fileBytesChanged());
+ if (!allFileBytes.isEmpty()) {
+ model.setFileBytes(allFileBytes.get(0));
+ }
+ return fileBytesComboBox;
}
/**
* Display the dialog filled with default values.
* Used to enter a new MemoryBlock.
- * @param nlines default value displayed in the text field.
+ * @param tool the tool that owns this dialog
*/
void showDialog(PluginTool tool) {
@@ -237,17 +299,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
lengthField.setValue(Long.valueOf(0));
model.setLength(0);
commentField.setText("");
-
- readCB.setSelected(true);
- writeCB.setSelected(true);
- executeCB.setSelected(false);
- volatileCB.setSelected(false);
-
initialValueField.setValue(Long.valueOf(0));
model.setBlockType(MemoryBlockType.DEFAULT);
- model.setIsInitialized(initializedRB.isSelected());
+ model.setInitializedType(AddBlockModel.InitializedType.UNITIALIZED);
model.setInitialValue(0);
+ readCB.setSelected(model.isRead());
+ writeCB.setSelected(model.isWrite());
+ executeCB.setSelected(model.isExecute());
+ volatileCB.setSelected(model.isVolatile());
+
setOkEnabled(false);
tool.showDialog(this, tool.getComponentProvider(PluginConstants.MEMORY_MAP));
}
@@ -262,9 +323,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
*/
@Override
protected void okCallback() {
-
- if (model.execute(commentField.getText(), readCB.isSelected(), writeCB.isSelected(),
- executeCB.isSelected(), volatileCB.isSelected())) {
+ if (model.execute()) {
close();
}
else {
@@ -277,27 +336,17 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
if (updatingInitializedRB) {
return;
}
- boolean selected = initializedRB.isSelected();
- model.setIsInitialized(selected);
- initialValueField.setEnabled(selected);
- initialValueLabel.setEnabled(selected);
- if (!selected) {
- initialValueField.setValue(new Long(0));
- model.setInitialValue(0);
+ if (initializedRB.isSelected()) {
+ model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
+ initializedTypeCardLayout.show(inializedTypePanel, INITIALIZED);
}
- }
-
- private void uninitializedRBChanged() {
- if (updatingInitializedRB) {
- return;
+ else if (uninitializedRB.isSelected()) {
+ model.setInitializedType(InitializedType.UNITIALIZED);
+ initializedTypeCardLayout.show(inializedTypePanel, UNITIALIZED);
}
- boolean selected = uninitializedRB.isSelected();
- model.setIsInitialized(!selected);
- initialValueField.setEnabled(!selected);
- initialValueLabel.setEnabled(!selected);
- if (!selected) {
- initialValueField.setValue(new Long(0));
- model.setInitialValue(0);
+ else if (initializedFromFileBytesRB.isSelected()) {
+ model.setInitializedType(InitializedType.INITIALIZED_FROM_FILE_BYTES);
+ initializedTypeCardLayout.show(inializedTypePanel, FILE_BYTES);
}
}
@@ -318,21 +367,40 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
model.setBlockName(name);
}
+ private void commentChanged() {
+ String comment = commentField.getText().trim();
+ model.setComment(comment);
+ }
+
private void lengthChanged() {
- int length = 0;
+ long length = 0;
Long val = lengthField.getValue();
if (val != null) {
- length = val.intValue();
+ length = val.longValue();
}
model.setLength(length);
}
+ private void fileOffsetChanged() {
+ long fileOffset = -1;
+ Long val = fileOffsetField.getValue();
+ if (val != null) {
+ fileOffset = val.longValue();
+ }
+ model.setFileOffset(fileOffset);
+ }
+
+ private void fileBytesChanged() {
+ model.setFileBytes((FileBytes) fileBytesComboBox.getSelectedItem());
+ }
+
private void addrChanged() {
Address addr = null;
try {
addr = addrField.getAddress();
}
catch (IllegalArgumentException e) {
+ // just let it be null
}
model.setStartAddress(addr);
}
@@ -342,90 +410,19 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
model.setBaseAddress(baseAddress);
}
- private JPanel createComboBoxPanel() {
- JPanel panel = new JPanel(new BorderLayout());
- panel.setBorder(BorderFactory.createTitledBorder("Block Types"));
-
- MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT,
- MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
-
- comboBox = new GhidraComboBox<>(items);
- comboBox.addItemListener(e -> blockTypeSelected());
- panel.add(comboBox);
- return panel;
- }
-
private void blockTypeSelected() {
-
MemoryBlockType blockType = (MemoryBlockType) comboBox.getSelectedItem();
model.setBlockType(blockType);
- if (blockType == MemoryBlockType.DEFAULT) {
- cardLayout.show(viewPanel, OTHER);
- }
- else if (blockType == MemoryBlockType.OVERLAY) {
- cardLayout.show(viewPanel, OTHER);
- }
- else if (blockType == MemoryBlockType.BIT_MAPPED) {
- cardLayout.show(viewPanel, MAPPED);
+ if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) {
+ typeCardLayout.show(viewPanel, UNMAPPED);
}
else {
- // type is Byte mapped
- cardLayout.show(viewPanel, MAPPED);
+ typeCardLayout.show(viewPanel, MAPPED);
}
}
- private JPanel createRadioPanel() {
- JPanel panel = new JPanel();
- BoxLayout bl = new BoxLayout(panel, BoxLayout.X_AXIS);
- panel.setLayout(bl);
-
- ButtonGroup radioGroup = new ButtonGroup();
- initializedRB = new GRadioButton("Initialized", false);
- initializedRB.setName(initializedRB.getText());
- initializedRB.addActionListener(ev -> initializeRBChanged());
-
- uninitializedRB = new GRadioButton("Uninitialized", true);
- uninitializedRB.setName(uninitializedRB.getText());
- uninitializedRB.addActionListener(ev -> uninitializedRBChanged());
-
- radioGroup.add(initializedRB);
- radioGroup.add(uninitializedRB);
-
- panel.add(initializedRB);
- panel.add(uninitializedRB);
-
- JPanel outerPanel = new JPanel();
- BoxLayout bl2 = new BoxLayout(outerPanel, BoxLayout.Y_AXIS);
- outerPanel.setLayout(bl2);
- outerPanel.add(panel);
- createInitializedPanel();
- outerPanel.add(initializedPanel);
- outerPanel.setBorder(BorderFactory.createEtchedBorder());
- return outerPanel;
- }
-
- private void createCardPanels() {
- viewPanel.add(createAddressPanel(), MAPPED);
- viewPanel.add(createRadioPanel(), OTHER);
- cardLayout.show(viewPanel, OTHER);
- }
-
- private void createInitializedPanel() {
- initialValueLabel = new GDLabel("Initial Value");
- initialValueField = new RegisterField(8, null, false);
- initialValueField.setName("Initial Value");
- initialValueField.setEnabled(false);
-
- initialValueField.setChangeListener(e -> initialValueChanged());
-
- initializedPanel = new JPanel(new PairLayout(4, 10));
- initializedPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
- initializedPanel.add(initialValueLabel);
- initializedPanel.add(initialValueField);
- }
-
- private JPanel createAddressPanel() {
- JPanel addressPanel = new JPanel(new PairLayout());
+ private JPanel buildMappedPanel() {
+ JPanel panel = new JPanel(new PairLayout());
baseAddrField = new AddressInput();
baseAddrField.setAddressFactory(addrFactory);
@@ -440,10 +437,77 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
}
baseAddrField.setAddress(minAddr);
model.setBaseAddress(minAddr);
- addressPanel.add(new GLabel("Source Addr:"));
- addressPanel.add(baseAddrField);
- addressPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
- return addressPanel;
+ panel.add(new GLabel("Source Addr:"));
+ panel.add(baseAddrField);
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ return panel;
+ }
+
+ private Component buildCommentField() {
+ commentField = new JTextField();
+ commentField.setName("Comment");
+ commentField.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ commentChanged();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ commentChanged();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ commentChanged();
+ }
+ });
+ return commentField;
+ }
+
+ private Component buildLengthField() {
+ lengthField = new RegisterField(36, null, false);
+ lengthField.setName("Length");
+ lengthField.setChangeListener(e -> lengthChanged());
+ return lengthField;
+ }
+
+ private Component buildFileOffsetField() {
+ fileOffsetField = new RegisterField(60, null, false);
+ fileOffsetField.setName("File Offset");
+ fileOffsetField.setChangeListener(e -> fileOffsetChanged());
+ return fileOffsetField;
+ }
+
+ private Component buildAddressField() {
+ addrField = new AddressInput();
+ addrField.setName("Start Addr");
+ addrFactory = model.getProgram().getAddressFactory();
+ addrField.setAddressFactory(addrFactory, true);
+ addrField.addChangeListener(ev -> addrChanged());
+ return addrField;
+ }
+
+ private Component buildNameField() {
+ nameField = new JTextField();
+ nameField.setName("Block Name");
+ nameField.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ nameChanged();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ nameChanged();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ nameChanged();
+ }
+ });
+ return nameField;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java
index 836ee087f5..017cc39ca8 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java
@@ -17,13 +17,16 @@ package ghidra.app.plugin.core.memory;
import javax.swing.event.ChangeListener;
-import ghidra.app.cmd.memory.AddMemoryBlockCmd;
+import ghidra.app.cmd.memory.*;
+import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.NamingUtilities;
import ghidra.util.datastruct.StringKeyIndexer;
+import ghidra.util.exception.AssertException;
/**
*
@@ -39,23 +42,25 @@ class AddBlockModel {
private String blockName;
private Address startAddr;
private Address baseAddr;
- private int length;
+ private long length;
private MemoryBlockType blockType;
private int initialValue;
private String message;
private ChangeListener listener;
private boolean isValid;
- private boolean readEnabled;
- private boolean writeEnabled;
- private boolean executeEnabled;
- private boolean volatileEnabled;
- private boolean isInitialized;
+ private boolean isRead;
+ private boolean isWrite;
+ private boolean isExecute;
+ private boolean isVolatile;
+ private InitializedType initializedType;
+ private String comment;
+ private FileBytes fileBytes;
+ private long fileBytesOffset = -1;
+
+ enum InitializedType {
+ UNITIALIZED, INITIALIZED_FROM_VALUE, INITIALIZED_FROM_FILE_BYTES;
+ }
- /**
- * Construct a new model.
- * @param tool
- * @param program
- */
AddBlockModel(PluginTool tool, Program program) {
this.tool = tool;
this.program = program;
@@ -64,10 +69,6 @@ class AddBlockModel {
startAddr = program.getImageBase();
blockType = MemoryBlockType.DEFAULT;
initialValue = 0;
- readEnabled = true;
- writeEnabled = true;
- executeEnabled = true;
- volatileEnabled = true;
}
void setChangeListener(ChangeListener listener) {
@@ -80,18 +81,34 @@ class AddBlockModel {
listener.stateChanged(null);
}
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
void setStartAddress(Address addr) {
startAddr = addr;
validateInfo();
listener.stateChanged(null);
}
- void setLength(int length) {
+ void setLength(long length) {
this.length = length;
validateInfo();
listener.stateChanged(null);
}
+ void setFileOffset(long fileOffset) {
+ this.fileBytesOffset = fileOffset;
+ validateInfo();
+ listener.stateChanged(null);
+ }
+
+ void setFileBytes(FileBytes fileBytes) {
+ this.fileBytes = fileBytes;
+ validateInfo();
+ listener.stateChanged(null);
+ }
+
void setInitialValue(int initialValue) {
this.initialValue = initialValue;
validateInfo();
@@ -100,16 +117,35 @@ class AddBlockModel {
void setBlockType(MemoryBlockType blockType) {
this.blockType = blockType;
- readEnabled = true;
- writeEnabled = true;
- executeEnabled = true;
- volatileEnabled = true;
+ isRead = true;
+ isWrite = true;
+ isExecute = false;
+ isVolatile = false;
+ initializedType = InitializedType.UNITIALIZED;
validateInfo();
listener.stateChanged(null);
}
- void setIsInitialized(boolean isInitialized) {
- this.isInitialized = isInitialized;
+ void setRead(boolean b) {
+ this.isRead = b;
+ }
+
+ void setWrite(boolean b) {
+ this.isWrite = b;
+ }
+
+ void setExecute(boolean b) {
+ this.isExecute = b;
+ }
+
+ void setVolatile(boolean b) {
+ this.isVolatile = b;
+ }
+
+ void setInitializedType(InitializedType type) {
+ this.initializedType = type;
+ validateInfo();
+ listener.stateChanged(null);
}
void setBaseAddress(Address baseAddr) {
@@ -142,45 +178,33 @@ class AddBlockModel {
return program;
}
- boolean isReadEnabled() {
- return readEnabled;
+ boolean isRead() {
+ return isRead;
}
- boolean isWriteEnabled() {
- return writeEnabled;
+ boolean isWrite() {
+ return isWrite;
}
- boolean isExecuteEnabled() {
- return executeEnabled;
+ boolean isExecute() {
+ return isExecute;
}
- boolean isVolatileEnabled() {
- return volatileEnabled;
+ boolean isVolatile() {
+ return isVolatile;
}
- boolean getInitializedState() {
- return isInitialized;
+ InitializedType getInitializedType() {
+ return initializedType;
}
- /**
- * Add the block.
- * @param comment block comment
- * @param isRead read permissions
- * @param isWrite write permissions
- * @param isExecute execute permissions
- * @param isVolatile volatile setting
- * @return true if the block was successfully added
- */
- boolean execute(String comment, boolean isRead, boolean isWrite, boolean isExecute,
- boolean isVolatile) {
+ boolean execute() {
validateInfo();
if (!isValid) {
return false;
}
- AddMemoryBlockCmd cmd = new AddMemoryBlockCmd(blockName, comment, "- none -", startAddr,
- length, isRead, isWrite, isExecute, isVolatile, (byte) initialValue, blockType,
- baseAddr, isInitialized);
+ Command cmd = createAddBlockCommand();
if (!tool.execute(cmd, program)) {
message = cmd.getStatusMsg();
return false;
@@ -188,60 +212,162 @@ class AddBlockModel {
return true;
}
+ Command createAddBlockCommand() {
+ String source = "";
+ switch (blockType) {
+ case BIT_MAPPED:
+ return new AddBitMappedMemoryBlockCmd(blockName, comment, source, startAddr, length,
+ isRead, isWrite, isExecute, isVolatile, baseAddr);
+ case BYTE_MAPPED:
+ return new AddByteMappedMemoryBlockCmd(blockName, comment, source, startAddr,
+ length, isRead, isWrite, isExecute, isVolatile, baseAddr);
+ case DEFAULT:
+ return createNonMappedMemoryBlock(source, false);
+ case OVERLAY:
+ return createNonMappedMemoryBlock(source, true);
+ default:
+ throw new AssertException("Encountered unexpected block type: " + blockType);
+
+ }
+ }
+
+ private Command createNonMappedMemoryBlock(String source, boolean isOverlay) {
+ switch (initializedType) {
+ case INITIALIZED_FROM_FILE_BYTES:
+ return new AddFileBytesMemoryBlockCmd(blockName, comment, source, startAddr, length,
+ isRead, isWrite, isExecute, isVolatile, fileBytes, fileBytesOffset, isOverlay);
+ case INITIALIZED_FROM_VALUE:
+ return new AddInitializedMemoryBlockCmd(blockName, comment, source, startAddr,
+ length, isRead, isWrite, isExecute, isVolatile, (byte) initialValue, isOverlay);
+ case UNITIALIZED:
+ return new AddUninitializedMemoryBlockCmd(blockName, comment, source, startAddr,
+ length, isRead, isWrite, isExecute, isVolatile, isOverlay);
+ default:
+ throw new AssertException(
+ "Encountered unexpected intialized type: " + initializedType);
+
+ }
+ }
+
void dispose() {
tool = null;
program = null;
}
private void validateInfo() {
-
message = "";
- isValid = false;
- if (initialValue < 0 && isInitialized) {
- message = "Please enter a valid initial byte value";
- return;
+ isValid = hasValidName() && hasValidStartAddress() && hasValidLength() &&
+ hasNoMemoryConflicts() && hasMappedAddressIfNeeded() && hasUniqueNameIfOverlay() &&
+ hasInitialValueIfNeeded() && hasFileBytesInfoIfNeeded();
+ }
+
+ private boolean hasFileBytesInfoIfNeeded() {
+
+ if (initializedType != InitializedType.INITIALIZED_FROM_FILE_BYTES) {
+ return true;
}
- if (blockName == null || blockName.length() == 0) {
- message = "Please enter a name";
- return;
+
+ if (fileBytes == null) {
+ message = "Please select a FileBytes entry";
+ return false;
}
- if (nameExists(blockName)) {
- message = "Block name already exists";
- return;
+
+ if (fileBytesOffset < 0 || fileBytesOffset >= fileBytes.getSize()) {
+ message =
+ "Please enter a valid file bytes offset (0 - " + (fileBytes.getSize() - 1) + ")";
+ return false;
}
- if (!NamingUtilities.isValidName(blockName)) {
- message = "Block name is invalid";
- return;
+
+ if (fileBytesOffset + length > fileBytes.getSize()) {
+ message = "File bytes offset + length exceeds file bytes size: " + fileBytes.getSize();
+ return false;
}
- if (startAddr == null) {
- message = "Please enter a valid starting address";
- return;
+ return true;
+ }
+
+ private boolean hasInitialValueIfNeeded() {
+
+ if (initializedType != InitializedType.INITIALIZED_FROM_VALUE) {
+ return true;
}
+
+ if (initialValue >= 0 && initialValue <= 255) {
+ return true;
+ }
+ message = "Please enter a valid initial byte value";
+ return false;
+ }
+
+ private boolean hasUniqueNameIfOverlay() {
+ if (blockType != MemoryBlockType.OVERLAY) {
+ return true;
+ }
+ AddressFactory factory = program.getAddressFactory();
+ AddressSpace[] spaces = factory.getAddressSpaces();
+ for (AddressSpace space : spaces) {
+ if (space.getName().equals(blockName)) {
+ message = "Address Space named " + blockName + " already exists";
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean hasMappedAddressIfNeeded() {
if (blockType == MemoryBlockType.BIT_MAPPED || blockType == MemoryBlockType.BYTE_MAPPED) {
- isInitialized = false;
if (baseAddr == null) {
String blockTypeStr = (blockType == MemoryBlockType.BIT_MAPPED) ? "bit" : "overlay";
message = "Please enter a source address for the " + blockTypeStr + " block";
- return;
+ return false;
}
}
- long sizeLimit =
- isInitialized ? Memory.MAX_INITIALIZED_BLOCK_SIZE : Memory.MAX_UNINITIALIZED_BLOCK_SIZE;
- if (length <= 0 || length > sizeLimit) {
- message = "Please enter a valid length > 0 and <= 0x" + Long.toHexString(sizeLimit);
- return;
- }
+ return true;
+ }
+ private boolean hasNoMemoryConflicts() {
if (blockType == MemoryBlockType.OVERLAY) {
- AddressFactory factory = program.getAddressFactory();
- AddressSpace[] spaces = factory.getAddressSpaces();
- for (int i = 0; i < spaces.length; i++) {
- if (spaces[i].getName().equals(blockName)) {
- message = "Address Space named " + blockName + " already exists";
- return;
- }
- }
+ return true;
}
- isValid = true;
+ Address endAddr = startAddr.add(length - 1);
+ AddressSet intersectRange = program.getMemory().intersectRange(startAddr, endAddr);
+ if (!intersectRange.isEmpty()) {
+ AddressRange firstRange = intersectRange.getFirstRange();
+ message = "Block address conflict: " + firstRange;
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hasValidLength() {
+ long sizeLimit = Memory.MAX_BLOCK_SIZE;
+ if (length > 0 && length <= sizeLimit) {
+ return true;
+ }
+ message = "Please enter a valid length between 0 and 0x" + Long.toHexString(sizeLimit);
+ return false;
+ }
+
+ private boolean hasValidStartAddress() {
+ if (startAddr != null) {
+ return true;
+ }
+ message = "Please enter a valid starting address";
+ return false;
+ }
+
+ private boolean hasValidName() {
+ if (blockName == null || blockName.length() == 0) {
+ message = "Please enter a name";
+ return false;
+ }
+ if (nameExists(blockName)) {
+ message = "Block name already exists";
+ return false;
+ }
+ if (!NamingUtilities.isValidName(blockName)) {
+ message = "Block name is invalid";
+ return false;
+ }
+ return true;
}
/**
@@ -258,8 +384,8 @@ class AddBlockModel {
Memory memory = program.getMemory();
MemoryBlock[] blocks = memory.getBlocks();
- for (int i = 0; i < blocks.length; i++) {
- nameIndexer.put(blocks[i].getName());
+ for (MemoryBlock block : blocks) {
+ nameIndexer.put(block.getName());
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java
index 5745da6b37..cc0bd55c68 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java
@@ -16,6 +16,7 @@
package ghidra.app.plugin.core.memory;
import java.util.*;
+import java.util.stream.Collectors;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
@@ -32,6 +33,7 @@ import docking.widgets.table.AbstractSortedTableModel;
import ghidra.framework.model.DomainFile;
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@@ -51,8 +53,9 @@ class MemoryMapModel extends AbstractSortedTableModel {
final static byte VOLATILE = 7;
final static byte BLOCK_TYPE = 8;
final static byte INIT = 9;
- final static byte SOURCE = 10;
- final static byte COMMENT = 11;
+ final static byte BYTE_SOURCE = 10;
+ final static byte SOURCE = 11;
+ final static byte COMMENT = 12;
final static String NAME_COL = "Name";
final static String START_COL = "Start";
@@ -64,6 +67,7 @@ class MemoryMapModel extends AbstractSortedTableModel {
final static String VOLATILE_COL = "Volatile";
final static String BLOCK_TYPE_COL = "Type";
final static String INIT_COL = "Initialized";
+ final static String BYTE_SOURCE_COL = "Byte Source";
final static String SOURCE_COL = "Source";
final static String COMMENT_COL = "Comment";
@@ -74,7 +78,7 @@ class MemoryMapModel extends AbstractSortedTableModel {
private final static String COLUMN_NAMES[] =
{ NAME_COL, START_COL, END_COL, LENGTH_COL, READ_COL, WRITE_COL, EXECUTE_COL, VOLATILE_COL,
- BLOCK_TYPE_COL, INIT_COL, SOURCE_COL, COMMENT_COL };
+ BLOCK_TYPE_COL, INIT_COL, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL };
MemoryMapModel(MemoryMapProvider provider, Program program) {
super(START);
@@ -146,38 +150,10 @@ class MemoryMapModel extends AbstractSortedTableModel {
*/
@Override
public int findColumn(String columnName) {
- if (columnName.equals(NAME_COL)) {
- return NAME;
- }
- if (columnName.equals(START_COL)) {
- return START;
- }
- if (columnName.equals(END_COL)) {
- return END;
- }
- if (columnName.equals(LENGTH_COL)) {
- return LENGTH;
- }
- if (columnName.equals(READ_COL)) {
- return READ;
- }
- if (columnName.equals(WRITE_COL)) {
- return WRITE;
- }
- if (columnName.equals(EXECUTE_COL)) {
- return EXECUTE;
- }
- if (columnName.equals(VOLATILE_COL)) {
- return VOLATILE;
- }
- if (columnName.equals(SOURCE_COL)) {
- return SOURCE;
- }
- if (columnName.equals(COMMENT_COL)) {
- return COMMENT;
- }
- if (columnName.equals(BLOCK_TYPE_COL)) {
- return BLOCK_TYPE;
+ for (int i = 0; i < COLUMN_NAMES.length; i++) {
+ if (COLUMN_NAMES[i].equals(columnName)) {
+ return i;
+ }
}
return 0;
}
@@ -523,10 +499,13 @@ class MemoryMapModel extends AbstractSortedTableModel {
return null;
}
return (block.isInitialized() ? Boolean.TRUE : Boolean.FALSE);
+ case BYTE_SOURCE:
+ return getByteSourceDescription(block.getSourceInfos());
case SOURCE:
if ((block.getType() == MemoryBlockType.BIT_MAPPED) ||
(block.getType() == MemoryBlockType.BYTE_MAPPED)) {
- return ((MappedMemoryBlock) block).getOverlayedMinAddress().toString();
+ SourceInfo info = block.getSourceInfos().get(0);
+ return info.getMappedRange().get().getMinAddress().toString();
}
return block.getSourceName();
case COMMENT:
@@ -543,6 +522,21 @@ class MemoryMapModel extends AbstractSortedTableModel {
return null;
}
+ private String getByteSourceDescription(List sourceInfos) {
+ List limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4);
+
+ //@formatter:off
+ String description = limited
+ .stream()
+ .map(info -> info.getDescription())
+ .collect(Collectors.joining(", "));
+ //@formatter:on
+ if (limited != sourceInfos) {
+ description += "...";
+ }
+ return description;
+ }
+
@Override
public List getModelData() {
return memList;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java
index cd382bd70e..dd349e6be9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java
@@ -26,6 +26,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.AddressLabelInfo;
+import ghidra.util.Msg;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
@@ -187,6 +188,7 @@ public class MemoryBlockUtil {
comment, source, r, w, x, monitor);
}
+
/**
* Creates an initialized memory block using the specified input stream. If the length
* of the block is greater than the maximum size of a memory block (0x40000000), then
@@ -241,7 +243,7 @@ public class MemoryBlockUtil {
long blockLength = 0; // special case first time through loop, don't change start address
while (dataLength > 0) {
start = start.add(blockLength);
- blockLength = Math.min(dataLength, Memory.MAX_INITIALIZED_BLOCK_SIZE);
+ blockLength = Math.min(dataLength, Memory.MAX_BLOCK_SIZE);
String blockName = getBlockName(name, blockNum);
monitor.setMessage(
"Creating memory block \"" + blockName + "\" at 0x" + start + "...");
@@ -301,8 +303,8 @@ public class MemoryBlockUtil {
//remove their ranges from our address set.
//
MemoryBlock[] existingBlocks = memory.getBlocks();
- for (int i = 0; i < existingBlocks.length; ++i) {
- set.deleteRange(existingBlocks[i].getStart(), existingBlocks[i].getEnd());
+ for (MemoryBlock existingBlock : existingBlocks) {
+ set.deleteRange(existingBlock.getStart(), existingBlock.getEnd());
}
appendMessage("WARNING!!\n\tMemory block [" + name +
@@ -605,16 +607,21 @@ public class MemoryBlockUtil {
messages.append(msg);
}
- private void renameFragment(Address blockStart, String blockName) {
+ private void renameFragment(Address address, String name) {
+ adjustFragment(listing, address, name);
+ }
+
+ public static void adjustFragment(Listing listing, Address address, String name) {
String[] treeNames = listing.getTreeNames();
- for (int i = 0; i < treeNames.length; ++i) {
+ for (String treeName : treeNames) {
try {
- ProgramFragment frag = listing.getFragment(treeNames[i], blockStart);
- frag.setName(blockName);
+ ProgramFragment frag = listing.getFragment(treeName, address);
+ frag.setName(name);
}
catch (DuplicateNameException e) {
+ Msg.warn(MemoryBlockUtil.class,
+ "Could not rename fragment to match newly created block because of name conflict");
}
}
}
-
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java
new file mode 100644
index 0000000000..670bfb8816
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java
@@ -0,0 +1,263 @@
+/* ###
+ * 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.app.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import ghidra.app.util.bin.ByteProvider;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.mem.*;
+import ghidra.util.Msg;
+import ghidra.util.exception.DuplicateNameException;
+
+/**
+ * Convenience methods for creating memory blocks.
+ */
+public class MemoryBlockUtils {
+
+ /**
+ * Creates a new uninitialized memory block.
+ * @param program the program in which to create the block.
+ * @param isOverlay if true, the block will be created in a new overlay space for that block
+ * @param name the name of the new block.
+ * @param start the starting address of the new block.
+ * @param length the length of the new block
+ * @param comment the comment text to associate with the new block.
+ * @param source the source of the block (This field is not well defined - currently another comment)
+ * @param r the read permission for the new block.
+ * @param w the write permission for the new block.
+ * @param x the execute permission for the new block.
+ * @param log a {@link MessageLog} for appending error messages
+ * @return the newly created block or null if the operation failed.
+ */
+ public static MemoryBlock createUninitializedBlock(Program program, boolean isOverlay,
+ String name, Address start, long length, String comment, String source, boolean r,
+ boolean w, boolean x, MessageLog log) {
+
+ Memory memory = program.getMemory();
+ try {
+ MemoryBlock block = memory.createUninitializedBlock(name, start, length, isOverlay);
+ setBlockAttributes(block, comment, source, r, w, x);
+ adjustFragment(program, start, name);
+ return block;
+ }
+ catch (LockException e) {
+ log.appendMsg("Failed to create memory block: exclusive lock/checkout required");
+ }
+ catch (Exception e) {
+ log.appendMsg("Failed to create '" + name + "' memory block: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new bit mapped memory block. (A bit mapped block is a block where each byte value
+ * is either 1 or 0 and the value is taken from a bit in a byte at some other address in memory)
+ *
+ * @param program the program in which to create the block.
+ * @param name the name of the new block.
+ * @param start the starting address of the new block.
+ * @param base the address of the region in memory to map to.
+ * @param length the length of the new block
+ * @param comment the comment text to associate with the new block.
+ * @param source the source of the block (This field is not well defined - currently another comment)
+ * @param r the read permission for the new block.
+ * @param w the write permission for the new block.
+ * @param x the execute permission for the new block.
+ * @param log a {@link StringBuffer} for appending error messages
+ * @return the new created block
+ */
+ public static MemoryBlock createBitMappedBlock(Program program, String name,
+ Address start, Address base, int length, String comment, String source, boolean r,
+ boolean w, boolean x, MessageLog log) {
+
+ Memory memory = program.getMemory();
+ try {
+
+ MemoryBlock block = memory.createBitMappedBlock(name, start, base, length);
+
+ setBlockAttributes(block, comment, source, r, w, x);
+ adjustFragment(program, start, name);
+ return block;
+ }
+ catch (LockException e) {
+ log.appendMsg("Failed to create '" + name +
+ "'bit mapped memory block: exclusive lock/checkout required");
+ }
+ catch (Exception e) {
+ log.appendMsg("Failed to create '" + name + "' mapped memory block: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new byte mapped memory block. (A byte mapped block is a block where each byte value
+ * is taken from a byte at some other address in memory)
+ *
+ * @param program the program in which to create the block.
+ * @param name the name of the new block.
+ * @param start the starting address of the new block.
+ * @param base the address of the region in memory to map to.
+ * @param length the length of the new block
+ * @param comment the comment text to associate with the new block.
+ * @param source the source of the block (This field is not well defined - currently another comment)
+ * @param r the read permission for the new block.
+ * @param w the write permission for the new block.
+ * @param x the execute permission for the new block.
+ * @param log a {@link MessageLog} for appending error messages
+ * @return the new created block
+ */
+ public static MemoryBlock createByteMappedBlock(Program program, String name, Address start,
+ Address base, int length, String comment, String source, boolean r, boolean w,
+ boolean x, MessageLog log) {
+
+ Memory memory = program.getMemory();
+ try {
+
+ MemoryBlock block = memory.createByteMappedBlock(name, start, base, length);
+
+ setBlockAttributes(block, comment, source, r, w, x);
+ adjustFragment(program, start, name);
+ return block;
+ }
+ catch (LockException e) {
+ log.appendMsg("Failed to create '" + name +
+ "' byte mapped memory block: exclusive lock/checkout required");
+ }
+ catch (Exception e) {
+ log.appendMsg("Failed to create '" + name + "' mapped memory block: " + e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Creates a new initialized block in memory using the bytes from a {@link FileBytes} object.
+ *
+ * @param program the program in which to create the block.
+ * @param isOverlay if true, the block will be created in a new overlay space for that block
+ * @param name the name of the new block.
+ * @param start the starting address of the new block.
+ * @param fileBytes the {@link FileBytes} object that supplies the bytes for this block.
+ * @param offset the offset into the {@link FileBytes} object where the bytes for this block reside.
+ * @param length the length of the new block
+ * @param comment the comment text to associate with the new block.
+ * @param source the source of the block (This field is not well defined - currently another comment)
+ * @param r the read permission for the new block.
+ * @param w the write permission for the new block.
+ * @param x the execute permission for the new block.
+ * @param log a {@link MessageLog} for appending error messages
+ * @return the new created block
+ * @throws AddressOverflowException if the address
+ */
+
+ public static MemoryBlock createInitializedBlock(Program program, boolean isOverlay,
+ String name, Address start, FileBytes fileBytes, long offset, long length,
+ String comment, String source, boolean r, boolean w, boolean x, MessageLog log)
+ throws AddressOverflowException {
+
+ if (!program.hasExclusiveAccess()) {
+ log.appendMsg("Failed to create memory block: exclusive access/checkout required");
+ return null;
+ }
+ MemoryBlock block;
+ try {
+ block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset,
+ length, isOverlay);
+ }
+ catch (MemoryConflictException e) {
+ try {
+ block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset,
+ length, true);
+ }
+ catch (LockException | DuplicateNameException | MemoryConflictException e1) {
+ throw new RuntimeException(e);
+ }
+ }
+ catch (LockException | DuplicateNameException e) {
+ throw new RuntimeException(e);
+ }
+
+ setBlockAttributes(block, comment, source, r, w, x);
+ adjustFragment(program, start, name);
+ return block;
+ }
+
+ /**
+ * Adjusts the name of the fragment at the given address to the given name.
+ * @param program the program whose fragment is to be renamed.
+ * @param address the address of the fragment to be renamed.
+ * @param name the new name for the fragment.
+ */
+ public static void adjustFragment(Program program, Address address, String name) {
+ Listing listing = program.getListing();
+ String[] treeNames = listing.getTreeNames();
+ for (String treeName : treeNames) {
+ try {
+ ProgramFragment frag = listing.getFragment(treeName, address);
+ frag.setName(name);
+ }
+ catch (DuplicateNameException e) {
+ Msg.warn(MemoryBlockUtil.class,
+ "Could not rename fragment to match newly created block because of name conflict");
+ }
+ }
+ }
+
+ /**
+ * Creates a new {@link FileBytes} object using all the bytes from a {@link ByteProvider}
+ * @param program the program in which to create a new FileBytes object
+ * @param provider the ByteProvider from which to get the bytes.
+ * @return the newly created FileBytes object.
+ * @throws IOException if an IOException occurred.
+ */
+ public static FileBytes createFileBytes(Program program, ByteProvider provider)
+ throws IOException {
+ return createFileBytes(program, provider, 0, provider.length());
+ }
+
+ /**
+ * Creates a new {@link FileBytes} object using a portion of the bytes from a {@link ByteProvider}
+ * @param program the program in which to create a new FileBytes object
+ * @param provider the ByteProvider from which to get the bytes.
+ * @param offset the offset into the ByteProvider from which to start loading bytes.
+ * @param length the number of bytes to load
+ * @return the newly created FileBytes object.
+ * @throws IOException if an IOException occurred.
+ */
+ public static FileBytes createFileBytes(Program program, ByteProvider provider, long offset,
+ long length) throws IOException {
+ Memory memory = program.getMemory();
+ try (InputStream fis = provider.getInputStream(offset)) {
+ return memory.createFileBytes(provider.getName(), offset, length, fis);
+ }
+ }
+
+ private static void setBlockAttributes(MemoryBlock block, String comment, String source,
+ boolean r,
+ boolean w, boolean x) {
+ block.setComment(comment);
+ block.setSourceName(source);
+ block.setRead(r);
+ block.setWrite(w);
+ block.setExecute(x);
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java
index 2dd06c60d3..068a50292f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java
@@ -16,7 +16,6 @@
package ghidra.app.util.opinion;
import java.io.IOException;
-import java.io.InputStream;
import java.util.*;
import ghidra.app.util.*;
@@ -25,10 +24,12 @@ import ghidra.app.util.importer.MemoryConflictHandler;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
+import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
+import ghidra.program.model.mem.*;
import ghidra.util.Msg;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
@@ -265,8 +266,7 @@ public class BinaryLoader extends AbstractProgramLoader {
@Override
protected List loadProgram(ByteProvider provider, String programName,
DomainFolder programFolder, LoadSpec loadSpec, List options, MessageLog log,
- Object consumer, TaskMonitor monitor)
- throws IOException, CancelledException {
+ Object consumer, TaskMonitor monitor) throws IOException, CancelledException {
LanguageCompilerSpecPair pair = loadSpec.getLanguageCompilerSpec();
Language importerLanguage = getLanguageService().getLanguage(pair.languageID);
CompilerSpec importerCompilerSpec =
@@ -274,8 +274,8 @@ public class BinaryLoader extends AbstractProgramLoader {
Address baseAddr =
importerLanguage.getAddressFactory().getDefaultAddressSpace().getAddress(0);
- Program prog = createProgram(provider, programName, baseAddr, getName(),
- importerLanguage, importerCompilerSpec, consumer);
+ Program prog = createProgram(provider, programName, baseAddr, getName(), importerLanguage,
+ importerCompilerSpec, consumer);
boolean success = false;
try {
success = loadInto(provider, loadSpec, options, log, prog, monitor,
@@ -314,10 +314,8 @@ public class BinaryLoader extends AbstractProgramLoader {
length = clipToMemorySpace(length, log, prog);
- MemoryBlockUtil mbu = new MemoryBlockUtil(prog, handler);
-
- boolean success = false;
- try (InputStream fis = provider.getInputStream(fileOffset)) {
+ FileBytes fileBytes = MemoryBlockUtils.createFileBytes(prog, provider, fileOffset, length);
+ try {
AddressSpace space = prog.getAddressFactory().getDefaultAddressSpace();
if (baseAddr == null) {
baseAddr = space.getAddress(0);
@@ -325,20 +323,19 @@ public class BinaryLoader extends AbstractProgramLoader {
if (blockName == null || blockName.length() == 0) {
blockName = generateBlockName(prog, isOverlay, baseAddr.getAddressSpace());
}
- if (isOverlay) {
- mbu.createOverlayBlock(blockName, baseAddr, fis, length, "",
- provider.getAbsolutePath(), true, false, false, monitor);
+ try {
+ MemoryBlock block = prog.getMemory().createInitializedBlock(blockName, baseAddr,
+ fileBytes, 0, length, isOverlay);
+ block.setRead(true);
+ block.setWrite(isOverlay ? false : true);
+ block.setExecute(isOverlay ? false : true);
+ block.setSourceName("Binary Loader");
+ MemoryBlockUtil.adjustFragment(prog.getListing(), block.getStart(), blockName);
}
- else {
- mbu.createInitializedBlock(blockName, baseAddr, fis, length,
- "fileOffset=" + fileOffset + ", length=" + length, provider.getAbsolutePath(),
- true, true, true, monitor);
- }
- success = true;
- String msg = mbu.getMessages();
- if (msg.length() > 0) {
- log.appendMsg(msg);
+ catch (LockException | MemoryConflictException e) {
+ Msg.error(this, "Unexpected exception creating memory block", e);
}
+ return true;
}
catch (AddressOverflowException e) {
throw new IllegalArgumentException("Invalid address range specified: start:" +
@@ -347,10 +344,6 @@ public class BinaryLoader extends AbstractProgramLoader {
catch (DuplicateNameException e) {
throw new IllegalArgumentException("Duplicate block name specified: " + blockName);
}
- finally {
- mbu.dispose();
- }
- return success;
}
private long clipToMemorySpace(long length, MessageLog log, Program program) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java
index c1cb25076a..3681cad999 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java
@@ -2879,9 +2879,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
throw new IOException(msg);
}
- int maxSectionSizeGBytes = initialized ? Memory.MAX_INITIALIZED_BLOCK_SIZE_GB
- : Memory.MAX_UNINITIALIZED_BLOCK_SIZE_GB;
- long maxSectionSizeBytes = (long) maxSectionSizeGBytes << Memory.GBYTE_SHIFT_FACTOR;
+ int maxSectionSizeGBytes = Memory.MAX_BLOCK_SIZE_GB;
+ long maxSectionSizeBytes = Memory.MAX_BLOCK_SIZE;
if (dataLength < 0 || dataLength > maxSectionSizeBytes) {
float sizeGB = (float) dataLength / (float) Memory.GBYTE;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java
index 614bd40594..037004432a 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java
@@ -24,6 +24,7 @@ import org.xml.sax.SAXParseException;
import ghidra.app.util.MemoryBlockUtil;
import ghidra.app.util.importer.MemoryConflictHandler;
import ghidra.app.util.importer.MessageLog;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@@ -294,14 +295,18 @@ class MemoryMapXmlMgr {
writer.startElement("MEMORY_SECTION", attrs);
if (block.getType() == MemoryBlockType.BIT_MAPPED) {
+ // bit mapped blocks can only have one sub-block
+ SourceInfo info = block.getSourceInfos().get(0);
attrs.addAttribute("SOURCE_ADDRESS",
- ((MappedMemoryBlock) block).getOverlayedMinAddress().toString());
+ info.getMappedRange().get().getMinAddress().toString());
writer.startElement("BIT_MAPPED", attrs);
writer.endElement("BIT_MAPPED");
}
else if (block.getType() == MemoryBlockType.BYTE_MAPPED) {
+ // byte mapped blocks can only have one sub-block
+ SourceInfo info = block.getSourceInfos().get(0);
attrs.addAttribute("SOURCE_ADDRESS",
- ((MappedMemoryBlock) block).getOverlayedMinAddress().toString());
+ info.getMappedRange().get().getMinAddress().toString());
writer.startElement("BYTE_MAPPED", attrs);
writer.endElement("BYTE_MAPPED");
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java
index 04bd5826ec..689e1a616e 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java
@@ -22,6 +22,7 @@ import javax.swing.event.ChangeListener;
import org.junit.*;
+import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
@@ -92,10 +93,6 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setBlockType(MemoryBlockType.DEFAULT);
assertTrue(model.isValidInfo());
- assertTrue(model.isReadEnabled());
- assertTrue(model.isWriteEnabled());
- assertTrue(model.isExecuteEnabled());
- assertTrue(model.isVolatileEnabled());
model.setInitialValue(0xa);
assertTrue(model.isValidInfo());
@@ -130,10 +127,6 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setBlockType(MemoryBlockType.BIT_MAPPED);
assertTrue(!model.isValidInfo());
- assertTrue(model.isReadEnabled());
- assertTrue(model.isWriteEnabled());
- assertTrue(model.isExecuteEnabled());
- assertTrue(model.isVolatileEnabled());
model.setBaseAddress(getAddr(0x2000));
assertTrue(model.isValidInfo());
@@ -152,10 +145,6 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setBlockType(MemoryBlockType.OVERLAY);
assertTrue(model.isValidInfo());
- assertTrue(model.isReadEnabled());
- assertTrue(model.isWriteEnabled());
- assertTrue(model.isExecuteEnabled());
- assertTrue(model.isVolatileEnabled());
model.setBaseAddress(getAddr(0x2000));
assertTrue(model.isValidInfo());
@@ -174,9 +163,13 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setStartAddress(getAddr(0x100));
model.setLength(100);
model.setBlockType(MemoryBlockType.DEFAULT);
- model.setIsInitialized(true);
+ model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
model.setInitialValue(0xa);
- assertTrue(model.execute("Test", true, true, true, false));
+ model.setComment("Test");
+ model.setRead(true);
+ model.setWrite(true);
+ model.setExecute(true);
+ assertTrue(model.execute());
MemoryBlock block = program.getMemory().getBlock(getAddr(0x100));
assertNotNull(block);
assertEquals((byte) 0xa, block.getByte(getAddr(0x100)));
@@ -189,9 +182,9 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setStartAddress(getAddr(0x100));
model.setLength(100);
model.setBlockType(MemoryBlockType.OVERLAY);
- model.setIsInitialized(true);
+ model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
model.setInitialValue(0xa);
- assertTrue(model.execute("Test", true, true, true, false));
+ assertTrue(model.execute());
MemoryBlock block = null;
AddressSpace[] spaces = program.getAddressFactory().getAddressSpaces();
AddressSpace ovSpace = null;
@@ -214,9 +207,9 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setStartAddress(getAddr(0x01001000));
model.setLength(100);
model.setBlockType(MemoryBlockType.OVERLAY);
- model.setIsInitialized(true);
+ model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
model.setInitialValue(0xa);
- assertTrue(model.execute("Test", true, true, true, false));
+ assertTrue(model.execute());
MemoryBlock block = null;
AddressSpace[] spaces = program.getAddressFactory().getAddressSpaces();
AddressSpace ovSpace = null;
@@ -238,10 +231,10 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setStartAddress(getAddr(0x100));
model.setLength(100);
model.setBlockType(MemoryBlockType.BIT_MAPPED);
- assertTrue(!model.getInitializedState());
+ assertEquals(InitializedType.UNITIALIZED, model.getInitializedType());
model.setBaseAddress(getAddr(0x2000));
- assertTrue(model.execute("Test", true, true, true, false));
+ assertTrue(model.execute());
MemoryBlock block = program.getMemory().getBlock(getAddr(0x100));
assertNotNull(block);
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
@@ -253,10 +246,10 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setStartAddress(getAddr(0x100));
model.setLength(100);
model.setBlockType(MemoryBlockType.BYTE_MAPPED);
- assertTrue(!model.getInitializedState());
+ assertEquals(InitializedType.UNITIALIZED, model.getInitializedType());
model.setBaseAddress(getAddr(0x2000));
- assertTrue(model.execute("Test", true, true, true, false));
+ assertTrue(model.execute());
MemoryBlock block = program.getMemory().getBlock(getAddr(0x100));
assertNotNull(block);
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
@@ -277,7 +270,7 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setLength(100);
model.setBlockType(MemoryBlockType.DEFAULT);
model.setInitialValue(0xa);
- assertTrue(!model.execute("Test", true, true, true, false));
+ assertFalse(model.execute());
}
@Test
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java
index a6f1c6bc5f..1b094fda95 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapPluginTest.java
@@ -28,8 +28,7 @@ import javax.swing.table.TableModel;
import org.junit.*;
import docking.action.DockingActionIf;
-import ghidra.app.cmd.memory.AddMemoryBlockCmd;
-import ghidra.app.cmd.memory.DeleteBlockCmd;
+import ghidra.app.cmd.memory.*;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.plugin.core.navigation.NavigationHistoryPlugin;
@@ -37,7 +36,8 @@ import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.*;
+import ghidra.program.model.mem.Memory;
+import ghidra.program.model.mem.MemoryBlock;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.task.TaskMonitorAdapter;
@@ -199,8 +199,8 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testBlockAdded() {
MemoryBlock[] blocks = memory.getBlocks();
- tool.execute(new AddMemoryBlockCmd(".test", "comments", "test", getAddr(0), 0x100, true,
- true, true, false, (byte) 1, MemoryBlockType.DEFAULT, null, true), program);
+ tool.execute(new AddInitializedMemoryBlockCmd(".test", "comments", "test", getAddr(0),
+ 0x100, true, true, true, false, (byte) 1, false), program);
JTable table = provider.getTable();
assertEquals(".test", table.getModel().getValueAt(0, MemoryMapModel.NAME));
@@ -241,8 +241,8 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testBlockReplaced() throws Exception {
MemoryBlock[] blocks = memory.getBlocks();
- tool.execute(new AddMemoryBlockCmd(".test", "comments", "test", getAddr(0), 0x100, true,
- true, true, false, (byte) 1, MemoryBlockType.DEFAULT, null, false), program);
+ tool.execute(new AddUninitializedMemoryBlockCmd(".test", "comments", "test", getAddr(0),
+ 0x100, true, true, true, false, false), program);
JTable table = provider.getTable();
assertEquals(blocks.length + 1, table.getModel().getRowCount());
assertEquals(".test", table.getModel().getValueAt(0, MemoryMapModel.NAME));
@@ -260,8 +260,8 @@ public class MemoryMapPluginTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testBlockSplit() throws Exception {
MemoryBlock[] blocks = memory.getBlocks();
- tool.execute(new AddMemoryBlockCmd(".test", "comments", "test", getAddr(0), 0x100, true,
- true, true, false, (byte) 1, MemoryBlockType.DEFAULT, null, true), program);
+ tool.execute(new AddInitializedMemoryBlockCmd(".test", "comments", "test", getAddr(0),
+ 0x100, true, true, true, false, (byte) 1, false), program);
JTable table = provider.getTable();
assertEquals(blocks.length + 1, table.getModel().getRowCount());
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java
index 69f95b362f..fcbd105873 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java
@@ -35,6 +35,7 @@ import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.plugin.core.navigation.NavigationHistoryPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
@@ -556,11 +557,13 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
for (int i = 0; i < sources.length; i++) {
boolean doAssert = true;
- for (MemoryBlock element : blocks) {
- if (element.getSourceName().equals(sources[i]) &&
- element.getType() == MemoryBlockType.BIT_MAPPED) {
- assertEquals(((MappedMemoryBlock) element).getOverlayedMinAddress().toString(),
- model.getValueAt(i, MemoryMapModel.SOURCE));
+ for (MemoryBlock memBlock : blocks) {
+ if (memBlock.getSourceName().equals(sources[i]) &&
+ memBlock.getType() == MemoryBlockType.BIT_MAPPED) {
+ SourceInfo info = memBlock.getSourceInfos().get(0);
+ Address addr = info.getMappedRange().get().getMinAddress();
+
+ assertEquals(addr.toString(), model.getValueAt(i, MemoryMapModel.SOURCE));
doAssert = false;
break;
}
@@ -599,11 +602,12 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
for (int i = 0; i < sources.length; i++) {
int idx = sources.length - 1 - i;
boolean doAssert = true;
- for (MemoryBlock element : blocks) {
- if (element.getSourceName().equals(sources[idx]) &&
- element.getType() == MemoryBlockType.BIT_MAPPED) {
- assertEquals(((MappedMemoryBlock) element).getOverlayedMinAddress().toString(),
- model.getValueAt(i, MemoryMapModel.SOURCE));
+ for (MemoryBlock memBlock : blocks) {
+ if (memBlock.getSourceName().equals(sources[idx]) &&
+ memBlock.getType() == MemoryBlockType.BIT_MAPPED) {
+ SourceInfo info = memBlock.getSourceInfos().get(0);
+ Address addr = info.getMappedRange().get().getMinAddress();
+ assertEquals(addr.toString(), model.getValueAt(i, MemoryMapModel.SOURCE));
doAssert = false;
break;
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java
index 8209a275ad..5b8956ba35 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java
@@ -128,17 +128,14 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
JCheckBox readCB = (JCheckBox) findComponentByName(d.getComponent(), "Read");
assertNotNull(readCB);
- assertTrue(readCB.isEnabled());
assertTrue(readCB.isSelected());
JCheckBox writeCB = (JCheckBox) findComponentByName(d.getComponent(), "Write");
assertNotNull(writeCB);
- assertTrue(writeCB.isEnabled());
assertTrue(writeCB.isSelected());
JCheckBox executeCB = (JCheckBox) findComponentByName(d.getComponent(), "Execute");
assertNotNull(executeCB);
- assertTrue(executeCB.isEnabled());
assertTrue(!executeCB.isSelected());
RegisterField initialValue =
@@ -192,7 +189,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
lengthField.setText("0x100");
commentField.setText("this is a block test");
initialValue.setText("0xa");
- executeCB.setSelected(true);
+ pressButton(executeCB);
});
int x = 1;
@@ -220,7 +217,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT));
- assertEquals("- none -", model.getValueAt(0, MemoryMapModel.SOURCE));
+ assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
assertEquals("this is a block test", model.getValueAt(0, MemoryMapModel.COMMENT));
assertEquals(0xa, memory.getByte(getAddr(0)));
@@ -269,7 +266,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
lengthField.setText("0x100");
commentField.setText("this is a block test");
initialValue.setText("0xb");
- executeCB.setSelected(true);
+ pressButton(executeCB);
});
int x = 1;
@@ -297,7 +294,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT));
- assertEquals("- none -", model.getValueAt(0, MemoryMapModel.SOURCE));
+ assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
assertEquals("this is a block test", model.getValueAt(0, MemoryMapModel.COMMENT));
assertEquals(0xb, memory.getByte(getAddr(0x200)));
@@ -341,15 +338,12 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
lengthField.setText("0x100");
commentField.setText("this is a block test");
initialValue.setText("0xb");
- executeCB.setSelected(true);
+ pressButton(executeCB);
});
- assertTrue(okButton.isEnabled());
-
- SwingUtilities.invokeLater(() -> okButton.getActionListeners()[0].actionPerformed(null));
- waitForPostedSwingRunnables();
+ assertFalse(okButton.isEnabled());
String msg = findLabelStr(d.getComponent(), "statusLabel");
- assertTrue(msg.indexOf("already exists in memory") > 0);
+ assertTrue(msg.startsWith("Block address conflict"));
assertTrue(!okButton.isEnabled());
runSwing(() -> d.close());
}
@@ -445,7 +439,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertTrue(uninitializedRB.isSelected()); // default choice
final RegisterField initialValue =
(RegisterField) findComponentByName(d.getComponent(), "Initial Value");
- assertFalse(initialValue.isEnabled());
+
final JButton okButton = findButton(d.getComponent(), "OK");
SwingUtilities.invokeAndWait(() -> {
@@ -456,7 +450,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertTrue(!okButton.isEnabled());
String msg = findLabelStr(d.getComponent(), "statusLabel");
- assertEquals("Please enter a valid length > 0 and <= 0x300000000", msg);
+ assertEquals("Please enter a valid length between 0 and 0x400000000", msg);
assertTrue(!okButton.isEnabled());
runSwing(() -> d.close());
}
@@ -496,7 +490,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertTrue(!okButton.isEnabled());
String msg = findLabelStr(d.getComponent(), "statusLabel");
- assertEquals("Please enter a valid length > 0 and <= 0x40000000", msg);
+ assertEquals("Please enter a valid length between 0 and 0x400000000", msg);
assertTrue(!okButton.isEnabled());
runSwing(() -> d.close());
}
@@ -586,7 +580,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.INIT));
- assertEquals("- none -", model.getValueAt(0, MemoryMapModel.SOURCE));
+ assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
assertEquals("this is an uninitialized block test",
model.getValueAt(0, MemoryMapModel.COMMENT));
@@ -634,7 +628,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
lengthField.setText("0x100");
commentField.setText("this is a block test");
initialValue.setText("0xa");
- executeCB.setSelected(true);
+ pressButton(executeCB);
});
int x = 1;
@@ -642,9 +636,9 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
clickMouse(initializedRB, 1, x, y, 1, 0);
assertTrue(okButton.isEnabled());
- assertTrue(readCB.isEnabled());
- assertTrue(writeCB.isEnabled());
- assertTrue(executeCB.isEnabled());
+ assertTrue(readCB.isSelected());
+ assertTrue(writeCB.isSelected());
+ assertTrue(executeCB.isSelected());
SwingUtilities.invokeAndWait(() -> okButton.getActionListeners()[0].actionPerformed(null));
program.flushEvents();
@@ -675,7 +669,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(MemoryBlockType.OVERLAY.toString(),
model.getValueAt(row, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.INIT));
- assertEquals("- none -", model.getValueAt(row, MemoryMapModel.SOURCE));
+ assertEquals("", model.getValueAt(row, MemoryMapModel.SOURCE));
assertEquals("this is a block test", model.getValueAt(row, MemoryMapModel.COMMENT));
assertEquals(0xa, memory.getByte(block.getStart()));
@@ -713,7 +707,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
nameField.setText(".test");
lengthField.setText("0x100");
commentField.setText("this is a block test");
- executeCB.setSelected(true);
+ pressButton(executeCB);
uninitRB.setSelected(true);
uninitRB.getActionListeners()[0].actionPerformed(null);
});
@@ -752,7 +746,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(MemoryBlockType.OVERLAY.toString(),
model.getValueAt(row, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.FALSE, model.getValueAt(row, MemoryMapModel.INIT));
- assertEquals("- none -", model.getValueAt(row, MemoryMapModel.SOURCE));
+ assertEquals("", model.getValueAt(row, MemoryMapModel.SOURCE));
assertEquals("this is a block test", model.getValueAt(row, MemoryMapModel.COMMENT));
try {
@@ -863,7 +857,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
RegisterField initialValue =
(RegisterField) findComponentByName(d.getComponent(), "Initial Value");
assertNotNull(initialValue);
- assertTrue(initialValue.isShowing());
+ assertTrue(!initialValue.isShowing());
final AddressInput addrField =
(AddressInput) findComponentByName(d.getComponent(), "Source Addr");
assertNotNull(addrField);
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressMapDB64BitTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressMapDB64BitTest.java
index 1811932aad..aba5d97cd2 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressMapDB64BitTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressMapDB64BitTest.java
@@ -15,7 +15,7 @@
*/
package ghidra.program.database.map;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import java.util.List;
@@ -30,9 +30,9 @@ import ghidra.program.model.mem.MemoryConflictException;
import ghidra.util.task.TaskMonitorAdapter;
public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
-
+
private static final LanguageID LANGUAGE_64BIT = new LanguageID("sparc:BE:64:default");
-
+
/**
* Constructor for AddressMapTest.
* @param arg0
@@ -40,9 +40,9 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
public AddressMapDB64BitTest() {
super();
}
-
+
@Override
- protected Program createTestProgram() throws Exception {
+ protected Program createTestProgram() throws Exception {
Program p = createProgram(LANGUAGE_64BIT);
boolean success = false;
int txId = p.startTransaction("Define blocks");
@@ -52,32 +52,36 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
Memory mem = p.getMemory();
// Block1 is located within first chunk following image base (base #0 allocated)
- mem.createUninitializedBlock("Block1", space.getAddress(0x2000000000L), 0x100000, false);
-
+ mem.createUninitializedBlock("Block1", space.getAddress(0x2000000000L), 0x100000,
+ false);
+
try {
- mem.createUninitializedBlock("Block2", space.getAddress(0xfffffd000L), 0x4000, false);
+ mem.createUninitializedBlock("Block2", space.getAddress(0xfffffd000L), 0x4000,
+ false);
Assert.fail("Expected MemoryConflictException");
}
catch (MemoryConflictException e) {
// Expected
}
-
+
try {
- mem.createUninitializedBlock("Block2", space.getAddress(0xfffffffffff00000L), 0x100001, false);
+ mem.createUninitializedBlock("Block2", space.getAddress(0xfffffffffff00000L),
+ 0x100001, false);
Assert.fail("Expected AddressOverflowException");
}
catch (AddressOverflowException e) {
// Expected
}
-
+
// Block2 is at absolute end of space (base #1 allocated)
- mem.createUninitializedBlock("Block2", space.getAddress(0xfffffffffff00000L), 0x100000, false);
-
+ mem.createUninitializedBlock("Block2", space.getAddress(0xfffffffffff00000L), 0x100000,
+ false);
+
// Block3 spans two (2) memory chunks and spans transition between positive and negative offset values
// (base #2(end of block) and #3(start of block) allocated
- mem.createInitializedBlock("Block3", space.getAddress(0x7ffffffffff00000L), 0x200000, (byte)0,
- TaskMonitorAdapter.DUMMY_MONITOR, false);
-
+ mem.createInitializedBlock("Block3", space.getAddress(0x7ffffffffff00000L), 0x200000,
+ (byte) 0, TaskMonitorAdapter.DUMMY_MONITOR, false);
+
success = true;
}
finally {
@@ -88,39 +92,43 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
}
return p;
}
-
-@Test
- public void testKeyRanges() {
-
+
+ @Test
+ public void testKeyRanges() {
+
List keyRanges = addrMap.getKeyRanges(addr(0), addr(0xffffffffffffffffL), false);
-
+
assertEquals(4, keyRanges.size());
KeyRange kr = keyRanges.get(0);
- System.out.println(addrMap.decodeAddress(kr.minKey) +"->"+ addrMap.decodeAddress(kr.maxKey));
+ System.out.println(
+ addrMap.decodeAddress(kr.minKey) + "->" + addrMap.decodeAddress(kr.maxKey));
assertEquals(addr(0x2000000000L), addrMap.decodeAddress(kr.minKey));
assertEquals(addr(0x20ffffffffL), addrMap.decodeAddress(kr.maxKey));
kr = keyRanges.get(1);
- System.out.println(addrMap.decodeAddress(kr.minKey) +"->"+ addrMap.decodeAddress(kr.maxKey));
+ System.out.println(
+ addrMap.decodeAddress(kr.minKey) + "->" + addrMap.decodeAddress(kr.maxKey));
assertEquals(addr(0x7fffffff00000000L), addrMap.decodeAddress(kr.minKey));
assertEquals(addr(0x7fffffffffffffffL), addrMap.decodeAddress(kr.maxKey));
kr = keyRanges.get(2);
- System.out.println(addrMap.decodeAddress(kr.minKey) +"->"+ addrMap.decodeAddress(kr.maxKey));
+ System.out.println(
+ addrMap.decodeAddress(kr.minKey) + "->" + addrMap.decodeAddress(kr.maxKey));
assertEquals(addr(0x8000000000000000L), addrMap.decodeAddress(kr.minKey));
assertEquals(addr(0x80000000ffffffffL), addrMap.decodeAddress(kr.maxKey));
kr = keyRanges.get(3);
- System.out.println(addrMap.decodeAddress(kr.minKey) +"->"+ addrMap.decodeAddress(kr.maxKey));
+ System.out.println(
+ addrMap.decodeAddress(kr.minKey) + "->" + addrMap.decodeAddress(kr.maxKey));
assertEquals(addr(0x0ffffffff00000000L), addrMap.decodeAddress(kr.minKey));
assertEquals(addr(0x0ffffffffffffffffL), addrMap.decodeAddress(kr.maxKey));
}
-
-@Test
- public void testRelocatableAddress() {
-
+
+ @Test
+ public void testRelocatableAddress() {
+
Address addr = addr(0x1000000000L);
assertEquals(AddressMap.INVALID_ADDRESS_KEY, addrMap.getKey(addr, false));
-
+
int txId = program.startTransaction("New address region");
try {
// base #5 allocated
@@ -131,20 +139,20 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
finally {
program.endTransaction(txId, true);
}
-
+
addr = addr(0x2000001000L);
long key = addrMap.getKey(addr, false);
assertEquals(0x2000000000000000L + 0x1000, key);
assertEquals(addr, addrMap.decodeAddress(key));
-
+
addr = addr(0x7ffffffffff00000L);
key = addrMap.getKey(addr, false);
- assertEquals(0x2000000300000000L + 0x0fff00000L, key);
+ assertEquals(0x2000000200000000L + 0x0fff00000L, key);
assertEquals(addr, addrMap.decodeAddress(key));
-
+
addr = addr(0x8ffffffffff00000L);
assertEquals(AddressMap.INVALID_ADDRESS_KEY, addrMap.getKey(addr, false));
-
+
txId = program.startTransaction("New address region");
try {
key = addrMap.getKey(addr, true);
@@ -155,18 +163,18 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
program.endTransaction(txId, true);
}
}
-
-@Test
- public void testAbsoluteAddress() {
-
+
+ @Test
+ public void testAbsoluteAddress() {
+
Address addr = addr(0x1000000000L);
long key = addrMap.getAbsoluteEncoding(addr, false);
assertEquals(0x1000000000000000L, key);
assertEquals(addr, addrMap.decodeAddress(key));
-
+
addr = addr(0x2000001000L);
assertEquals(AddressMap.INVALID_ADDRESS_KEY, addrMap.getAbsoluteEncoding(addr, false));
-
+
int txId = program.startTransaction("New address region");
try {
key = addrMap.getAbsoluteEncoding(addr, true);
@@ -176,15 +184,15 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
finally {
program.endTransaction(txId, true);
}
-
+
addr = addr(0x7fffffeffff00000L);
key = addrMap.getAbsoluteEncoding(addr, false);
- assertEquals(0x1000000300000000L + 0x0fff00000L, key);
+ assertEquals(0x1000000200000000L + 0x0fff00000L, key);
assertEquals(addr, addrMap.decodeAddress(key));
-
+
addr = addr(0x8ffffffffff00000L);
assertEquals(AddressMap.INVALID_ADDRESS_KEY, addrMap.getAbsoluteEncoding(addr, false));
-
+
txId = program.startTransaction("New address region");
try {
key = addrMap.getAbsoluteEncoding(addr, true);
@@ -195,5 +203,5 @@ public class AddressMapDB64BitTest extends AbstractAddressMapDBTestClass {
program.endTransaction(txId, true);
}
}
-
+
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/InitializedMemoryBlock.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/InitializedMemoryBlock.java
deleted file mode 100644
index db7983ded4..0000000000
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/InitializedMemoryBlock.java
+++ /dev/null
@@ -1,424 +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.mem;
-
-import java.io.*;
-
-import ghidra.program.model.address.Address;
-import ghidra.program.model.address.AddressOverflowException;
-import ghidra.program.model.mem.*;
-
-/**
- * Default implementation for a MemoryBlock containing initialized data.
- */
-class InitializedMemoryBlock implements MemoryBlock {
-
- private final static long serialVersionUID = 1;
-
- private String name;
- protected byte[] data;
- private Address start;
- private Address end;
- private String comment;
- private String sourceName;
- private int permissions = READ | WRITE;
- private long sourceOffset;
-
- /**
- * Constructor for InitializedMemoryBlock.
- * @param name name of the block
- * @param start starting address of the block
- * @param data bytes that make up the block
- * @throws AddressOverflowException if the block size extends beyond the end
- * of the address space
- */
- InitializedMemoryBlock(String name, Address start, byte[] data)
- throws AddressOverflowException {
-
- if ((data == null) || (data.length == 0)) {
- throw new IllegalArgumentException("Missing or zero length data byte array");
- }
- this.name = name;
- this.start = start;
- this.data = data;
- end = start.addNoWrap(data.length - 1);
- }
-
- /**
- * @see MemoryBlock#contains(Address)
- */
- @Override
- public boolean contains(Address addr) {
- try {
- long diff = addr.subtract(start);
- return diff >= 0 && diff < data.length;
-
- }
- catch (IllegalArgumentException e) {
- }
-
- return false;
- }
-
- /**
- * @see MemoryBlock#getStart()
- */
- @Override
- public Address getStart() {
- return start;
- }
-
- /**
- * @see MemoryBlock#getEnd()
- */
- @Override
- public Address getEnd() {
- return end;
- }
-
- /**
- * @see MemoryBlock#getSize()
- */
- @Override
- public long getSize() {
- return data.length;
- }
-
- /**
- * @see MemoryBlock#getName()
- */
- @Override
- public String getName() {
- return name;
- }
-
- /**
- * @see MemoryBlock#setName(String)
- */
- @Override
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * @see MemoryBlock#getComment()
- */
- @Override
- public String getComment() {
- return comment;
- }
-
- /**
- * @see MemoryBlock#setComment(String)
- */
- @Override
- public void setComment(String comment) {
- this.comment = comment;
- }
-
- /**
- * @see MemoryBlock#getSourceName()
- */
- @Override
- public String getSourceName() {
- return sourceName;
- }
-
- /**
- * @see MemoryBlock#setSourceName(String)
- */
- @Override
- public void setSourceName(String sourceName) {
- this.sourceName = sourceName;
- }
-
- /**
- * @see MemoryBlock#getByte(Address)
- */
- @Override
- public byte getByte(Address addr) throws MemoryAccessException {
- int index = getIndex(addr);
- return data[index];
- }
-
- /**
- * @see MemoryBlock#getBytes(Address, byte[])
- */
- @Override
- public int getBytes(Address addr, byte[] b) throws MemoryAccessException {
- int index = getIndex(addr);
- int len = b.length;
- if (index + len > data.length) {
- len = data.length - index;
- }
-
- System.arraycopy(data, index, b, 0, len);
- return len;
- }
-
- /**
- * @see MemoryBlock#getBytes(Address, byte[], int, int)
- */
- @Override
- public int getBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
- int index = getIndex(addr);
- int length = len;
- if (index + length > data.length) {
- length = data.length - index;
- }
- System.arraycopy(data, index, b, off, length);
- return length;
- }
-
- /**
- * @see MemoryBlock#putByte(Address, byte)
- */
- @Override
- public void putByte(Address addr, byte b) throws MemoryAccessException {
- changeData(addr, new byte[] { b });
- }
-
- /**
- * @see MemoryBlock#putBytes(Address, byte[])
- */
- @Override
- public int putBytes(Address addr, byte[] b) throws MemoryAccessException {
- return changeData(addr, b);
- }
-
- /**
- * @see MemoryBlock#putBytes(Address, byte[], int, int)
- */
- @Override
- public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
- int index = getIndex(addr);
- int length = len;
- if (index + length > data.length) {
- length = data.length - index;
- }
-
- byte[] oldValue = new byte[length];
- System.arraycopy(data, index, oldValue, 0, oldValue.length);
-
- byte[] newValue = new byte[length];
- System.arraycopy(b, off, newValue, 0, length);
-
- System.arraycopy(b, off, data, index, length);
-
- return length;
- }
-
- /**
- * Compare the start address of this block to obj's start address if
- * obj is a MemoryBlock.
- * @see java.lang.Comparable#compareTo(Object)
- */
- @Override
- public int compareTo(MemoryBlock block) {
- return start.compareTo(block.getStart());
- }
-
- MemoryBlock create(String lName, Address lStart, int offset, int length)
- throws AddressOverflowException {
-
- byte[] b = copyData(offset, length);
- InitializedMemoryBlock block = new InitializedMemoryBlock(lName, lStart, b);
- copyProperties(block);
- block.sourceOffset += offset;
- return block;
- }
-
- /**
- * Append the given block to this block.
- */
- MemoryBlock append(MemoryBlock block) throws AddressOverflowException, MemoryBlockException {
-
- if (!(block instanceof InitializedMemoryBlock)) {
- throw new MemoryBlockException("Cannot append: Block is not a InitializedMemoryBlock");
- }
- InitializedMemoryBlock imb = (InitializedMemoryBlock) block;
- byte[] b = combineData(imb);
-
- InitializedMemoryBlock newblock = new InitializedMemoryBlock(name, start, b);
- copyProperties(newblock);
- return newblock;
- }
-
- /**
- * Copy properties from this block to the given block.
- */
- private void copyProperties(InitializedMemoryBlock block) {
- block.permissions = permissions;
- block.comment = comment;
- block.sourceName = sourceName;
- block.sourceOffset = sourceOffset;
- }
-
- /**
- * Get the index into this block for the given address.
- * @throws IllegalArgumentException if the the address is not in this
- * block.
- */
- private int getIndex(Address addr) {
- long diff = addr.subtract(start);
- if (diff < 0 || diff >= data.length) {
- throw new IllegalArgumentException("Address " + addr + " is not in this block");
- }
- return (int) diff;
- }
-
- /**
- * Change the data and notify the listeners.
- * @param addr address of where to do the change
- * @param newValue new byte values
- * @return number of bytes that changed
- */
- private int changeData(Address addr, byte[] newValue) {
- int index = getIndex(addr);
-
- int len = newValue.length;
- if (index + len > data.length) {
- len = data.length - index;
- }
-
- byte[] oldValue = new byte[len];
- System.arraycopy(data, index, oldValue, 0, oldValue.length);
-
- System.arraycopy(newValue, 0, data, index, len);
- return len;
- }
-
- private byte[] copyData(int offset, int length) {
- if (data.length == 0) {
- return new byte[0];
- }
- int nbytes = Math.min(data.length, length);
- byte[] b = new byte[length];
- System.arraycopy(data, offset, b, 0, nbytes);
- return b;
- }
-
- private byte[] combineData(InitializedMemoryBlock block) {
- if (data.length == 0 && block.data.length == 0) {
- return new byte[0];
- }
-
- int newdataSize = data.length + block.data.length;
-
- byte[] b = new byte[newdataSize];
-
- System.arraycopy(data, 0, b, 0, data.length);
- System.arraycopy(block.data, 0, b, data.length, block.data.length);
- return b;
- }
-
- private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
- ois.defaultReadObject();
- }
-
- @Override
- public boolean isVolatile() {
- return (permissions & VOLATILE) != 0;
- }
-
- @Override
- public boolean isExecute() {
- return (permissions & EXECUTE) != 0;
- }
-
- @Override
- public boolean isRead() {
- return (permissions & READ) != 0;
- }
-
- @Override
- public boolean isWrite() {
- return (permissions & WRITE) != 0;
- }
-
- @Override
- public void setVolatile(boolean v) {
- if (v) {
- permissions |= VOLATILE;
- }
- else {
- permissions &= ~VOLATILE;
- }
- }
-
- @Override
- public void setExecute(boolean e) {
- if (e) {
- permissions |= EXECUTE;
- }
- else {
- permissions &= ~EXECUTE;
- }
- }
-
- @Override
- public void setRead(boolean r) {
- if (r) {
- permissions |= READ;
- }
- else {
- permissions &= ~READ;
- }
- }
-
- @Override
- public void setWrite(boolean w) {
- if (w) {
- permissions |= WRITE;
- }
- else {
- permissions &= ~WRITE;
- }
- }
-
- @Override
- public MemoryBlockType getType() {
- if (start.getAddressSpace().isOverlaySpace()) {
- return MemoryBlockType.OVERLAY;
- }
- return MemoryBlockType.DEFAULT;
- }
-
- @Override
- public InputStream getData() {
- return new ByteArrayInputStream(data);
- }
-
- @Override
- public int getPermissions() {
- return permissions;
- }
-
- @Override
- public boolean isInitialized() {
- return true;
- }
-
- @Override
- public boolean isMapped() {
- return false;
- }
-
- @Override
- public boolean isLoaded() {
- return start.getAddressSpace().isLoadedMemorySpace();
- }
-}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java
index 9e703514a8..fba2b05b5f 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java
@@ -31,7 +31,7 @@ import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.ToyProgramBuilder;
-import ghidra.util.exception.NotFoundException;
+import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
/**
@@ -140,7 +140,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
for (int i = 0; i < 0x1000; i++) {
block2.putByte(addr(0x1000 + i), (byte) 0xff);
}
- mem.removeBlock(block2, TaskMonitorAdapter.DUMMY_MONITOR);
+ mem.removeBlock(block2, TaskMonitor.DUMMY);
// Verify buffer
block2 = mem.createBlock(block, "Test2", addr(0x1000), 0x1000);
@@ -522,7 +522,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testUnconvertBlock() throws Exception {
MemoryBlock block = mem.createInitializedBlock("Initialized", addr(1000), 100, (byte) 5,
- TaskMonitorAdapter.DUMMY_MONITOR, false);
+ TaskMonitor.DUMMY, false);
program.getSymbolTable().createLabel(addr(1001), "BOB", SourceType.USER_DEFINED);
assertNotNull(program.getSymbolTable().getPrimarySymbol(addr(1001)));
@@ -561,19 +561,18 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
Assert.fail("Join should have failed!!!");
}
catch (MemoryBlockException e) {
+ // expected
}
mem.removeBlock(nmb, new TaskMonitorAdapter());
- byte[] bytes = new byte[20];
- MemoryBlock mb2 = new InitializedMemoryBlock("Block2", addr(0x100), bytes);
+ MemoryBlock mb2 = new MemoryBlockStub();
// try to join mb2 that is not in memory
try {
mem.join(mb, mb2);
Assert.fail("Join should have failed! -- not in memory!");
}
- catch (IllegalArgumentException e) {
- }
- catch (NotFoundException e) {
+ catch (Exception e) {
+ // expected
}
mb2 = createBlock("Block2", addr(0x100), 20);
@@ -855,14 +854,13 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
mem.setBytes(addr(0x693), b);
mem.setBytes(addr(0x84d), b);
- Address addr =
- mem.findBytes(mem.getMinAddress(), b, masks, true, TaskMonitorAdapter.DUMMY_MONITOR);
+ Address addr = mem.findBytes(mem.getMinAddress(), b, masks, true, TaskMonitor.DUMMY);
assertNotNull(addr);
assertEquals(addr(0x693), addr);
addr = addr.add(b.length);
- addr = mem.findBytes(addr, b, masks, true, TaskMonitorAdapter.DUMMY_MONITOR);
+ addr = mem.findBytes(addr, b, masks, true, TaskMonitor.DUMMY);
assertNotNull(addr);
assertEquals(addr(0x84d), addr);
}
@@ -870,22 +868,19 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testCreateOverlayBlock() throws Exception {
MemoryBlock block = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, true);
+ TaskMonitor.DUMMY, true);
assertEquals(MemoryBlockType.OVERLAY, block.getType());
}
@Test
public void testCreateBitMappedBlock() throws Exception {
- mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, false);
+ mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa, TaskMonitor.DUMMY, false);
MemoryBlock bitBlock = mem.createBitMappedBlock("bit", addr(0x2000), addr(0xf00), 0x1000);
assertEquals(MemoryBlockType.BIT_MAPPED, bitBlock.getType());
- MappedMemoryBlock mappedBlock = (MappedMemoryBlock) bitBlock;
- assertEquals(addr(0xf00), mappedBlock.getOverlayedMinAddress());
- assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)),
- mappedBlock.getOverlayedAddressRange());
+ SourceInfo info = bitBlock.getSourceInfos().get(0);
+ assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)), info.getMappedRange().get());
AddressSet expectedInitializedSet = new AddressSet();
expectedInitializedSet.add(addr(0), addr(0xfff));
expectedInitializedSet.add(addr(0x2000), addr(0x27ff));
@@ -895,16 +890,13 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testCreateByteMappedBlock() throws Exception {
- mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, false);
+ mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa, TaskMonitor.DUMMY, false);
MemoryBlock byteBlock = mem.createByteMappedBlock("byte", addr(0x2000), addr(0xf00), 0x200);
assertEquals(MemoryBlockType.BYTE_MAPPED, byteBlock.getType());
- MappedMemoryBlock mappedBlock = (MappedMemoryBlock) byteBlock;
- assertEquals(addr(0xf00), mappedBlock.getOverlayedMinAddress());
- assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)),
- mappedBlock.getOverlayedAddressRange());
+ SourceInfo info = byteBlock.getSourceInfos().get(0);
+ assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)), info.getMappedRange().get());
AddressSet expectedInitializedSet = new AddressSet();
expectedInitializedSet.add(addr(0), addr(0xfff));
expectedInitializedSet.add(addr(0x2000), addr(0x20ff));
@@ -915,11 +907,11 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testCreateRemoveCreateOverlayBlock() throws Exception {
MemoryBlock block = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, true);
+ TaskMonitor.DUMMY, true);
assertEquals(MemoryBlockType.OVERLAY, block.getType());
- mem.removeBlock(block, TaskMonitorAdapter.DUMMY_MONITOR);
- block = mem.createInitializedBlock("ov2", addr(0), 0x2000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, true);
+ mem.removeBlock(block, TaskMonitor.DUMMY);
+ block =
+ mem.createInitializedBlock("ov2", addr(0), 0x2000, (byte) 0xa, TaskMonitor.DUMMY, true);
assertEquals("ov2", block.getStart().getAddressSpace().getName());
assertEquals("ov2", block.getEnd().getAddressSpace().getName());
}
@@ -927,10 +919,10 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testJoinOverlayBlocks() throws Exception {
MemoryBlock blockOne = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, true);
+ TaskMonitor.DUMMY, true);
MemoryBlock blockTwo = mem.createInitializedBlock(".overlay2", addr(0x1000), 0x100,
- (byte) 0xa, TaskMonitorAdapter.DUMMY_MONITOR, true);
+ (byte) 0xa, TaskMonitor.DUMMY, true);
try {
mem.join(blockOne, blockTwo);
@@ -943,7 +935,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testSplitOverlayBlocks() throws Exception {
MemoryBlock blockOne = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
- TaskMonitorAdapter.DUMMY_MONITOR, true);
+ TaskMonitor.DUMMY, true);
try {
mem.split(blockOne, addr(0x50));
Assert.fail("Split should have caused and Exception!");
@@ -960,8 +952,10 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
byte[] b = new byte[10];
try {
mem.getBytes(addr(0), b, 9, 50);
+ fail("Expected exception");
}
catch (ArrayIndexOutOfBoundsException e) {
+ // expected
}
}
@@ -996,8 +990,8 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
private MemoryBlock createBlock(String name, Address start, long size, int initialValue)
throws Exception {
- return mem.createInitializedBlock(name, start, size, (byte) initialValue,
- TaskMonitorAdapter.DUMMY_MONITOR, false);
+ return mem.createInitializedBlock(name, start, size, (byte) initialValue, TaskMonitor.DUMMY,
+ false);
}
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java
index a5852677b8..5d0c317194 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java
@@ -22,9 +22,11 @@ import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.framework.cmd.Command;
import ghidra.program.database.ProgramBuilder;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
-import ghidra.program.model.mem.*;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.exception.RollbackException;
/**
@@ -33,7 +35,7 @@ import ghidra.util.exception.RollbackException;
public class AddMemoryBlockCmdTest extends AbstractGenericTest {
private Program notepad;
private Program x08;
- private AddMemoryBlockCmd command;
+ private Command command;
public AddMemoryBlockCmdTest() {
super();
@@ -54,8 +56,8 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
@Test
public void testAddBlock() throws Exception {
- command = new AddMemoryBlockCmd(".test", "A Test", "new block", getNotepadAddr(0x100), 100,
- true, true, true, false, (byte) 0xa, MemoryBlockType.DEFAULT, null, true);
+ command = new AddInitializedMemoryBlockCmd(".test", "A Test", "new block",
+ getNotepadAddr(0x100), 100, true, true, true, false, (byte) 0xa, false);
assertTrue(applyCmd(notepad, command));
MemoryBlock block = notepad.getMemory().getBlock(getNotepadAddr(0x100));
assertNotNull(block);
@@ -87,8 +89,8 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
@Test
public void testOverlap() {
- command = new AddMemoryBlockCmd(".test", "A Test", "new block", getNotepadAddr(0x1001010),
- 100, true, true, true, false, (byte) 0xa, MemoryBlockType.DEFAULT, null, true);
+ command = new AddInitializedMemoryBlockCmd(".test", "A Test", "new block",
+ getNotepadAddr(0x1001010), 100, true, true, true, false, (byte) 0xa, false);
try {
applyCmd(notepad, command);
Assert.fail("Should have gotten exception");
@@ -102,26 +104,28 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
@Test
public void testAddBitBlock() {
Address addr = getX08Addr(0x3000);
- command = new AddMemoryBlockCmd(".testBit", "A Test", "new block", addr, 100, true, true,
- true, false, (byte) 0, MemoryBlockType.BIT_MAPPED, getX08Addr(0), false);
+ command = new AddBitMappedMemoryBlockCmd(".testBit", "A Test", "new block", addr, 100, true,
+ true, true, false, getX08Addr(0));
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
- assertEquals(getX08Addr(0), ((MappedMemoryBlock) block).getOverlayedMinAddress());
+ SourceInfo info = block.getSourceInfos().get(0);
+ assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
}
@Test
public void testAddByteBlock() {
Address addr = getX08Addr(0x3000);
- command = new AddMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100, true, true,
- true, false, (byte) 0, MemoryBlockType.BYTE_MAPPED, getX08Addr(0), false);
+ command = new AddByteMappedMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100,
+ true, true, true, false, getX08Addr(0));
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
- assertEquals(getX08Addr(0), ((MappedMemoryBlock) block).getOverlayedMinAddress());
+ SourceInfo info = block.getSourceInfos().get(0);
+ assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
}
@@ -129,8 +133,8 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
@Test
public void testAddOverlayBlock() throws Exception {
Address addr = getX08Addr(0x3000);
- command = new AddMemoryBlockCmd(".overlay", "A Test", "new block", addr, 100, true, true,
- true, false, (byte) 0xa, MemoryBlockType.OVERLAY, getX08Addr(0), true);
+ command = new AddInitializedMemoryBlockCmd(".overlay", "A Test", "new block", addr, 100,
+ true, true, true, false, (byte) 0xa, true);
assertTrue(applyCmd(x08, command));
MemoryBlock block = null;
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
index 8bd415dea0..93f955310f 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java
@@ -15,13 +15,16 @@
*/
package ghidra.app.plugin.core.checksums;
+import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
+import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
-import ghidra.util.exception.CancelledException;
-import ghidra.util.exception.NotFoundException;
+import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
class MyTestMemory extends AddressSet implements Memory {
@@ -70,8 +73,8 @@ class MyTestMemory extends AddressSet implements Memory {
@Override
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
- long length, TaskMonitor monitor, boolean overlay) throws MemoryConflictException,
- AddressOverflowException, CancelledException {
+ long length, TaskMonitor monitor, boolean overlay)
+ throws MemoryConflictException, AddressOverflowException, CancelledException {
throw new UnsupportedOperationException();
}
@@ -104,6 +107,22 @@ class MyTestMemory extends AddressSet implements Memory {
throw new UnsupportedOperationException();
}
+ @Override
+ public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean deleteFileBytes(FileBytes descriptor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getAllFileBytes() {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public MemoryBlock createBlock(MemoryBlock block, String name, Address start, long length)
throws MemoryConflictException, AddressOverflowException {
@@ -298,7 +317,8 @@ class MyTestMemory extends AddressSet implements Memory {
}
@Override
- public void setShort(Address addr, short value, boolean bigEndian) throws MemoryAccessException {
+ public void setShort(Address addr, short value, boolean bigEndian)
+ throws MemoryAccessException {
throw new UnsupportedOperationException();
}
@@ -348,4 +368,11 @@ class MyTestMemory extends AddressSet implements Memory {
return set;
}
+ @Override
+ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
+ long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
+ MemoryConflictException, AddressOverflowException {
+ throw new UnsupportedOperationException();
+ }
+
}
diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java
index 1d41005d3d..b771d75855 100644
--- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java
+++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java
@@ -16,7 +16,9 @@
package ghidra.app.plugin.core.checksums;
import java.io.InputStream;
+import java.util.List;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
@@ -104,6 +106,11 @@ class MyTestMemoryBlock implements MemoryBlock {
throw new UnsupportedOperationException();
}
+ @Override
+ public void setPermissions(boolean read, boolean write, boolean execute) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void setExecute(boolean e) {
throw new UnsupportedOperationException();
@@ -186,4 +193,9 @@ class MyTestMemoryBlock implements MemoryBlock {
public boolean isLoaded() {
return start.getAddressSpace().isLoadedMemorySpace();
}
+
+ @Override
+ public List getSourceInfos() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java
index 6133d913d7..f95a2077c8 100644
--- a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java
+++ b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java
@@ -15,9 +15,12 @@
*/
package ghidra.feature.vt.db;
+import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@@ -37,8 +40,8 @@ public class MemoryTestDummy extends AddressSet implements Memory {
}
@Override
- public MemoryBlock convertToUninitialized(MemoryBlock initializedBlock) throws LockException,
- MemoryBlockException, NotFoundException {
+ public MemoryBlock convertToUninitialized(MemoryBlock initializedBlock)
+ throws LockException, MemoryBlockException, NotFoundException {
return null;
}
@@ -62,17 +65,17 @@ public class MemoryTestDummy extends AddressSet implements Memory {
@Override
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
- long length, TaskMonitor monitor, boolean overlay) throws LockException,
- MemoryConflictException, AddressOverflowException, CancelledException,
- DuplicateNameException {
+ long length, TaskMonitor monitor, boolean overlay)
+ throws LockException, MemoryConflictException, AddressOverflowException,
+ CancelledException, DuplicateNameException {
return null;
}
@Override
public MemoryBlock createInitializedBlock(String name, Address start, long size,
- byte initialValue, TaskMonitor monitor, boolean overlay) throws LockException,
- DuplicateNameException, MemoryConflictException, AddressOverflowException,
- CancelledException {
+ byte initialValue, TaskMonitor monitor, boolean overlay)
+ throws LockException, DuplicateNameException, MemoryConflictException,
+ AddressOverflowException, CancelledException {
return null;
}
@@ -248,8 +251,8 @@ public class MemoryTestDummy extends AddressSet implements Memory {
}
@Override
- public MemoryBlock join(MemoryBlock blockOne, MemoryBlock blockTwo) throws LockException,
- MemoryBlockException, NotFoundException {
+ public MemoryBlock join(MemoryBlock blockOne, MemoryBlock blockTwo)
+ throws LockException, MemoryBlockException, NotFoundException {
return null;
}
@@ -312,14 +315,37 @@ public class MemoryTestDummy extends AddressSet implements Memory {
}
@Override
- public void setShort(Address addr, short value, boolean bigEndian) throws MemoryAccessException {
+ public void setShort(Address addr, short value, boolean bigEndian)
+ throws MemoryAccessException {
// no op
}
@Override
- public void split(MemoryBlock block, Address addr) throws MemoryBlockException, LockException,
- NotFoundException {
+ public void split(MemoryBlock block, Address addr)
+ throws MemoryBlockException, LockException, NotFoundException {
// no op
}
+ @Override
+ public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getAllFileBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean deleteFileBytes(FileBytes descriptor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
+ long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
+ MemoryConflictException, AddressOverflowException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/Ghidra/Framework/DB/src/main/java/db/DBBuffer.java b/Ghidra/Framework/DB/src/main/java/db/DBBuffer.java
index d830a969ce..b0ee1552c5 100644
--- a/Ghidra/Framework/DB/src/main/java/db/DBBuffer.java
+++ b/Ghidra/Framework/DB/src/main/java/db/DBBuffer.java
@@ -23,8 +23,8 @@ import java.io.InputStream;
*/
public class DBBuffer {
- private DBHandle dbh;
- private ChainedBuffer buf;
+ final DBHandle dbh;
+ final ChainedBuffer buf;
/**
* Constructor for an existing ChainedBuffer.
diff --git a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
index 2d4ef78973..00aac4fa7c 100644
--- a/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
+++ b/Ghidra/Framework/DB/src/main/java/db/DBHandle.java
@@ -605,8 +605,9 @@ public class DBHandle {
//TODO: Does not throw ReadOnlyException - should it?
- if (txStarted)
+ if (txStarted) {
throw new AssertException("Can't save during transaction");
+ }
long txId = startTransaction();
try {
@@ -634,8 +635,9 @@ public class DBHandle {
public synchronized void saveAs(BufferFile outFile, boolean associateWithNewFile,
TaskMonitor monitor) throws IOException, CancelledException {
- if (txStarted)
+ if (txStarted) {
throw new AssertException("Can't save during transaction");
+ }
long txId = startTransaction();
boolean addedTx = false;
@@ -675,8 +677,9 @@ public class DBHandle {
protected synchronized void saveAs(BufferFile outFile, Long newDatabaseId, TaskMonitor monitor)
throws IOException, CancelledException {
- if (txStarted)
+ if (txStarted) {
throw new IllegalStateException("Can't save during transaction");
+ }
long txId = startTransaction();
try {
@@ -735,13 +738,29 @@ public class DBHandle {
* This method may only be invoked while a database transaction
* is in progress. A database transaction must also be in progress
* when invoking the various put, delete and setSize methods on the returned buffer.
- * @param length
- * @return Buffer
+ * @param length the size of the buffer to create
+ * @return Buffer the newly created buffer
* @throws IOException if an I/O error occurs while creating the buffer.
*/
public DBBuffer createBuffer(int length) throws IOException {
checkTransaction();
- return new DBBuffer(this, new ChainedBuffer(length, bufferMgr));
+ return new DBBuffer(this, new ChainedBuffer(length, true, bufferMgr));
+ }
+
+ /**
+ * Create a new buffer that layers on top of another buffer. This buffer
+ * will return values from the shadowBuffer unless they have been changed in this buffer.
+ * This method may only be invoked while a database transaction
+ * is in progress. A database transaction must also be in progress
+ * when invoking the various put, delete and setSize methods on the returned buffer.
+ * @param shadowBuffer the source of the byte values to use unless they have been changed.
+ * @return Buffer the newly created buffer
+ * @throws IOException if an I/O error occurs while creating the buffer.
+ */
+ public DBBuffer createBuffer(DBBuffer shadowBuffer) throws IOException {
+ checkTransaction();
+ return new DBBuffer(this,
+ new ChainedBuffer(shadowBuffer.length(), true, shadowBuffer.buf, 0, bufferMgr));
}
/**
@@ -749,13 +768,28 @@ public class DBHandle {
* providing an improper id. A database transaction must be in progress
* when invoking the various put, delete and setSize methods on the returned buffer.
* @param id the buffer id.
- * @return Buffer
+ * @return Buffer the buffer associated with the given id.
* @throws IOException if an I/O error occurs while getting the buffer.
*/
public DBBuffer getBuffer(int id) throws IOException {
return new DBBuffer(this, new ChainedBuffer(bufferMgr, id));
}
+ /**
+ * Get an existing buffer that uses a shadowBuffer for byte values if they haven't been
+ * explicitly changed in this buffer. This method should be used with care to avoid
+ * providing an improper id. A database transaction must be in progress
+ * when invoking the various put, delete and setSize methods on the returned buffer.
+ * @param id the buffer id.
+ * @param shadowBuffer the buffer to use for byte values if they haven't been changed in
+ * this buffer.
+ * @return Buffer the buffer associated with the given id.
+ * @throws IOException if an I/O error occurs while getting the buffer.
+ */
+ public DBBuffer getBuffer(int id, DBBuffer shadowBuffer) throws IOException {
+ return new DBBuffer(this, new ChainedBuffer(bufferMgr, id, shadowBuffer.buf, 0));
+ }
+
/**
* Determine if this database can be updated.
* @return true if this database handle is intended for update
@@ -777,15 +811,15 @@ public class DBHandle {
tables = new Hashtable<>();
TableRecord[] tableRecords = masterTable.getTableRecords();
- for (int i = 0; i < tableRecords.length; i++) {
+ for (TableRecord tableRecord : tableRecords) {
// Process each primary tables
- if (tableRecords[i].getIndexedColumn() < 0) {
- Table table = new Table(this, tableRecords[i]);
+ if (tableRecord.getIndexedColumn() < 0) {
+ Table table = new Table(this, tableRecord);
tables.put(table.getName(), table);
}
else { //secondary table indexes
- IndexTable.getIndexTable(this, tableRecords[i]);
+ IndexTable.getIndexTable(this, tableRecord);
}
}
}
@@ -801,16 +835,16 @@ public class DBHandle {
Hashtable oldTables = tables;
tables = new Hashtable<>();
TableRecord[] tableRecords = masterTable.refreshTableRecords();
- for (int i = 0; i < tableRecords.length; i++) {
+ for (TableRecord tableRecord : tableRecords) {
- String tableName = tableRecords[i].getName();
+ String tableName = tableRecord.getName();
// Process each primary tables
- if (tableRecords[i].getIndexedColumn() < 0) {
+ if (tableRecord.getIndexedColumn() < 0) {
Table t = oldTables.get(tableName);
if (t == null || t.isInvalid()) {
oldTables.remove(tableName);
- t = new Table(this, tableRecords[i]);
+ t = new Table(this, tableRecord);
tableAdded(t);
}
tables.put(tableName, t);
@@ -818,7 +852,7 @@ public class DBHandle {
// secondary table indexes
else if (!oldTables.containsKey(tableName)) {
- IndexTable.getIndexTable(this, tableRecords[i]);
+ IndexTable.getIndexTable(this, tableRecord);
}
}
dbRestored();
@@ -852,8 +886,9 @@ public class DBHandle {
*/
public synchronized Table createTable(String name, Schema schema) throws IOException {
- if (tables.containsKey(name))
+ if (tables.containsKey(name)) {
throw new IOException("Table already exists");
+ }
Table table = new Table(this, masterTable.createTableRecord(name, schema, -1));
tables.put(name, table);
tableAdded(table);
@@ -867,12 +902,13 @@ public class DBHandle {
public synchronized Table createTable(String name, Schema schema, int[] indexedColumns)
throws IOException {
- if (tables.containsKey(name))
+ if (tables.containsKey(name)) {
throw new IOException("Table already exists");
+ }
Table table = new Table(this, masterTable.createTableRecord(name, schema, -1));
tables.put(name, table);
- for (int i = 0; i < indexedColumns.length; i++) {
- IndexTable.createIndexTable(table, indexedColumns[i]);
+ for (int indexedColumn : indexedColumns) {
+ IndexTable.createIndexTable(table, indexedColumn);
}
tableAdded(table);
return table;
@@ -890,8 +926,9 @@ public class DBHandle {
return false;
}
checkTransaction();
- if (tables.containsKey(newName))
+ if (tables.containsKey(newName)) {
throw new DuplicateNameException("Table already exists");
+ }
Table table = tables.remove(oldName);
if (table == null) {
return false;
@@ -908,11 +945,12 @@ public class DBHandle {
*/
public synchronized void deleteTable(String name) throws IOException {
Table table = tables.get(name);
- if (table == null)
+ if (table == null) {
return;
+ }
int[] indexedColumns = table.getIndexedColumns();
- for (int i = 0; i < indexedColumns.length; i++) {
- table.removeIndex(indexedColumns[i]);
+ for (int indexedColumn : indexedColumns) {
+ table.removeIndex(indexedColumn);
}
table.deleteAll();
masterTable.deleteTableRecord(table.getTableNum());
diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java
index 236bd1e11e..0ffb21bf56 100644
--- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java
+++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/DomainObjectChangeSupport.java
@@ -49,7 +49,7 @@ class DomainObjectChangeSupport {
DomainObjectChangeSupport(DomainObject src, int timeInterval, int bufsize, Lock lock) {
this.src = src;
- this.domainObjectLock = lock;
+ this.domainObjectLock = Objects.requireNonNull(lock);
changesQueue = new ArrayList<>(bufsize);
listeners = WeakDataStructureFactory.createCopyOnWriteWeakSet();
@@ -127,7 +127,7 @@ class DomainObjectChangeSupport {
void flush() {
Thread lockOwner = domainObjectLock.getOwner();
- if (domainObjectLock != null && lockOwner == Thread.currentThread()) {
+ if (lockOwner == Thread.currentThread()) {
/*
* We have decided that flushing events with a lock can lead to deadlocks. There
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java
index 6424f63cc8..5fd89101d7 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java
@@ -20,10 +20,12 @@ import java.util.Arrays;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.MemoryFaultHandler;
import ghidra.pcode.memstate.MemoryPage;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
+import ghidra.util.exception.AssertException;
// Derived from ProgramMappedMemory
public class ProgramLoadImage {
@@ -37,23 +39,23 @@ public class ProgramLoadImage {
Memory memory = program.getMemory();
initializedAddressSet = memory.getLoadedAndInitializedAddressSet();
for (MemoryBlock block : memory.getBlocks()) {
- if (!block.isInitialized() && (block instanceof MappedMemoryBlock)) {
- initializedAddressSet = addMappedInitializedMemory((MappedMemoryBlock) block);
+ if (!block.isInitialized() && block.isMapped()) {
+ initializedAddressSet = addMappedInitializedMemory(block);
}
}
this.faultHandler = faultHandler;
// TODO: consider adding program consumer (would require proper dispose)
}
- private AddressSetView addMappedInitializedMemory(MappedMemoryBlock mappedBlock) {
- long size = mappedBlock.getSize();
- if (size <= 0) {
- // TODO: can't handle massive mapped blocks
- return initializedAddressSet;
+ private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
+ SourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
+ if (!sourceInfo.getMappedRange().isPresent()) {
+ throw new AssertException("Mapped block did not have mapped range!");
}
+ AddressRange mappedRange = sourceInfo.getMappedRange().get();
+ Address mapStart = mappedRange.getMinAddress();
+ Address mapEnd = mappedRange.getMaxAddress();
AddressSet modifiedSet = new AddressSet(initializedAddressSet);
- Address mapStart = mappedBlock.getOverlayedMinAddress();
- Address mapEnd = mapStart.add(size - 1);
AddressSet mappedAreas = initializedAddressSet.intersectRange(mapStart, mapEnd);
for (AddressRange range : mappedAreas) {
Address start = mappedBlock.getStart().add(range.getMinAddress().subtract(mapStart));
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java
index 050aebf844..2bf5f61cad 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java
@@ -20,10 +20,12 @@ import java.util.Arrays;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.MemoryFaultHandler;
import ghidra.pcode.memstate.MemoryPage;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
+import ghidra.util.exception.AssertException;
public class ProgramMappedMemory {
@@ -39,8 +41,8 @@ public class ProgramMappedMemory {
initializedAddressSet = memory.getLoadedAndInitializedAddressSet();
for (MemoryBlock block : memory.getBlocks()) {
- if (!block.isInitialized() && (block instanceof MappedMemoryBlock)) {
- initializedAddressSet = addMappedInitializedMemory((MappedMemoryBlock) block);
+ if (!block.isInitialized() && block.isMapped()) {
+ initializedAddressSet = addMappedInitializedMemory(block);
}
}
@@ -48,15 +50,15 @@ public class ProgramMappedMemory {
this.faultHandler = faultHandler;
}
- private AddressSetView addMappedInitializedMemory(MappedMemoryBlock mappedBlock) {
- long size = mappedBlock.getSize();
- if (size <= 0) {
- // TODO: can't handle massive mapped blocks
- return initializedAddressSet;
- }
+ private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
AddressSet modifiedSet = new AddressSet(initializedAddressSet);
- Address mapStart = mappedBlock.getOverlayedMinAddress();
- Address mapEnd = mapStart.add(size - 1);
+ SourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
+ if (!sourceInfo.getMappedRange().isPresent()) {
+ throw new AssertException("Mapped block did not have mapped range!");
+ }
+ AddressRange mappedRange = sourceInfo.getMappedRange().get();
+ Address mapStart = mappedRange.getMinAddress();
+ Address mapEnd = mappedRange.getMaxAddress();
AddressSet mappedAreas = initializedAddressSet.intersectRange(mapStart, mapEnd);
for (AddressRange range : mappedAreas) {
Address start = mappedBlock.getStart().add(range.getMinAddress().subtract(mapStart));
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java
index 19a0300d6b..868f430107 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDB.java
@@ -199,8 +199,8 @@ public class AddressMapDB implements AddressMap {
* @throws IOException thrown if a dabase io error occurs.
* @throws VersionException if the database version does not match the expected version.
*/
- public AddressMapDB(DBHandle handle, int openMode, AddressFactory factory,
- long baseImageOffset, TaskMonitor monitor) throws IOException, VersionException {
+ public AddressMapDB(DBHandle handle, int openMode, AddressFactory factory, long baseImageOffset,
+ TaskMonitor monitor) throws IOException, VersionException {
this.readOnly = (openMode == DBConstants.READ_ONLY);
this.addrFactory = factory;
this.baseImageOffset = baseImageOffset;
@@ -240,7 +240,8 @@ public class AddressMapDB implements AddressMap {
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);
+ sortedBaseEndAddrs[i] =
+ sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off);
}
if (rebuildAddrToIndexMap) {
addrToIndexMap.clear();
@@ -402,13 +403,14 @@ public class AddressMapDB implements AddressMap {
Integer tIndex = addrToIndexMap.get(tBase);
if (tIndex != null) {
return tIndex;
- } else if (indexOperation == INDEX_MATCH) {
+ }
+ else if (indexOperation == INDEX_MATCH) {
return Integer.MIN_VALUE;
}
- int search =
- normalize ? Arrays.binarySearch(sortedBaseStartAddrs, addr,
- normalizingAddressComparator) : Arrays.binarySearch(sortedBaseStartAddrs, addr);
+ int search = normalize
+ ? Arrays.binarySearch(sortedBaseStartAddrs, addr, normalizingAddressComparator)
+ : Arrays.binarySearch(sortedBaseStartAddrs, addr);
if (search < 0) {
search = -search - 2;
@@ -448,7 +450,8 @@ public class AddressMapDB implements AddressMap {
// Create new base without modifying database
Address[] newBaseAddrs = new Address[baseAddrs.length + 1];
System.arraycopy(baseAddrs, 0, newBaseAddrs, 0, baseAddrs.length);
- newBaseAddrs[index] = addr.getAddressSpace().getAddressInThisSpaceOnly(normalizedBaseOffset);
+ newBaseAddrs[index] =
+ addr.getAddressSpace().getAddressInThisSpaceOnly(normalizedBaseOffset);
baseAddrs = newBaseAddrs;
}
else {
@@ -466,8 +469,8 @@ public class AddressMapDB implements AddressMap {
void checkAddressSpace(AddressSpace addrSpace) {
AddressSpace[] spaces = addrFactory.getPhysicalSpaces();
- for (int i = 0; i < spaces.length; i++) {
- if (addrSpace.equals(spaces[i])) {
+ for (AddressSpace space : spaces) {
+ if (addrSpace.equals(space)) {
return;
}
}
@@ -578,8 +581,8 @@ public class AddressMapDB implements AddressMap {
}
catch (AddressOutOfBoundsException e) {
// Recover bad stack address as best we can (used to be a common 32-bit stack space)
- return new OldGenericNamespaceAddress(stackSpace, truncateStackOffset(
- offset, stackSpace), nameSpaceID);
+ return new OldGenericNamespaceAddress(stackSpace,
+ truncateStackOffset(offset, stackSpace), nameSpaceID);
}
}
try {
@@ -834,7 +837,6 @@ public class AddressMapDB implements AddressMap {
}
}
-
/**
* Create all memory base segments within the specified range.
* NOTE: minAddress and maxAddress must have the same address space!
@@ -899,13 +901,11 @@ public class AddressMapDB implements AddressMap {
// Try optimized single range approach first
long maxKey;
- long minKey =
- absolute ? encodeAbsolute(normalizedStart, INDEX_MATCH) : encodeRelative(
- normalizedStart, true, INDEX_MATCH);
+ long minKey = absolute ? encodeAbsolute(normalizedStart, INDEX_MATCH)
+ : encodeRelative(normalizedStart, true, INDEX_MATCH);
if (minKey != INVALID_ADDRESS_KEY) {
- maxKey =
- absolute ? encodeAbsolute(normalizedEnd, INDEX_MATCH) : encodeRelative(
- normalizedEnd, true, INDEX_MATCH);
+ maxKey = absolute ? encodeAbsolute(normalizedEnd, INDEX_MATCH)
+ : encodeRelative(normalizedEnd, true, INDEX_MATCH);
if (maxKey != INVALID_ADDRESS_KEY && (minKey & BASE_MASK) == (maxKey & BASE_MASK)) {
keyRangeList.add(new KeyRange(minKey, maxKey));
return;
@@ -926,12 +926,10 @@ public class AddressMapDB implements AddressMap {
Address addr2 = min(normalizedEnd, sortedBaseEndAddrs[index]);
if (addr1.compareTo(addr2) <= 0) {
// Collapse range where minKey and maxKey fall within existing base segments
- minKey =
- absolute ? encodeAbsolute(addr1, INDEX_MATCH_OR_NEXT) : encodeRelative(addr1,
- true, INDEX_MATCH_OR_NEXT);
- maxKey =
- absolute ? encodeAbsolute(addr2, INDEX_MATCH_OR_PREVIOUS) : encodeRelative(
- addr2, true, INDEX_MATCH_OR_PREVIOUS);
+ minKey = absolute ? encodeAbsolute(addr1, INDEX_MATCH_OR_NEXT)
+ : encodeRelative(addr1, true, INDEX_MATCH_OR_NEXT);
+ maxKey = absolute ? encodeAbsolute(addr2, INDEX_MATCH_OR_PREVIOUS)
+ : encodeRelative(addr2, true, INDEX_MATCH_OR_PREVIOUS);
if (minKey != INVALID_ADDRESS_KEY && maxKey != INVALID_ADDRESS_KEY) {
keyRangeList.add(new KeyRange(minKey, maxKey));
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java
new file mode 100644
index 0000000000..eb21fffcae
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java
@@ -0,0 +1,49 @@
+/* ###
+ * 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.mem;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.mem.MemoryBlock;
+
+public class BitMappedByteSourceRange extends ByteSourceRange {
+
+ public BitMappedByteSourceRange(MemoryBlock block, Address start, long sourceId, long offset,
+ long size) {
+
+ super(block, start, size, sourceId, offset);
+ }
+
+ @Override
+ public Address getEnd() {
+ return getStart().add(size * 8 - 1);
+ }
+
+ @Override
+ public ByteSourceRange intersect(ByteSourceRange range) {
+ if (sourceId != range.sourceId) {
+ return null;
+ }
+ long maxOffset = Math.max(byteSourceOffset, range.byteSourceOffset);
+ long minEndOffset =
+ Math.min(byteSourceOffset + size - 1, range.byteSourceOffset + range.size - 1);
+ if (maxOffset > minEndOffset) {
+ return null;
+ }
+ long sourceSize = minEndOffset - maxOffset + 1;
+ return new BitMappedByteSourceRange(block, start.add((maxOffset - byteSourceOffset) / 8),
+ sourceId, maxOffset, sourceSize);
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java
new file mode 100644
index 0000000000..de8103abae
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java
@@ -0,0 +1,228 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.util.List;
+
+import db.Record;
+import ghidra.program.database.map.AddressMapDB;
+import ghidra.program.model.address.*;
+import ghidra.program.model.mem.*;
+
+/**
+ * Class for handling bit mapped memory sub blocks
+ */
+class BitMappedSubMemoryBlock extends SubMemoryBlock {
+ private final MemoryMapDB memMap;
+ private final Address mappedAddress;
+ private boolean ioPending;
+
+ BitMappedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
+ super(adapter, record);
+ this.memMap = adapter.getMemoryMap();
+ AddressMapDB addressMap = memMap.getAddressMap();
+ mappedAddress = addressMap.decodeAddress(
+ record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return false;
+ }
+
+ @Override
+ public byte getByte(long offset) throws MemoryAccessException, IOException {
+ if (ioPending) {
+ throw new MemoryAccessException("Cyclic Access");
+ }
+ try {
+ ioPending = true;
+ return getBitOverlayByte(offset);
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ public AddressRange getMappedRange() {
+ Address endMappedAddress = mappedAddress.add((length - 1) / 8);
+ return new AddressRangeImpl(mappedAddress, endMappedAddress);
+ }
+
+ @Override
+ public int getBytes(long offset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ try {
+ ioPending = true;
+ len = (int) Math.min(len, length - offset);
+ for (int i = 0; i < len; i++) {
+ b[i + off] = getBitOverlayByte(offset++);
+ }
+ return len;
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ @Override
+ public void putByte(long offset, byte b) throws MemoryAccessException, IOException {
+ try {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ ioPending = true;
+ doPutByte(mappedAddress.addNoWrap(offset / 8), (int) (offset % 8), b);
+ }
+ catch (AddressOverflowException e) {
+ new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+
+ }
+
+ @Override
+ public int putBytes(long offset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException {
+ try {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ ioPending = true;
+ len = (int) Math.min(len, length - offset);
+ for (int i = 0; i < len; i++) {
+ doPutByte(mappedAddress.addNoWrap(offset / 8), (int) (offset % 8), b[off + i]);
+ offset++;
+ }
+ return len;
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ private byte getBitOverlayByte(long blockOffset)
+ throws AddressOverflowException, MemoryAccessException {
+ Address otherAddr = mappedAddress.addNoWrap(blockOffset / 8);
+ byte b = memMap.getByte(otherAddr);
+ return (byte) ((b >> (blockOffset % 8)) & 0x01);
+ }
+
+ private void doPutByte(Address addr, int bitIndex, byte b) throws MemoryAccessException {
+ ioPending = true;
+ byte value = memMap.getByte(addr);
+ int mask = 1 << (bitIndex % 8);
+ if (b == 0) {
+ value &= ~mask;
+ }
+ else {
+ value |= mask;
+ }
+ memMap.setByte(addr, value);
+ }
+
+ @Override
+ protected boolean join(SubMemoryBlock sub2) {
+ return false;
+ }
+
+ @Override
+ protected boolean isMapped() {
+ return true;
+ }
+
+ @Override
+ protected MemoryBlockType getType() {
+ return MemoryBlockType.BIT_MAPPED;
+ }
+
+ @Override
+ protected SubMemoryBlock split(long offset) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Bit Mapped: " + mappedAddress;
+ }
+
+ @Override
+ protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long memBlockOffset,
+ long size) {
+ ByteSourceRangeList result = new ByteSourceRangeList();
+
+ // Since mapped blocks are mapped onto other memory blocks, find those blocks and
+ // handle each one separately
+
+ // converts to byte space since 8 bytes in this block's space maps to 1 byte in real memory
+ Address startMappedAddress = mappedAddress.add(memBlockOffset / 8);
+ Address endMappedAddress = mappedAddress.add((memBlockOffset + size - 1) / 8);
+ List blocks = memMap.getBlocks(startMappedAddress, endMappedAddress);
+
+ // for each block, get its ByteSourceSet and then translate that set back into this block's
+ // addresses
+ for (MemoryBlockDB mappedBlock : blocks) {
+ Address startInBlock = max(mappedBlock.getStart(), startMappedAddress);
+ Address endInBlock = min(mappedBlock.getEnd(), endMappedAddress);
+ long blockSize = endInBlock.subtract(startInBlock) + 1;
+ ByteSourceRangeList ranges =
+ mappedBlock.getByteSourceRangeList(startInBlock, blockSize);
+ for (ByteSourceRange bsRange : ranges) {
+ result.add(translate(block, bsRange, start, memBlockOffset, size));
+ }
+ }
+ return result;
+ }
+
+ // translates the ByteSourceRange back to addresse
+ private ByteSourceRange translate(MemoryBlock block, ByteSourceRange bsRange, Address start,
+ long offset,
+ long bitLength) {
+ Address startMappedAddress = mappedAddress.add(offset / 8);
+ Address normalizedStart = start.subtract(offset % 8);
+ long mappedOffsetFromStart = bsRange.getStart().subtract(startMappedAddress);
+ long offsetFromStart = mappedOffsetFromStart * 8;
+ Address startAddress = normalizedStart.add(offsetFromStart);
+
+ return new BitMappedByteSourceRange(block, startAddress, bsRange.getSourceId(),
+ bsRange.getOffset(), bsRange.getSize());
+ }
+
+ Address min(Address a1, Address a2) {
+ return a1.compareTo(a2) <= 0 ? a1 : a2;
+ }
+
+ Address max(Address a1, Address a2) {
+ return a1.compareTo(a2) >= 0 ? a1 : a2;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java
new file mode 100644
index 0000000000..23d0dfef0e
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java
@@ -0,0 +1,129 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+
+import db.DBBuffer;
+import db.Record;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.mem.*;
+
+/**
+ * Implementation of SubMemoryBlock for blocks that store bytes in their own private database
+ * buffers
+ */
+class BufferSubMemoryBlock extends SubMemoryBlock {
+ final DBBuffer buf;
+
+ BufferSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException {
+ super(adapter, record);
+ int bufferID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
+ buf = adapter.getBuffer(bufferID);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public byte getByte(long offset) throws IOException {
+ return buf.getByte((int) (offset - startingOffset));
+ }
+
+ @Override
+ public int getBytes(long offset, byte[] b, int off, int len) throws IOException {
+ len = Math.min(len, (int) (length - (offset - startingOffset)));
+ buf.get((int) (offset - startingOffset), b, off, len);
+ return len;
+ }
+
+ @Override
+ public void putByte(long offset, byte b) throws IOException {
+ buf.putByte((int) (offset - startingOffset), b);
+ }
+
+ @Override
+ public int putBytes(long offset, byte[] b, int off, int len) throws IOException {
+ len = Math.min(len, (int) (length - offset - startingOffset));
+ buf.put((int) (offset - startingOffset), b, off, len);
+ return len;
+ }
+
+ @Override
+ public void delete() throws IOException {
+ buf.delete();
+ super.delete();
+ }
+
+ @Override
+ protected boolean join(SubMemoryBlock block) throws IOException {
+ if (!(block instanceof BufferSubMemoryBlock)) {
+ return false;
+ }
+ BufferSubMemoryBlock other = (BufferSubMemoryBlock) block;
+ if (other.length + length > Memory.GBYTE) {
+ return false;
+ }
+ buf.append(other.buf);
+ setLength(length + other.length);
+ adapter.deleteSubBlock(other.record.getKey());
+ return true;
+ }
+
+ long getKey() {
+ return record.getKey();
+ }
+
+ @Override
+ protected MemoryBlockType getType() {
+ return MemoryBlockType.DEFAULT;
+ }
+
+ @Override
+ protected SubMemoryBlock split(long memBlockOffset) throws IOException {
+ // convert from offset in block to offset in this sub block
+ int offset = (int) (memBlockOffset - startingOffset);
+ long newLength = length - offset;
+ length = offset;
+ record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
+ adapter.updateSubBlockRecord(record);
+
+ DBBuffer split = buf.split(offset);
+
+ Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
+ MemoryMapDBAdapter.SUB_TYPE_BUFFER, split.getId(), 0);
+
+ return new BufferSubMemoryBlock(adapter, newSubRecord);
+ }
+
+ @Override
+ protected String getDescription() {
+ return "";
+ }
+
+ @Override
+ protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long memBlockOffset,
+ long size) {
+ long sourceId = -buf.getId(); // buffers use negative id values; FileBytes use positive id values.
+ ByteSourceRange bsRange =
+ new ByteSourceRange(block, start, size, sourceId, memBlockOffset - startingOffset);
+ return new ByteSourceRangeList(bsRange);
+ }
+}
+
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java
new file mode 100644
index 0000000000..2c1538a6cd
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java
@@ -0,0 +1,204 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.util.List;
+
+import db.Record;
+import ghidra.program.database.map.AddressMapDB;
+import ghidra.program.model.address.*;
+import ghidra.program.model.mem.*;
+
+/**
+ * Class for handling byte mapped memory sub blocks
+ */
+class ByteMappedSubMemoryBlock extends SubMemoryBlock {
+
+ private final MemoryMapDB memMap;
+ private final Address mappedAddress;
+ private boolean ioPending;
+
+ ByteMappedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
+ super(adapter, record);
+ this.memMap = adapter.getMemoryMap();
+ AddressMapDB addressMap = memMap.getAddressMap();
+ mappedAddress = addressMap.decodeAddress(
+ record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return false;
+ }
+
+ @Override
+ public byte getByte(long offset) throws MemoryAccessException, IOException {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ try {
+ ioPending = true;
+ return memMap.getByte(mappedAddress.addNoWrap(offset - startingOffset));
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ @Override
+ public int getBytes(long offset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ try {
+ ioPending = true;
+ len = (int) Math.min(len, length - (offset - startingOffset));
+ return memMap.getBytes(mappedAddress.addNoWrap(offset), b, off, len);
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ @Override
+ public void putByte(long offset, byte b) throws MemoryAccessException, IOException {
+ try {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ ioPending = true;
+ memMap.setByte(mappedAddress.addNoWrap(offset - startingOffset), b);
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+
+ }
+
+ @Override
+ public int putBytes(long offset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException {
+ try {
+ if (ioPending) {
+ new MemoryAccessException("Cyclic Access");
+ }
+ ioPending = true;
+ len = (int) Math.min(len, length - (offset - startingOffset));
+ memMap.setBytes(mappedAddress.addNoWrap(offset - startingOffset), b, off, len);
+ return len;
+ }
+ catch (AddressOverflowException e) {
+ throw new MemoryAccessException("No memory at address");
+ }
+ finally {
+ ioPending = false;
+ }
+ }
+
+ public AddressRange getMappedRange() {
+ Address endMappedAddress = mappedAddress.add(length - 1);
+ return new AddressRangeImpl(mappedAddress, endMappedAddress);
+ }
+
+ @Override
+ protected boolean join(SubMemoryBlock sub2) {
+ return false;
+ }
+
+ @Override
+ protected boolean isMapped() {
+ return true;
+ }
+
+ @Override
+ protected MemoryBlockType getType() {
+ return MemoryBlockType.BYTE_MAPPED;
+ }
+
+ @Override
+ protected SubMemoryBlock split(long memBlockOffset) throws IOException {
+ // convert from offset in block to offset in this sub block
+ int offset = (int) (memBlockOffset - startingOffset);
+ long newLength = length - offset;
+ length = offset;
+ record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
+ adapter.updateSubBlockRecord(record);
+
+ Address newAddr = mappedAddress.add(offset);
+ AddressMapDB addressMap = adapter.getMemoryMap().getAddressMap();
+ long encodedAddr = addressMap.getKey(newAddr, true);
+
+ Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
+ MemoryMapDBAdapter.SUB_TYPE_BYTE_MAPPED, 0, encodedAddr);
+
+ return new ByteMappedSubMemoryBlock(adapter, newSubRecord);
+ }
+
+ @Override
+ protected String getDescription() {
+ return "Byte Mapped: " + mappedAddress;
+ }
+
+ @Override
+ protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long offset, long size) {
+ ByteSourceRangeList result = new ByteSourceRangeList();
+ long relativeOffset = offset - startingOffset;
+ Address startAddress = mappedAddress.add(relativeOffset);
+ Address endAddress = startAddress.add(size - 1);
+ List blocks = memMap.getBlocks(startAddress, endAddress);
+ for (MemoryBlockDB mappedBlock : blocks) {
+ Address startInBlock = max(mappedBlock.getStart(), startAddress);
+ Address endInBlock = min(mappedBlock.getEnd(), endAddress);
+ AddressRange blockRange = new AddressRangeImpl(startInBlock, endInBlock);
+ ByteSourceRangeList ranges =
+ mappedBlock.getByteSourceRangeList(startInBlock, blockRange.getLength());
+ for (ByteSourceRange bsRange : ranges) {
+ result.add(translate(block, bsRange, start, relativeOffset));
+ }
+ }
+ return result;
+ }
+
+ private ByteSourceRange translate(MemoryBlock block, ByteSourceRange bsRange, Address addr,
+ long relativeOffset) {
+ Address mappedStart = bsRange.getStart();
+ long offset = mappedStart.subtract(mappedAddress);
+ Address start = addr.add(offset - relativeOffset);
+ return new ByteSourceRange(block, start, bsRange.getSize(), bsRange.getSourceId(),
+ bsRange.getOffset());
+ }
+
+ Address min(Address a1, Address a2) {
+ return a1.compareTo(a2) <= 0 ? a1 : a2;
+ }
+
+ Address max(Address a1, Address a2) {
+ return a1.compareTo(a2) >= 0 ? a1 : a2;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java
new file mode 100644
index 0000000000..92264286b2
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java
@@ -0,0 +1,126 @@
+/* ###
+ * 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.mem;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.mem.MemoryBlock;
+
+public class ByteSourceRange {
+ protected final Address start;
+ protected final long size;
+ protected final long sourceId;
+ protected final long byteSourceOffset;
+ protected MemoryBlock block;
+
+ public ByteSourceRange(MemoryBlock block, Address start, long size, long sourceId,
+ long offset) {
+ this.block = block;
+ this.start = start;
+ this.size = size;
+ this.sourceId = sourceId;
+ this.byteSourceOffset = offset;
+ }
+
+ public Address getStart() {
+ return start;
+ }
+
+ public Address getEnd() {
+ return start.add(size - 1);
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public long getSourceId() {
+ return sourceId;
+ }
+
+ public long getOffset() {
+ return byteSourceOffset;
+ }
+
+ public ByteSourceRange intersect(ByteSourceRange range) {
+ if (sourceId != range.sourceId) {
+ return null;
+ }
+ long maxOffset = Math.max(byteSourceOffset, range.byteSourceOffset);
+ long minEndOffset =
+ Math.min(byteSourceOffset + size - 1, range.byteSourceOffset + range.size - 1);
+ if (maxOffset > minEndOffset) {
+ return null;
+ }
+ return new ByteSourceRange(block, start.add(maxOffset - byteSourceOffset),
+ minEndOffset - maxOffset + 1, sourceId, maxOffset);
+ }
+
+ public MemoryBlock getMemoryBlock() {
+ return block;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (byteSourceOffset ^ (byteSourceOffset >>> 32));
+ result = prime * result + (int) (size ^ (size >>> 32));
+ result = prime * result + (int) (sourceId ^ (sourceId >>> 32));
+ result = prime * result + ((start == null) ? 0 : start.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ByteSourceRange other = (ByteSourceRange) obj;
+ if (block == null) {
+ if (other.block != null) {
+ return false;
+ }
+ }
+ else if (!block.equals(other.block)) {
+ return false;
+ }
+ if (byteSourceOffset != other.byteSourceOffset) {
+ return false;
+ }
+ if (size != other.size) {
+ return false;
+ }
+ if (sourceId != other.sourceId) {
+ return false;
+ }
+ if (start == null) {
+ if (other.start != null) {
+ return false;
+ }
+ }
+ else if (!start.equals(other.start)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java
new file mode 100644
index 0000000000..9bc1f5660b
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java
@@ -0,0 +1,220 @@
+/* ###
+ * 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.mem;
+
+import java.util.*;
+
+import ghidra.program.model.mem.MemoryBlock;
+
+public class ByteSourceRangeList implements Iterable {
+ List ranges;
+
+ public ByteSourceRangeList(ByteSourceRange bsRange) {
+ this();
+ ranges.add(bsRange);
+ }
+
+ public ByteSourceRangeList() {
+ ranges = new ArrayList<>();
+ }
+
+ @Override
+ public Iterator iterator() {
+ return ranges.iterator();
+ }
+
+ public void add(ByteSourceRange range) {
+ if (range != null) {
+ ranges.add(range);
+ }
+ }
+
+ public void add(ByteSourceRangeList byteSourceList) {
+ ranges.addAll(byteSourceList.ranges);
+ }
+
+ public int getRangeCount() {
+ return ranges.size();
+ }
+
+ public ByteSourceRange get(int i) {
+ return ranges.get(i);
+ }
+
+ public boolean isEmpty() {
+ return ranges.isEmpty();
+ }
+
+ public Set getOverlappingBlocks() {
+ List entries = new ArrayList<>();
+ for (ByteSourceRange range : ranges) {
+ entries.add(new BlockRangeStart(this, range));
+ entries.add(new BlockRangeEnd(this, range));
+ }
+ Collections.sort(entries);
+ return findOverlappingBlocks(entries);
+ }
+
+ public ByteSourceRangeList intersect(ByteSourceRangeList rangeList) {
+ List entries = new ArrayList<>();
+ for (ByteSourceRange range : ranges) {
+ entries.add(new BlockRangeStart(this, range));
+ entries.add(new BlockRangeEnd(this, range));
+ }
+ for (ByteSourceRange range : rangeList) {
+ entries.add(new BlockRangeStart(rangeList, range));
+ entries.add(new BlockRangeEnd(rangeList, range));
+ }
+ Collections.sort(entries);
+ return getIntersectingRanges(entries);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((ranges == null) ? 0 : ranges.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ByteSourceRangeList other = (ByteSourceRangeList) obj;
+ if (ranges == null) {
+ if (other.ranges != null) {
+ return false;
+ }
+ }
+ else if (!ranges.equals(other.ranges)) {
+ return false;
+ }
+ return true;
+ }
+
+ private ByteSourceRangeList getIntersectingRanges(List entries) {
+ ByteSourceRangeList result = new ByteSourceRangeList();
+
+ Set currentSet = new HashSet<>();
+
+ for (BlockRangeEntry entry : entries) {
+ if (entry.isStart()) {
+ currentSet.add(entry.range);
+ }
+ else {
+ currentSet.remove(entry.range);
+ addIntersections(result, entry, currentSet);
+ }
+ }
+
+ return result;
+ }
+
+ private void addIntersections(ByteSourceRangeList set, BlockRangeEntry entry,
+ Set currentSet) {
+
+ if (currentSet.isEmpty()) {
+ return;
+ }
+ for (ByteSourceRange byteSourceRange : currentSet) {
+ if (entry.owner == this) {
+ set.add(entry.range.intersect(byteSourceRange));
+ }
+ else {
+ set.add(byteSourceRange.intersect(entry.range));
+ }
+ }
+ }
+
+ private Set findOverlappingBlocks(List entries) {
+ Set overlappingBlocks = new HashSet<>();
+ Set currentSet = new HashSet<>();
+
+ for (BlockRangeEntry entry : entries) {
+ if (entry.isStart()) {
+ currentSet.add(entry.range);
+ }
+ else {
+ currentSet.remove(entry.range);
+ if (!currentSet.isEmpty()) {
+ overlappingBlocks.add(entry.range.block);
+ for (ByteSourceRange byteSourceRange : currentSet) {
+ overlappingBlocks.add(byteSourceRange.block);
+ }
+ }
+ }
+ }
+ return overlappingBlocks;
+ }
+
+ abstract class BlockRangeEntry implements Comparable {
+ private ByteSourceRange range;
+ private long sourceId;
+ private long offset;
+ private ByteSourceRangeList owner;
+
+ BlockRangeEntry(ByteSourceRangeList owner, ByteSourceRange range, long offset) {
+ this.owner = owner;
+ this.range = range;
+ this.offset = offset;
+ this.sourceId = range.getSourceId();
+ }
+
+ abstract boolean isStart();
+
+ @Override
+ public int compareTo(BlockRangeEntry o) {
+ if (sourceId != o.sourceId) {
+ return sourceId > o.sourceId ? 1 : -1;
+ }
+ if (offset == o.offset) {
+ return (isStart() == o.isStart()) ? 0 : (isStart() ? -1 : 1);
+ }
+ return offset > o.offset ? 1 : -1;
+ }
+ }
+
+ class BlockRangeStart extends BlockRangeEntry {
+ BlockRangeStart(ByteSourceRangeList owner, ByteSourceRange range) {
+ super(owner, range, range.getOffset());
+ }
+ @Override
+ boolean isStart() {
+ return true;
+ }
+
+ }
+
+ class BlockRangeEnd extends BlockRangeEntry {
+ BlockRangeEnd(ByteSourceRangeList owner, ByteSourceRange range) {
+ super(owner, range, range.getOffset() + range.size - 1);
+ }
+
+ @Override
+ boolean isStart() {
+ return false;
+ }
+
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytes.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytes.java
new file mode 100644
index 0000000000..18c364b8d6
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytes.java
@@ -0,0 +1,362 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.util.ConcurrentModificationException;
+
+import db.*;
+
+/**
+ * FileBytes provides access to the all the byte values (both original and modified) from an
+ * imported file.
+ */
+public class FileBytes {
+
+ private final DBBuffer[] originalBuffers;
+ private final DBBuffer[] layeredBuffers;
+ private final String filename;
+ private final long id;
+ private final long fileOffset;
+ private final long size;
+ private boolean invalid = false;
+ private MemoryMapDB memMap;
+
+ public FileBytes(FileBytesAdapter adapter, MemoryMapDB memMap, Record record)
+ throws IOException {
+ this.memMap = memMap;
+ this.filename = record.getString(FileBytesAdapter.FILENAME_COL);
+ this.fileOffset = record.getLongValue(FileBytesAdapter.OFFSET_COL);
+ this.size = record.getLongValue(FileBytesAdapter.SIZE_COL);
+ this.id = record.getKey();
+ BinaryField field = (BinaryField) record.getFieldValue(FileBytesAdapter.BUF_IDS_COL);
+
+ int[] bufferIds = new BinaryCodedField(field).getIntArray();
+ originalBuffers = new DBBuffer[bufferIds.length];
+ for (int i = 0; i < bufferIds.length; i++) {
+ originalBuffers[i] = adapter.getBuffer(bufferIds[i]);
+ }
+
+ field = (BinaryField) record.getFieldValue(FileBytesAdapter.LAYERED_BUF_IDS_COL);
+ bufferIds = new BinaryCodedField(field).getIntArray();
+ layeredBuffers = new DBBuffer[bufferIds.length];
+ for (int i = 0; i < bufferIds.length; i++) {
+ layeredBuffers[i] = adapter.getBuffer(bufferIds[i], originalBuffers[i]);
+ }
+
+ }
+
+ /**
+ * Returns the name of the file that supplied the bytes.
+ * @return the name of the file that supplied the bytes.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * Returns the offset in the original file from where these bytes originated. Normally this will
+ * be 0, but in the case where the program is actually a piece in some other file (e.g. tar,zip),
+ * this will be the offset into the file corresponding to the first byte in this FileBytes object.
+ *
+ * @return the offset in the original file from where these bytes originated.
+ */
+ public long getFileOffset() {
+ return fileOffset;
+ }
+
+ /**
+ * Returns the number of bytes from the original source file that are stored in the database.
+ * @return the number of bytes from the original source file that are stored in the database.
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the (possibly modified) byte at the given offset for this file bytes object.
+ * @param offset the offset into the file bytes for the byte to retrieve.
+ * @return the (possibly modified) byte at the given offset for this file bytes object.
+ * @throws IOException if there is a problem reading the database.
+ * @throws IndexOutOfBoundsException if the given offset is invalid.
+ */
+ public byte getModifiedByte(long offset) throws IOException {
+ return getByte(layeredBuffers, offset);
+ }
+
+ /**
+ * Returns the original byte value at the given offset for this file bytes object.
+ * @param offset the offset into the file bytes for the byte to retrieve.
+ * @return the original byte at the given offset for this file bytes object.
+ * @throws IOException if there is a problem reading the database.
+ * @throws IndexOutOfBoundsException if the given offset is invalid.
+ */
+ public byte getOriginalByte(long offset) throws IOException {
+ return getByte(originalBuffers, offset);
+ }
+
+ /**
+ * Tries to get b.length (possibly modified) bytes from this FileBytes entry at the given offset into the file
+ * bytes. May return fewer bytes if the requested length is beyond the end of the file bytes.
+ *
+ * @param offset the offset into the files bytes to start.
+ * @param b the byte array to populate.
+ * @return the number of bytes actually populated.
+ * @throws IOException if there is an error reading from the database
+ */
+ public int getModifiedBytes(long offset, byte[] b) throws IOException {
+ return getBytes(layeredBuffers, offset, b, 0, b.length);
+ }
+
+ /**
+ * Tries to get b.length original bytes from this FileBytes entry at the given offset into the file
+ * bytes. May return fewer bytes if the requested length is beyond the end of the file bytes.
+ *
+ * @param offset the offset into the files bytes to start.
+ * @param b the byte array to populate.
+ * @return the number of bytes actually populated.
+ * @throws IOException if there is an error reading from the database
+ */
+ public int getOriginalBytes(long offset, byte[] b) throws IOException {
+ return getBytes(originalBuffers, offset, b, 0, b.length);
+ }
+
+ /**
+ * Tries to get length (possibly modified) bytes from the files starting at the given offset and put them
+ * into the given byte array at the specified offset into the byte array. May return
+ * fewer bytes if the requested length is beyond the end of the file bytes.
+ *
+ * @param offset the offset into the files bytes to start.
+ * @param b the byte array to populate.
+ * @param off the offset into the byte array.
+ * @param length the number of bytes to get.
+ * @return the number of bytes actually populated.
+ * @throws IOException if there is an error reading from the database
+ * @throws IndexOutOfBoundsException if the destination offset and length would exceed the
+ * size of the buffer b.
+ */
+ public int getModifiedBytes(long offset, byte[] b, int off, int length) throws IOException {
+ return getBytes(layeredBuffers, offset, b, off, length);
+ }
+
+ /**
+ * Tries to get length (original) bytes from the files starting at the given offset and put them
+ * into the given byte array at the specified offset into the byte array. May return
+ * fewer bytes if the requested length is beyond the end of the file bytes.
+ *
+ * @param offset the offset into the files bytes to start.
+ * @param b the byte array to populate.
+ * @param off the offset into the byte array.
+ * @param length the number of bytes to get.
+ * @return the number of bytes actually populated.
+ * @throws IOException if there is an error reading from the database
+ * @throws IndexOutOfBoundsException if the destination offset and length would exceed the
+ * size of the buffer b.
+ */
+ public int getOriginalBytes(long offset, byte[] b, int off, int length) throws IOException {
+ return getBytes(originalBuffers, offset, b, off, length);
+ }
+
+ void checkValid() {
+ if (invalid) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ void invalidate() {
+ invalid = true;
+ }
+
+ long getId() {
+ return id;
+ }
+
+ /**
+ * Changes the byte at the given offset to the given value. Note, the
+ * original byte can still be accessed via {@link #getOriginalByte(long)}
+ * If the byte is changed more than once, only the original value is preserved.
+ *
+ * @param offset the offset into the file bytes.
+ * @param b the new byte value;
+ * @throws IOException if the write to the database fails.
+ */
+ void putByte(long offset, byte b) throws IOException {
+ if (offset < 0 || offset >= size) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ checkValid();
+
+ // The max buffer size will be the size of the first buffer. (If more than
+ // one buffer exists, then the first buffer will be the true max size. If only one buffer,
+ // then its actual size can be used as the max size and it won't matter.)
+ int maxBufferSize = layeredBuffers[0].length();
+
+ int dbBufferIndex = (int) (offset / maxBufferSize);
+ int localOffset = (int) (offset % maxBufferSize);
+ layeredBuffers[dbBufferIndex].putByte(localOffset, b);
+ }
+
+ /**
+ * Changes the bytes at the given offset to the given values. Note, the
+ * original bytes can still be accessed via {@link #getOriginalBytes(long, byte[])}
+ * If the bytes are changed more than once, only the original values are preserved.
+ *
+ * @param offset the offset into the file bytes.
+ * @param b a byte array with the new values to write.
+ * @return the number of bytes written
+ * @throws IOException if the write to the database fails.
+ */
+ int putBytes(long offset, byte[] b) throws IOException {
+ return putBytes(offset, b, 0, b.length);
+ }
+
+ /**
+ * Changes the bytes at the given offset to the given values. Note, the
+ * original bytes can still be accessed via {@link #getOriginalBytes(long, byte[], int, int)}
+ * If the bytes are changed more than once, only the original values are preserved.
+ *
+ * @param offset the offset into the file bytes.
+ * @param b a byte array with the new values to write.
+ * @param off the offset into the byte array to get the bytes to write.
+ * @param length the number of bytes to write.
+ * @return the number of bytes written
+ * @throws IOException if the write to the database fails.
+ */
+ int putBytes(long offset, byte[] b, int off, int length) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ }
+ else if (off < 0 || length < 0 || length > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
+ else if (length == 0) {
+ return 0;
+ }
+
+ checkValid();
+
+ // adjust size if asking length is more than we have
+ length = (int) Math.min(length, size - offset);
+ if (length == 0) {
+ return 0;
+ }
+
+ // The max buffer size will be the size of the first buffer. (If more than
+ // one buffer exists, then the first buffer will be the true max size. If only one buffer,
+ // then its actual size can be used as the max size and it won't matter.)
+ int maxBufferSize = layeredBuffers[0].length();
+ long fileBytesOffset = offset;
+ int byteArrayOffset = off;
+ int n = length;
+
+ while (n > 0) {
+ int dbBufferIndex = (int) (fileBytesOffset / maxBufferSize);
+ int localOffset = (int) (fileBytesOffset % maxBufferSize);
+ int writeLen = Math.min(maxBufferSize - localOffset, n);
+ layeredBuffers[dbBufferIndex].put(localOffset, b, byteArrayOffset, writeLen);
+ n -= writeLen;
+ fileBytesOffset += writeLen;
+ byteArrayOffset += writeLen;
+ }
+ return length;
+ }
+
+ private byte getByte(DBBuffer[] buffers, long offset) throws IOException {
+ if (offset < 0 || offset >= size) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ checkValid();
+
+ // The max buffer size will be the size of the first buffer. (If more than
+ // one buffer exists, then the first buffer will be the true max size. If only one buffer,
+ // then its actual size can be used as the max size and it won't matter.)
+ int maxBufferSize = buffers[0].length();
+
+ int dbBufferIndex = (int) (offset / maxBufferSize);
+ int localOffset = (int) (offset % maxBufferSize);
+ return buffers[dbBufferIndex].getByte(localOffset);
+ }
+
+ private int getBytes(DBBuffer[] buffers, long offset, byte[] b, int off, int length)
+ throws IOException {
+
+ if (off < 0 || length < 0 || length > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
+ else if (length == 0) {
+ return 0;
+ }
+
+ checkValid();
+
+ // adjust size if asking length is more than we have
+ length = (int) Math.min(length, size - offset);
+ if (length == 0) {
+ return 0;
+ }
+
+ // The max buffer size will be the size of the first buffer. (If more than
+ // one buffer exists, then the first buffer will be the true max size. If only one buffer,
+ // then its actual size can be used as the max size and it won't matter.)
+ int maxBufferSize = buffers[0].length();
+ long fileBytesOffset = offset;
+ int byteArrayOffset = off;
+ int n = length;
+
+ while (n > 0) {
+ int dbBufferIndex = (int) (fileBytesOffset / maxBufferSize);
+ int localOffset = (int) (fileBytesOffset % maxBufferSize);
+ int readLen = Math.min(maxBufferSize - localOffset, n);
+ buffers[dbBufferIndex].get(localOffset, b, byteArrayOffset, readLen);
+ n -= readLen;
+ fileBytesOffset += readLen;
+ byteArrayOffset += readLen;
+ }
+ return length;
+ }
+
+ @Override
+ public String toString() {
+ return filename;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) id;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ FileBytes other = (FileBytes) obj;
+
+ return id == other.id;
+ }
+
+ MemoryMapDB getMemMap() {
+ return memMap;
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapter.java
new file mode 100644
index 0000000000..ecb260735b
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapter.java
@@ -0,0 +1,125 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import db.*;
+import ghidra.util.exception.VersionException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * Database Adapter for storing and retrieving original file bytes.
+ */
+abstract class FileBytesAdapter {
+ private static final int MAX_BUF_SIZE = 1_000_000_000;
+
+ public static final int FILENAME_COL = FileBytesAdapterV0.V0_FILENAME_COL;
+ public static final int OFFSET_COL = FileBytesAdapterV0.V0_OFFSET_COL;
+ public static final int SIZE_COL = FileBytesAdapterV0.V0_SIZE_COL;
+ public static final int BUF_IDS_COL = FileBytesAdapterV0.V0_BUF_IDS_COL;
+ public static final int LAYERED_BUF_IDS_COL = FileBytesAdapterV0.V0_LAYERED_BUF_IDS_COL;
+
+ protected DBHandle handle;
+ private static int maxBufSize = MAX_BUF_SIZE; // shadowed so that it can be changed for testing
+
+ protected MemoryMapDB memMap;
+
+ FileBytesAdapter(DBHandle handle, MemoryMapDB memMap) {
+ this.handle = handle;
+ this.memMap = memMap;
+ }
+
+ static FileBytesAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap,
+ TaskMonitor monitor) throws VersionException, IOException {
+
+ if (openMode == DBConstants.CREATE) {
+ return new FileBytesAdapterV0(handle, memMap, true);
+ }
+ try {
+ return new FileBytesAdapterV0(handle, memMap, false);
+ }
+ catch (VersionException e) {
+ if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
+ throw e;
+ }
+ FileBytesAdapter adapter = findReadOnlyAdapter(handle, memMap);
+ if (openMode == DBConstants.UPGRADE) {
+ adapter = upgrade(handle, memMap, adapter, monitor);
+ }
+ return adapter;
+ }
+
+ }
+
+ private static FileBytesAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap) {
+ return new FileBytesAdapterNoTable(handle, memMap);
+ }
+
+ private static FileBytesAdapter upgrade(DBHandle handle, MemoryMapDB memMap,
+ FileBytesAdapter oldAdapter, TaskMonitor monitor) throws VersionException, IOException {
+ return new FileBytesAdapterV0(handle, memMap, true);
+ }
+
+ abstract FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException;
+
+ /**
+ * Returns a DBBuffer object for the given database buffer id
+ * @param bufferID the id of the first buffer in the DBBuffer.
+ * @return a DBBuffer object for the given database buffer id
+ * @throws IOException if a database IO error occurs.
+ */
+ DBBuffer getBuffer(int bufferID) throws IOException {
+ if (bufferID >= 0) {
+ return handle.getBuffer(bufferID);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a layered DBBuffer object for the given database buffer id
+ * @param bufferID the id of the first buffer in the DBBuffer.
+ * @param shadowBuffer the buffer to use for byte values unless the bytes have been
+ * explicitly set in this buffer.
+ * @return a DBBuffer object for the given database buffer id using the given shadow buffer.
+ * @throws IOException if a database IO error occurs.
+ */
+ DBBuffer getBuffer(int bufferID, DBBuffer shadowBuffer) throws IOException {
+ if (bufferID >= 0) {
+ return handle.getBuffer(bufferID, shadowBuffer);
+ }
+ return null;
+ }
+
+ static int getMaxBufferSize() {
+ return maxBufSize;
+ }
+
+ // *** FOR TESTING PURPOSES ONLY ***
+ static void setMaxBufferSize(int testSize) {
+ maxBufSize = testSize;
+ }
+
+ abstract List getAllFileBytes();
+
+ abstract void refresh() throws IOException;
+
+ abstract boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterNoTable.java
new file mode 100644
index 0000000000..d111e784ab
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterNoTable.java
@@ -0,0 +1,59 @@
+/* ###
+ * 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.mem;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+
+import db.DBBuffer;
+import db.DBHandle;
+
+/**
+ * Version of the FileBytesAdapter used to access older databases for read-only and upgrade purposes.
+ */
+class FileBytesAdapterNoTable extends FileBytesAdapter {
+
+ public FileBytesAdapterNoTable(DBHandle handle, MemoryMapDB memMap) {
+ super(handle, memMap);
+ }
+
+ @Override
+ FileBytes createFileBytes(String filename, long offset, long size, InputStream is) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ DBBuffer getBuffer(int i) {
+ return null;
+ }
+
+ @Override
+ List getAllFileBytes() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ void refresh() {
+ // do nothing
+ }
+
+ @Override
+ boolean deleteFileBytes(FileBytes fileBytes) {
+ return false;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java
new file mode 100644
index 0000000000..fb82ab0073
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesAdapterV0.java
@@ -0,0 +1,165 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+import db.*;
+import ghidra.util.exception.VersionException;
+
+/**
+ * Initial version of the FileBytesAdapter
+ */
+class FileBytesAdapterV0 extends FileBytesAdapter {
+ static final String TABLE_NAME = "File Bytes";
+ static final int VERSION = 0;
+
+ public static final int V0_FILENAME_COL = 0;
+ public static final int V0_OFFSET_COL = 1;
+ public static final int V0_SIZE_COL = 2;
+ public static final int V0_BUF_IDS_COL = 3;
+ public static final int V0_LAYERED_BUF_IDS_COL = 4;
+
+ static final Schema SCHEMA = new Schema(VERSION, "Key",
+ new Class[] { StringField.class, LongField.class, LongField.class, BinaryField.class,
+ BinaryField.class },
+ new String[] { "Filename", "Offset", "Size", "Chain Buffer IDs",
+ "Layered Chain Buffer IDs" });
+
+ private Table table;
+ private List fileBytesList = new ArrayList<>();
+
+ FileBytesAdapterV0(DBHandle handle, MemoryMapDB memMap, boolean create)
+ throws VersionException, IOException {
+ super(handle, memMap);
+
+ if (create) {
+ table = handle.createTable(TABLE_NAME, SCHEMA);
+ }
+ else {
+ table = handle.getTable(TABLE_NAME);
+ if (table == null) {
+ throw new VersionException(true);
+ }
+ if (table.getSchema().getVersion() != VERSION) {
+ throw new VersionException(VersionException.NEWER_VERSION, false);
+ }
+ }
+
+ // load existing file bytes
+ RecordIterator iterator = table.iterator();
+ while (iterator.hasNext()) {
+ Record record = iterator.next();
+ fileBytesList.add(new FileBytes(this, memMap, record));
+ }
+
+ }
+
+ @Override
+ FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException {
+ DBBuffer[] buffers = createBuffers(size, is);
+ DBBuffer[] layeredBuffers = createLayeredBuffers(buffers);
+ int[] bufIds = getIds(buffers);
+ int[] layeredBufIds = getIds(layeredBuffers);
+ Record record = SCHEMA.createRecord(table.getKey());
+ record.setString(V0_FILENAME_COL, filename);
+ record.setLongValue(V0_OFFSET_COL, offset);
+ record.setLongValue(V0_SIZE_COL, size);
+ record.setField(V0_BUF_IDS_COL, new BinaryCodedField(bufIds));
+ record.setField(V0_LAYERED_BUF_IDS_COL, new BinaryCodedField(layeredBufIds));
+ table.putRecord(record);
+ FileBytes fileBytes = new FileBytes(this, memMap, record);
+ fileBytesList.add(fileBytes);
+ return fileBytes;
+ }
+
+ @Override
+ List getAllFileBytes() {
+ return fileBytesList;
+ }
+
+ @Override
+ void refresh() throws IOException {
+ Map map = new HashMap<>();
+ List newList = new ArrayList<>();
+
+ for (FileBytes fileBytes : fileBytesList) {
+ map.put(fileBytes.getId(), fileBytes);
+ }
+
+ RecordIterator iterator = table.iterator();
+ while (iterator.hasNext()) {
+ Record record = iterator.next();
+ FileBytes fileBytes = map.remove(record.getKey());
+ if (fileBytes == null) {
+ fileBytes = new FileBytes(this, memMap, record);
+ }
+ newList.add(fileBytes);
+ }
+
+ for (FileBytes fileBytes : map.values()) {
+ fileBytes.invalidate();
+ }
+ fileBytesList = newList;
+ }
+
+ @Override
+ boolean deleteFileBytes(FileBytes fileBytes) throws IOException {
+ if (table.deleteRecord(fileBytes.getId())) {
+ fileBytesList.remove(fileBytes);
+ return true;
+ }
+ return false;
+ }
+
+ private int[] getIds(DBBuffer[] buffers) {
+ int[] ids = new int[buffers.length];
+ for (int i = 0; i < ids.length; i++) {
+ ids[i] = buffers[i].getId();
+ }
+ return ids;
+ }
+
+ private DBBuffer[] createLayeredBuffers(DBBuffer[] buffers) throws IOException {
+ DBBuffer[] layeredBuffers = new DBBuffer[buffers.length];
+ for (int i = 0; i < buffers.length; i++) {
+ layeredBuffers[i] = handle.createBuffer(buffers[i]);
+ }
+ return layeredBuffers;
+ }
+
+ private DBBuffer[] createBuffers(long size, InputStream is) throws IOException {
+ int maxBufSize = getMaxBufferSize();
+ int bufCount = (int) (size / maxBufSize);
+ int sizeLastBuf = (int) (size % maxBufSize);
+ if (sizeLastBuf > 0) {
+ bufCount++;
+ }
+ DBBuffer[] buffers = new DBBuffer[bufCount];
+ for (int i = 0; i < bufCount - 1; i++) {
+ buffers[i] = handle.createBuffer(maxBufSize);
+ }
+ buffers[bufCount - 1] = handle.createBuffer(sizeLastBuf);
+
+ for (DBBuffer buffer : buffers) {
+ buffer.fill(is);
+ }
+ return buffers;
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java
new file mode 100644
index 0000000000..b002ab232d
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java
@@ -0,0 +1,140 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+
+import db.Record;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.mem.*;
+
+/**
+ * Class for handling {@link FileBytes} memory sub blocks (blocks whose bytes are backed by a FileBytes object
+ */
+class FileBytesSubMemoryBlock extends SubMemoryBlock {
+ private final FileBytes fileBytes;
+ private final long fileBytesOffset;
+
+ FileBytesSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException {
+ super(adapter, record);
+ long fileBytesID = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
+ fileBytesOffset = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL);
+ fileBytes = adapter.getMemoryMap().getLayeredFileBytes(fileBytesID);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public byte getByte(long memBlockOffset) throws IOException {
+ return fileBytes.getModifiedByte(fileBytesOffset + memBlockOffset - startingOffset);
+ }
+
+ @Override
+ public int getBytes(long memBlockOffset, byte[] b, int off, int len) throws IOException {
+ return fileBytes.getModifiedBytes(fileBytesOffset + memBlockOffset - startingOffset, b, off,
+ len);
+ }
+
+ @Override
+ public void putByte(long memBlockOffset, byte b) throws MemoryAccessException, IOException {
+ fileBytes.putByte(fileBytesOffset + memBlockOffset - startingOffset, b);
+ }
+
+ @Override
+ public int putBytes(long memBlockOffset, byte[] b, int off, int len) throws IOException {
+ return fileBytes.putBytes(fileBytesOffset + memBlockOffset - startingOffset, b, off, len);
+ }
+
+ @Override
+ protected boolean join(SubMemoryBlock block) throws IOException {
+ if (!(block instanceof FileBytesSubMemoryBlock)) {
+ return false;
+ }
+ FileBytesSubMemoryBlock other = (FileBytesSubMemoryBlock) block;
+ if (fileBytes != other.fileBytes) {
+ return false;
+ }
+ // are the two block consecutive in the fileBytes space?
+ if (other.fileBytesOffset != fileBytesOffset + length) {
+ return false;
+ }
+ // ok we can join them
+ setLength(length + other.length);
+ adapter.deleteSubBlock(other.record.getKey());
+ return true;
+ }
+
+ public FileBytes getFileBytes() {
+ return fileBytes;
+ }
+
+ public long getFileBytesOffset() {
+ return fileBytesOffset;
+ }
+
+ @Override
+ protected MemoryBlockType getType() {
+ return MemoryBlockType.DEFAULT;
+ }
+
+ @Override
+ protected SubMemoryBlock split(long memBlockOffset) throws IOException {
+ // convert from offset in block to offset in this sub block
+ int offset = (int) (memBlockOffset - startingOffset);
+ long newLength = length - offset;
+ length = offset;
+ record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
+ adapter.updateSubBlockRecord(record);
+
+ int fileBytesID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
+ Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
+ MemoryMapDBAdapter.SUB_TYPE_FILE_BYTES, fileBytesID, fileBytesOffset + offset);
+
+ return new FileBytesSubMemoryBlock(adapter, newSubRecord);
+ }
+
+ @Override
+ protected String getDescription() {
+ String fileName = fileBytes.getFilename();
+
+ if (fileBytes.getFileOffset()> 0) {
+ fileName = "[" + fileName + " + 0x" + Long.toHexString(fileBytes.getFileOffset()) + "]";
+ }
+
+ String hexString = Long.toHexString(fileBytesOffset);
+ return "File: " + fileName + ": 0x" + hexString;
+ }
+
+ @Override
+ protected boolean uses(FileBytes fb) {
+ return fileBytes.equals(fb);
+ }
+
+ @Override
+ protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long memBlockOffset,
+ long size) {
+ long sourceId = fileBytes.getId();
+ ByteSourceRange bsRange = new ByteSourceRange(block, start, size, sourceId,
+ fileBytesOffset + memBlockOffset - startingOffset);
+ return new ByteSourceRangeList(bsRange);
+ }
+
+}
+
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
index 9720d629d6..f039276723 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java
@@ -17,112 +17,79 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ConcurrentModificationException;
+import java.util.*;
import db.DBBuffer;
import db.Record;
import ghidra.framework.store.LockException;
+import ghidra.program.database.map.AddressMap;
import ghidra.program.database.map.AddressMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.*;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.DuplicateNameException;
-/**
- * Database implementation of a memory block. Each block has a corresponding record in
- * the memory map table which includes a buffer id for a DBBuffer that contains the actual
- * bytes in the memory block.
- */
-
-class MemoryBlockDB implements MemoryBlock {
-
- private static final MemoryAccessException UNITIALIZED_EXCEPTION =
- new MemoryAccessException("Cannot access uninitialized memory!");
-
+public class MemoryBlockDB implements MemoryBlock {
private MemoryMapDBAdapter adapter;
protected Record record;
+ private Address startAddress;
+ private long length;
+ private List subBlocks;
protected MemoryMapDB memMap;
- protected AddressMapDB addrMap;
- private int id;
- protected Address startAddress;
- protected long length;
private volatile boolean invalid;
- private DBBuffer buf;
- protected MemoryBlockType blockType;
- private boolean isInitialized;
+ private long id;
+ private SubMemoryBlock lastSubBlock;
- /**
- * Constructs a new MemoryBlockDB
- * @param adapter the memory map database adapter
- * @param record the record for this block
- * @param buf the DBBuffer containing the bytes for this block
- * @param memMap the memory map manager.
- * @throws IOException if a database io error occurs.
- */
- MemoryBlockDB(MemoryMapDBAdapter adapter, Record record, DBBuffer buf, MemoryMapDB memMap)
- throws IOException {
+ MemoryBlockDB(MemoryMapDBAdapter adapter, Record record, List subBlocks) {
this.adapter = adapter;
- this.memMap = memMap;
- this.addrMap = memMap.getAddressMap();
- this.buf = buf;
- id = (int) record.getKey();
- refresh(record);
+ this.record = record;
+ this.memMap = adapter.getMemoryMap();
+ id = record.getKey();
+ refresh(record, subBlocks);
}
- void refresh(Record lRecord) throws IOException {
- this.record = lRecord;
+ /**
+ * Returns the id for this memory block
+ * @return the id for this memory block
+ */
+ long getID() {
+ return id;
+ }
+
+ void refresh(Record lRecord, List list) {
if (id != lRecord.getKey()) {
throw new AssertException("Incorrect block record");
}
-
+ this.record = lRecord;
+ AddressMap addrMap = memMap.getAddressMap();
startAddress =
- addrMap.decodeAddress(lRecord.getLongValue(MemoryMapDBAdapter.START_ADDR_COL), false);
+ addrMap.decodeAddress(lRecord.getLongValue(MemoryMapDBAdapter.START_ADDR_COL));
if (startAddress instanceof SegmentedAddress) {
SegmentedAddress imageBase = (SegmentedAddress) addrMap.getImageBase();
int baseSegment = imageBase.getSegment();
int segment = lRecord.getIntValue(MemoryMapDBAdapter.SEGMENT_COL);
startAddress = ((SegmentedAddress) startAddress).normalize(segment + baseSegment);
}
- int dbBlockType = lRecord.getShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL);
- blockType = getBlockType(dbBlockType);
- isInitialized = (dbBlockType == MemoryMapDBAdapter.INITIALIZED);
length = lRecord.getLongValue(MemoryMapDBAdapter.LENGTH_COL);
- int bufferID = lRecord.getIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL);
- buf = adapter.getBuffer(bufferID);
+ lastSubBlock = null;
+ subBlocks = list;
}
- private MemoryBlockType getBlockType(int dbType) {
- switch (dbType) {
- case MemoryMapDBAdapter.INITIALIZED:
- case MemoryMapDBAdapter.UNINITIALIZED:
- return startAddress.getAddressSpace().isOverlaySpace() ? MemoryBlockType.OVERLAY
- : MemoryBlockType.DEFAULT;
- case MemoryMapDBAdapter.BIT_MAPPED:
- return MemoryBlockType.BIT_MAPPED;
- case MemoryMapDBAdapter.BYTE_MAPPED:
- return MemoryBlockType.BYTE_MAPPED;
- }
- return MemoryBlockType.DEFAULT;
+ @Override
+ public int compareTo(MemoryBlock o) {
+ return startAddress.compareTo(o.getStart());
+ }
+
+ @Override
+ public int getPermissions() {
+ return record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL);
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getData()
- */
@Override
public InputStream getData() {
return new MemoryBlockInputStream(this);
}
- /**
- * Returns the id for this memory block
- */
- int getID() {
- return id;
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#contains(ghidra.program.model.address.Address)
- */
@Override
public boolean contains(Address addr) {
if (addr.hasSameAddressSpace(startAddress)) {
@@ -132,50 +99,30 @@ class MemoryBlockDB implements MemoryBlock {
return false;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getStart()
- */
@Override
public Address getStart() {
return startAddress;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getEnd()
- */
@Override
public Address getEnd() {
return startAddress.add(length - 1);
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getSize()
- */
@Override
public long getSize() {
return length;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getName()
- */
@Override
public String getName() {
return record.getString(MemoryMapDBAdapter.NAME_COL);
}
- @Override
- public String toString() {
- return getName() + "[" + getStart() + ":" + getEnd() + "]";
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setName(java.lang.String)
- */
@Override
public void setName(String name) throws DuplicateNameException, LockException {
- memMap.lock.acquire();
String oldName = getName();
+ memMap.lock.acquire();
try {
checkValid();
try {
@@ -195,17 +142,11 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getComment()
- */
@Override
public String getComment() {
return record.getString(MemoryMapDBAdapter.COMMENTS_COL);
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setComment(java.lang.String)
- */
@Override
public void setComment(String comment) {
memMap.lock.acquire();
@@ -225,47 +166,11 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#isRead()
- */
@Override
public boolean isRead() {
return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & READ) != 0;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getPermissions()
- */
- @Override
- public int getPermissions() {
- return record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL);
- }
-
- @Override
- public boolean isInitialized() {
- return isInitialized || memMap.getLiveMemoryHandler() != null;
- }
-
- private void setPermissionBit(int permBitMask, boolean enable) {
- byte p = record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL);
- if (enable) {
- p |= permBitMask;
- }
- else {
- p &= ~permBitMask;
- }
- record.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, p);
- try {
- adapter.updateBlockRecord(record);
- }
- catch (IOException e) {
- memMap.dbError(e);
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setRead(boolean)
- */
@Override
public void setRead(boolean r) {
memMap.lock.acquire();
@@ -279,17 +184,11 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#isWrite()
- */
@Override
public boolean isWrite() {
return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & WRITE) != 0;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setWrite(boolean)
- */
@Override
public void setWrite(boolean w) {
memMap.lock.acquire();
@@ -303,17 +202,11 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#isExecute()
- */
@Override
public boolean isExecute() {
return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & EXECUTE) != 0;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setExecute(boolean)
- */
@Override
public void setExecute(boolean x) {
memMap.lock.acquire();
@@ -327,17 +220,26 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#isVolatile()
- */
+ @Override
+ public void setPermissions(boolean read, boolean write, boolean execute) {
+ memMap.lock.acquire();
+ try {
+ checkValid();
+ setPermissionBit(READ, read);
+ setPermissionBit(WRITE, write);
+ setPermissionBit(EXECUTE, execute);
+ memMap.fireBlockChanged(this);
+ }
+ finally {
+ memMap.lock.release();
+ }
+ }
+
@Override
public boolean isVolatile() {
return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & VOLATILE) != 0;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setVolatile(boolean)
- */
@Override
public void setVolatile(boolean v) {
memMap.lock.acquire();
@@ -351,17 +253,11 @@ class MemoryBlockDB implements MemoryBlock {
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getSourceName()
- */
@Override
public String getSourceName() {
return record.getString(MemoryMapDBAdapter.SOURCE_COL);
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#setSourceName(java.lang.String)
- */
@Override
public void setSourceName(String sourceName) {
memMap.lock.acquire();
@@ -381,6 +277,163 @@ class MemoryBlockDB implements MemoryBlock {
}
}
+ @Override
+ public byte getByte(Address addr) throws MemoryAccessException {
+ if (memMap.getLiveMemoryHandler() != null) {
+ return memMap.getByte(addr);
+ }
+ checkValid();
+ long offset = getBlockOffset(addr);
+ return getByte(offset);
+ }
+
+ @Override
+ public int getBytes(Address addr, byte[] b) throws MemoryAccessException {
+ return getBytes(addr, b, 0, b.length);
+ }
+
+ @Override
+ public int getBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
+ if (memMap.getLiveMemoryHandler() != null) {
+ return memMap.getBytes(addr, b, off, len);
+ }
+
+ checkValid();
+ long offset = getBlockOffset(addr);
+ return getBytes(offset, b, off, len);
+ }
+
+ @Override
+ public void putByte(Address addr, byte b) throws MemoryAccessException {
+ if (memMap.getLiveMemoryHandler() != null) {
+ memMap.setByte(addr, b);
+ return;
+ }
+ long offset = getBlockOffset(addr);
+ memMap.lock.acquire();
+ try {
+ checkValid();
+ memMap.checkMemoryWrite(this, addr, 1);
+ putByte(offset, b);
+ memMap.fireBytesChanged(addr, 1);
+ }
+ finally {
+ memMap.lock.release();
+ }
+ }
+
+ @Override
+ public int putBytes(Address addr, byte[] b) throws MemoryAccessException {
+ return putBytes(addr, b, 0, b.length);
+ }
+
+ @Override
+ public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
+ if (memMap.getLiveMemoryHandler() != null) {
+ memMap.setBytes(addr, b, off, len);
+ return len;
+ }
+ memMap.lock.acquire();
+ try {
+ checkValid();
+ memMap.checkMemoryWrite(this, addr, len);
+
+ long offset = getBlockOffset(addr);
+ int n = putBytes(offset, b, off, len);
+
+ memMap.fireBytesChanged(addr, n);
+ return n;
+ }
+ finally {
+ memMap.lock.release();
+ }
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return subBlocks.get(0).isInitialized();
+ }
+
+ @Override
+ public boolean isMapped() {
+ return subBlocks.get(0).isMapped();
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return startAddress.getAddressSpace().isLoadedMemorySpace();
+ }
+
+ void checkValid() {
+ if (invalid) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ private void setPermissionBit(int permBitMask, boolean enable) {
+ byte p = record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL);
+ if (enable) {
+ p |= permBitMask;
+ }
+ else {
+ p &= ~permBitMask;
+ }
+ record.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, p);
+ try {
+ adapter.updateBlockRecord(record);
+ }
+ catch (IOException e) {
+ memMap.dbError(e);
+ }
+ }
+
+ @Override
+ public MemoryBlockType getType() {
+ if (startAddress.getAddressSpace().isOverlaySpace()) {
+ return MemoryBlockType.OVERLAY;
+ }
+ return subBlocks.get(0).getType();
+ }
+
+ public byte getByte(long offset) throws MemoryAccessException {
+ SubMemoryBlock subBlock = getSubBlock(offset);
+ try {
+ return subBlock.getByte(offset);
+ }
+ catch (IOException e) {
+ checkValid(); // may have changed without lock
+ memMap.dbError(e);
+ }
+ return 0;
+ }
+
+ public int getBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
+ if (off < 0 || off + len > b.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ if (offset < 0 || offset >= length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ len = (int) Math.min(len, length - offset);
+
+ int totalCopied = 0;
+
+ try {
+ while (totalCopied < len) {
+ SubMemoryBlock subBlock = getSubBlock(offset + totalCopied);
+ totalCopied += subBlock.getBytes(offset + totalCopied, b, off + totalCopied,
+ len - totalCopied);
+ }
+ }
+ catch (IOException e) {
+ checkValid();
+ memMap.dbError(e);
+ }
+
+ return totalCopied;
+ }
+
protected long getBlockOffset(Address addr) throws MemoryAccessException {
if (!addr.hasSameAddressSpace(startAddress)) {
throw new MemoryAccessException("Address not contained in block: " + addr);
@@ -392,210 +445,94 @@ class MemoryBlockDB implements MemoryBlock {
return offset;
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getByte(ghidra.program.model.address.Address)
- */
- @Override
- public byte getByte(Address addr) throws MemoryAccessException {
-
- long offset = getBlockOffset(addr);
- if (memMap.getLiveMemoryHandler() != null) {
- return memMap.getByte(addr);
- }
- checkBlockType();
+ private void putByte(long offset, byte b) throws MemoryAccessException {
+ SubMemoryBlock subBlock = getSubBlock(offset);
+ memMap.lock.acquire();
try {
- return getByte(offset);
+ subBlock.putByte(offset - subBlock.getStartingOffset(), b);
}
catch (IOException e) {
memMap.dbError(e);
}
- return 0; // should not happen
- }
-
- byte getByte(long offset) throws IOException {
- checkValid();
- try {
- return buf.getByte((int) offset);
- }
- catch (Exception e) {
- checkValid(); // may have changed without lock
- if (e instanceof IOException) {
- throw (IOException) e;
- }
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- throw new RuntimeException("Unexpected Error", e);
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getBytes(ghidra.program.model.address.Address, byte[])
- */
- @Override
- public int getBytes(Address addr, byte[] b) throws MemoryAccessException {
- return getBytes(addr, b, 0, b.length);
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getBytes(ghidra.program.model.address.Address,
- * byte[], int, int)
- */
- @Override
- public int getBytes(Address addr, byte[] b, int off, int size) throws MemoryAccessException {
- long offset = getBlockOffset(addr);
- int len = (int) Math.min(size, length - offset);
- if (memMap.getLiveMemoryHandler() != null) {
- return memMap.getBytes(addr, b, off, len);
- }
- checkValid();
- checkBlockType();
- try {
- buf.get((int) offset, b, off, len);
- }
- catch (Exception e) {
- checkValid(); // may have changed without lock
- if (e instanceof IOException) {
- memMap.dbError((IOException) e);
- }
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- throw new RuntimeException("Unexpected Error", e);
- }
- return len;
- }
-
- int getBytes(long offset, byte[] b, int off, int size) throws IOException {
- int len = (int) Math.min(size, length - offset);
- checkValid();
- try {
- buf.get((int) offset, b, off, len);
- }
- catch (Exception e) {
- checkValid(); // may have changed without lock
- if (e instanceof IOException) {
- throw (IOException) e;
- }
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- }
- throw new RuntimeException("Unexpected Error", e);
- }
- return len;
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#putByte(ghidra.program.model.address.Address, byte)
- */
- @Override
- public void putByte(Address addr, byte b) throws MemoryAccessException {
- long offset = getBlockOffset(addr);
- if (memMap.getLiveMemoryHandler() != null) {
- memMap.setByte(addr, b);
- return;
- }
- memMap.lock.acquire();
- try {
- checkValid();
- checkBlockType();
- memMap.checkMemoryWrite(addr);
- try {
- buf.putByte((int) offset, b);
- }
- catch (IOException e) {
- memMap.dbError(e);
- }
- memMap.fireBytesChanged(addr, 1);
- }
finally {
memMap.lock.release();
}
}
- /**
- * @see ghidra.program.model.mem.MemoryBlock#putBytes(ghidra.program.model.address.Address,
- * byte[])
- */
- @Override
- public int putBytes(Address addr, byte[] b) throws MemoryAccessException {
- return putBytes(addr, b, 0, b.length);
- }
+ private int putBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
+ if (off < 0 || off + len > b.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ if (offset < 0 || offset >= length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
- /**
- * @see ghidra.program.model.mem.MemoryBlock#putBytes(ghidra.program.model.address.Address, byte[], int, int)
- */
- @Override
- public int putBytes(Address addr, byte[] b, int off, int size) throws MemoryAccessException {
+ len = (int) Math.min(len, length - offset);
- int len = 0;
-
- long offset = getBlockOffset(addr);
- len = (int) Math.min(size, length - offset);
- if (len > 0) {
- if (memMap.getLiveMemoryHandler() != null) {
- memMap.setBytes(addr, b, off, len);
- return len;
- }
- memMap.lock.acquire();
- try {
- checkValid();
- checkBlockType();
- memMap.checkMemoryWrite(addr, len);
- try {
- buf.put((int) offset, b, off, len);
- }
- catch (IOException e) {
- memMap.dbError(e);
- }
- memMap.fireBytesChanged(addr, len);
- }
- finally {
- memMap.lock.release();
+ int totalCopied = 0;
+ try {
+ while (totalCopied < len) {
+ SubMemoryBlock subBlock = getSubBlock(offset);
+ totalCopied += subBlock.putBytes(offset + totalCopied, b, off + totalCopied,
+ len - totalCopied);
}
}
- return len;
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getType()
- */
- @Override
- public MemoryBlockType getType() {
- return blockType;
- }
-
- void checkValid() {
- if (invalid) {
- throw new ConcurrentModificationException();
+ catch (IOException e) {
+ checkValid();
+ memMap.dbError(e);
}
+
+ return totalCopied;
}
- void invalidate() {
+ private SubMemoryBlock getSubBlock(long offset) {
+ if (lastSubBlock != null && lastSubBlock.contains(offset)) {
+ return lastSubBlock;
+ }
+ lastSubBlock = findBlock(0, subBlocks.size() - 1, offset);
+ return lastSubBlock;
+ }
+
+ private SubMemoryBlock findBlock(int minIndex, int maxIndex, long offset) {
+ if (minIndex > maxIndex) {
+ throw new IllegalArgumentException("address or offset out of bounds");
+ }
+
+ int index = (maxIndex + minIndex) / 2;
+ SubMemoryBlock block = subBlocks.get(index);
+ if (block.contains(offset)) {
+ return block;
+ }
+ long startingOffset = block.getStartingOffset();
+ if (offset < startingOffset) {
+ return findBlock(minIndex, index - 1, offset);
+ }
+ return findBlock(index + 1, maxIndex, offset);
+ }
+
+ public void invalidate() {
invalid = true;
}
- /**
- * Allows sorting blocks by start address
- * @see java.lang.Comparable#compareTo(java.lang.Object)
- */
- @Override
- public int compareTo(MemoryBlock block) {
- MemoryBlockDB blockDB = (MemoryBlockDB) block;
- return startAddress.compareTo(blockDB.startAddress);
+ void delete() throws IOException {
+ for (SubMemoryBlock subBlock : subBlocks) {
+ subBlock.delete();
+ }
+ adapter.deleteMemoryBlock(getID());
+ invalidate();
}
- void setStartAddress(Address newStartAddr) throws AddressOverflowException, IOException {
+ void setStartAddress(Address newStartAddr) throws IOException, AddressOverflowException {
startAddress = newStartAddr;
- long addr = addrMap.getKey(newStartAddr, true);
// ensure that end address key has also been generated
- Address endAddr = newStartAddr.addNoWrap(length - 1);
- addrMap.getKey(endAddr, true);
+ AddressSet set = new AddressSet(startAddress, startAddress.addNoWrap(length - 1));
+ AddressMapDB addrMap = adapter.getMemoryMap().getAddressMap();
+ addrMap.getKeyRanges(set, true);
- record.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addr);
+ record.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(newStartAddr, true));
if (newStartAddr instanceof SegmentedAddress) {
- SegmentedAddress imageBase = (SegmentedAddress) addrMap.getImageBase();
+ SegmentedAddress imageBase = (SegmentedAddress) memMap.getAddressMap().getImageBase();
int baseSegment = imageBase.getSegment();
int segment = ((SegmentedAddress) startAddress).getSegment();
record.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, segment - baseSegment);
@@ -603,23 +540,8 @@ class MemoryBlockDB implements MemoryBlock {
adapter.updateBlockRecord(record);
}
- void join(MemoryBlockDB memBlock2) throws IOException {
-
- length += memBlock2.length;
- record.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
- if (buf != null) {
- buf.append(memBlock2.buf);
- memBlock2.buf = null;
- }
- adapter.updateBlockRecord(record);
-
- adapter.deleteMemoryBlock(memBlock2);
- }
-
- /**
- * the memoryMapDB must refresh the memory blocks after calling this method.
- */
MemoryBlockDB split(Address addr) throws IOException {
+ lastSubBlock = null;
long offset = addr.subtract(startAddress);
long newLength = length - offset;
@@ -627,77 +549,160 @@ class MemoryBlockDB implements MemoryBlock {
record.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
adapter.updateBlockRecord(record);
- DBBuffer newBuf = null;
- MemoryBlockDB newBlock;
- try {
- if (buf != null) {
- newBuf = buf.split((int) offset);
- newBlock = adapter.createInitializedBlock(getName() + ".split", addr, newBuf,
- getPermissions());
- }
- else {
- newBlock = adapter.createBlock(getType(), getName() + ".split", addr, newLength,
- getOverlayAddress(offset), isInitialized(), getPermissions());
- }
+ List splitBlocks = new ArrayList<>();
+
+ int index = getIndexOfSubBlockToSplit(offset);
+ SubMemoryBlock subMemoryBlock = subBlocks.get(index);
+ if (subMemoryBlock.getStartingOffset() == offset) {
+ // move the sub blocks after the split point to the new split off memory block
+ List subList = subBlocks.subList(index, subBlocks.size());
+ splitBlocks.addAll(subList);
+ subList.clear();
}
- catch (AddressOverflowException e) {
- // Should not occur
- throw new AssertException(e);
+ else {
+ SubMemoryBlock split = subMemoryBlock.split(offset);
+ splitBlocks.add(split);
+ // move the sub blocks after the split point to the new split off memory block
+ List subList = subBlocks.subList(index + 1, subBlocks.size());
+ splitBlocks.addAll(subList);
+ subList.clear();
}
- return newBlock;
+ return adapter.createBlock(getName() + ".split", addr, newLength, getPermissions(),
+ splitBlocks);
}
- Address getOverlayAddress(long offset) {
- return null;
+ private int getIndexOfSubBlockToSplit(long offset) {
+ for (int i = 0; i < subBlocks.size(); i++) {
+ if (subBlocks.get(i).contains(offset)) {
+ return i;
+ }
+ }
+ throw new IllegalArgumentException("offset " + offset + " not in this block");
}
- void initializeBlock(byte initialValue) throws IOException {
- if (length > Integer.MAX_VALUE) {
- throw new AssertException();
+ public void initializeBlock(byte initialValue) throws IOException {
+ lastSubBlock = null;
+ for (SubMemoryBlock subBlock : subBlocks) {
+ subBlock.delete();
}
- buf = adapter.createBuffer((int) length, initialValue);
- record.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
- record.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
- (short) MemoryMapDBAdapter.INITIALIZED);
- isInitialized = true;
+ subBlocks.clear();
+
+ int numFullBlocks = (int) (length / Memory.GBYTE);
+ int lastSubBlockSize = (int) (length % Memory.GBYTE);
+ long blockOffset = 0;
+ for (int i = 0; i < numFullBlocks; i++) {
+ createBufferSubBlock(initialValue, blockOffset, (int) Memory.GBYTE);
+ blockOffset += Memory.GBYTE;
+ }
+ if (lastSubBlockSize > 0) {
+ createBufferSubBlock(initialValue, blockOffset, lastSubBlockSize);
+ }
+ }
+
+ private void createBufferSubBlock(byte initialValue, long blockOffset, int size)
+ throws IOException {
+ DBBuffer buffer = adapter.createBuffer(size, initialValue);
+ Record subBlockRecord = adapter.createSubBlockRecord(id, blockOffset, size,
+ MemoryMapDBAdapter.SUB_TYPE_BUFFER, buffer.getId(), 0);
+
+ BufferSubMemoryBlock sub = new BufferSubMemoryBlock(adapter, subBlockRecord);
+ subBlocks.add(sub);
+ }
+
+ void join(MemoryBlockDB memBlock2) throws IOException {
+ lastSubBlock = null;
+ length += memBlock2.length;
+ record.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
+ int n = subBlocks.size();
+ subBlocks.addAll(memBlock2.subBlocks);
+ possiblyMergeSubBlocks(n - 1, n);
+ sequenceSubBlocks();
+ adapter.deleteMemoryBlock(memBlock2.id);
adapter.updateBlockRecord(record);
+
+ }
+
+ private void sequenceSubBlocks() throws IOException {
+ long startingOffset = 0;
+ for (SubMemoryBlock subBlock : subBlocks) {
+ subBlock.setParentIdAndStartingOffset(id, startingOffset);
+ startingOffset += subBlock.length;
+ }
+ }
+
+ private void possiblyMergeSubBlocks(int lastOld, int firstNew) throws IOException {
+ SubMemoryBlock sub1 = subBlocks.get(lastOld);
+ SubMemoryBlock sub2 = subBlocks.get(firstNew);
+ if (sub1.join(sub2)) {
+ subBlocks.remove(firstNew);
+ }
}
void uninitializeBlock() throws IOException {
- buf.delete();
- buf = null;
- record.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, -1);
- record.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
- (short) MemoryMapDBAdapter.UNINITIALIZED);
- isInitialized = false;
- adapter.updateBlockRecord(record);
- }
-
- private void checkBlockType() throws MemoryAccessException {
- if (!isInitialized) {
- throw UNITIALIZED_EXCEPTION;
+ lastSubBlock = null;
+ for (SubMemoryBlock subBlock : subBlocks) {
+ subBlock.delete();
}
+ subBlocks.clear();
+ Record subRecord = adapter.createSubBlockRecord(id, 0, length,
+ MemoryMapDBAdapter.SUB_TYPE_UNITIALIZED, 0, 0);
+ subBlocks.add(new UninitializedSubMemoryBlock(adapter, subRecord));
+
}
+ // used for upgrade from old versions
DBBuffer getBuffer() {
- return buf;
- }
-
- void delete() throws IOException {
- if (buf != null) {
- buf.delete();
+ if (subBlocks.size() > 1) {
+ throw new IllegalStateException(
+ "Old blocks to be upgraded should only have one sub block");
}
- adapter.deleteMemoryBlock(this);
+ SubMemoryBlock subMemoryBlock = subBlocks.get(0);
+ if (subMemoryBlock instanceof BufferSubMemoryBlock) {
+ return ((BufferSubMemoryBlock) subMemoryBlock).buf;
+ }
+ throw new IllegalStateException("Old blocks to be upgraded not expected type");
+
}
@Override
- public boolean isMapped() {
+ public List getSourceInfos() {
+ List infos = new ArrayList<>(subBlocks.size());
+ for (SubMemoryBlock subBlock : subBlocks) {
+ infos.add(subBlock.getSourceInfo(this));
+ }
+ return infos;
+ }
+
+ boolean uses(FileBytes fileBytes) {
+ for (SubMemoryBlock subBlock : subBlocks) {
+ if (subBlock.uses(fileBytes)) {
+ return true;
+ }
+ }
return false;
}
- @Override
- public boolean isLoaded() {
- return startAddress.getAddressSpace().isLoadedMemorySpace();
+ ByteSourceRangeList getByteSourceRangeList(Address address, long size) {
+ long offset = address.subtract(startAddress);
+ size = Math.min(size, length - offset);
+
+ SubMemoryBlock subBlock = getSubBlock(offset);
+ long subSize = Math.min(size, subBlock.length - (offset - subBlock.getStartingOffset()));
+ if (subSize == size) {
+ return subBlock.getByteSourceRangeList(this, address, offset, size);
+ }
+ Address start = address;
+ ByteSourceRangeList set = subBlock.getByteSourceRangeList(this, start, offset, subSize);
+
+ long total = subSize;
+ while (total < size) {
+ subBlock = getSubBlock(offset + total);
+ subSize = Math.min(size - total, subBlock.length);
+ start = address.add(total);
+ set.add(subBlock.getByteSourceRangeList(this, start, offset + total, subSize));
+ total += subSize;
+ }
+ return set;
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockInputStream.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockInputStream.java
index dab078b89a..2b33d7779f 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockInputStream.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockInputStream.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +18,8 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
+import ghidra.program.model.mem.MemoryAccessException;
+
/**
* Maps a MemoryBlockDB into an InputStream.
*/
@@ -92,7 +93,12 @@ class MemoryBlockInputStream extends InputStream {
if (index >= numBytes) {
return -1;
}
- return block.getByte(index++) & 0xff;
+ try {
+ return block.getByte(index++) & 0xff;
+ }
+ catch (MemoryAccessException e) {
+ throw new IOException(e);
+ }
}
@Override
@@ -104,9 +110,14 @@ class MemoryBlockInputStream extends InputStream {
if (remaining < len) {
len = (int) remaining;
}
- len = block.getBytes(index, b, off, len);
- index += len;
- return len;
+ try {
+ len = block.getBytes(index, b, off, len);
+ index += len;
+ return len;
+ }
+ catch (MemoryAccessException e) {
+ throw new IOException(e);
+ }
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
index 33255c9fa5..0e7d49dcb2 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java
@@ -35,7 +35,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.util.ChangeManager;
import ghidra.util.*;
-import ghidra.util.datastruct.IntObjectHashtable;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
@@ -48,20 +47,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
private ProgramDB program;
private AddressMapDB addrMap;
private MemoryMapDBAdapter adapter;
+ private FileBytesAdapter fileBytesAdapter;
private static final DataConverter BIG_ENDIAN = BigEndianDataConverter.INSTANCE;
private static final DataConverter LITTLE_ENDIAN = LittleEndianDataConverter.INSTANCE;
private DataConverter defaultEndian;
- private MemoryBlock[] blocks;// sorted list of blocks
- private IntObjectHashtable blockMap = new IntObjectHashtable<>();// maps id to MemoryBlockDB objects
- private AddressSet addrSet;
- private AddressSet initializedLoadedAddrSet;
- private AddressSet allInitializedAddrSet;
+ private List blocks;// sorted list of blocks
+ private AddressSet addrSet = new AddressSet();
+ private AddressSet initializedLoadedAddrSet = new AddressSet();
+ private AddressSet allInitializedAddrSet = new AddressSet();
private MemoryBlock lastBlock;// the last accessed block
private LiveMemoryHandler liveMemory;
Lock lock;
+ private Set potentialOverlappingBlocks;
private static Comparator BLOCK_ADDRESS_COMPARATOR = (o1, o2) -> {
MemoryBlock block = (MemoryBlock) o1;
@@ -84,9 +84,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
Lock lock, TaskMonitor monitor) throws IOException, VersionException {
this.addrMap = addrMap;
this.lock = lock;
- adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
- init(false);
+ adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
+ fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, this, monitor);
+ initializeBlocks();
+ buildAddressSets();
+ }
+
+ // for testing
+ MemoryMapDB(DBHandle handle, AddressMapDB addrMap, int openMode, boolean isBigEndian,
+ Lock lock) {
+ this.addrMap = addrMap;
+ this.lock = lock;
+ defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
+ }
+
+ // for testing
+ void init(MemoryMapDBAdapter memoryAdapter, FileBytesAdapter bytesAdapter) {
+ this.adapter = memoryAdapter;
+ this.fileBytesAdapter = bytesAdapter;
}
/**
@@ -104,55 +120,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public void invalidateCache(boolean all) throws IOException {
lock.acquire();
try {
- init(true);
+ reloadAll();
}
finally {
lock.release();
}
}
- private void init(boolean reload) throws IOException {
+ private void buildAddressSets() {
+ addrSet = new AddressSet();
+ initializedLoadedAddrSet = new AddressSet();
+ allInitializedAddrSet = new AddressSet();
+ // we have to process the non-mapped blocks first because to process the mapped
+ // blocks we need the address sets for the non-mapped blocks to be complete
+ for (MemoryBlockDB block : blocks) {
+ if (!block.isMapped()) {
+ addBlockAddresses(block);
+ }
+ }
+ for (MemoryBlockDB block : blocks) {
+ if (block.isMapped()) {
+ addBlockAddresses(block);
+ }
+ }
+
+ }
+
+ private void addBlockAddresses(MemoryBlockDB block) {
+ AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
+ addrSet = addrSet.union(blockSet);
+ if (block.isMapped()) {
+ allInitializedAddrSet =
+ allInitializedAddrSet.union(getMappedIntersection(block, allInitializedAddrSet));
+ initializedLoadedAddrSet = initializedLoadedAddrSet.union(
+ getMappedIntersection(block, initializedLoadedAddrSet));
+
+ }
+ else if (block.isInitialized()) {
+ allInitializedAddrSet = allInitializedAddrSet.union(blockSet);
+ if (block.isLoaded()) {
+ initializedLoadedAddrSet = initializedLoadedAddrSet.union(blockSet);
+ }
+ }
+ }
+
+
+ private void reloadAll() throws IOException {
synchronized (this) {
- if (reload) {
- adapter.refreshMemory();
- }
-
- // Minimize chance of accessing field variables while in flux
- MemoryBlockDB[] newBlocks = adapter.getMemoryBlocks();
- AddressSet newAddrSet = new AddressSet();
- AddressSet newLoadedInitializedAddrSet = new AddressSet();
- AddressSet newAllInitializedAddrSet = new AddressSet();
- List mappedMemoryBlocks = new LinkedList<>();
- IntObjectHashtable newBlockMap = new IntObjectHashtable<>();
- for (MemoryBlockDB block : newBlocks) {
- newBlockMap.put(block.getID(), block);
- newAddrSet.addRange(block.getStart(), block.getEnd());
- if (block.isInitialized()) {
- newAllInitializedAddrSet.addRange(block.getStart(), block.getEnd());
- if (block.isLoaded()) {
- newLoadedInitializedAddrSet.addRange(block.getStart(), block.getEnd());
- }
- }
- if (block.isMapped() && block instanceof MappedMemoryBlock) {
- mappedMemoryBlocks.add((MappedMemoryBlock) block);
- }
- }
-
- // Mapped blocks are uninitialized, but ranges of them may map to initialized ranges.
- // Add in these mapped initialized ranges. Don't forget to keep them up to date as
- // initialized blocks that they map to come and go (this happens automatically because
- // this method gets called on every add/remove).
- newAllInitializedAddrSet.add(
- getMappedIntersection(mappedMemoryBlocks, newAllInitializedAddrSet));
- newLoadedInitializedAddrSet.add(
- getMappedIntersection(mappedMemoryBlocks, newLoadedInitializedAddrSet));
-
- lastBlock = null;
- blocks = newBlocks;
- addrSet = newAddrSet;
- initializedLoadedAddrSet = newLoadedInitializedAddrSet;
- allInitializedAddrSet = newAllInitializedAddrSet;
- blockMap = newBlockMap;
+ fileBytesAdapter.refresh();
+ adapter.refreshMemory();
+ initializeBlocks();
+ buildAddressSets();
}
if (liveMemory != null) {
liveMemory.clearCache();
@@ -160,6 +178,13 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
addrMap.memoryMapChanged(this);
}
+ private synchronized void initializeBlocks() {
+ List newBlocks = adapter.getMemoryBlocks();
+ lastBlock = null;
+ blocks = newBlocks;
+ addrMap.memoryMapChanged(this);
+ }
+
public void setLanguage(Language newLanguage) {
defaultEndian = newLanguage.isBigEndian() ? BIG_ENDIAN : LITTLE_ENDIAN;
}
@@ -171,7 +196,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public void setProgram(ProgramDB program) {
this.program = program;
try {
- init(true);
+ reloadAll();
}
catch (IOException e) {
dbError(e);
@@ -233,32 +258,36 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return initializedLoadedAddrSet;
}
- void checkMemoryWrite(Address start, int length) throws MemoryAccessException {
- CodeManager codeManager = program.getCodeManager();
- Instruction instr = codeManager.getInstructionContaining(start);
- if (instr != null) {
- throw new MemoryAccessException(
- "Memory change conflicts with instruction at " + instr.getMinAddress());
- }
- if (length > 1) {
- instr = codeManager.getInstructionAfter(start);
- if (instr != null) {
- Address end = start.add(length - 1);
- if (instr.getMinAddress().compareTo(end) <= 0) {
- throw new MemoryAccessException(
- "Memory change conflicts with instruction at " + instr.getMinAddress());
+ void checkMemoryWrite(MemoryBlockDB block, Address start, long length)
+ throws MemoryAccessException {
+ checkRangeForInstructions(start, start.add(length - 1));
+
+ Set overlappingBlocks = getPotentialOverlappingBlocks();
+ ByteSourceRangeList changeingByteSource = block.getByteSourceRangeList(start, length);
+ if (overlappingBlocks.contains(block)) {
+ for (MemoryBlock b : overlappingBlocks) {
+ if (b.equals(block)) {
+ continue;
+ }
+ ByteSourceRangeList set =
+ ((MemoryBlockDB) b).getByteSourceRangeList(b.getStart(), b.getSize());
+ ByteSourceRangeList intersect = set.intersect(changeingByteSource);
+ for (ByteSourceRange range : intersect) {
+ checkRangeForInstructions(range.getStart(), range.getEnd());
}
}
}
}
- void checkMemoryWrite(Address addr) throws MemoryAccessException {
- CodeManager codeManager = program.getCodeManager();
- Instruction instr = codeManager.getInstructionContaining(addr);
- if (instr != null) {
- throw new MemoryAccessException(
- "Memory change conflicts with instruction at " + instr.getMinAddress());
+ private Set getPotentialOverlappingBlocks() {
+ if (potentialOverlappingBlocks == null) {
+ ByteSourceRangeList byteSourceList = new ByteSourceRangeList();
+ for (MemoryBlockDB block : blocks) {
+ byteSourceList.add(block.getByteSourceRangeList(block.getStart(), block.getSize()));
+ }
+ potentialOverlappingBlocks = byteSourceList.getOverlappingBlocks();
}
+ return potentialOverlappingBlocks;
}
/**
@@ -288,15 +317,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return lastBlock;
}
}
- MemoryBlock[] tmpBlocks = blocks;
- int index = Arrays.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
+ List tmpBlocks = blocks;
+ int index = Collections.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
if (index >= 0) {
- lastBlock = tmpBlocks[index];
+ lastBlock = tmpBlocks.get(index);
return lastBlock;
}
index = -index - 2;
if (index >= 0) {
- MemoryBlock block = tmpBlocks[index];
+ MemoryBlock block = tmpBlocks.get(index);
if (block.contains(addr)) {
lastBlock = block;
return block;
@@ -305,16 +334,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return null;
}
-// MemoryBlockDB getBlock(int id) {
-// lock.acquire();
-// try {
-// return (MemoryBlockDB) blockMap.get(id);
-// }
-// finally {
-// lock.release();
-// }
-// }
-
void fireBlockAdded(MemoryBlock newBlock) {
AddressRange range = new AddressRangeImpl(newBlock.getStart(), newBlock.getEnd());
program.getTreeManager().addMemoryBlock(newBlock.getName(), range);
@@ -352,7 +371,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
newBlock);
}
- void fireBlockChanged(MemoryBlockDB block) {
+ void fireBlockChanged(MemoryBlock block) {
if (program != null) {
program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null);
}
@@ -468,9 +487,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
}
try {
- adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
- init(true);
- MemoryBlock newBlock = getBlockDB(start);
+ MemoryBlockDB newBlock =
+ adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@@ -486,6 +506,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
finally {
lock.release();
}
+ }
+
+ @Override
+ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
+ long offset, long length, boolean overlay) throws LockException, DuplicateNameException,
+ MemoryConflictException, AddressOverflowException {
+
+ lock.acquire();
+ try {
+ checkBlockSize(length, true);
+ program.checkExclusiveAccess();
+ checkFileBytesRange(fileBytes, offset, length);
+
+ if (overlay) {
+ start = createOverlaySpace(name, start, length);
+ }
+ else {
+ checkRange(start, length);
+ }
+ try {
+ MemoryBlockDB newBlock =
+ adapter.createFileBytesBlock(name, start, length, fileBytes, offset,
+ MemoryBlock.READ);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
+ fireBlockAdded(newBlock);
+ return newBlock;
+ }
+ catch (IOException e) {
+ program.dbError(e);
+ }
+
+ return null;
+ }
+ finally {
+ lock.release();
+ }
+ }
+
+ private void checkFileBytesRange(FileBytes fileBytes, long offset, long length) {
+ if (length < 0) {
+ throw new IllegalArgumentException("Length must be >= 0, got " + length);
+ }
+ if (offset < 0 || offset >= fileBytes.getSize()) {
+ throw new IndexOutOfBoundsException(
+ "Offset must be in range [0," + length + "], got " + offset);
+ }
+ if (offset + length > fileBytes.getSize()) {
+ throw new IndexOutOfBoundsException(
+ "Specified length extends beyond file bytes length");
+ }
}
@@ -507,10 +578,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, size);
}
try {
- adapter.createBlock(MemoryBlockType.DEFAULT, name, start, size, null, false,
- MemoryBlock.READ);
- init(true);
- MemoryBlock newBlock = getBlockDB(start);
+ MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.DEFAULT, name, start,
+ size, null, false, MemoryBlock.READ);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@@ -534,10 +605,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
overlayAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space
try {
- adapter.createBlock(MemoryBlockType.BIT_MAPPED, name, start, length, overlayAddress,
- false, MemoryBlock.READ);
- init(true);
- MemoryBlock newBlock = getBlockDB(start);
+ MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BIT_MAPPED, name,
+ start, length, overlayAddress, false, MemoryBlock.READ);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@@ -561,10 +632,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
overlayAddress.addNoWrap(length - 1);// just to check if length fits in address space
try {
- adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
- overlayAddress, false, MemoryBlock.READ);
- init(true);
- MemoryBlock newBlock = getBlockDB(start);
+ MemoryBlockDB newBlock =
+ adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
+ overlayAddress, false, MemoryBlock.READ);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@@ -589,13 +661,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
try {
Address overlayAddr = null;
- if (block instanceof MappedMemoryBlock) {
- overlayAddr = ((MappedMemoryBlock) block).getOverlayedMinAddress();
+ if (block.isMapped()) {
+ SourceInfo info = block.getSourceInfos().get(0);
+ overlayAddr = info.getMappedRange().get().getMinAddress();
}
- adapter.createBlock(block.getType(), name, start, length, overlayAddr,
+ MemoryBlockDB newBlock =
+ adapter.createBlock(block.getType(), name, start, length, overlayAddr,
block.isInitialized(), block.getPermissions());
- init(true);
- MemoryBlock newBlock = getBlockDB(start);
+ initializeBlocks();
+ addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@@ -625,7 +699,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public MemoryBlock[] getBlocks() {
lock.acquire();
try {
- return blocks.clone();
+ return blocks.toArray(new MemoryBlock[blocks.size()]);
}
finally {
lock.release();
@@ -643,15 +717,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new MemoryBlockException(
"Memory move operation not permitted while live memory is active");
}
- Address oldStartAddr = block.getStart();
- if (!(block instanceof MemoryBlockDB)) {
- throw new NotFoundException("Block does not belong to this program");
- }
+ checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
- if (memBlock.memMap != this) {
- throw new NotFoundException("Block does not belong to this program");
- }
- if (memBlock.getType() == MemoryBlockType.OVERLAY) {
+
+ Address oldStartAddr = block.getStart();
+ if (block.getType() == MemoryBlockType.OVERLAY) {
throw new IllegalArgumentException("Overlay blocks cannot be moved");
}
if (newStartAddr.getAddressSpace().isOverlaySpace()) {
@@ -662,7 +732,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
// events go out that would cause screen updates;
// the code manager will be locked until the remove is done
try {
- memBlock.checkValid();
Address newEndAddr = newStartAddr.addNoWrap(block.getSize() - 1);
AddressSet set = new AddressSet(addrSet);
set.deleteRange(block.getStart(), block.getEnd());
@@ -672,12 +741,12 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
try {
memBlock.setStartAddress(newStartAddr);
- init(false);
+ reloadAll();
}
catch (IOException e) {
program.dbError(e);
}
- program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.length, monitor);
+ program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.getSize(), monitor);
}
finally {
program.invalidate();
@@ -701,9 +770,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new MemoryBlockException(
"Memory split operation not permitted while live memory is active");
}
- if (!(block instanceof MemoryBlockDB)) {
- throw new NotFoundException("Block does not belong to this program");
- }
+ checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
if (!memBlock.contains(addr)) {
throw new IllegalArgumentException("Block must contain split address");
@@ -719,7 +786,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
try {
memBlock.split(addr);
- init(true);
+ initializeBlocks();
fireBlockSplit();
}
catch (IOException e) {
@@ -738,57 +805,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throws MemoryBlockException, NotFoundException, LockException {
lock.acquire();
try {
- program.checkExclusiveAccess();
- if (liveMemory != null) {
- throw new MemoryBlockException(
- "Memory join operation not permitted while live memory is active");
- }
- if (!(blockOne instanceof MemoryBlockDB) || !(blockTwo instanceof MemoryBlockDB)) {
- throw new NotFoundException("Blocks do not belong to this program");
- }
- if (blockOne.getType() != blockTwo.getType()) {
- throw new MemoryBlockException("Blocks of different types can not be joined");
- }
- if (blockOne.isInitialized() != blockTwo.isInitialized()) {
- throw new MemoryBlockException(
- "Both blocks must be either initialized or uninitialized");
- }
- if (blockOne.getType() == MemoryBlockType.OVERLAY) {
- throw new IllegalArgumentException("Cannot join overlay blocks");
- }
- if (blockOne.getType() == MemoryBlockType.BIT_MAPPED) {
- throw new IllegalArgumentException("Cannot join bit mapped blocks");
- }
- if (blockOne.getType() == MemoryBlockType.BYTE_MAPPED) {
- throw new IllegalArgumentException("Cannot join byte mapped blocks");
- }
- long size1 = blockOne.getSize();
- long size2 = blockTwo.getSize();
- if (size1 + size2 > Integer.MAX_VALUE) {
- throw new MemoryBlockException("Blocks are too large to be joined");
- }
+ // swap if second block is before first block
if (blockOne.getStart().compareTo(blockTwo.getStart()) > 0) {
MemoryBlock tmp = blockOne;
blockOne = blockTwo;
blockTwo = tmp;
}
+
+ checkPreconditionsForJoining(blockOne, blockTwo);
+
MemoryBlockDB memBlock1 = (MemoryBlockDB) blockOne;
MemoryBlockDB memBlock2 = (MemoryBlockDB) blockTwo;
+
Address block1Addr = blockOne.getStart();
Address block2Addr = blockTwo.getStart();
- memBlock1.checkValid();
- memBlock2.checkValid();
- if (memBlock1.memMap != this || memBlock2.memMap != this) {
- throw new NotFoundException("Blocks do not belong to this program");
- }
- if (!(memBlock1.getEnd().isSuccessor(memBlock2.getStart()))) {
- throw new MemoryBlockException("Blocks are not contiguous");
- }
MemoryBlock newBlock = null;
try {
memBlock1.join(memBlock2);
- init(true);
+ reloadAll();
newBlock = getBlockDB(block1Addr);
fireBlocksJoined(newBlock, block2Addr);
@@ -804,15 +839,62 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
+ private void checkPreconditionsForJoining(MemoryBlock block1, MemoryBlock block2)
+ throws MemoryBlockException, NotFoundException, LockException {
+
+ program.checkExclusiveAccess();
+ if (liveMemory != null) {
+ throw new MemoryBlockException(
+ "Memory join operation not permitted while live memory is active");
+ }
+
+ checkBlockForJoining(block1);
+ checkBlockForJoining(block2);
+
+ if (block1.isInitialized() != block2.isInitialized()) {
+ throw new MemoryBlockException(
+ "Both blocks must be either initialized or uninitialized");
+ }
+
+ if (!(block1.getEnd().isSuccessor(block2.getStart()))) {
+ throw new MemoryBlockException("Blocks are not contiguous");
+ }
+
+ }
+
+ private void checkBlockForJoining(MemoryBlock block) {
+ checkBlock(block);
+ switch (block.getType()) {
+ case BIT_MAPPED:
+ throw new IllegalArgumentException("Cannot join bit mapped blocks");
+ case BYTE_MAPPED:
+ throw new IllegalArgumentException("Cannot join byte mapped blocks");
+ case OVERLAY:
+ throw new IllegalArgumentException("Cannot join overlay blocks");
+ case DEFAULT:
+ default:
+ // do nothing, these types are ok for joining
+ }
+ }
+
+ private void checkBlock(MemoryBlock block) {
+ if (!(block instanceof MemoryBlockDB)) {
+ throw new IllegalArgumentException("Blocks do not belong to this program");
+ }
+ MemoryBlockDB blockDB = (MemoryBlockDB) block;
+ if (blockDB.memMap != this) {
+ throw new IllegalArgumentException("Blocks do not belong to this program");
+ }
+ blockDB.checkValid();
+ }
+
@Override
public MemoryBlock convertToInitialized(MemoryBlock unitializedBlock, byte initialValue)
throws MemoryBlockException, NotFoundException, LockException {
lock.acquire();
try {
+ checkBlock(unitializedBlock);
program.checkExclusiveAccess();
- if (!(unitializedBlock instanceof MemoryBlockDB)) {
- throw new NotFoundException("Block does not belong to this program");
- }
if (unitializedBlock.isInitialized()) {
throw new IllegalArgumentException(
"Only an Uninitialized Block may be converted to an Initialized Block");
@@ -822,7 +904,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new IllegalArgumentException("Block is of a type that cannot be initialized");
}
long size = unitializedBlock.getSize();
- if (size > MAX_INITIALIZED_BLOCK_SIZE) {
+ if (size > MAX_BLOCK_SIZE) {
throw new MemoryBlockException("Block too large to initialize");
}
MemoryBlockDB memBlock = (MemoryBlockDB) unitializedBlock;
@@ -852,9 +934,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
lock.acquire();
try {
program.checkExclusiveAccess();
- if (!(initializedBlock instanceof MemoryBlockDB)) {
- throw new NotFoundException("Block does not belong to this program");
- }
+ checkBlock(initializedBlock);
if (!initializedBlock.isInitialized()) {
throw new IllegalArgumentException(
"Only an Initialized Block may be converted to an Uninitialized Block");
@@ -1667,14 +1747,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
lock.acquire();
try {
program.checkExclusiveAccess();
-
- if (!(block instanceof MemoryBlockDB)) {
- throw new IllegalArgumentException("Block not in program");
- }
+ checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
- if (blockMap.get(memBlock.getID()) != memBlock) {
- throw new IllegalArgumentException("Block not in program");
- }
Address startAddress = block.getStart();
@@ -1685,7 +1759,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
try {
program.deleteAddressRange(startAddress, memBlock.getEnd(), monitor);
memBlock.delete();
- init(true);
+ reloadAll();
}
catch (IOException e) {
program.dbError(e);
@@ -1758,29 +1832,32 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
* Gets the intersected set of addresses between a list of mapped memory blocks, and some other
* address set.
*
- * @param mappedMemoryBlocks The mapped memory blocks to use in the intersection.
- * @param otherAddrSet Some other address set to use in the intersection.
- * @return The intersected set of addresses between 'mappedMemoryBlocks' and 'otherAddrSet'.
+ * @param block The mapped memory block to use in the intersection.
+ * @param set Some other address set to use in the intersection.
+ * @return The intersected set of addresses between 'mappedMemoryBlock' and other address set
*/
- private AddressSet getMappedIntersection(List mappedMemoryBlocks,
- AddressSet otherAddrSet) {
+ private AddressSet getMappedIntersection(MemoryBlock block, AddressSet set) {
AddressSet mappedIntersection = new AddressSet();
- for (MappedMemoryBlock mappedBlock : mappedMemoryBlocks) {
- AddressSet resolvedIntersection =
- otherAddrSet.intersect(new AddressSet(mappedBlock.getOverlayedAddressRange()));
- for (AddressRange resolvedRange : resolvedIntersection) {
- mappedIntersection.add(getMappedRange(mappedBlock, resolvedRange));
- }
+ List sourceInfos = block.getSourceInfos();
+ // mapped blocks can only ever have one sourceInfo
+ SourceInfo info = sourceInfos.get(0);
+ AddressRange range = info.getMappedRange().get();
+ AddressSet resolvedIntersection = set.intersect(new AddressSet(range));
+ for (AddressRange resolvedRange : resolvedIntersection) {
+ mappedIntersection.add(getMappedRange(block, resolvedRange));
}
-
return mappedIntersection;
}
- private AddressRange getMappedRange(MappedMemoryBlock mappedBlock, AddressRange resolvedRange) {
+ /**
+ * Converts the given address range back from the source range back to the mapped range.
+ */
+ private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange resolvedRange) {
Address start, end;
+ SourceInfo info = mappedBlock.getSourceInfos().get(0);
long startOffset =
- resolvedRange.getMinAddress().subtract(mappedBlock.getOverlayedMinAddress());
+ resolvedRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress());
boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED;
if (isBitMapped) {
start = mappedBlock.getStart().add(startOffset * 8);
@@ -1812,7 +1889,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public final String toString() {
lock.acquire();
try {
- if (blocks == null || blocks.length == 0) {
+ if (blocks == null || blocks.isEmpty()) {
return "[empty]\n";
}
StringBuffer buffer = new StringBuffer();
@@ -1913,11 +1990,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
private void checkBlockSize(long newBlockLength, boolean initialized) {
- long limit = initialized ? MAX_INITIALIZED_BLOCK_SIZE : MAX_UNINITIALIZED_BLOCK_SIZE;
- if (newBlockLength > limit) {
+ if (newBlockLength > MAX_BLOCK_SIZE) {
throw new IllegalStateException(
"New memory block NOT added: exceeds the maximum memory block byte size of " +
- (limit >> GBYTE_SHIFT_FACTOR) + " GByte(s)");
+ MAX_BLOCK_SIZE_GB + " GByte(s)");
}
long newSize = getNumAddresses() + newBlockLength;
@@ -1929,4 +2005,108 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
}
+ @Override
+ public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException {
+ lock.acquire();
+ try {
+ return fileBytesAdapter.createFileBytes(filename, offset, size, is);
+ }
+ finally {
+ lock.release();
+ }
+ }
+
+ @Override
+ public List getAllFileBytes() {
+ List allFileBytes = fileBytesAdapter.getAllFileBytes();
+ return Collections.unmodifiableList(allFileBytes);
+ }
+
+ @Override
+ public boolean deleteFileBytes(FileBytes fileBytes) throws IOException {
+ if (fileBytes.getMemMap() != this) {
+ throw new IllegalArgumentException(
+ "Attempted to delete FileBytes that doesn't belong to this program");
+ }
+ lock.acquire();
+ try {
+ if (inUse(fileBytes)) {
+ return false;
+ }
+ return fileBytesAdapter.deleteFileBytes(fileBytes);
+ }
+ finally {
+ lock.release();
+ }
+ }
+
+ private boolean inUse(FileBytes fileBytes) {
+ for (MemoryBlockDB block : blocks) {
+ if (block.uses(fileBytes)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ FileBytes getLayeredFileBytes(long fileBytesID) throws IOException {
+ List allFileBytes = fileBytesAdapter.getAllFileBytes();
+ for (FileBytes layeredFileBytes : allFileBytes) {
+ if (layeredFileBytes.getId() == fileBytesID) {
+ return layeredFileBytes;
+ }
+ }
+ throw new IOException("No File Bytes found for ID: " + fileBytesID);
+ }
+
+ /**
+ * Returns a list of all memory blocks that contain any addresses in the given range
+ * @param start the start address
+ * @param end the end address
+ * @return a list of all memory blocks that contain any addresses in the given range
+ */
+ List getBlocks(Address start, Address end) {
+ List list = new ArrayList<>();
+
+ List tmpBlocks = blocks;
+ int index = Collections.binarySearch(tmpBlocks, start, BLOCK_ADDRESS_COMPARATOR);
+ if (index < 0) {
+ index = -index - 2;
+ }
+ if (index >= 0) {
+ MemoryBlockDB block = tmpBlocks.get(index);
+ if (block.contains(start)) {
+ list.add(block);
+ }
+ }
+
+ while (++index < tmpBlocks.size()) {
+ MemoryBlockDB block = tmpBlocks.get(index);
+ if (block.getStart().compareTo(end) > 0) {
+ break;
+ }
+ list.add(block);
+ }
+
+ return list;
+ }
+
+ void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException {
+ CodeManager codeManager = program.getCodeManager();
+ Instruction instr = codeManager.getInstructionContaining(start);
+ if (instr != null) {
+ throw new MemoryAccessException(
+ "Memory change conflicts with instruction at " + instr.getMinAddress());
+ }
+ if (!end.equals(start)) {
+ instr = codeManager.getInstructionAfter(start);
+ if (instr != null) {
+ if (instr.getMinAddress().compareTo(end) <= 0) {
+ throw new MemoryAccessException(
+ "Memory change conflicts with instruction at " + instr.getMinAddress());
+ }
+ }
+ }
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java
index fa69812f85..1cd90b11e1 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java
@@ -17,6 +17,7 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
import db.*;
import ghidra.program.model.address.Address;
@@ -28,41 +29,40 @@ import ghidra.util.task.TaskMonitor;
abstract class MemoryMapDBAdapter {
- static final String TABLE_NAME = "Memory Blocks";
+ static final int CURRENT_VERSION = MemoryMapDBAdapterV3.V3_VERSION;
- static final int CURRENT_VERSION = 2;
+ static Schema BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_BLOCK_SCHEMA;
+ static Schema SUB_BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_SUB_BLOCK_SCHEMA;
- static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
- new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
- LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
- IntField.class },
- new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
- "Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
+ public static final int NAME_COL = MemoryMapDBAdapterV3.V3_NAME_COL;
+ public static final int COMMENTS_COL = MemoryMapDBAdapterV3.V3_COMMENTS_COL;
+ public static final int SOURCE_COL = MemoryMapDBAdapterV3.V3_SOURCE_COL;
+ public static final int PERMISSIONS_COL = MemoryMapDBAdapterV3.V3_PERMISSIONS_COL;
+ public static final int START_ADDR_COL = MemoryMapDBAdapterV3.V3_START_ADDR_COL;
+ public static final int LENGTH_COL = MemoryMapDBAdapterV3.V3_LENGTH_COL;
+ public static final int SEGMENT_COL = MemoryMapDBAdapterV3.V3_SEGMENT_COL;
- static final int NAME_COL = 0;
- static final int COMMENTS_COL = 1;
- static final int SOURCE_COL = 2;
- static final int PERMISSIONS_COL = 3;
- static final int START_ADDR_COL = 4;
- static final int BLOCK_TYPE_COL = 5;
- static final int OVERLAY_ADDR_COL = 6;
- static final int LENGTH_COL = 7;
- static final int CHAIN_BUF_COL = 8;
- static final int SEGMENT_COL = 9;
+ public static final int SUB_PARENT_ID_COL = MemoryMapDBAdapterV3.V3_SUB_PARENT_ID_COL;
+ public static final int SUB_TYPE_COL = MemoryMapDBAdapterV3.V3_SUB_TYPE_COL;
+ public static final int SUB_LENGTH_COL = MemoryMapDBAdapterV3.V3_SUB_LENGTH_COL;
+ public static final int SUB_START_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_START_OFFSET_COL;
+ public static final int SUB_SOURCE_ID_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_ID_COL;
+ public static final int SUB_SOURCE_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_OFFSET_COL;
- static final int INITIALIZED = 0;
- static final int UNINITIALIZED = 1;
- static final int BIT_MAPPED = 2;
- static final int BYTE_MAPPED = 4;
+ public static final byte SUB_TYPE_BIT_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BIT_MAPPED;
+ public static final byte SUB_TYPE_BYTE_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BYTE_MAPPED;
+ public static final byte SUB_TYPE_BUFFER = MemoryMapDBAdapterV3.V3_SUB_TYPE_BUFFER;
+ public static final byte SUB_TYPE_UNITIALIZED = MemoryMapDBAdapterV3.V3_SUB_TYPE_UNITIALIZED;
+ public static final byte SUB_TYPE_FILE_BYTES = MemoryMapDBAdapterV3.V3_SUB_TYPE_FILE_BYTES;
static MemoryMapDBAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap,
TaskMonitor monitor) throws VersionException, IOException {
if (openMode == DBConstants.CREATE) {
- return new MemoryMapDBAdapterV2(handle, memMap, true);
+ return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
}
try {
- return new MemoryMapDBAdapterV2(handle, memMap, false);
+ return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, false);
}
catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
@@ -78,10 +78,17 @@ abstract class MemoryMapDBAdapter {
static MemoryMapDBAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap)
throws VersionException, IOException {
+ try {
+ return new MemoryMapDBAdapterV2(handle, memMap);
+ }
+ catch (VersionException e) {
+ // try next oldest version
+ }
try {
return new MemoryMapDBAdapterV1(handle, memMap);
}
catch (VersionException e) {
+ // try next oldest version
}
return new MemoryMapDBAdapterV0(handle, memMap);
}
@@ -91,40 +98,34 @@ abstract class MemoryMapDBAdapter {
try {
monitor.setMessage("Upgrading Memory Blocks...");
- MemoryBlockDB[] blocks = oldAdapter.getMemoryBlocks();
- monitor.initialize(blocks.length * 2);
+ List blocks = oldAdapter.getMemoryBlocks();
+ oldAdapter.deleteTable(handle);
- MemoryMapDBAdapter newAdapter = new MemoryMapDBAdapterV2(handle, memMap, true);
- for (int i = 0; i < blocks.length; i++) {
- MemoryBlockDB block = blocks[i];
+ monitor.initialize(blocks.size() * 2);
+
+ MemoryMapDBAdapter newAdapter =
+ new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
+ for (MemoryBlockDB block : blocks) {
MemoryBlock newBlock = null;
- if (blocks[i].isInitialized()) {
+ if (block.isInitialized()) {
DBBuffer buf = block.getBuffer();
newBlock = newAdapter.createInitializedBlock(block.getName(), block.getStart(),
- buf, MemoryBlock.READ);
+ buf, block.getPermissions());
}
else {
Address mappedAddress = null;
- MemoryBlockType type = block.getType();
- if (type == MemoryBlockType.BIT_MAPPED || type == MemoryBlockType.BYTE_MAPPED) {
- mappedAddress = ((MappedMemoryBlock) block).getOverlayedMinAddress();
+
+ if (block.isMapped()) {
+ SourceInfo info = block.getSourceInfos().get(0);
+ mappedAddress = info.getMappedRange().get().getMinAddress();
}
- newBlock = newAdapter.createBlock(block.getType(), block.getName(),
- block.getStart(), block.getSize(), mappedAddress, false, MemoryBlock.READ);
+ newBlock =
+ newAdapter.createBlock(block.getType(), block.getName(), block.getStart(),
+ block.getSize(), mappedAddress, false, block.getPermissions());
}
newBlock.setComment(block.getComment());
newBlock.setSourceName(block.getSourceName());
- if (block.isExecute()) {
- newBlock.setExecute(true);
- }
- if (block.isWrite()) {
- newBlock.setWrite(true);
- }
- if (block.isVolatile()) {
- newBlock.setVolatile(true);
- }
}
- oldAdapter.deleteTable(handle);
newAdapter.refreshMemory();
return newAdapter;
}
@@ -134,20 +135,13 @@ abstract class MemoryMapDBAdapter {
}
}
- static MemoryBlockDB getMemoryBlock(MemoryMapDBAdapter adapter, Record record, DBBuffer buf,
- MemoryMapDB memMap) throws IOException {
-
- int blockType = record.getShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL);
- switch (blockType) {
- case INITIALIZED:
- case UNINITIALIZED:
- return new MemoryBlockDB(adapter, record, buf, memMap);
- case BIT_MAPPED:
- case BYTE_MAPPED:
- return new OverlayMemoryBlockDB(adapter, record, memMap);
- }
- throw new IllegalStateException("Bad block type");
- }
+ /**
+ * Returns a DBBuffer object for the given database buffer id
+ * @param bufferID the id of the first buffer in the DBBuffer.
+ * @return the DBBuffer for the given id.
+ * @throws IOException if a database IO error occurs.
+ */
+ abstract DBBuffer getBuffer(int bufferID) throws IOException;
abstract void deleteTable(DBHandle handle) throws IOException;
@@ -159,8 +153,9 @@ abstract class MemoryMapDBAdapter {
/**
* Returns an array of memory blocks sorted on start Address
+ * @return all the memory blocks
*/
- abstract MemoryBlockDB[] getMemoryBlocks();
+ abstract List getMemoryBlocks();
/**
* Creates a new initialized block object using data provided from an
@@ -208,31 +203,12 @@ abstract class MemoryMapDBAdapter {
long length, Address mappedAddress, boolean initializeBytes, int permissions)
throws AddressOverflowException, IOException;
- /**
- * Splits a memory block at the given offset and create a new block at the split location.
- * @param block the the split.
- * @param offset the offset within the block at which to split off into a new block
- * @return the new memory block created.
- * @throws IOException if a database IO error occurs.
- */
- abstract MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException;
-
- /**
- * Combines two memory blocks into one.
- * @param block1 the first block
- * @param block2 the second block
- * @return the block that contains the bytes of block1 and block2.
- * @throws IOException if a database IO error occurs.
- */
- abstract MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2)
- throws IOException;
-
/**
* Deletes the given memory block.
- * @param block the block to delete.
+ * @param key the key for the memory block record
* @throws IOException if a database IO error occurs.
*/
- abstract void deleteMemoryBlock(MemoryBlockDB block) throws IOException;
+ abstract void deleteMemoryBlock(long key) throws IOException;
/**
* Updates the memory block record.
@@ -245,15 +221,74 @@ abstract class MemoryMapDBAdapter {
* Creates a new DBuffer object with the given length and initial value.
* @param length block/chunk buffer length (length limited by ChainedBuffer implementation)
* @param initialValue fill value
+ * @return a new DBuffer object with the given length and initial value.
* @throws IOException if a database IO error occurs.
*/
abstract DBBuffer createBuffer(int length, byte initialValue) throws IOException;
/**
- * Returns a DBBuffer object for the given database buffer id
- * @param bufferID the id of the first buffer in the DBBuffer.
+ * Returns the MemoryMap that owns this adapter.
+ * @return the MemoryMap that owns this adapter.
+ */
+ abstract MemoryMapDB getMemoryMap();
+
+ /**
+ * Deletes the sub block record for the given key.
+ * @param key the record id of the sub block record to delete.
+ * @throws IOException if a database error occurs.
+ */
+ abstract void deleteSubBlock(long key) throws IOException;
+
+ /**
+ * Updates the sub memory block record.
+ * @param record the record to update.
* @throws IOException if a database IO error occurs.
*/
- abstract DBBuffer getBuffer(int bufferID) throws IOException;
+ protected abstract void updateSubBlockRecord(Record record) throws IOException;
+ /**
+ * Creates a record for a new created sub block
+ * @param memBlockId the id of the memory block that contains this sub block
+ * @param startingOffset the starting offset relative to the containing memory block where this
+ * sub block starts
+ * @param length the length of this sub block
+ * @param subType the type of the subBlock
+ * @param sourceID if the type is a buffer, then this is the buffer id. If the type is file bytes,
+ * then this is the FileBytes id.
+ * @param sourceOffset if the type is file bytes, then this is the offset into the filebytes. If
+ * the type is mapped, then this is the encoded mapped address.
+ * @return the newly created record.
+ * @throws IOException if a database error occurs
+ */
+ abstract Record createSubBlockRecord(long memBlockId, long startingOffset, long length,
+ byte subType, int sourceID, long sourceOffset) throws IOException;
+
+ /**
+ * Creates a new memory block.
+ * @param name the name of the block
+ * @param startAddress the start address of the block
+ * @param length the length of the block
+ * @param permissions the permissions for the block
+ * @param splitBlocks the list of subBlock objects that make up this block
+ * @return the new MemoryBlock
+ * @throws IOException if a database error occurs
+ */
+ protected abstract MemoryBlockDB createBlock(String name, Address startAddress, long length,
+ int permissions, List splitBlocks) throws IOException;
+
+ /**
+ * Creates a new memory block using a FileBytes
+ * @param name the name of the block
+ * @param startAddress the start address of the block
+ * @param length the length of the block
+ * @param fileBytes the {@link FileBytes} object that provides the bytes for this block
+ * @param offset the offset into the {@link FileBytes} object
+ * @param permissions the permissions for the block
+ * @return the new MemoryBlock
+ * @throws IOException if a database error occurs
+ * @throws AddressOverflowException if block length is too large for the underlying space
+ */
+ protected abstract MemoryBlockDB createFileBytesBlock(String name, Address startAddress,
+ long length, FileBytes fileBytes, long offset, int permissions)
+ throws IOException, AddressOverflowException;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java
index b9a0f72d7f..96c87fa3db 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java
@@ -17,17 +17,11 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
+import java.util.*;
-import db.DBBuffer;
-import db.DBHandle;
-import db.Record;
-import db.RecordIterator;
-import db.Table;
+import db.*;
import ghidra.program.database.map.AddressMap;
-import ghidra.program.model.address.Address;
-import ghidra.program.model.address.AddressFactory;
-import ghidra.program.model.address.SegmentedAddress;
+import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.exception.VersionException;
@@ -73,12 +67,10 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
// "Block Type", "Base Address",
// "Source Block ID"});
- private MemoryBlockDB[] blocks;
+ private List blocks;
private DBHandle handle;
+ private MemoryMapDB memMap;
- /**
- * @param handle
- */
MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap) throws VersionException, IOException {
this(handle, memMap, VERSION);
}
@@ -86,6 +78,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
protected MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap, int expectedVersion)
throws VersionException, IOException {
this.handle = handle;
+ this.memMap = memMap;
AddressMap addrMap = memMap.getAddressMap();
Table table = handle.getTable(V0_TABLE_NAME);
if (table == null) {
@@ -97,10 +90,11 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
", got " + versionNumber);
}
int recCount = table.getRecordCount();
- blocks = new MemoryBlockDB[recCount];
+ blocks = new ArrayList(recCount);
AddressFactory addrFactory = memMap.getAddressFactory();
- int i = 0;
+ int key = 0;
+
RecordIterator it = table.iterator();
while (it.hasNext()) {
Record rec = it.next();
@@ -116,130 +110,117 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
}
Address start = addrFactory.oldGetAddressFromLong(rec.getLongValue(V0_START_ADDR_COL));
long startAddr = addrMap.getKey(start, false);
-
- long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
- try {
- Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
- overlayAddr = addrMap.getKey(ov, false);
- }
- catch (Exception e) {
- }
-
+ long length = rec.getLongValue(V0_LENGTH_COL);
+ long bufID = rec.getIntValue(V0_BUFFER_ID_COL);
int segment = 0;
if (expectedVersion == 1 && (start instanceof SegmentedAddress)) {
segment = rec.getIntValue(V0_SEGMENT_COL);
-// ((SegmentedAddress)start).normalize(segment);
}
- // Convert to new record format
- Record blockRec = MemoryMapDBAdapter.BLOCK_SCHEMA.createRecord(i);
+ Record blockRecord = BLOCK_SCHEMA.createRecord(key);
+ Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
- blockRec.setString(MemoryMapDBAdapter.NAME_COL, rec.getString(V0_NAME_COL));
- blockRec.setString(MemoryMapDBAdapter.COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
- blockRec.setString(MemoryMapDBAdapter.SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
- blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
- blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, startAddr);
- blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
- rec.getShortValue(V0_TYPE_COL));
- blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL, overlayAddr);
- blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, rec.getLongValue(V0_LENGTH_COL));
- blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL,
- rec.getIntValue(V0_BUFFER_ID_COL));
- blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, segment);
+ blockRecord.setString(NAME_COL, rec.getString(V0_NAME_COL));
+ blockRecord.setString(COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
+ blockRecord.setString(SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
+ blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
+ blockRecord.setLongValue(START_ADDR_COL, startAddr);
+ blockRecord.setLongValue(LENGTH_COL, length);
+ blockRecord.setIntValue(SEGMENT_COL, segment);
- blocks[i++] = MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
+ subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
+ subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
+ subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
+
+ int type = rec.getShortValue(V0_TYPE_COL);
+ long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
+ overlayAddr = updateOverlayAddr(addrMap, addrFactory, overlayAddr, type);
+
+ SubMemoryBlock subBlock = getSubBlock(memMap, bufID, subBlockRecord, type, overlayAddr);
+
+ blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
}
- Arrays.sort(blocks);
+ Collections.sort(blocks);
+ }
+
+ private SubMemoryBlock getSubBlock(MemoryMapDB memMap, long bufID, Record record, int type,
+ long overlayAddr) throws IOException {
+ switch (type) {
+ case MemoryMapDBAdapterV2.BIT_MAPPED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
+ record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
+ return new BitMappedSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.BYTE_MAPPED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
+ record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
+ return new ByteMappedSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.INITIALIZED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
+ record.setLongValue(SUB_SOURCE_OFFSET_COL, bufID);
+ return new BufferSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.UNINITIALIZED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
+ return new UninitializedSubMemoryBlock(this, record);
+ default:
+ throw new IOException("Unknown memory block type: " + type);
+ }
+ }
+
+ private long updateOverlayAddr(AddressMap addrMap, AddressFactory addrFactory, long overlayAddr,
+ int type) {
+ if (type == MemoryMapDBAdapterV2.BIT_MAPPED || type == MemoryMapDBAdapterV2.BYTE_MAPPED) {
+ Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
+ overlayAddr = addrMap.getKey(ov, false);
+ }
+ return overlayAddr;
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
- */
@Override
void refreshMemory() throws IOException {
+ // do nothing
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
- */
@Override
- MemoryBlockDB[] getMemoryBlocks() {
+ List getMemoryBlocks() {
return blocks;
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
- */
- @Override
- MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
- throw new UnsupportedOperationException();
- }
-
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
- long length, int permissions)
- throws IOException {
+ long length, int permissions) throws IOException {
throw new UnsupportedOperationException();
}
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
- int permissions)
- throws IOException {
+ int permissions) throws IOException {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
- */
- @Override
- MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#setBlockSize(ghidra.program.database.mem2.MemoryBlockDB, long)
- */
void setBlockSize(MemoryBlockDB block, long size) {
-
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.database.mem2.MemoryBlockDB)
- */
@Override
- void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
+ void deleteMemoryBlock(long key) throws IOException {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
- */
@Override
void deleteTable(DBHandle dbHandle) throws IOException {
dbHandle.deleteTable(V0_TABLE_NAME);
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
- */
@Override
void updateBlockRecord(Record record) throws IOException {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
- */
@Override
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(ghidra.program.model.mem.MemoryBlockType, java.lang.String, ghidra.program.model.address.Address, long, ghidra.program.model.address.Address, boolean)
- */
@Override
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
long length, Address overlayAddr, boolean initializeBytes, int permissions)
@@ -247,9 +228,6 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
- */
@Override
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
@@ -258,4 +236,38 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
return null;
}
+ @Override
+ MemoryMapDB getMemoryMap() {
+ return null;
+ }
+
+ @Override
+ void deleteSubBlock(long key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void updateSubBlockRecord(Record record) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
+ int sourceID, long sourceOffset) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
+ List splitBlocks) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
+ FileBytes fileBytes, long offset, int permissions)
+ throws IOException, AddressOverflowException {
+ throw new UnsupportedOperationException();
+ }
+
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java
index 92cc177967..97b2abba82 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +15,14 @@
*/
package ghidra.program.database.mem;
-import ghidra.util.exception.VersionException;
-
import java.io.IOException;
import db.DBHandle;
+import ghidra.util.exception.VersionException;
+/**
+ * Adapter for version 1
+ */
class MemoryMapDBAdapterV1 extends MemoryMapDBAdapterV0 {
private final static int VERSION = 1;
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java
index eb21fe0508..69b67e6086 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java
@@ -17,266 +17,180 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
+import java.util.*;
import db.*;
-import ghidra.program.database.map.AddressMap;
-import ghidra.program.model.address.*;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.mem.MemoryBlockType;
-import ghidra.util.exception.IOCancelledException;
import ghidra.util.exception.VersionException;
+/**
+ * Adapter for version 2
+ */
class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
+ private static final int V2_VERSION = 2;
+ static final String V2_TABLE_NAME = "Memory Blocks";
- private static final int VERSION = CURRENT_VERSION;
+ static final int V2_NAME_COL = 0;
+ static final int V2_COMMENTS_COL = 1;
+ static final int V2_SOURCE_COL = 2;
+ static final int V2_PERMISSIONS_COL = 3;
+ static final int V2_START_ADDR_COL = 4;
+ static final int V2_BLOCK_TYPE_COL = 5;
+ static final int V2_OVERLAY_ADDR_COL = 6;
+ static final int V2_LENGTH_COL = 7;
+ static final int V2_CHAIN_BUF_COL = 8;
+ static final int V2_SEGMENT_COL = 9;
+
+ static final int INITIALIZED = 0;
+ static final int UNINITIALIZED = 1;
+ static final int BIT_MAPPED = 2;
+ static final int BYTE_MAPPED = 4;
private DBHandle handle;
- private Table blockTable;
private MemoryMapDB memMap;
- private AddressMap addrMap;
- private MemoryBlockDB[] blocks = new MemoryBlockDB[0];
- MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap, boolean create)
+ private List blocks = new ArrayList<>();
+
+// The following schema definition documents the schema used in version 2No
+//
+// static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
+// new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
+// LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
+// IntField.class },
+// new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
+// "Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
+
+
+ protected MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap)
throws VersionException, IOException {
this.handle = handle;
this.memMap = memMap;
- this.addrMap = memMap.getAddressMap();
- if (create) {
- blockTable = handle.createTable(TABLE_NAME, BLOCK_SCHEMA);
+ Table table = handle.getTable(V2_TABLE_NAME);
+ if (table == null) {
+ throw new VersionException("Memory Block table not found");
}
- else {
- blockTable = handle.getTable(TABLE_NAME);
- if (blockTable == null) {
- throw new VersionException(
- handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
- }
- if (blockTable.getSchema().getVersion() != VERSION) {
- int version = blockTable.getSchema().getVersion();
- throw new VersionException(version < VERSION);
- }
-// refreshMemory();
+ int versionNumber = table.getSchema().getVersion();
+ if (versionNumber != V2_VERSION) {
+ throw new VersionException(
+ "Memory Block table: Expected Version " + V2_VERSION + ", got " + versionNumber);
}
- }
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
- */
- @Override
- void refreshMemory() throws IOException {
- MemoryBlockDB[] updatedBlocks = new MemoryBlockDB[blockTable.getRecordCount()];
- RecordIterator it = blockTable.iterator();
- int index = 0;
+ int recCount = table.getRecordCount();
+ blocks = new ArrayList<>(recCount);
+
+ int key = 0;
+
+ RecordIterator it = table.iterator();
while (it.hasNext()) {
- Record blockRec = it.next();
- long key = blockRec.getKey();
- for (int n = 0; n < blocks.length; n++) {
- if (blocks[n] != null && blocks[n].getID() == key) {
- updatedBlocks[index] = blocks[n];
- updatedBlocks[index].refresh(blockRec);
- blocks[n] = null;
- break;
- }
- }
- if (updatedBlocks[index] == null) {
- updatedBlocks[index] =
- MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
- }
- ++index;
+ Record rec = it.next();
+ int permissions = rec.getByteValue(V2_PERMISSIONS_COL);
+
+ long startAddr = rec.getLongValue(V2_START_ADDR_COL);
+ long length = rec.getLongValue(V2_LENGTH_COL);
+ int bufID = rec.getIntValue(V2_CHAIN_BUF_COL);
+ int segment = rec.getIntValue(V2_SEGMENT_COL);
+
+ Record blockRecord = BLOCK_SCHEMA.createRecord(key);
+ Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
+
+ blockRecord.setString(NAME_COL, rec.getString(V2_NAME_COL));
+ blockRecord.setString(COMMENTS_COL, rec.getString(V2_COMMENTS_COL));
+ blockRecord.setString(SOURCE_COL, rec.getString(V2_SOURCE_COL));
+ blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
+ blockRecord.setLongValue(START_ADDR_COL, startAddr);
+ blockRecord.setLongValue(LENGTH_COL, length);
+ blockRecord.setIntValue(SEGMENT_COL, segment);
+
+ subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
+ subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
+ subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
+
+ int type = rec.getShortValue(V2_BLOCK_TYPE_COL);
+ long overlayAddr = rec.getLongValue(V2_OVERLAY_ADDR_COL);
+
+ SubMemoryBlock subBlock = getSubBlock(bufID, subBlockRecord, type, overlayAddr);
+
+ blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
+
+ }
+ Collections.sort(blocks);
+
+ }
+
+ private SubMemoryBlock getSubBlock(int bufID, Record record, int type, long overlayAddr)
+ throws IOException {
+ switch (type) {
+ case MemoryMapDBAdapterV2.BIT_MAPPED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
+ record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
+ return new BitMappedSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.BYTE_MAPPED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
+ record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
+ return new ByteMappedSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.INITIALIZED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
+ record.setIntValue(SUB_SOURCE_ID_COL, bufID);
+ return new BufferSubMemoryBlock(this, record);
+ case MemoryMapDBAdapterV2.UNINITIALIZED:
+ record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
+ return new UninitializedSubMemoryBlock(this, record);
+ default:
+ throw new IOException("Unknown memory block type: " + type);
}
- for (int i = 0; i < blocks.length; i++) {
- if (blocks[i] != null) {
- blocks[i].invalidate();
- }
- }
- Arrays.sort(updatedBlocks);
- blocks = updatedBlocks;
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
- */
@Override
- MemoryBlockDB[] getMemoryBlocks() {
+ List getMemoryBlocks() {
return blocks;
}
- private int getSegment(Address addr) {
- if (addr instanceof SegmentedAddress) {
-// SegmentedAddress imageBase = (SegmentedAddress)addrMap.getImageBase();
-// int baseSegment = imageBase.getSegment();
- return ((SegmentedAddress) addr).getSegment();
- }
- return 0;
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(java.lang.String, ghidra.program.model.address.Address, db.DBBuffer)
- */
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
int permissions) throws AddressOverflowException, IOException {
-
- // Ensure that address key has been generated for end address
- Address endAddr = startAddr.addNoWrap(buf.length() - 1);
- addrMap.getKey(endAddr, true);
-
- int blockID = (int) blockTable.getKey();
- Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
- blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
- blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
- blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
- blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
- blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
- blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, buf.length());
- blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
-
- blockTable.putRecord(blockRec);
-
- return new MemoryBlockDB(this, blockRec, buf, memMap);
+ throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createInitializedBlock(java.lang.String, ghidra.program.model.address.Address, java.io.InputStream, long)
- */
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
long length, int permissions) throws AddressOverflowException, IOException {
-
- // Ensure that address key has been generated for end address
- Address endAddr = startAddr.addNoWrap(length - 1);
- addrMap.getKey(endAddr, true);
-
- int blockID = (int) blockTable.getKey();
- Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
- blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
- blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
- blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
- blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
- blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
- blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
- DBBuffer buf = createBuffer(length, is);
-
- blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
-
- blockTable.putRecord(blockRec);
- return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, buf, memMap);
+ throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(int, java.lang.String, ghidra.program.database.mem2.MemoryChunkDB[], ghidra.program.model.address.Address)
- */
@Override
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
long length, Address mappedAddress, boolean initializeBytes, int permissions)
throws AddressOverflowException, IOException {
- if (initializeBytes) {
- return createInitializedBlock(name, startAddr, null, length, permissions);
- }
-
- // Ensure that address key has been generated for end address
- Address endAddr = startAddr.addNoWrap(length - 1);
- addrMap.getKey(endAddr, true);
-
- int blockID = (int) blockTable.getKey();
- Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
- blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
- blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
- blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
- blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
- (short) encodeBlockType(blockType));
- blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
- blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
- blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, -1);
- if (mappedAddress != null) {
- blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL,
- addrMap.getKey(mappedAddress, true));
- }
-
- blockTable.putRecord(blockRec);
- return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
- }
-
- private int encodeBlockType(MemoryBlockType blockType) {
- if (blockType == MemoryBlockType.BIT_MAPPED) {
- return BIT_MAPPED;
- }
- if (blockType == MemoryBlockType.BYTE_MAPPED) {
- return BYTE_MAPPED;
- }
- return UNINITIALIZED;
- }
-
- private DBBuffer createBuffer(long length, InputStream is) throws IOException {
-
- DBBuffer buf = handle.createBuffer((int) length);
- if (is != null) {
- try {
- buf.fill(is);
- }
- catch (IOCancelledException e) {
- buf.delete();
- throw e;
- }
- }
- return buf;
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
- */
- @Override
- MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
- */
- @Override
- MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.model.mem.MemoryBlock)
- */
- @Override
- void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
- blockTable.deleteRecord(block.getID());
- block.invalidate();
- }
-
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
- */
- @Override
- void deleteTable(DBHandle dbHandle) throws IOException {
throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
- */
+ @Override
+ void deleteMemoryBlock(long key) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void deleteTable(DBHandle dbHandle) throws IOException {
+ dbHandle.deleteTable(V2_TABLE_NAME);
+ }
+
@Override
void updateBlockRecord(Record record) throws IOException {
- blockTable.putRecord(record);
+ throw new UnsupportedOperationException();
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
- */
@Override
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
- DBBuffer buffer = handle.createBuffer(length);
- buffer.fill(0, length - 1, initialValue);
- return buffer;
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void refreshMemory() throws IOException {
+ // do nothing
}
- /**
- * @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
- */
@Override
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
@@ -285,4 +199,38 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
return null;
}
+ @Override
+ MemoryMapDB getMemoryMap() {
+ return memMap;
+ }
+
+ @Override
+ void deleteSubBlock(long key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void updateSubBlockRecord(Record record) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
+ int sourceID, long sourceOffset) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
+ List splitBlocks) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
+ FileBytes fileBytes, long offset, int permissions)
+ throws IOException, AddressOverflowException {
+ throw new UnsupportedOperationException();
+ }
+
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java
new file mode 100644
index 0000000000..1d9e923d1a
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java
@@ -0,0 +1,447 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import db.*;
+import ghidra.program.database.map.AddressMapDB;
+import ghidra.program.model.address.*;
+import ghidra.program.model.mem.MemoryBlockType;
+import ghidra.util.exception.*;
+
+/**
+ * MemoryMap adapter for version 3.
+ * This version introduces the concept of sub memory blocks and FileBytes
+ */
+public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter {
+ public static final int V3_VERSION = 3;
+ public static final String TABLE_NAME = "Memory Blocks";
+ public static final String SUB_BLOCK_TABLE_NAME = "Sub Memory Blocks";
+
+ public static final int V3_NAME_COL = 0;
+ public static final int V3_COMMENTS_COL = 1;
+ public static final int V3_SOURCE_COL = 2;
+ public static final int V3_PERMISSIONS_COL = 3;
+ public static final int V3_START_ADDR_COL = 4;
+ public static final int V3_LENGTH_COL = 5;
+ public static final int V3_SEGMENT_COL = 6;
+
+ public static final int V3_SUB_PARENT_ID_COL = 0;
+ public static final int V3_SUB_TYPE_COL = 1;
+ public static final int V3_SUB_LENGTH_COL = 2;
+ public static final int V3_SUB_START_OFFSET_COL = 3;
+ public static final int V3_SUB_SOURCE_ID_COL = 4;
+ public static final int V3_SUB_SOURCE_OFFSET_COL = 5;
+
+ public static final byte V3_SUB_TYPE_BIT_MAPPED = 0;
+ public static final byte V3_SUB_TYPE_BYTE_MAPPED = 1;
+ public static final byte V3_SUB_TYPE_BUFFER = 2;
+ public static final byte V3_SUB_TYPE_UNITIALIZED = 3;
+ public static final byte V3_SUB_TYPE_FILE_BYTES = 4;
+
+ static Schema V3_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
+ new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
+ LongField.class, LongField.class, IntField.class },
+ new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", "Length",
+ "Segment" });
+
+ static Schema V3_SUB_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
+ new Class[] { LongField.class, ByteField.class, LongField.class, LongField.class,
+ IntField.class, LongField.class },
+ new String[] { "Parent ID", "Type", "Length", "Starting Offset", "Source ID",
+ "Source Address/Offset" });
+
+ private DBHandle handle;
+
+ private Table memBlockTable;
+ private Table subBlockTable;
+ private MemoryMapDB memMap;
+ private AddressMapDB addrMap;
+
+ private List memoryBlocks = new ArrayList<>();
+ private long maxSubBlockSize;
+
+ public MemoryMapDBAdapterV3(DBHandle handle, MemoryMapDB memMap, long maxSubBlockSize,
+ boolean create) throws VersionException, IOException {
+ this.handle = handle;
+ this.memMap = memMap;
+ this.maxSubBlockSize = maxSubBlockSize;
+ this.addrMap = memMap.getAddressMap();
+
+ if (create) {
+ memBlockTable = handle.createTable(TABLE_NAME, V3_BLOCK_SCHEMA);
+ subBlockTable = handle.createTable(SUB_BLOCK_TABLE_NAME, V3_SUB_BLOCK_SCHEMA);
+ }
+ else {
+ memBlockTable = handle.getTable(TABLE_NAME);
+ subBlockTable = handle.getTable(SUB_BLOCK_TABLE_NAME);
+
+ if (memBlockTable == null) {
+ // the table name changed going from V1 to V2
+ throw new VersionException(
+ handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
+ }
+ if (subBlockTable == null || memBlockTable.getSchema().getVersion() != V3_VERSION) {
+ int version = memBlockTable.getSchema().getVersion();
+ throw new VersionException(version < V3_VERSION);
+ }
+ }
+
+ }
+
+ @Override
+ void deleteTable(DBHandle dbHandle) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void refreshMemory() throws IOException {
+ Map> subBlockMap = getSubBlockMap();
+
+ Map blockMap = memoryBlocks.stream().collect(
+ Collectors.toMap(MemoryBlockDB::getID, Function.identity()));
+
+ List newBlocks = new ArrayList<>();
+ RecordIterator it = memBlockTable.iterator();
+ while (it.hasNext()) {
+ Record record = it.next();
+ long key = record.getKey();
+ MemoryBlockDB block = blockMap.remove(key);
+ if (block != null) {
+ block.refresh(record, subBlockMap.get(key));
+ }
+ else {
+ block = new MemoryBlockDB(this, record, subBlockMap.get(key));
+ }
+ newBlocks.add(block);
+ }
+ for (MemoryBlockDB block : blockMap.values()) {
+ block.invalidate();
+ }
+ Collections.sort(newBlocks);
+ memoryBlocks = newBlocks;
+ }
+
+ @Override
+ List getMemoryBlocks() {
+ return memoryBlocks;
+ }
+
+ @Override
+ MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
+ long length, int permissions) throws AddressOverflowException, IOException {
+ updateAddressMapForAllAddresses(startAddr, length);
+
+ List subBlocks = new ArrayList<>();
+ try {
+ Record blockRecord = createMemoryBlockRecord(name, startAddr, length, permissions);
+ long key = blockRecord.getKey();
+ int numFullBlocks = (int) (length / maxSubBlockSize);
+ int lastSubBlockSize = (int) (length % maxSubBlockSize);
+ long blockOffset = 0;
+ for (int i = 0; i < numFullBlocks; i++) {
+ subBlocks.add(createBufferSubBlock(key, blockOffset, maxSubBlockSize, is));
+ blockOffset += maxSubBlockSize;
+ }
+ if (lastSubBlockSize > 0) {
+ subBlocks.add(createBufferSubBlock(key, blockOffset, lastSubBlockSize, is));
+ }
+ memBlockTable.putRecord(blockRecord);
+
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
+ memoryBlocks.add(newBlock);
+ Collections.sort(memoryBlocks);
+ return newBlock;
+ }
+ catch (IOException e) {
+ // clean up any created DBBufferss
+ for (SubMemoryBlock subMemoryBlock : subBlocks) {
+ BufferSubMemoryBlock bufferSubMemoryBlock = (BufferSubMemoryBlock) subMemoryBlock;
+ subBlockTable.deleteRecord(bufferSubMemoryBlock.getKey());
+ bufferSubMemoryBlock.buf.delete();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
+ long length, Address mappedAddress, boolean initializeBytes, int permissions)
+ throws AddressOverflowException, IOException {
+
+ if (initializeBytes) {
+ return createInitializedBlock(name, startAddr, null, length, permissions);
+ }
+ else if (blockType == MemoryBlockType.BIT_MAPPED) {
+ return createBitMappedBlock(name, startAddr, length, mappedAddress, permissions);
+ }
+ else if (blockType == MemoryBlockType.BYTE_MAPPED) {
+ return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions);
+ }
+ return createUnitializedBlock(name, startAddr, length, permissions);
+ }
+
+ @Override
+ MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
+ int permissions) throws AddressOverflowException, IOException {
+ updateAddressMapForAllAddresses(startAddr, buf.length());
+
+ List subBlocks = new ArrayList<>();
+ Record blockRecord = createMemoryBlockRecord(name, startAddr, buf.length(), permissions);
+ long key = blockRecord.getKey();
+
+ Record subRecord =
+ createSubBlockRecord(key, 0, buf.length(), V3_SUB_TYPE_BUFFER, buf.getId(), 0);
+ subBlockTable.putRecord(subRecord);
+ subBlocks.add(new BufferSubMemoryBlock(this, subRecord));
+
+ memBlockTable.putRecord(blockRecord);
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
+ memoryBlocks.add(newBlock);
+ Collections.sort(memoryBlocks);
+ return newBlock;
+ }
+
+ MemoryBlockDB createUnitializedBlock(String name, Address startAddress, long length,
+ int permissions) throws IOException, AddressOverflowException {
+ updateAddressMapForAllAddresses(startAddress, length);
+
+ List subBlocks = new ArrayList<>();
+ Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
+ long key = blockRecord.getKey();
+
+ Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_UNITIALIZED, 0, 0);
+ subBlocks.add(new UninitializedSubMemoryBlock(this, subRecord));
+
+ memBlockTable.putRecord(blockRecord);
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
+ memoryBlocks.add(newBlock);
+ Collections.sort(memoryBlocks);
+ return newBlock;
+ }
+
+ @Override
+ protected MemoryBlockDB createBlock(String name, Address startAddress, long length,
+ int permissions, List splitBlocks) throws IOException {
+ Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
+ long key = blockRecord.getKey();
+
+ long startingOffset = 0;
+ for (SubMemoryBlock subMemoryBlock : splitBlocks) {
+ subMemoryBlock.setParentIdAndStartingOffset(key, startingOffset);
+ startingOffset += subMemoryBlock.length;
+ }
+
+ memBlockTable.putRecord(blockRecord);
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, splitBlocks);
+ int insertionIndex = Collections.binarySearch(memoryBlocks, newBlock);
+ if (insertionIndex >= 0) { // should not find direct hit
+ throw new AssertException("New memory block collides with existing block");
+ }
+ memoryBlocks.add(-insertionIndex - 1, newBlock);
+ return newBlock;
+ }
+
+ MemoryBlockDB createBitMappedBlock(String name, Address startAddress, long length,
+ Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
+ return createMappedBlock(V3_SUB_TYPE_BIT_MAPPED, name, startAddress, length, mappedAddress,
+ permissions);
+ }
+
+ MemoryBlockDB createByteMappedBlock(String name, Address startAddress, long length,
+ Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
+ return createMappedBlock(V3_SUB_TYPE_BYTE_MAPPED, name, startAddress, length, mappedAddress,
+ permissions);
+ }
+
+ @Override
+ protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
+ FileBytes fileBytes, long offset, int permissions)
+ throws IOException, AddressOverflowException {
+
+ updateAddressMapForAllAddresses(startAddress, length);
+ List subBlocks = new ArrayList<>();
+ Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
+ long key = blockRecord.getKey();
+
+ Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_FILE_BYTES,
+ (int) fileBytes.getId(), offset);
+ subBlocks.add(createSubBlock(subRecord));
+
+ memBlockTable.putRecord(blockRecord);
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
+ memoryBlocks.add(newBlock);
+ Collections.sort(memoryBlocks);
+ return newBlock;
+ }
+
+ private MemoryBlockDB createMappedBlock(byte type, String name, Address startAddress,
+ long length, Address mappedAddress, int permissions)
+ throws IOException, AddressOverflowException {
+ updateAddressMapForAllAddresses(startAddress, length);
+
+ List subBlocks = new ArrayList<>();
+ Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
+ long key = blockRecord.getKey();
+
+ long encoded = addrMap.getKey(mappedAddress, true);
+ Record subRecord = createSubBlockRecord(key, 0, length, type, 0, encoded);
+ subBlocks.add(createSubBlock(subRecord));
+
+ memBlockTable.putRecord(blockRecord);
+ MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
+ memoryBlocks.add(newBlock);
+ Collections.sort(memoryBlocks);
+ return newBlock;
+ }
+
+ @Override
+ void deleteMemoryBlock(long key) throws IOException {
+ memBlockTable.deleteRecord(key);
+ }
+
+ @Override
+ void deleteSubBlock(long key) throws IOException {
+ subBlockTable.deleteRecord(key);
+ }
+
+ @Override
+ void updateBlockRecord(Record record) throws IOException {
+ memBlockTable.putRecord(record);
+ }
+
+ @Override
+ protected void updateSubBlockRecord(Record record) throws IOException {
+ subBlockTable.putRecord(record);
+ }
+
+ @Override
+ DBBuffer getBuffer(int bufferID) throws IOException {
+ if (bufferID >= 0) {
+ return handle.getBuffer(bufferID);
+ }
+ return null;
+ }
+
+ @Override
+ MemoryMapDB getMemoryMap() {
+ return memMap;
+ }
+
+ @Override
+ Record createSubBlockRecord(long parentKey, long startingOffset, long length, byte type,
+ int sourceId, long sourceOffset) throws IOException {
+
+ Record record = V3_SUB_BLOCK_SCHEMA.createRecord(subBlockTable.getKey());
+ record.setLongValue(V3_SUB_PARENT_ID_COL, parentKey);
+ record.setByteValue(V3_SUB_TYPE_COL, type);
+ record.setLongValue(V3_SUB_LENGTH_COL, length);
+ record.setLongValue(V3_SUB_START_OFFSET_COL, startingOffset);
+ record.setIntValue(V3_SUB_SOURCE_ID_COL, sourceId);
+ record.setLongValue(V3_SUB_SOURCE_OFFSET_COL, sourceOffset);
+ subBlockTable.putRecord(record);
+
+ return record;
+ }
+
+ private Record createMemoryBlockRecord(String name, Address startAddr, long length,
+ int permissions) {
+ Record record = V3_BLOCK_SCHEMA.createRecord(memBlockTable.getKey());
+ record.setString(V3_NAME_COL, name);
+ record.setLongValue(V3_START_ADDR_COL, addrMap.getKey(startAddr, true));
+ record.setLongValue(V3_LENGTH_COL, length);
+ record.setByteValue(V3_PERMISSIONS_COL, (byte) permissions);
+ record.setIntValue(V3_SEGMENT_COL, getSegment(startAddr));
+ return record;
+ }
+
+ private Map> getSubBlockMap() throws IOException {
+ List subBlocks = new ArrayList<>(subBlockTable.getRecordCount());
+ RecordIterator it = subBlockTable.iterator();
+ while (it.hasNext()) {
+ Record record = it.next();
+ subBlocks.add(createSubBlock(record));
+ }
+ return subBlocks.stream().collect(Collectors.groupingBy(SubMemoryBlock::getParentBlockID));
+ }
+
+ private int getSegment(Address addr) {
+ if (addr instanceof SegmentedAddress) {
+ return ((SegmentedAddress) addr).getSegment();
+ }
+ return 0;
+ }
+
+ private void updateAddressMapForAllAddresses(Address startAddress, long length)
+ throws AddressOverflowException {
+ AddressSet set = new AddressSet(startAddress, startAddress.addNoWrap(length - 1));
+ addrMap.getKeyRanges(set, true);
+ }
+
+ private SubMemoryBlock createSubBlock(Record record) throws IOException {
+ byte byteValue = record.getByteValue(V3_SUB_TYPE_COL);
+
+ switch (byteValue) {
+ case V3_SUB_TYPE_BIT_MAPPED:
+ return new BitMappedSubMemoryBlock(this, record);
+ case V3_SUB_TYPE_BYTE_MAPPED:
+ return new ByteMappedSubMemoryBlock(this, record);
+ case V3_SUB_TYPE_BUFFER:
+ return new BufferSubMemoryBlock(this, record);
+ case V3_SUB_TYPE_UNITIALIZED:
+ return new UninitializedSubMemoryBlock(this, record);
+ case V3_SUB_TYPE_FILE_BYTES:
+ return new FileBytesSubMemoryBlock(this, record);
+ default:
+ throw new AssertException("Unhandled sub block type: " + byteValue);
+ }
+ }
+
+ private SubMemoryBlock createBufferSubBlock(long parentKey, long offset, long length,
+ InputStream is) throws IOException {
+ DBBuffer buffer = createBuffer(length, is);
+ Record record =
+ createSubBlockRecord(parentKey, offset, length, V3_SUB_TYPE_BUFFER, buffer.getId(), 0);
+ return new BufferSubMemoryBlock(this, record);
+ }
+
+ private DBBuffer createBuffer(long length, InputStream is) throws IOException {
+ DBBuffer buf = handle.createBuffer((int) length);
+ if (is != null) {
+ try {
+ buf.fill(is);
+ }
+ catch (IOCancelledException e) {
+ buf.delete();
+ throw e;
+ }
+ }
+ return buf;
+ }
+
+ @Override
+ DBBuffer createBuffer(int length, byte initialValue) throws IOException {
+ DBBuffer buffer = handle.createBuffer(length);
+ buffer.fill(0, length - 1, initialValue);
+ return buffer;
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/OverlayMemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/OverlayMemoryBlockDB.java
deleted file mode 100644
index a631a9ab54..0000000000
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/OverlayMemoryBlockDB.java
+++ /dev/null
@@ -1,261 +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.mem;
-
-import java.io.IOException;
-
-import db.Record;
-import ghidra.program.model.address.*;
-import ghidra.program.model.mem.*;
-
-/**
- * Class to handle memory blocks that are "overlayed" on other blocks. In other words, this
- * block just maps request for bytes from one address to another. It also handles bit
- * overlay blocks, in which case bit value requests are translated into a byte/bit into another
- * block.
- */
-class OverlayMemoryBlockDB extends MemoryBlockDB implements MappedMemoryBlock {
- // ioPending is flag used to prevent cyclic memory access. Since this memory
- // block uses memory to resolve its reads and writes, we have to be careful that
- // some other block that this block uses, doesn't also use this block in return.
-
- private boolean ioPending;
- private static final MemoryAccessException IOPENDING_EXCEPTION =
- new MemoryAccessException("Cyclic Access");
-
- private static final MemoryAccessException MEMORY_ACCESS_EXCEPTION =
- new MemoryAccessException("No memory at address");
-
- private Address overlayStart;
- private Address overlayEnd;
- private boolean bitOverlay;
-
- /**
- * Constructs a new OverlayMemoryBlockDB
- * @param adapter the memory block database adapter
- * @param record the record for this block
- * @param memMap the memory map manager
- * @param bitOverlay if true this is a bit overlay memory.
- * @throws IOException if a database IO error occurs.
- */
- OverlayMemoryBlockDB(MemoryMapDBAdapter adapter, Record record, MemoryMapDB memMap)
- throws IOException {
- super(adapter, record, null, memMap);
- }
-
- @Override
- void refresh(Record lRecord) throws IOException {
- super.refresh(lRecord);
- this.bitOverlay = blockType == MemoryBlockType.BIT_MAPPED;
- long base = record.getLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL);
- overlayStart = addrMap.decodeAddress(base);
- try {
- overlayEnd = overlayStart.addNoWrap((bitOverlay ? (length - 1) / 8 : (length - 1)));
- }
- catch (AddressOverflowException e) {
- throw new RuntimeException("Overlay range extends beyond address space");
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MappedMemoryBlock#getOverlayedMinAddress()
- */
- @Override
- public Address getOverlayedMinAddress() {
- return overlayStart;
- }
-
- @Override
- Address getOverlayAddress(long offset) {
- return overlayStart.add(bitOverlay ? offset / 8 : offset);
- }
-
- private byte getBitOverlayByte(long blockOffset)
- throws AddressOverflowException, MemoryAccessException {
- Address otherAddr = overlayStart.addNoWrap(blockOffset / 8);
- byte b = memMap.getByte(otherAddr);
- return (byte) ((b >> (blockOffset % 8)) & 0x01);
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getByte(ghidra.program.model.address.Address)
- */
- @Override
- public byte getByte(Address addr) throws MemoryAccessException {
- memMap.lock.acquire();
- try {
- checkValid();
- if (ioPending) {
- throw IOPENDING_EXCEPTION;
- }
- ioPending = true;
- long offset = getBlockOffset(addr);
- if (bitOverlay) {
- return getBitOverlayByte(offset);
- }
- return memMap.getByte(overlayStart.addNoWrap(offset));
- }
- catch (AddressOverflowException e) {
- throw MEMORY_ACCESS_EXCEPTION;
- }
- finally {
- ioPending = false;
- memMap.lock.release();
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#getBytes(ghidra.program.model.address.Address, byte, int, int)
- */
- @Override
- public int getBytes(Address addr, byte[] bytes, int off, int len) throws MemoryAccessException {
- memMap.lock.acquire();
- try {
- checkValid();
- if (ioPending) {
- throw IOPENDING_EXCEPTION;
- }
- ioPending = true;
- long offset = getBlockOffset(addr);
- long size = getSize();
- if (len > size - (addr.subtract(startAddress))) {
- len = (int) (size - addr.subtract(startAddress));
- }
- if (bitOverlay) {
- for (int i = 0; i < len; i++) {
- bytes[i + off] = getBitOverlayByte(offset++);
- }
- return len;
- }
- return memMap.getBytes(overlayStart.addNoWrap(offset), bytes, off, len);
- }
- catch (AddressOverflowException e) {
- throw MEMORY_ACCESS_EXCEPTION;
- }
- finally {
- ioPending = false;
- memMap.lock.release();
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#putByte(ghidra.program.model.address.Address, byte)
- */
- @Override
- public void putByte(Address addr, byte b) throws MemoryAccessException {
- memMap.lock.acquire();
- try {
- checkValid();
- if (ioPending) {
- throw IOPENDING_EXCEPTION;
- }
- ioPending = true;
- long offset = getBlockOffset(addr);
- if (bitOverlay) {
- checkValid();
- doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b);
- }
- else {
- memMap.setByte(overlayStart.add(offset), b);
- }
- }
- finally {
- ioPending = false;
- memMap.lock.release();
- }
- }
-
- /**
- * @see ghidra.program.model.mem.MemoryBlock#putBytes(ghidra.program.model.address.Address, byte, int, int)
- */
- @Override
- public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
- memMap.lock.acquire();
- try {
- checkValid();
- if (ioPending) {
- throw IOPENDING_EXCEPTION;
- }
- ioPending = true;
- long offset = getBlockOffset(addr);
- long size = getSize();
- if (len > size - (addr.subtract(startAddress))) {
- len = (int) (size - addr.subtract(startAddress));
- }
- if (bitOverlay) {
- for (int i = 0; i < len; i++) {
- doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b[off + i]);
- addr = addr.add(1);
- offset++;
- }
- }
- else {
- memMap.setBytes(overlayStart.add(offset), b, off, len);
- }
- return len;
- }
- finally {
- ioPending = false;
- memMap.lock.release();
- }
- }
-
- private void doPutByte(Address addr, int bitIndex, byte b) throws MemoryAccessException {
- ioPending = true;
- byte value = memMap.getByte(addr);
- int mask = 1 << (bitIndex % 8);
- if (b == 0) {
- value &= ~mask;
- }
- else {
- value |= mask;
- }
- memMap.setByte(addr, value);
- }
-
-// void dataChanged(Address addr, int cnt) {
-// Address endAddr = addr.addWrap(cnt);
-// if (addr.compareTo(overlayEnd) > 0 || endAddr.compareTo(overlayStart) < 0) {
-// return;
-// }
-// if (ioPending) {
-// return;
-// }
-// try {
-// ioPending = true;
-// Address minAddr = addr.compareTo(overlayStart) > 0 ? addr : overlayStart;
-// Address maxAddr = endAddr.compareTo(overlayEnd) < 0 ? endAddr : overlayEnd;
-// Address myStartAddr =
-// startAddress.add(minAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
-// Address myEndAddr =
-// startAddress.add(maxAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
-// memMap.fireBytesChanged(myStartAddr, (int) myEndAddr.subtract(myStartAddr) + 1);
-// }
-// finally {
-// ioPending = false;
-// }
-// }
-
- @Override
- public boolean isMapped() {
- return true;
- }
-
- @Override
- public AddressRange getOverlayedAddressRange() {
- return new AddressRangeImpl(overlayStart, overlayEnd);
- }
-}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java
new file mode 100644
index 0000000000..69b65f5003
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java
@@ -0,0 +1,117 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.database.mem;
+
+import java.util.Optional;
+
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.mem.MemoryBlock;
+
+/**
+ * Class for describing the source of bytes for a memory block.
+ */
+public class SourceInfo {
+
+ final MemoryBlock block;
+ final SubMemoryBlock subBlock;
+
+ SourceInfo(MemoryBlock block, SubMemoryBlock subBlock) {
+ this.block = block;
+ this.subBlock = subBlock;
+ }
+
+ /**
+ * Returns the length of this block byte source.
+ * @return the length of this block byte source.
+ */
+ public long getLength() {
+ return subBlock.length;
+ }
+
+ /**
+ * Returns the start address where this byte source is mapped.
+ * @return the start address where this byte source is mapped.
+ */
+ public Address getMinAddress() {
+ return block.getStart().add(subBlock.startingOffset);
+ }
+
+ /**
+ * Returns the end address where this byte source is mapped.
+ * @return the end address where this byte source is mapped.
+ */
+ public Address getMaxAddress() {
+ return block.getStart().add(subBlock.startingOffset + subBlock.length - 1);
+ }
+
+ /**
+ * Returns a description of this SourceInfo object.
+ * @return a description of this SourceInfo object.
+ */
+ public String getDescription() {
+ return subBlock.getDescription();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + ": StartAddress = " + getMinAddress() + ", length = " +
+ getLength();
+
+ }
+
+ /**
+ * Returns an {@link Optional} {@link FileBytes} object if a FileBytes object is the byte
+ * source for this SourceInfo. Otherwise, the Optional will be empty.
+ * @return the {@link FileBytes} object if it is the byte source for this section
+ */
+ public Optional getFileBytes() {
+ if (subBlock instanceof FileBytesSubMemoryBlock) {
+ return Optional.of(((FileBytesSubMemoryBlock) subBlock).getFileBytes());
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Returns the offset into the {@link FileBytes} object where this section starts getting its bytes or
+ * -1 if this SourceInfo does not have an associated {@link FileBytes}
+ * @return the offset into the {@link FileBytes} object where this section starts getting its bytes.
+ */
+ public long getFileBytesOffset() {
+ if (subBlock instanceof FileBytesSubMemoryBlock) {
+ return ((FileBytesSubMemoryBlock) subBlock).getFileBytesOffset();
+ }
+ return -1;
+ }
+
+ /**
+ * Returns an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
+ * memory block (bit mapped or byte mapped). Otherwise, the Optional is empty.
+ * @return an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
+ * memory block
+ */
+ public Optional getMappedRange() {
+ if (subBlock instanceof BitMappedSubMemoryBlock) {
+ BitMappedSubMemoryBlock bitMapped = (BitMappedSubMemoryBlock) subBlock;
+ return Optional.of(bitMapped.getMappedRange());
+ }
+ if (subBlock instanceof ByteMappedSubMemoryBlock) {
+ ByteMappedSubMemoryBlock byteMapped = (ByteMappedSubMemoryBlock) subBlock;
+ return Optional.of(byteMapped.getMappedRange());
+ }
+ return Optional.empty();
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java
new file mode 100644
index 0000000000..fba75aff6b
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java
@@ -0,0 +1,251 @@
+/* ###
+ * 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.mem;
+
+import java.io.IOException;
+
+import db.Record;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressRange;
+import ghidra.program.model.mem.*;
+
+/**
+ * Interface for the various types of memory block sections. They are used by a {@link MemoryBlockDB}
+ * to do the actual storing and fetching of the bytes that make up a MemoryBlock
+ */
+abstract class SubMemoryBlock {
+
+ protected final MemoryMapDBAdapter adapter;
+ protected final Record record;
+ protected long length;
+ protected long startingOffset;
+
+ protected SubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
+ this.adapter = adapter;
+ this.record = record;
+ this.startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
+ this.length = record.getLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL);
+ }
+
+ /**
+ * Return whether this block has been initialized (has byte values)
+ *
+ * @return true if the block has associated byte values.
+ */
+ public abstract boolean isInitialized();
+
+ /**
+ * Returns the id of the MemoryBlockDB object that owns this sub block.
+ * @return the id of the MemoryBlockDB object that owns this sub block.
+ */
+ public final long getParentBlockID() {
+ return record.getLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL);
+ }
+
+ /**
+ * Returns the starting offset for this sub block. In other words, the first byte in this sub
+ * block is at this starting offset relative to the containing {@link MemoryBlockDB}
+ *
+ * @return the starting offset for this sub block.
+ */
+ public final long getStartingOffset() {
+ return startingOffset;
+ }
+
+ /**
+ * Returns the length of this sub block
+ * @return the length of this sub block
+ */
+ public final long getLength() {
+ return length;
+ }
+
+ /**
+ * Returns true if the given {@link MemoryBlockDB} offset is in this sub block.
+ *
+ * @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
+ * @return true if the offset is valid for this block
+ */
+ public final boolean contains(long memBlockOffset) {
+ return memBlockOffset >= startingOffset && memBlockOffset < startingOffset + length;
+ }
+
+ /**
+ * Returns the byte in this sub block corresponding to the given offset relative to the containing
+ * {@link MemoryBlockDB}. In other words, the first byte in this sub block can be retrieved
+ * using an offset equal to this blocks starting offset.
+ *
+ * @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
+ * @return the byte at the given containing block offset.
+ * @throws MemoryAccessException if the block is uninitialized.
+ * @throws IOException if there is a problem reading from the database
+ */
+ public abstract byte getByte(long memBlockOffset) throws MemoryAccessException, IOException;
+
+ /**
+ * Tries to get len bytes from this block at the given offset (relative to the containing
+ * {@link MemoryBlockDB} and put them into the given byte array at the specified offset.
+ * May return fewer bytes if the requested length is beyond the end of the block.
+ * @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
+ * @param b the byte array to populate.
+ * @param off the offset into the byte array.
+ * @param len the number of bytes to get.
+ * @return the number of bytes actually populated.
+ * @throws MemoryAccessException if any of the requested bytes are
+ * uninitialized.
+ * @throws IOException if there is a problem reading from the database
+ * @throws IllegalArgumentException if the offset is not in this block.
+ */
+ public abstract int getBytes(long memBlockOffset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException;
+
+ /**
+ * Stores the byte in this sub block at the given offset relative to the containing
+ * {@link MemoryBlockDB}. In other words, the first byte in this sub block can be targeted
+ * using an offset equal to this blocks starting offset.
+ *
+ * @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
+ * @param b the byte value to store at the given offset.
+ * @throws MemoryAccessException if the block is uninitialized
+ * @throws IOException if there is a problem writing to the database
+ * @throws IllegalArgumentException if the offset is not in this block.
+ */
+ public abstract void putByte(long memBlockOffset, byte b)
+ throws MemoryAccessException, IOException;
+
+ /**
+ * Tries to write len bytes to this block at the given offset (relative to the containing
+ * {@link MemoryBlockDB} using the bytes contained in the given byte array at the specified byte
+ * array offset.
+ * May write fewer bytes if the requested length is beyond the end of the block.
+ *
+ * @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
+ * @param b the byte array with the bytes to store.
+ * @param off the offset into the byte array.
+ * @param len the number of bytes to write.
+ * @return the number of bytes actually written
+ * @throws MemoryAccessException if this block is uninitialized.
+ * @throws IOException if there is a problem writing to the database
+ * @throws IllegalArgumentException if the offset is not in this block.
+ */
+ public abstract int putBytes(long memBlockOffset, byte[] b, int off, int len)
+ throws MemoryAccessException, IOException;
+
+ /**
+ * Deletes this SumMemoryBlock
+ * @throws IOException if a database error occurs
+ */
+ public void delete() throws IOException {
+ adapter.deleteSubBlock(record.getKey());
+ }
+
+ /**
+ * Sets the length of a subblock (Used by the split command)
+ * @param length the new length of the block
+ * @throws IOException if a database error occurs
+ */
+ protected void setLength(long length) throws IOException {
+ this.length = length;
+ record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
+ adapter.updateSubBlockRecord(record);
+ }
+
+ /**
+ * Attempts to join the given SubMemoryBlock with this block if possible
+ *
+ * @param other the SubMemoryBlock to join with this one.
+ * @return true if the given SubMemoryBlock was successfully merged into this one
+ * @throws IOException if a database error occurs.
+ */
+ protected abstract boolean join(SubMemoryBlock other) throws IOException;
+
+ /**
+ * Returns true if this is either a bit-mapped or byte-mapped block.
+ *
+ * @return true if this is either a bit-mapped or byte-mapped block.
+ */
+ protected boolean isMapped() {
+ return false;
+ }
+
+ /**
+ * Get the {@link MemoryBlockType} for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
+ *
+ * @return the type for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
+ */
+ protected abstract MemoryBlockType getType();
+
+ /**
+ * Returns the {@link SourceInfo} object for this SubMemoryBlock
+ * @param block the {@link MemoryBlock} that this block belongs to.
+ * @return the {@link SourceInfo} object for this SubMemoryBlock
+ */
+ protected final SourceInfo getSourceInfo(MemoryBlock block) {
+ return new SourceInfo(block, this);
+ }
+
+ /**
+ * Splits this SubMemoryBlock into two memory blocks
+ * @param memBlockOffset the offset relative to the owning MemoryBlock (not this SubMemoryBlock)
+ * To get the offset relative to this SubMemoryBlock, you have to subtract this sub blocks
+ * starting offset.
+ * @return the new SubMemoryBlock that contains the back half of this block
+ * @throws IOException if a database error occurs.
+ */
+ protected abstract SubMemoryBlock split(long memBlockOffset) throws IOException;
+
+ /**
+ * Updates this SubMemoryBlock to have a new owning MemoryBlock and offset within that block.
+ * This is used when splitting a block and entire sub blocks have to be moved to the new split
+ * block.
+ * @param key the id of the new owning memory block.
+ * @param startingOffset the starting offset of this sub block in the new block.
+ * @throws IOException if a database error occurs.
+ */
+ protected void setParentIdAndStartingOffset(long key, long startingOffset) throws IOException {
+ record.setLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL, key);
+ record.setLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL, startingOffset);
+ adapter.updateSubBlockRecord(record);
+ }
+
+ /**
+ * Returns a description of this SubMemoryBlock suitable to be displayed to the user.
+ * @return a description of this SubMemoryBlock suitable to be displayed to the user.
+ */
+ protected abstract String getDescription();
+
+ /**
+ * Returns true if this subBlock uses the given fileBytes as its byte source.
+ * @param fileBytes the {@link FileBytes} to check for use
+ * @return true if this subBlock uses the given fileBytes as its byte source.
+ */
+ protected boolean uses(FileBytes fileBytes) {
+ return false;
+ }
+
+ /**
+ * Gets the list of BytesSourceRanges from this sub block for the given memBlockOffset and associates
+ * it with the given {@link AddressRange}
+ * @param block the {@link MemoryBlock} that generated the BytesSourceSet.
+ * @param start the program address for which to get a ByteSourceSet
+ * @param memBlockOffset the offset from the beginning of the containing MemoryBlock.
+ * @param size the size of region to get byte sources
+ * @return the set of ByteSourceRanges which maps program addresses to byte source locations.
+ */
+ protected abstract ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long memBlockOffset, long size);
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java
new file mode 100644
index 0000000000..03da2e644c
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java
@@ -0,0 +1,106 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.database.mem;
+
+import java.io.IOException;
+
+import db.Record;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.mem.*;
+
+/**
+ * Implementation of SubMemoryBlock for uninitialized blocks.
+ */
+class UninitializedSubMemoryBlock extends SubMemoryBlock {
+
+ UninitializedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
+ super(adapter, record);
+ startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return false;
+ }
+
+ @Override
+ public byte getByte(long offset) throws MemoryAccessException {
+ if (offset < startingOffset || offset >= startingOffset + length) {
+ throw new IllegalArgumentException(
+ "Offset " + offset + "is out of bounds. Should be in [" + startingOffset + "," +
+ (startingOffset + length - 1));
+ }
+ throw new MemoryAccessException("Attempted to read from uninitialized block");
+ }
+
+ @Override
+ public int getBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
+ throw new MemoryAccessException("Attempted to read from uninitialized block");
+ }
+
+ @Override
+ public void putByte(long offset, byte b) throws MemoryAccessException {
+ throw new MemoryAccessException("Attempted to read from uninitialized block");
+ }
+
+ @Override
+ public int putBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
+ throw new MemoryAccessException("Attempted to read from uninitialized block");
+ }
+
+ @Override
+ protected boolean join(SubMemoryBlock block) throws IOException {
+ if (!(block instanceof UninitializedSubMemoryBlock)) {
+ return false;
+ }
+ setLength(length + block.length);
+ adapter.deleteSubBlock(block.record.getKey());
+ return true;
+ }
+
+ @Override
+ protected MemoryBlockType getType() {
+ return MemoryBlockType.DEFAULT;
+ }
+
+ @Override
+ protected SubMemoryBlock split(long memBlockOffset) throws IOException {
+ // convert from offset in block to offset in this sub block
+ long offset = memBlockOffset - startingOffset;
+ long newLength = length - offset;
+ length = offset;
+ record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
+ adapter.updateSubBlockRecord(record);
+
+ Record newSubRecord = adapter.createSubBlockRecord(-1, 0, newLength,
+ MemoryMapDBAdapter.SUB_TYPE_UNITIALIZED, 0, 0);
+
+ return new UninitializedSubMemoryBlock(adapter, newSubRecord);
+ }
+
+ @Override
+ protected String getDescription() {
+ return "";
+ }
+
+ @Override
+ protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
+ long memBlockOffset,
+ long size) {
+ return new ByteSourceRangeList();
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeImpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeImpl.java
index b6d30b71a0..7fe16d8fbb 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeImpl.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeImpl.java
@@ -72,7 +72,7 @@ public class AddressRangeImpl implements AddressRange, Serializable {
* @param length the length of the range.
* @exception AddressOverflowException if the length would wrap.
*/
- public AddressRangeImpl(Address start, int length) throws AddressOverflowException {
+ public AddressRangeImpl(Address start, long length) throws AddressOverflowException {
minAddress = start;
maxAddress = start.addNoWrap(length - 1);
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MappedMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MappedMemoryBlock.java
deleted file mode 100644
index 63b0b26f56..0000000000
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MappedMemoryBlock.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/* ###
- * IP: GHIDRA
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package ghidra.program.model.mem;
-
-import ghidra.program.model.address.Address;
-import ghidra.program.model.address.AddressRange;
-
-/**
- * Additional methods for mapped memory blocks
- */
-public interface MappedMemoryBlock extends MemoryBlock {
- /**
- * Get the address from the source block.
- */
- public Address getOverlayedMinAddress();
-
- /**
- * Returns the AddressRange of the memory this block is mapped onto.
- * @return the AddressRange of the memory this block is mapped onto.
- */
- public AddressRange getOverlayedAddressRange();
-
-}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java
index 625a9483e3..5bb8e3a937 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java
@@ -15,10 +15,12 @@
*/
package ghidra.program.model.mem;
+import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
-import db.ChainedBuffer;
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.*;
@@ -41,23 +43,10 @@ public interface Memory extends AddressSetView {
public static final long MAX_BINARY_SIZE = (long) MAX_BINARY_SIZE_GB << GBYTE_SHIFT_FACTOR;
/**
- * Initialized blocks must be addressable by an int, 1-GByte.
- * This value has been established due to limitations of the
- * {@link ChainedBuffer} implementation use positive integers
- * to convey length.
+ * The current max size of a memory block.
*/
- public static final int MAX_INITIALIZED_BLOCK_SIZE_GB = 1;
- public static final long MAX_INITIALIZED_BLOCK_SIZE =
- (long) MAX_INITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
-
- /**
- * Uninitialized blocks size limit, 12-GByte (limit number of 32-bit segments).
- * This restriction is somewhat arbitrary but is established to prevent an excessive
- * number of memory map segments ({@link #MAX_BINARY_SIZE_GB}).
- */
- public static final int MAX_UNINITIALIZED_BLOCK_SIZE_GB = 12;
- public static final long MAX_UNINITIALIZED_BLOCK_SIZE =
- (long) MAX_UNINITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
+ public static final int MAX_BLOCK_SIZE_GB = 16; // set to 16 because anything larger, ghidra bogs down
+ public static final long MAX_BLOCK_SIZE = (long) MAX_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
/**
* Returns the program that this memory belongs to.
@@ -123,9 +112,10 @@ public interface Memory extends AddressSetView {
* @param start start address of the block
* @param is source of the data used to fill the block.
* @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 paramaeter, but in the new address space.
+ * offset as the given start address parameter, but in the new address space.
* @return new Initialized Memory Block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
@@ -133,6 +123,8 @@ public interface Memory extends AddressSetView {
* @throws AddressOverflowException if the start is beyond the
* address space
* @throws CancelledException user cancelled operation
+ * @throws DuplicateNameException if overlay is true and there is already an overlay address
+ * space with the same name as this memory block
*/
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
long length, TaskMonitor monitor, boolean overlay)
@@ -150,6 +142,8 @@ public interface Memory extends AddressSetView {
* overlay address space will be created and the block will have a starting address at the same
* offset as the given start address paramaeter, but in the new address space.
* @return new Initialized Memory Block
+ * @throws DuplicateNameException if overlay is true and there is already an overlay address
+ * space with the same name as this memory block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
* previous block
@@ -162,6 +156,30 @@ public interface Memory extends AddressSetView {
throws LockException, DuplicateNameException, MemoryConflictException,
AddressOverflowException, CancelledException;
+ /**
+ * Create an initialized memory block using bytes from a {@link FileBytes} object.
+ *
+ * @param name block name
+ * @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
+ * @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.
+ * @return new Initialized Memory Block
+ * @throws LockException if exclusive lock not in place (see haveLock())
+ * @throws DuplicateNameException if overlay is true and there is already an overlay address
+ * space with the same name as this memory block
+ * @throws MemoryConflictException if the new block overlaps with a
+ * previous block
+ * @throws AddressOverflowException if the start is beyond the
+ * address space
+ */
+ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
+ long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
+ MemoryConflictException, AddressOverflowException;
+
/**
* Create an uninitialized memory block and add it to this Memory.
* @param name block name
@@ -176,6 +194,8 @@ public interface Memory extends AddressSetView {
* previous block
* @throws AddressOverflowException if the start is beyond the
* address space
+ * @throws DuplicateNameException if overlay is true and there is already an overlay address
+ * space with the same name as this memory block
*/
public MemoryBlock createUninitializedBlock(String name, Address start, long size,
boolean overlay) throws LockException, DuplicateNameException, MemoryConflictException,
@@ -192,6 +212,10 @@ public interface Memory extends AddressSetView {
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
* previous block
+ * @throws MemoryConflictException if the new block overlaps with a
+ * previous block
+ * @throws AddressOverflowException if the start is beyond the
+ * address space
*/
public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress,
long length) throws LockException, MemoryConflictException, AddressOverflowException;
@@ -695,4 +719,33 @@ public interface Memory extends AddressSetView {
*/
public void setLong(Address addr, long value, boolean bigEndian) throws MemoryAccessException;
+ /**
+ * Stores a sequence of bytes into the program. Typically, this method is used by importers
+ * to store the original raw program bytes.
+ *
+ * @param filename the name of the file from where the bytes originated
+ * @param offset the offset into the file for the first byte in the input stream.
+ * @param size the number of bytes to store from the input stream.
+ * @param is the input stream that will supply the bytes to store in the program.
+ * @return a FileBytes that was created to access the bytes.
+ * @throws IOException if there was an IOException saving the bytes to the program database.
+ */
+ public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException;
+
+ /**
+ * Returns a list of all the stored original file bytes objects
+ * @return a list of all the stored original file bytes objects
+ */
+ public List getAllFileBytes();
+
+ /**
+ * Deletes a stored sequence of file bytes. The file bytes can only be deleted if there
+ * are no memory block references to the file bytes.
+ * @param fileBytes the FileBytes for the file bytes to be deleted.
+ * @return true if the FileBytes was deleted. If any memory blNoocks are reference this FileBytes,
+ * then it will not be deleted and false will be returned.
+ * @throws IOException if there was an error updating the database.
+ */
+ public boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java
index 924ecdfd49..01712dcc92 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java
@@ -17,8 +17,10 @@ package ghidra.program.model.mem;
import java.io.InputStream;
import java.io.Serializable;
+import java.util.List;
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
@@ -136,6 +138,14 @@ public interface MemoryBlock extends Serializable, Comparable {
*/
public void setExecute(boolean e);
+ /**
+ * Sets the read, write, execute permissions on this block
+ * @param read the read permission
+ * @param write the write permission
+ * @param execute the execute permission
+ */
+ public void setPermissions(boolean read, boolean write, boolean execute);
+
/**
* Returns the value of the volatile property associated with this block.
* This attribute is generally associated with block of I/O regions of memory.
@@ -249,9 +259,19 @@ public interface MemoryBlock extends Serializable, Comparable {
/**
* Returns true if this memory block is a real loaded block (i.e. RAM) and not a special block
* containing file header data such as debug sections.
+ * @return true if this is a loaded block and not a "special" block such as a file header.
*/
public boolean isLoaded();
-
+
+ /**
+ * Returns a list of {@link SourceInfo} objects for this block. A block may consist of
+ * multiple sequences of bytes from different sources. Each such source of bytes is described
+ * by its respective SourceInfo object. Blocks may have multiple sources after two or more
+ * memory blocks have been joined together and the underlying byte sources can't be joined.
+ * @return a list of SourceInfo objects, one for each different source of bytes in this block.
+ */
+ public List getSourceInfos();
+
/**
* Determine if the specified address is contained within the reserved EXTERNAL block.
* @param address address of interest
@@ -266,4 +286,5 @@ public interface MemoryBlock extends Serializable, Comparable {
MemoryBlock block = memory.getBlock(address);
return block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
}
+
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java
index de082c7fe1..44a25e4f45 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java
@@ -15,12 +15,14 @@
*/
package ghidra.program.model.mem;
+import java.io.InputStream;
+import java.util.List;
+
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.util.exception.DuplicateNameException;
-import java.io.InputStream;
-
/**
* MemoryBlockStub can be extended for use by tests. It throws an UnsupportedOperationException
* for all methods in the MemoryBlock interface. Any method that is needed for your test can then
@@ -113,6 +115,11 @@ public class MemoryBlockStub implements MemoryBlock {
throw new UnsupportedOperationException();
}
+ @Override
+ public void setPermissions(boolean read, boolean write, boolean execute) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public boolean isVolatile() {
throw new UnsupportedOperationException();
@@ -183,4 +190,9 @@ public class MemoryBlockStub implements MemoryBlock {
throw new UnsupportedOperationException();
}
+ @Override
+ public List getSourceInfos() {
+ throw new UnsupportedOperationException();
+ }
+
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java
index 58e4deaeb9..3b600874d5 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java
@@ -15,10 +15,13 @@
*/
package ghidra.program.model.mem;
+import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
+import java.util.List;
import ghidra.framework.store.LockException;
+import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.*;
@@ -464,4 +467,26 @@ public class MemoryStub implements Memory {
throw new UnsupportedOperationException();
}
+ @Override
+ public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getAllFileBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean deleteFileBytes(FileBytes descriptor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
+ long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
+ MemoryConflictException, AddressOverflowException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java
index ea6008c5c7..c1957a034f 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/RefTypeFactory.java
@@ -22,7 +22,6 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Instruction;
-import ghidra.program.model.mem.MappedMemoryBlock;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
@@ -83,9 +82,9 @@ public class RefTypeFactory {
private static RefType[] memoryRefTypes = new RefType[] { RefType.INDIRECTION,
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
- RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM, RefType.DATA,
- RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
- RefType.READ_WRITE, RefType.READ_WRITE_IND };
+ RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM,
+ RefType.DATA, RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE,
+ RefType.WRITE_IND, RefType.READ_WRITE, RefType.READ_WRITE_IND };
private static HashSet validMemRefTypes = new HashSet<>();
static {
@@ -94,20 +93,19 @@ public class RefTypeFactory {
}
}
- private static RefType[] stackRefTypes = new RefType[] { RefType.DATA, RefType.READ,
- RefType.WRITE, RefType.READ_WRITE };
+ private static RefType[] stackRefTypes =
+ new RefType[] { RefType.DATA, RefType.READ, RefType.WRITE, RefType.READ_WRITE };
- private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM, RefType.READ,
- RefType.WRITE, RefType.READ_WRITE, };
+ private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM,
+ RefType.READ, RefType.WRITE, RefType.READ_WRITE, };
- private static RefType[] extRefTypes = new RefType[] {
- // TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
- RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
- RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
- RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
- RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
- RefType.READ_WRITE, RefType.READ_WRITE_IND
- };
+ private static RefType[] extRefTypes = new RefType[] {
+ // TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
+ RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
+ RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
+ RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
+ RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
+ RefType.READ_WRITE, RefType.READ_WRITE_IND };
public static RefType[] getMemoryRefTypes() {
return memoryRefTypes;
@@ -258,9 +256,8 @@ public class RefTypeFactory {
Varnode[] inputs = op.getInputs();
if (opCode == PcodeOp.COPY || opCode == PcodeOp.INT_ZEXT) {
if (addrs.contains(inputs[0].getAddress())) {
- RefType rt =
- getLoadStoreRefType(instrOps, opSeq + 1, op.getOutput().getAddress(),
- refType);
+ RefType rt = getLoadStoreRefType(instrOps, opSeq + 1,
+ op.getOutput().getAddress(), refType);
if (rt == RefType.READ) {
if (refType == RefType.WRITE) {
return RefType.READ_WRITE;
@@ -395,7 +392,7 @@ public class RefTypeFactory {
if (toAddr != null && toAddr.isMemoryAddress()) {
MemoryBlock block = cu.getProgram().getMemory().getBlock(toAddr);
- if (block instanceof MappedMemoryBlock) {
+ if (block != null && block.isMapped()) {
ignoreExistingReferences = true;
speculativeFlowNotAllowed = true;
}
@@ -446,8 +443,8 @@ public class RefTypeFactory {
}
if (!ignoreExistingReferences) {
- Reference[] refs =
- cu.getProgram().getReferenceManager().getReferencesFrom(cu.getMinAddress(), opIndex);
+ Reference[] refs = cu.getProgram().getReferenceManager().getReferencesFrom(
+ cu.getMinAddress(), opIndex);
for (Reference ref : refs) {
if (ref.getToAddress().equals(toAddr)) {
return ref.getReferenceType();
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java
new file mode 100644
index 0000000000..2546e0a66c
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java
@@ -0,0 +1,194 @@
+/* ###
+ * 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.mem;
+
+import static org.junit.Assert.*;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.junit.Test;
+
+import generic.test.AbstractGenericTest;
+import ghidra.program.model.address.*;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.mem.MemoryBlockStub;
+
+public class ByteSourceRangeListTest extends AbstractGenericTest {
+ private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
+ private MemoryBlock block = new MemoryBlockStub();
+
+ @Test
+ public void testConstructor() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+
+ ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
+ ByteSourceRangeList list2 = new ByteSourceRangeList();
+ list2.add(range1);
+
+ assertTrue(list1.equals(list2));
+ }
+
+ @Test
+ public void testAdd() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
+ ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
+ ByteSourceRangeList list2 = new ByteSourceRangeList(range2);
+ list1.add(list2);
+ assertEquals(2, list1.getRangeCount());
+ assertEquals(range1, list1.get(0));
+ assertEquals(range2, list1.get(1));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRangeList list1 = new ByteSourceRangeList();
+
+ assertTrue(list1.isEmpty());
+ list1.add(range1);
+ assertFalse(list1.isEmpty());
+ }
+
+ @Test
+ public void testAddNullRange() {
+ ByteSourceRange range = null;
+ ByteSourceRangeList list1 = new ByteSourceRangeList();
+ list1.add(range);
+ assertTrue(list1.isEmpty());
+ }
+
+ @Test
+ public void testIterator() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
+ ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
+ list1.add(range2);
+
+ Iterator it = list1.iterator();
+
+ assertTrue(it.hasNext());
+ assertEquals(range1, it.next());
+ assertTrue(it.hasNext());
+ assertEquals(range2, it.next());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void testIntersectSimple() {
+ ByteSourceRangeList list1 = new ByteSourceRangeList();
+ list1.add(new ByteSourceRange(block, addr(0), 0x100, 1, 0));
+
+ ByteSourceRangeList list2 = new ByteSourceRangeList();
+ list2.add(new ByteSourceRange(block, addr(0x100), 0x100, 1, 0x10));
+
+ // note that list1.intersect(list2) is not equal to list2.intersect(list1).
+ // The byte sources are the same but the corresponding real addresses are calling
+ // objects byte sources.
+
+ ByteSourceRangeList result = list1.intersect(list2);
+ assertEquals(1, result.getRangeCount());
+ ByteSourceRange range = result.get(0);
+
+ assertEquals(0xf0, range.getSize());
+ assertEquals(0x10, range.getOffset());
+ assertEquals(block, range.getMemoryBlock());
+ assertEquals(1, range.getSourceId());
+ assertEquals(addr(0x10), range.getStart());
+ assertEquals(addr(0xff), range.getEnd());
+
+ // now intersect from list2 perspective
+ result = list2.intersect(list1);
+ assertEquals(1, result.getRangeCount());
+ range = result.get(0);
+
+ assertEquals(0xf0, range.getSize());
+ assertEquals(0x10, range.getOffset());
+ assertEquals(block, range.getMemoryBlock());
+ assertEquals(1, range.getSourceId());
+
+ assertEquals(addr(0x100), range.getStart());
+ assertEquals(addr(0x1ef), range.getEnd());
+
+ }
+
+ @Test
+ public void testGetOverlappingBlocks() {
+ ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
+ MemoryBlock block1 = new MemoryBlockStub();
+ ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
+
+ // create a byte source overlap with the first block
+ MemoryBlock block2 = new MemoryBlockStub();
+ ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x50);
+
+ ByteSourceRangeList list = new ByteSourceRangeList();
+ list.add(range);
+ list.add(range1);
+ list.add(range2);
+
+ Set overlappingBlocks = list.getOverlappingBlocks();
+ assertEquals(2, overlappingBlocks.size());
+ assertTrue(overlappingBlocks.contains(block));
+ assertTrue(overlappingBlocks.contains(block2));
+ }
+
+ @Test
+ public void testGetOverlappingBlocksBlocksWhereBlocksAreAdjacentButDontOverlap() {
+ ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
+ MemoryBlock block1 = new MemoryBlockStub();
+ ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
+
+ // create a byte source overlap with the first block
+ MemoryBlock block2 = new MemoryBlockStub();
+ ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x100);
+
+ ByteSourceRangeList list = new ByteSourceRangeList();
+ list.add(range);
+ list.add(range1);
+ list.add(range2);
+
+ Set overlappingBlocks = list.getOverlappingBlocks();
+ assertEquals(0, overlappingBlocks.size());
+ }
+
+ @Test
+ public void testGetOverlappingBlocksBlocksWhereBlocksOverlapByExactlyOneByte() {
+ ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
+ MemoryBlock block1 = new MemoryBlockStub();
+ ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
+
+ // create a byte source overlap with the first block
+ MemoryBlock block2 = new MemoryBlockStub();
+ ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0xff);
+
+ ByteSourceRangeList list = new ByteSourceRangeList();
+ list.add(range);
+ list.add(range1);
+ list.add(range2);
+
+ Set overlappingBlocks = list.getOverlappingBlocks();
+ assertEquals(2, overlappingBlocks.size());
+ assertTrue(overlappingBlocks.contains(block));
+ assertTrue(overlappingBlocks.contains(block2));
+ }
+
+ private Address addr(long value) {
+ return space.getAddress(value);
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java
new file mode 100644
index 0000000000..3cbf620c0e
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java
@@ -0,0 +1,119 @@
+/* ###
+ * 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.mem;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import generic.test.AbstractGenericTest;
+import ghidra.program.model.address.*;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.mem.MemoryBlockStub;
+
+public class ByteSourceRangeTest extends AbstractGenericTest {
+ private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
+ private MemoryBlock block = new MemoryBlockStub();
+ @Test
+ public void testIntersectNotSameSource() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
+ assertNull(range1.intersect(range2));
+ }
+
+ @Test
+ public void testIntersectOneRangeSimpleOverlap() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 1, 0x60);
+
+ ByteSourceRange intersect = range1.intersect(range2);
+ assertNotNull(intersect);
+ assertEquals(addr(0x10), intersect.getStart());
+ assertEquals(addr(0x1f), intersect.getEnd());
+ assertEquals(0x10, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x60, intersect.getOffset());
+
+ intersect = range2.intersect(range1);
+ assertNotNull(intersect);
+ assertEquals(addr(0x100), intersect.getStart());
+ assertEquals(addr(0x10f), intersect.getEnd());
+ assertEquals(0x10, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x60, intersect.getOffset());
+ }
+
+ @Test
+ public void testIntersectOneRangeButsAgainsAnother() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 2, 0x70);
+
+ assertNull(range1.intersect(range2));
+ assertNull(range2.intersect(range1));
+ }
+
+
+ @Test
+ public void testIntersectOneRangeCompletelyInAnother() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x30, 1, 0x40);
+
+ ByteSourceRange intersect = range1.intersect(range2);
+ assertNotNull(intersect);
+ assertEquals(addr(0), intersect.getStart());
+ assertEquals(addr(0xf), intersect.getEnd());
+ assertEquals(0x10, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x50, intersect.getOffset());
+
+ intersect = range2.intersect(range1);
+ assertNotNull(intersect);
+ assertEquals(addr(0x110), intersect.getStart());
+ assertEquals(addr(0x11f), intersect.getEnd());
+ assertEquals(0x10, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x50, intersect.getOffset());
+ }
+
+ @Test
+ public void testBitMappedIntersect() {
+ ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
+ ByteSourceRange range2 = new BitMappedByteSourceRange(block, addr(0x100), 1, 0x55, 2);
+
+ ByteSourceRange intersect = range1.intersect(range2);
+ assertNotNull(intersect);
+ assertEquals(addr(5), intersect.getStart());
+ assertEquals(addr(6), intersect.getEnd());
+ assertEquals(2, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x55, intersect.getOffset());
+
+ intersect = range2.intersect(range1);
+ assertNotNull(intersect);
+ assertEquals(addr(0x100), intersect.getStart());
+ assertEquals(addr(0x10f), intersect.getEnd());
+ assertEquals(2, intersect.getSize());
+ assertEquals(1, intersect.getSourceId());
+ assertEquals(0x55, intersect.getOffset());
+
+ }
+
+ private Address addr(long value) {
+ return space.getAddress(value);
+ }
+
+}
+
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/FileBytesTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/FileBytesTest.java
new file mode 100644
index 0000000000..5b0cde2424
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/FileBytesTest.java
@@ -0,0 +1,249 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.program.database.mem;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+import java.util.List;
+
+import org.junit.*;
+
+import db.*;
+import db.buffers.BufferFile;
+import generic.jar.ResourceFile;
+import generic.test.AbstractGenericTest;
+import ghidra.framework.Application;
+import ghidra.framework.store.db.PrivateDatabase;
+import ghidra.program.database.ProgramDB;
+import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.Program;
+import ghidra.program.model.mem.Memory;
+import ghidra.program.util.DefaultLanguageService;
+import ghidra.util.task.TaskMonitor;
+
+public class FileBytesTest extends AbstractGenericTest {
+
+ private static final int MAX_BUFFER_SIZE_FOR_TESTING = 200;
+ private Program program;
+ private Memory mem;
+ private int transactionID;
+
+ public FileBytesTest() {
+ super();
+ }
+
+ @Test
+ public void testStoreAndRetrieveFileBytes() throws IOException {
+ int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
+ FileBytes fileBytes = createFileBytes("testFile", dataSize);
+
+ byte[] outBytes = new byte[200];
+ assertEquals("testFile", fileBytes.getFilename());
+ assertEquals(0L, fileBytes.getFileOffset());
+ assertEquals(dataSize, fileBytes.getSize());
+ int n = fileBytes.getOriginalBytes(0L, outBytes);
+ assertEquals(dataSize, n);
+ for (int i = 0; i < dataSize; i++) {
+ assertEquals("Byte[" + i + "]", i, outBytes[i]);
+ }
+ }
+
+ @Test
+ public void testRetrieveAfterSavingAndReopeningProgram() throws Exception {
+ int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
+ FileBytes fileBytes = createFileBytes("testFile", dataSize);
+
+ byte[] outBytes = new byte[200];
+
+ saveAndRestoreProgram();
+
+ List list = program.getMemory().getAllFileBytes();
+ fileBytes = list.get(0);
+
+ assertEquals("testFile", fileBytes.getFilename());
+ assertEquals(0L, fileBytes.getFileOffset());
+ assertEquals(dataSize, fileBytes.getSize());
+ int n = fileBytes.getOriginalBytes(0L, outBytes);
+ assertEquals(100, n);
+ for (int i = 0; i < dataSize; i++) {
+ assertEquals("Byte[" + i + "]", i, outBytes[i]);
+ }
+ }
+
+ @Test
+ public void testRequiresMultipleBuffers() throws Exception {
+ int dataSize = MAX_BUFFER_SIZE_FOR_TESTING + MAX_BUFFER_SIZE_FOR_TESTING / 2;
+ FileBytes fileBytes = createFileBytes("testFile", dataSize);
+
+ saveAndRestoreProgram();
+
+ byte[] outBytes = new byte[400];
+ List list = program.getMemory().getAllFileBytes();
+ fileBytes = list.get(0);
+
+ assertEquals("testFile", fileBytes.getFilename());
+ assertEquals(0L, fileBytes.getFileOffset());
+ assertEquals(dataSize, fileBytes.getSize());
+ int n = fileBytes.getOriginalBytes(0L, outBytes);
+ assertEquals(dataSize, n);
+ for (int i = 0; i < dataSize; i++) {
+ assertEquals("Byte[" + i + "]", (byte) i, outBytes[i]);
+ }
+ DBBuffer[] buffers = (DBBuffer[]) getInstanceField("originalBuffers", fileBytes);
+ assertEquals(2, buffers.length);
+ assertEquals(MAX_BUFFER_SIZE_FOR_TESTING, buffers[0].length());
+ }
+
+ @Test
+ public void testCreateMultipleFileBytes() throws Exception {
+ createFileBytes("file1", 10);
+ createFileBytes("file2", 20);
+ createFileBytes("file3", 30);
+
+ saveAndRestoreProgram();
+ List fileBytesList = mem.getAllFileBytes();
+ assertEquals(3, fileBytesList.size());
+ assertEquals("file1", fileBytesList.get(0).getFilename());
+ assertEquals(10, fileBytesList.get(0).getSize());
+ assertEquals("file2", fileBytesList.get(1).getFilename());
+ assertEquals(20, fileBytesList.get(1).getSize());
+ assertEquals("file3", fileBytesList.get(2).getFilename());
+ assertEquals(30, fileBytesList.get(2).getSize());
+ }
+
+ @Test
+ public void testDeleteFileBytesDescriptors() throws Exception {
+ createFileBytes("file1", 10);
+ createFileBytes("file2", 20);
+ createFileBytes("file3", 30);
+
+ saveAndRestoreProgram();
+ List fileBytes = mem.getAllFileBytes();
+
+ mem.deleteFileBytes(fileBytes.get(1));
+
+ saveAndRestoreProgram();
+ List fileBytesList = mem.getAllFileBytes();
+ assertEquals(2, fileBytesList.size());
+ assertEquals("file1", fileBytesList.get(0).getFilename());
+ assertEquals(10, fileBytesList.get(0).getSize());
+ assertEquals("file3", fileBytesList.get(1).getFilename());
+ assertEquals(30, fileBytesList.get(1).getSize());
+ }
+
+ @Test
+ public void testGetByte() throws Exception {
+ FileBytes fileBytes = createFileBytes("file1", 10);
+ assertEquals(5, fileBytes.getOriginalByte(5));
+ }
+
+ @Test
+ public void testGetLayeredByte() throws Exception {
+ FileBytes fileBytes = createFileBytes("file1", 10);
+ incrementFileBytes(fileBytes, 0, 10);
+
+ // check that the layered bytes are changed, but you can still get the originals
+ for (int i = 0; i < 10; i++) {
+ assertEquals(i, fileBytes.getOriginalByte(i));
+ assertEquals(i + 1, fileBytes.getModifiedByte(i));
+ }
+
+ }
+
+ private void incrementFileBytes(FileBytes fileBytes, int offset, int n) throws IOException {
+ for (int i = offset; i < offset + n; i++) {
+ fileBytes.putByte(i, (byte) (fileBytes.getModifiedByte(i) + 1));
+ }
+ }
+
+ @Test
+ public void testGetLayeredBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes("file1", 10);
+ incrementFileBytes(fileBytes, 0, 10);
+
+ // check that the layered bytes are changed, but you can still get the originals
+ byte[] original = new byte[10];
+ byte[] modified = new byte[10];
+ fileBytes.getOriginalBytes(0, original);
+ fileBytes.getModifiedBytes(0, modified);
+ for (int i = 0; i < 10; i++) {
+ assertEquals(i, original[i]);
+ assertEquals(i + 1, modified[i]);
+ }
+ }
+
+ private FileBytes createFileBytes(String name, int size) throws IOException {
+ byte[] bytes = new byte[size];
+ for (int i = 0; i < size; i++) {
+ bytes[i] = (byte) i;
+ }
+ try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) {
+ return mem.createFileBytes(name, 0, size, is);
+ }
+ }
+
+ private void saveAndRestoreProgram() throws Exception {
+ program.endTransaction(transactionID, true);
+ PrivateDatabase privateDatabase = saveProgram(program);
+ program = restoreProgram(privateDatabase);
+ mem = program.getMemory();
+ transactionID = program.startTransaction("test");
+ }
+
+ private PrivateDatabase saveProgram(Program program) throws Exception {
+ File dir = createTempDirectory("program");
+ File dbDir = new File(dir, "program.db");
+
+ DBHandle dbh = ((ProgramDB) program).getDBHandle();
+ BufferFile bfile = PrivateDatabase.createDatabase(dbDir, null, dbh.getBufferSize());
+ dbh.saveAs(bfile, true, TaskMonitor.DUMMY);
+ return new PrivateDatabase(dbDir);
+ }
+
+ private Program restoreProgram(PrivateDatabase db) throws Exception {
+ DBHandle dbh = db.open(TaskMonitor.DUMMY);
+ return new ProgramDB(dbh, DBConstants.UPDATE, null, this);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ FileBytesAdapter.setMaxBufferSize(MAX_BUFFER_SIZE_FOR_TESTING);
+ Language language = getLanguage("Toy:BE:64:default");
+ CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
+ program = new ProgramDB("Test", language, compilerSpec, this);
+
+ mem = program.getMemory();
+ transactionID = program.startTransaction("Test");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ program.endTransaction(transactionID, true);
+ program.release(this);
+ }
+
+ private Language getLanguage(String languageName) throws Exception {
+
+ ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
+ if (ldefFile != null) {
+ LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
+ Language language = languageService.getLanguage(new LanguageID(languageName));
+ return language;
+ }
+ throw new LanguageNotFoundException("Unsupported test language: " + languageName);
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java
new file mode 100644
index 0000000000..cad62f4e7f
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java
@@ -0,0 +1,857 @@
+/* ###
+ * 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.mem;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.junit.*;
+
+import db.DBConstants;
+import db.DBHandle;
+import generic.jar.ResourceFile;
+import generic.test.AbstractGenericTest;
+import ghidra.framework.Application;
+import ghidra.program.database.ProgramDB;
+import ghidra.program.database.map.AddressMapDB;
+import ghidra.program.model.address.*;
+import ghidra.program.model.lang.*;
+import ghidra.program.model.mem.*;
+import ghidra.program.util.DefaultLanguageService;
+import ghidra.util.Lock;
+import ghidra.util.task.TaskMonitor;
+
+public class MemBlockDBTest extends AbstractGenericTest {
+ private static final long MAX_SUB_BLOCK_SIZE = 16;
+ private MemoryMapDB mem;
+ private long txID;
+ private DBHandle handle;
+ private AddressFactory addressFactory;
+ private ProgramDB program;
+ private int ptxID;
+
+ @Before
+ public void setUp() throws Exception {
+ Language language = getLanguage("Toy:BE:64:default");
+ CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
+ program = new ProgramDB("Test", language, compilerSpec, this);
+ ptxID = program.startTransaction("test");
+
+ handle = new DBHandle();
+
+ txID = handle.startTransaction();
+
+ addressFactory = language.getAddressFactory();
+ AddressMapDB addrMap = (AddressMapDB) program.getAddressMap();
+ Lock lock = new Lock("Test");
+ int openMode = DBConstants.CREATE;
+ mem = new MemoryMapDB(handle, addrMap, openMode, true, lock);
+
+ MemoryMapDBAdapter adapter =
+ new MemoryMapDBAdapterV3(handle, mem, MAX_SUB_BLOCK_SIZE, true);
+ FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, mem, true);
+
+ mem.init(adapter, fileBytesAdapter);
+ mem.setProgram(program);
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ program.endTransaction(ptxID, true);
+ handle.endTransaction(txID, true);
+ handle.close();
+ program.release(this);
+ }
+
+ @Test
+ public void testCreateInitializedBlock() throws Exception {
+ MemoryBlock block =
+ mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, false);
+
+ assertEquals(10, block.getSize());
+ assertEquals("test", block.getName());
+ assertEquals(addr(0), block.getStart());
+ assertEquals(addr(9), block.getEnd());
+ assertEquals(MemoryBlockType.DEFAULT, block.getType());
+ assertEquals(true, block.isInitialized());
+ assertEquals(false, block.isMapped());
+ assertNull(block.getComment());
+ assertNull(block.getSourceName());
+ assertEquals(MemoryBlock.READ, block.getPermissions());
+
+ List sourceInfos = block.getSourceInfos();
+
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(10, info.getLength());
+ assertEquals(addr(0), info.getMinAddress());
+ assertEquals(addr(9), info.getMaxAddress());
+ for (int i = 0; i < 10; i++) {
+ assertEquals(1, block.getByte(addr(i)));
+ }
+ }
+
+ @Test
+ public void testCreateUninitializedBlock() throws Exception {
+ MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, false);
+
+ assertEquals(10, block.getSize());
+ assertEquals("test", block.getName());
+ assertEquals(addr(0), block.getStart());
+ assertEquals(addr(9), block.getEnd());
+ assertEquals(MemoryBlockType.DEFAULT, block.getType());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(10, info.getLength());
+ assertEquals(addr(0), info.getMinAddress());
+ assertEquals(addr(9), info.getMaxAddress());
+ try {
+ block.getByte(addr(0));
+ fail("expected exception trying to read bytes on unitialized block");
+ }
+ catch (MemoryAccessException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCreateUnitializedOverlayBlock() throws Exception {
+ MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, true);
+
+ assertEquals(10, block.getSize());
+ assertEquals("test", block.getName());
+ assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
+ assertEquals(0, block.getStart().getOffset());
+ assertEquals(9, block.getEnd().getOffset());
+ assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
+ assertEquals(MemoryBlockType.OVERLAY, block.getType());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(10, info.getLength());
+ try {
+ block.getByte(block.getStart());
+ fail("expected exception trying to read bytes on unitialized block");
+ }
+ catch (MemoryAccessException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCreateInitializedOverlayBlock() throws Exception {
+ MemoryBlock block =
+ mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, true);
+
+ assertEquals(10, block.getSize());
+ assertEquals("test", block.getName());
+ assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
+ assertEquals(0, block.getStart().getOffset());
+ assertEquals(9, block.getEnd().getOffset());
+ assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
+ assertEquals(MemoryBlockType.OVERLAY, block.getType());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(10, info.getLength());
+ for (int i = 0; i < 10; i++) {
+ assertEquals(1, block.getByte(block.getStart().add(i)));
+ }
+ }
+
+ @Test
+ public void testCreateByteMappedBlock() throws Exception {
+ mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
+ mem.createUninitializedBlock("test2", addr(50), 50, false);
+ MemoryBlock block = mem.createByteMappedBlock("mapped", addr(1000), addr(40), 20);
+
+ assertEquals(20, block.getSize());
+ assertEquals("mapped", block.getName());
+ assertEquals(addr(1000), block.getStart());
+ assertEquals(addr(1019), block.getEnd());
+ assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
+ assertEquals(false, block.isInitialized());
+ assertEquals(true, block.isMapped());
+ assertNull(block.getComment());
+ assertNull(block.getSourceName());
+ assertEquals(MemoryBlock.READ, block.getPermissions());
+
+ List sourceInfos = block.getSourceInfos();
+
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(20, info.getLength());
+ assertEquals(new AddressRangeImpl(addr(40), addr(59)), info.getMappedRange().get());
+
+ for (int i = 0; i < 10; i++) {
+ assertEquals(1, mem.getByte(block.getStart().add(i)));
+ }
+ try {
+ mem.getByte(block.getStart().add(10));
+ fail("expected exception trying to read bytes on mapped unitialized block");
+ }
+ catch (MemoryAccessException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCreateBitMappedBlock() throws Exception {
+ mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
+ mem.createUninitializedBlock("test2", addr(50), 50, false);
+ MemoryBlock block = mem.createBitMappedBlock("mapped", addr(1000), addr(49), 16);
+
+ assertEquals(16, block.getSize());
+ assertEquals("mapped", block.getName());
+ assertEquals(addr(1000), block.getStart());
+ assertEquals(addr(1015), block.getEnd());
+ assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
+ assertEquals(false, block.isInitialized());
+ assertEquals(true, block.isMapped());
+ assertNull(block.getComment());
+ assertNull(block.getSourceName());
+ assertEquals(MemoryBlock.READ, block.getPermissions());
+
+ List sourceInfos = block.getSourceInfos();
+
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(16, info.getLength());
+ assertEquals(new AddressRangeImpl(addr(49), addr(50)), info.getMappedRange().get());
+
+ assertEquals(1, mem.getByte(block.getStart()));
+ for (int i = 1; i < 8; i++) {
+ assertEquals(0, mem.getByte(block.getStart().add(i)));
+ }
+ try {
+ mem.getByte(block.getStart().add(8));
+ fail("expected exception trying to read bytes on mapped unitialized block");
+ }
+ catch (MemoryAccessException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCreateFileBytesBlock() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block = mem.createInitializedBlock("test", addr(100), fileBytes, 10, 50, false);
+
+ assertEquals(50, block.getSize());
+ assertEquals("test", block.getName());
+ assertEquals(addr(100), block.getStart());
+ assertEquals(addr(149), block.getEnd());
+ assertEquals(MemoryBlockType.DEFAULT, block.getType());
+ assertEquals(true, block.isInitialized());
+ assertEquals(false, block.isMapped());
+ assertNull(block.getComment());
+ assertNull(block.getSourceName());
+ assertEquals(MemoryBlock.READ, block.getPermissions());
+
+ List sourceInfos = block.getSourceInfos();
+
+ assertEquals(1, sourceInfos.size());
+ SourceInfo info = sourceInfos.get(0);
+ assertEquals(50, info.getLength());
+ assertEquals(addr(100), info.getMinAddress());
+ assertEquals(addr(149), info.getMaxAddress());
+
+ for (int i = 0; i < block.getSize(); i++) {
+ assertEquals(i + 10, block.getByte(addr(100 + i)));
+ }
+ }
+
+ @Test
+ public void testCreateFileBytesBlockOutSideRange() throws Exception {
+ byte[] bytes = new byte[256];
+ FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
+ try {
+ mem.createInitializedBlock("test", addr(100), fileBytes, 10, 100, false);
+ fail(
+ "Expected create filebytes block to fail because the offset+blockLength > fileBytesLength");
+ }
+ catch (IndexOutOfBoundsException e) {
+ // expected
+ }
+
+ }
+
+ @Test
+ public void testInitializedBlockAcrossSubBlocks() throws Exception {
+ mem.createInitializedBlock("test", addr(0), 100, (byte) 1, TaskMonitor.DUMMY, false);
+ assertEquals(0x0101010101010101L, mem.getLong(addr(MAX_SUB_BLOCK_SIZE - 1)));
+ }
+
+ @Test
+ public void testInitializedBlockAcrossMutlitipleSubBlocks() throws Exception {
+ byte[] bytes = new byte[256];
+ for (int i = 0; i < 256; i++) {
+ bytes[i] = (byte) i;
+ }
+ mem.createInitializedBlock("test", addr(0), new ByteArrayInputStream(bytes), 256,
+ TaskMonitor.DUMMY, false);
+ byte[] b = new byte[100];
+ assertEquals(100, mem.getBytes(addr(10), b));
+ for (int i = 0; i < 100; i++) {
+ assertEquals(i + 10, b[i]);
+ }
+ }
+
+ @Test
+ public void testJoinFileBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
+ MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 35, 10);
+ MemoryBlock block = mem.join(block1, block2);
+ assertEquals(1, mem.getBlocks().length);
+ assertEquals(20, block.getSize());
+ assertEquals(addr(10), block.getStart());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ SourceInfo sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(25, sourceInfo.getFileBytesOffset());
+ byte[] bytes = new byte[30];
+ assertEquals(20, block.getBytes(addr(10), bytes));
+ for (int i = 0; i < 20; i++) {
+ assertEquals(i + 25, bytes[i]);
+ }
+ }
+
+ @Test
+ public void testJoinNonConsecutiveFileBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
+ MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 70, 10);
+
+ MemoryBlock block = mem.join(block1, block2);
+ assertEquals(1, mem.getBlocks().length);
+ assertEquals(20, block.getSize());
+ assertEquals(addr(10), block.getStart());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(2, sourceInfos.size());
+ SourceInfo sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(25, sourceInfo.getFileBytesOffset());
+ assertEquals(10, sourceInfo.getLength());
+
+ sourceInfo = sourceInfos.get(1);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(70, sourceInfo.getFileBytesOffset());
+ assertEquals(10, sourceInfo.getLength());
+ }
+
+ @Test
+ public void testJoinFileBytesBlockAndBufferBlock() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
+ MemoryBlock block2 =
+ mem.createInitializedBlock("test", addr(20), 10, (byte) 1, TaskMonitor.DUMMY, false);
+
+ MemoryBlock block = mem.join(block1, block2);
+ assertEquals(1, mem.getBlocks().length);
+ assertEquals(20, block.getSize());
+ assertEquals(addr(10), block.getStart());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(2, sourceInfos.size());
+ SourceInfo sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(25, sourceInfo.getFileBytesOffset());
+ assertEquals(10, sourceInfo.getLength());
+
+ SourceInfo sourceInfo2 = sourceInfos.get(1);
+ assertEquals(10, sourceInfo2.getLength());
+ }
+
+ @Test
+ public void testJoinBlocksFromDifferentFileBytes() throws Exception {
+ FileBytes fileBytes1 = createFileBytes();
+ FileBytes fileBytes2 = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes1, addr(10), 25, 10);
+ MemoryBlock block2 = createFileBytesBlock(fileBytes2, addr(20), 35, 10);
+
+ MemoryBlock block = mem.join(block1, block2);
+ assertEquals(1, mem.getBlocks().length);
+ assertEquals(20, block.getSize());
+ assertEquals(addr(10), block.getStart());
+ List sourceInfos = block.getSourceInfos();
+ assertEquals(2, sourceInfos.size());
+
+ SourceInfo sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes1, sourceInfo.getFileBytes().get());
+ assertEquals(25, sourceInfo.getFileBytesOffset());
+ assertEquals(10, sourceInfo.getLength());
+
+ sourceInfo = sourceInfos.get(1);
+ assertEquals(fileBytes2, sourceInfo.getFileBytes().get());
+ assertEquals(35, sourceInfo.getFileBytesOffset());
+ assertEquals(10, sourceInfo.getLength());
+ }
+
+ @Test
+ public void testSplitFileBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
+ mem.split(block1, addr(30));
+
+ MemoryBlock[] blocks = mem.getBlocks();
+ assertEquals(2, blocks.length);
+
+ assertEquals(20, blocks[0].getSize());
+ assertEquals(30, blocks[1].getSize());
+
+ assertEquals(addr(10), blocks[0].getStart());
+ assertEquals(addr(30), blocks[1].getStart());
+
+ List sourceInfos = blocks[0].getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ SourceInfo sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(25, sourceInfo.getFileBytesOffset());
+
+ sourceInfos = blocks[1].getSourceInfos();
+ assertEquals(1, sourceInfos.size());
+ sourceInfo = sourceInfos.get(0);
+ assertEquals(fileBytes, sourceInfo.getFileBytes().get());
+ assertEquals(45, sourceInfo.getFileBytesOffset());
+
+ }
+
+ @Test
+ public void testDeleteFileBytesBlock() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
+ mem.removeBlock(block1, TaskMonitor.DUMMY);
+ assertEquals(0, mem.getBlocks().length);
+ }
+
+ @Test
+ public void testPutByteToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ byte[] bytes = new byte[20];
+ block.getBytes(addr(0), bytes);
+ checkBytes(bytes, 0);
+ block.getBytes(addr(20), bytes);
+ checkBytes(bytes, 20);
+
+ block.putBytes(addr(0), bytes);
+ block.getBytes(addr(0), bytes);
+ checkBytes(bytes, 20);
+ fileBytes.getOriginalBytes(0, bytes);
+ checkBytes(bytes, 0);
+
+ }
+
+ @Test
+ public void testSplitAndJoinUnitializedBlock() throws Exception {
+ MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 40, false);
+ mem.split(block, addr(10));
+ MemoryBlock[] blocks = mem.getBlocks();
+ assertEquals(2, blocks.length);
+ assertEquals(addr(0), blocks[0].getStart());
+ assertEquals(addr(10), blocks[1].getStart());
+ assertEquals(10, blocks[0].getSize());
+ assertEquals(30, blocks[1].getSize());
+ assertTrue(!blocks[0].isInitialized());
+ assertTrue(!blocks[1].isInitialized());
+
+ mem.join(blocks[0], blocks[1]);
+ blocks = mem.getBlocks();
+ assertEquals(1, blocks.length);
+ assertEquals(addr(0), blocks[0].getStart());
+ assertEquals(40, blocks[0].getSize());
+ List sourceInfos = blocks[0].getSourceInfos();
+ assertEquals(1, sourceInfos.size()); // make sure the sub blocks were merged
+ }
+
+ private void checkBytes(byte[] bytes, int startingValue) {
+ for (int i = 0; i < bytes.length; i++) {
+ assertEquals(startingValue + i, bytes[i]);
+ }
+ }
+
+ @Test
+ public void testPutBytesToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ assertEquals(0, block.getByte(addr(0)));
+ block.putByte(addr(0), (byte) 55);
+ assertEquals(55, block.getByte(addr(0)));
+ assertEquals(0, fileBytes.getOriginalByte(0));
+ }
+
+ @Test
+ public void testSplitOnSubBlockBoundary() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
+ MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(10), 30, 10);
+ mem.join(block1, block2);
+ MemoryBlock[] blocks = mem.getBlocks();
+ assertEquals(1, blocks.length);
+ mem.split(blocks[0], addr(10));
+ blocks = mem.getBlocks();
+ assertEquals(2, blocks.length);
+ assertEquals(1, blocks[0].getSourceInfos().size());
+ assertEquals(1, blocks[1].getSourceInfos().size());
+ }
+
+ @Test
+ public void testByteMappedGetPutByte() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
+ MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
+ assertEquals(5, mappedBlock.getByte(addr(105)));
+ assertEquals(5, block1.getByte(addr(5)));
+ mappedBlock.putByte(addr(105), (byte) 87);
+ assertEquals(87, block1.getByte(addr(5)));
+ }
+
+ @Test
+ public void testByteMappedGetPutBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
+ byte[] bytes = new byte[10];
+ mappedBlock.getBytes(addr(100), bytes);
+ checkBytes(bytes, 0);
+
+ mappedBlock.putBytes(addr(105), bytes);
+ block1.getBytes(addr(5), bytes);
+ checkBytes(bytes, 0);
+ }
+
+ @Test
+ public void testByteMappedJoin() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 10);
+ MemoryBlock mappedBlock2 = mem.createByteMappedBlock("mapped2", addr(110), addr(10), 10);
+ try {
+ mem.join(mappedBlock1, mappedBlock2);
+ fail("Expected exception when joining byte mapped blocks");
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testByteMappedSplit() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 20);
+ mem.split(mappedBlock1, addr(110));
+ MemoryBlock[] blocks = mem.getBlocks();
+ assertEquals(3, blocks.length);
+ assertEquals(addr(110), blocks[2].getStart());
+ }
+
+ @Test
+ public void testBitMappedGetPutByte() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 20);
+
+ assertEquals(0, mappedBlock.getByte(addr(100)));
+ assertEquals(0, mappedBlock.getByte(addr(101)));
+ assertEquals(0, mappedBlock.getByte(addr(114)));
+ assertEquals(1, mappedBlock.getByte(addr(108)));
+ assertEquals(0, mappedBlock.getByte(addr(116)));
+
+ mappedBlock.putByte(addr(100), (byte) 4);
+ assertEquals(1, block.getByte(addr(0)));
+ }
+
+ @Test
+ public void testBitMappedGetPutBytes() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 50);
+
+ byte[] bytes = new byte[8];
+
+ mappedBlock.getBytes(addr(108), bytes);
+ assertEquals(1, bytes[0]);
+ for (int i = 1; i < 8; i++) {
+ assertEquals(0, bytes[i]);
+ }
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = 1;
+ }
+ mappedBlock.putBytes(addr(100), bytes);
+ assertEquals(-1, block.getByte(addr(0)));
+ }
+
+ @Test
+ public void testBitMappedJoin() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
+ MemoryBlock mappedBlock2 = mem.createBitMappedBlock("mapped2", addr(116), addr(2), 16);
+ try {
+ mem.join(mappedBlock1, mappedBlock2);
+ fail("Expected exception when joining bit mapped blocks");
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testBitMappedSplit() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+ MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
+ try {
+ mem.split(mappedBlock1, addr(108));
+ fail("Expected exception when joining bit mapped blocks");
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testGetByteSourceSetForFileBytesBlock() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ MemoryBlockDB block = (MemoryBlockDB) createFileBytesBlock(fileBytes, addr(0), 10, 50);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(5), 10);
+
+ // we expect to get a single range ByteSourceSet pointing into the filebytes at offset
+ // 15 (10 because block was created at filebytes:10 and 5 because we start at the 5th byte
+ // in the block)
+
+ assertEquals(1, ranges.getRangeCount());
+ assertEquals(10, ranges.get(0).getSize());
+ assertEquals(5, ranges.get(0).getStart().getOffset());
+ assertEquals(14, ranges.get(0).getEnd().getOffset());
+ assertEquals(fileBytes.getId(), ranges.get(0).getSourceId());
+ assertEquals(15, ranges.get(0).getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceSetForBufferBlock() throws Exception {
+ MemoryBlockDB block = (MemoryBlockDB) mem.createInitializedBlock("test", addr(0), 30,
+ (byte) 1, TaskMonitor.DUMMY, false);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
+
+ // We expect to get to ranges because we made the buffer size small (16) so when we
+ // created a 30 size block, it had to make two separate sub blocks each with its own
+ // DBBuffer. The first range should contain the first 6 bytes of the requested range
+ // and the second buffer should contain the last 4 bytes of request range.
+
+ assertEquals(2, ranges.getRangeCount()); // we have two sublocks so two distinct ranges
+ assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
+
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(10, range.getStart().getOffset());
+ assertEquals(15, range.getEnd().getOffset());
+ assertEquals(6, range.getSize());
+ assertEquals(10, range.getOffset());
+
+ range = ranges.get(1);
+ assertEquals(16, range.getStart().getOffset());
+ assertEquals(19, range.getEnd().getOffset());
+ assertEquals(4, range.getSize());
+ assertEquals(0, range.getOffset());
+
+ }
+
+ @Test
+ public void testGetByteSourceForUndefinedBlock() throws Exception {
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createUninitializedBlock("test", addr(0), 30, false);
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
+ // undefined blocks have no source bytes
+ assertTrue(ranges.isEmpty());
+
+ }
+
+ @Test
+ public void testGetByteSourceForByteMappedBlock() throws Exception {
+ mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false);
+ mem.createUninitializedBlock("test2", addr(15), 20, false);
+ mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false);
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), addr(5), 40);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 30);
+
+ // Uninitialized blocks don't contribute, so we should have 10 address (5 from first and last blocks each).
+ assertEquals(2, ranges.getRangeCount());
+ assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(1005), range.getStart());
+ assertEquals(addr(1009), range.getEnd());
+ assertEquals(5, range.getSize());
+ assertEquals(10, range.getOffset());
+
+ range = ranges.get(1);
+ assertEquals(addr(1030), range.getStart());
+ assertEquals(addr(1034), range.getEnd());
+ assertEquals(5, range.getSize());
+ assertEquals(0, range.getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceForBitMappedBlock() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1000), 0x14);
+
+ assertEquals(1, ranges.getRangeCount());
+ assertEquals(3, ranges.get(0).getSize());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(0x1000), range.getStart());
+ assertEquals(addr(0x1017), range.getEnd());
+ assertEquals(3, range.getSize());
+ assertEquals(5, range.getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceForBitMappedBlockOffcutStart() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1005), 8);
+
+ assertEquals(1, ranges.getRangeCount());
+ assertEquals(2, ranges.get(0).getSize());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(0x1000), range.getStart());
+ assertEquals(addr(0x100f), range.getEnd());
+ assertEquals(2, range.getSize());
+ assertEquals(5, range.getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceForBitMappedBlockOffcutStartNotAtStart() throws Exception {
+ FileBytes fileBytes = createFileBytes();
+ createFileBytesBlock(fileBytes, addr(0), 0, 50);
+
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x44);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1015), 8);
+
+ assertEquals(1, ranges.getRangeCount());
+ assertEquals(2, ranges.get(0).getSize());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(0x1010), range.getStart());
+ assertEquals(addr(0x101f), range.getEnd());
+ assertEquals(2, range.getSize());
+ assertEquals(7, range.getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceForBitMappedBlock2() throws Exception {
+ mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
+ mem.createUninitializedBlock("test2", addr(0x4), 4, false);
+ mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1008), 0x30);
+
+ assertEquals(2, ranges.getRangeCount());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(0x1008), range.getStart());
+ assertEquals(addr(0x100f), range.getEnd());
+ assertEquals(1, range.getSize());
+ assertEquals(3, range.getOffset());
+
+ range = ranges.get(1);
+ assertEquals(addr(0x1030), range.getStart());
+ assertEquals(addr(0x1037), range.getEnd());
+ assertEquals(1, range.getSize());
+ assertEquals(0, range.getOffset());
+ }
+
+ @Test
+ public void testGetByteSourceForBitMappedBlock2Offcut() throws Exception {
+ mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
+ mem.createUninitializedBlock("test2", addr(0x4), 4, false);
+ mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
+ MemoryBlockDB block =
+ (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
+
+ ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1006), 0x34);
+
+ assertEquals(2, ranges.getRangeCount());
+
+ ByteSourceRange range = ranges.get(0);
+ assertEquals(addr(0x1000), range.getStart());
+ assertEquals(addr(0x100f), range.getEnd());
+ assertEquals(2, range.getSize());
+ assertEquals(2, range.getOffset());
+
+ range = ranges.get(1);
+ assertEquals(addr(0x1030), range.getStart());
+ assertEquals(addr(0x103f), range.getEnd());
+ assertEquals(2, range.getSize());
+ assertEquals(0, range.getOffset());
+ }
+ private MemoryBlock createFileBytesBlock(FileBytes fileBytes, Address addr, int offset,
+ int length) throws Exception {
+ return mem.createInitializedBlock("test" + addr.toString(), addr, fileBytes, offset, length,
+ false);
+ }
+
+ private FileBytes createFileBytes() throws IOException {
+ byte[] bytes = new byte[256];
+ for (int i = 0; i < 256; i++) {
+ bytes[i] = (byte) i;
+ }
+ FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
+ return fileBytes;
+ }
+
+ private Address addr(long offset) {
+ return addressFactory.getDefaultAddressSpace().getAddress(offset);
+ }
+
+ private Language getLanguage(String languageName) throws Exception {
+
+ ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
+ if (ldefFile != null) {
+ LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
+ Language language = languageService.getLanguage(new LanguageID(languageName));
+ return language;
+ }
+ throw new LanguageNotFoundException("Unsupported test language: " + languageName);
+ }
+}
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/NavigationScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/NavigationScreenShots.java
index e8428cf302..cd10649229 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/NavigationScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/NavigationScreenShots.java
@@ -15,7 +15,7 @@
*/
package help.screenshot;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.*;
import java.awt.Window;
@@ -26,14 +26,14 @@ import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.table.GTable;
import docking.widgets.table.threaded.GThreadedTablePanel;
import generic.test.TestUtils;
-import ghidra.app.cmd.memory.AddMemoryBlockCmd;
+import ghidra.app.cmd.memory.AddInitializedMemoryBlockCmd;
+import ghidra.app.cmd.memory.AddUninitializedMemoryBlockCmd;
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.plugin.core.table.TableServicePlugin;
import ghidra.app.services.GoToService;
import ghidra.app.util.navigation.GoToAddressLabelDialog;
import ghidra.program.model.address.Address;
-import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.table.GhidraProgramTableModel;
public class NavigationScreenShots extends GhidraScreenShotGenerator {
@@ -54,12 +54,12 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
public void testGoto_Ambiguous() {
//add fake memory blocks to mimic a multiple address space program
- tool.execute(new AddMemoryBlockCmd("CODE", "comments", "test1", addr(0), 0x100, true, true,
- true, false, (byte) 0x69, MemoryBlockType.OVERLAY, null, true), program);
- tool.execute(new AddMemoryBlockCmd("INTMEM", "comments", "test2", addr(0), 0x100, true,
- true, true, false, (byte) 1, MemoryBlockType.OVERLAY, null, false), program);
- tool.execute(new AddMemoryBlockCmd("DUMMY", "comments", "test3", addr(20), 0x100, true,
- true, true, false, (byte) 1, MemoryBlockType.OVERLAY, null, true), program);
+ tool.execute(new AddInitializedMemoryBlockCmd("CODE", "comments", "test1", addr(0), 0x100,
+ true, true, true, false, (byte) 0x69, true), program);
+ tool.execute(new AddUninitializedMemoryBlockCmd("INTMEM", "comments", "test2", addr(0),
+ 0x100, true, true, true, false, false), program);
+ tool.execute(new AddInitializedMemoryBlockCmd("DUMMY", "comments", "test3", addr(20), 0x100,
+ true, true, true, false, (byte) 1, true), program);
// go to an address outside the blocks that will be displayed
goToAddress(program.getMemory().getBlock("DUMMY").getStart());
@@ -98,7 +98,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressOkOnDialog();
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
@@ -107,7 +107,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressOkOnDialog();
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
@@ -116,7 +116,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressOkOnDialog();
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
@@ -125,7 +125,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressOkOnDialog();
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
@@ -134,7 +134,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressOkOnDialog();
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
@@ -147,7 +147,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
pressButtonByText(window, "OK");
waitForSwing();
waitForModel();
-
+
waitForSwing();
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
diff --git a/certification.local.manifest b/certification.local.manifest
index 3c02935109..fd2a0e0883 100644
--- a/certification.local.manifest
+++ b/certification.local.manifest
@@ -8,4 +8,5 @@ LICENSE||GHIDRA||||END|
NOTICE||GHIDRA||||END|
README.md||GHIDRA||||END|
build.gradle||GHIDRA||||END|
+ghidra.repos.config||GHIDRA||||END|
settings.gradle||GHIDRA||||END|