GT-2845: Storing original program bytes in the program database.

This commit is contained in:
ghidravore
2019-05-06 12:44:51 -04:00
committed by Ryan Kurtz
parent d95fd43762
commit 0792417979
73 changed files with 7129 additions and 2575 deletions
@@ -99,6 +99,13 @@
<P><I><B>Initialized -</B></I> Indicates whether the block has been initialized with values;
this property applies to Default and Overlay blocks.</P>
<P><I><B>Byte Source -</B></I> Provides information about the source of the bytes in this
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.</P>
<P><I><B>Source -</B></I> The name of the file that produced the bytes that make up this
block as set by the file importer; for <A href="#BitMappedType">Bit Mapped</A> or <A href=
@@ -228,10 +235,16 @@
<UL>
<LI><I><B>Default&nbsp;</B></I> - A normal memory block within the processor's address
space.&nbsp; These blocks cannot overlap any other default block.&nbsp; Default blocks
can be either initialized or uninitialized. If you select <I>Initialized</I> you can
enter a byte value that will be used to fill all the bytes in the new memory
block.</LI>
can be one of the following types:</LI>
<UL>
<LI><B>Initialized</B> - Specify a value and a new block will be created
using that value for every byte in the block. </LI>
<LI><B>Uninitialized</B> - An unitialized block will be created.</LI>
<LI><B>File Bytes</B> - 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.</LI>
<P><I><IMG src="../../shared/note.png" border="0"> You can use the "Add To Program"
using "Binary Import" to create new FileBytes that you can use here.</I></P>
</UL>
<LI><B><I>Overlay -</I></B> An overlay block is used to give an alternative set of
bytes (and related information) for a range in memory.&nbsp; This is achieved by
creating a new address space related to the actual processor address space and placing
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@@ -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;
}
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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) {
}
}
}
}
@@ -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);
}
}
File diff suppressed because it is too large Load Diff
@@ -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());
}
}
@@ -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<MemoryBlock> {
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<MemoryBlock> {
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<MemoryBlock> {
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<MemoryBlock> {
*/
@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<MemoryBlock> {
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<MemoryBlock> {
return null;
}
private String getByteSourceDescription(List<SourceInfo> sourceInfos) {
List<SourceInfo> 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<MemoryBlock> getModelData() {
return memList;
@@ -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");
}
}
}
}
@@ -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);
}
}
@@ -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<Program> loadProgram(ByteProvider provider, String programName,
DomainFolder programFolder, LoadSpec loadSpec, List<Option> 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) {
@@ -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;
@@ -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");
}
@@ -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
@@ -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());
@@ -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;
}
@@ -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);
@@ -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<KeyRange> 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);
}
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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;
@@ -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<FileBytes> 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();
}
}
@@ -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<SourceInfo> getSourceInfos() {
throw new UnsupportedOperationException();
}
}
@@ -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<FileBytes> 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();
}
}
@@ -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.
@@ -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<String, Table> 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());
@@ -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
@@ -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));
@@ -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));
@@ -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));
}
@@ -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);
}
}
@@ -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<MemoryBlockDB> 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;
}
}
@@ -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);
}
}
@@ -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<MemoryBlockDB> 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;
}
}
@@ -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;
}
}
@@ -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<ByteSourceRange> {
List<ByteSourceRange> ranges;
public ByteSourceRangeList(ByteSourceRange bsRange) {
this();
ranges.add(bsRange);
}
public ByteSourceRangeList() {
ranges = new ArrayList<>();
}
@Override
public Iterator<ByteSourceRange> 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<MemoryBlock> getOverlappingBlocks() {
List<BlockRangeEntry> 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<BlockRangeEntry> 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<BlockRangeEntry> entries) {
ByteSourceRangeList result = new ByteSourceRangeList();
Set<ByteSourceRange> 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<ByteSourceRange> 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<MemoryBlock> findOverlappingBlocks(List<BlockRangeEntry> entries) {
Set<MemoryBlock> overlappingBlocks = new HashSet<>();
Set<ByteSourceRange> 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<BlockRangeEntry> {
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;
}
}
}
@@ -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;
}
}
@@ -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<FileBytes> getAllFileBytes();
abstract void refresh() throws IOException;
abstract boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
}
@@ -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<FileBytes> getAllFileBytes() {
return Collections.emptyList();
}
@Override
void refresh() {
// do nothing
}
@Override
boolean deleteFileBytes(FileBytes fileBytes) {
return false;
}
}
@@ -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<FileBytes> 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<FileBytes> getAllFileBytes() {
return fileBytesList;
}
@Override
void refresh() throws IOException {
Map<Long, FileBytes> map = new HashMap<>();
List<FileBytes> 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;
}
}
@@ -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);
}
}
@@ -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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More