GT-2845: Storing original program bytes in the program database.
|
@ -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 </B></I> - A normal memory block within the processor's address
|
||||
space. These blocks cannot overlap any other default block. Default blocks
|
||||
can be either initialized or uninitialized. If you select <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. This is achieved by
|
||||
creating a new address space related to the actual processor address space and placing
|
||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 27 KiB |
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.memory;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
|
@ -27,14 +27,18 @@ import docking.widgets.checkbox.GCheckBox;
|
|||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
|
||||
import ghidra.app.plugin.core.misc.RegisterField;
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.HorizontalLayout;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
/**
|
||||
|
@ -50,10 +54,9 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
private JTextField commentField;
|
||||
|
||||
private JPanel viewPanel;
|
||||
private CardLayout cardLayout;
|
||||
private JPanel initializedPanel;
|
||||
private JPanel bottomPanel;
|
||||
private CardLayout typeCardLayout;
|
||||
private JRadioButton initializedRB;
|
||||
private GRadioButton initializedFromFileBytesRB;
|
||||
private JRadioButton uninitializedRB;
|
||||
|
||||
private JCheckBox readCB;
|
||||
|
@ -68,13 +71,25 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
private AddBlockModel model;
|
||||
private GhidraComboBox<MemoryBlockType> comboBox;
|
||||
private boolean updatingInitializedRB;
|
||||
private CardLayout initializedTypeCardLayout;
|
||||
|
||||
private final static String MAPPED = "Mapped";
|
||||
private final static String OTHER = "Other";
|
||||
private final static String UNMAPPED = "Unmapped";
|
||||
private static final String UNITIALIZED = "UNITIALIZED";
|
||||
private static final String INITIALIZED = "INITIALIZED";
|
||||
private static final String FILE_BYTES = "FILE_BYTES";
|
||||
private JPanel inializedTypePanel;
|
||||
private RegisterField fileOffsetField;
|
||||
private GhidraComboBox<FileBytes> fileBytesComboBox;
|
||||
|
||||
AddBlockDialog(AddBlockModel model) {
|
||||
super("Add Memory Block", true, true, true, false);
|
||||
init(model);
|
||||
this.model = model;
|
||||
model.setChangeListener(this);
|
||||
setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block"));
|
||||
addWorkPanel(buildWorkPanel());
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,150 +99,197 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
public void stateChanged(ChangeEvent e) {
|
||||
setStatusText(model.getMessage());
|
||||
setOkEnabled(model.isValidInfo());
|
||||
readCB.setEnabled(model.isReadEnabled());
|
||||
writeCB.setEnabled(model.isWriteEnabled());
|
||||
executeCB.setEnabled(model.isExecuteEnabled());
|
||||
volatileCB.setEnabled(model.isVolatileEnabled());
|
||||
if (initializedRB != null) {
|
||||
updatingInitializedRB = true;
|
||||
try {
|
||||
initializedRB.setSelected(model.getInitializedState());
|
||||
}
|
||||
finally {
|
||||
updatingInitializedRB = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void init(AddBlockModel blockModel) {
|
||||
this.model = blockModel;
|
||||
blockModel.setChangeListener(this);
|
||||
create();
|
||||
setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block"));
|
||||
readCB.setSelected(model.isRead());
|
||||
writeCB.setSelected(model.isWrite());
|
||||
executeCB.setSelected(model.isExecute());
|
||||
volatileCB.setSelected(model.isVolatile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the Main panel for the dialog here.
|
||||
*/
|
||||
private void create() {
|
||||
cardLayout = new CardLayout();
|
||||
viewPanel = new JPanel(cardLayout);
|
||||
private JComponent buildWorkPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
|
||||
panel.add(buildMainPanel(), BorderLayout.NORTH);
|
||||
panel.add(buildVariablePanel(), BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
nameField = new JTextField();
|
||||
nameField.setName("Block Name");
|
||||
private Component buildMainPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(buildBasicInfoPanel(), BorderLayout.NORTH);
|
||||
panel.add(buildPermissionsPanel(), BorderLayout.CENTER);
|
||||
panel.add(buildTypesPanel(), BorderLayout.SOUTH);
|
||||
|
||||
addrField = new AddressInput();
|
||||
addrField.setName("Start Addr");
|
||||
return panel;
|
||||
}
|
||||
|
||||
lengthField = new RegisterField(32, null, false);
|
||||
lengthField.setName("Length");
|
||||
private Component buildBasicInfoPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout(4, 10, 150));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
|
||||
commentField = new JTextField();
|
||||
commentField.setName("Comment");
|
||||
panel.add(new GLabel("Block Name:", SwingConstants.RIGHT));
|
||||
panel.add(buildNameField());
|
||||
panel.add(new GLabel("Start Addr:", SwingConstants.RIGHT));
|
||||
panel.add(buildAddressField());
|
||||
panel.add(new GLabel("Length:", SwingConstants.RIGHT));
|
||||
panel.add(buildLengthField());
|
||||
panel.add(new GLabel("Comment:", SwingConstants.RIGHT));
|
||||
panel.add(buildCommentField());
|
||||
|
||||
addrFactory = model.getProgram().getAddressFactory();
|
||||
addrField.setAddressFactory(addrFactory, true);
|
||||
return panel;
|
||||
}
|
||||
|
||||
nameField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
private Component buildPermissionsPanel() {
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
});
|
||||
|
||||
lengthField.setChangeListener(e -> lengthChanged());
|
||||
addrField.addChangeListener(ev -> addrChanged());
|
||||
|
||||
readCB = new GCheckBox();
|
||||
readCB = new GCheckBox("Read");
|
||||
readCB.setName("Read");
|
||||
readCB.setSelected(model.isRead());
|
||||
readCB.addActionListener(e -> model.setRead(readCB.isSelected()));
|
||||
|
||||
writeCB = new GCheckBox();
|
||||
writeCB = new GCheckBox("Write");
|
||||
writeCB.setName("Write");
|
||||
writeCB.setSelected(model.isWrite());
|
||||
writeCB.addActionListener(e -> model.setWrite(writeCB.isSelected()));
|
||||
|
||||
executeCB = new GCheckBox();
|
||||
executeCB = new GCheckBox("Execute");
|
||||
executeCB.setName("Execute");
|
||||
executeCB.setSelected(model.isExecute());
|
||||
executeCB.addActionListener(e -> model.setExecute(executeCB.isSelected()));
|
||||
|
||||
volatileCB = new GCheckBox();
|
||||
volatileCB = new GCheckBox("Volatile");
|
||||
volatileCB.setName("Volatile");
|
||||
volatileCB.setSelected(model.isVolatile());
|
||||
volatileCB.addActionListener(e -> model.setVolatile(volatileCB.isSelected()));
|
||||
|
||||
JPanel topPanel = new JPanel(new PairLayout(4, 10, 150));
|
||||
topPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
topPanel.add(new GLabel("Block Name:", SwingConstants.RIGHT));
|
||||
topPanel.add(nameField);
|
||||
topPanel.add(new GLabel("Start Addr:", SwingConstants.RIGHT));
|
||||
topPanel.add(addrField);
|
||||
topPanel.add(new GLabel("Length:", SwingConstants.RIGHT));
|
||||
topPanel.add(lengthField);
|
||||
topPanel.add(new GLabel("Comment:", SwingConstants.RIGHT));
|
||||
topPanel.add(commentField);
|
||||
JPanel panel = new JPanel(new HorizontalLayout(10));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 30, 20, 30));
|
||||
panel.add(readCB);
|
||||
panel.add(writeCB);
|
||||
panel.add(executeCB);
|
||||
panel.add(volatileCB);
|
||||
|
||||
JPanel execPanel = new JPanel();
|
||||
BoxLayout bl = new BoxLayout(execPanel, BoxLayout.X_AXIS);
|
||||
execPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
return panel;
|
||||
}
|
||||
|
||||
execPanel.setLayout(bl);
|
||||
execPanel.add(Box.createHorizontalStrut(10));
|
||||
execPanel.add(new GLabel("Read"));
|
||||
execPanel.add(readCB);
|
||||
execPanel.add(Box.createHorizontalStrut(10));
|
||||
private Component buildTypesPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Block Types"));
|
||||
|
||||
execPanel.add(new GLabel("Write"));
|
||||
execPanel.add(writeCB);
|
||||
execPanel.add(Box.createHorizontalStrut(10));
|
||||
MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT,
|
||||
MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
|
||||
|
||||
execPanel.add(new GLabel("Execute"));
|
||||
execPanel.add(executeCB);
|
||||
execPanel.add(Box.createHorizontalStrut(10));
|
||||
comboBox = new GhidraComboBox<>(items);
|
||||
comboBox.addItemListener(e -> blockTypeSelected());
|
||||
panel.add(comboBox);
|
||||
return panel;
|
||||
}
|
||||
|
||||
execPanel.add(new GLabel("Volatile"));
|
||||
execPanel.add(volatileCB);
|
||||
private Component buildVariablePanel() {
|
||||
typeCardLayout = new CardLayout();
|
||||
viewPanel = new JPanel(typeCardLayout);
|
||||
viewPanel.setBorder(BorderFactory.createEtchedBorder());
|
||||
|
||||
JPanel panel = new JPanel();
|
||||
panel.add(execPanel);
|
||||
viewPanel.add(buildMappedPanel(), MAPPED);
|
||||
viewPanel.add(buildUnmappedPanel(), UNMAPPED);
|
||||
typeCardLayout.show(viewPanel, UNMAPPED);
|
||||
return viewPanel;
|
||||
}
|
||||
|
||||
JPanel outerTopPanel = new JPanel(new BorderLayout());
|
||||
outerTopPanel.add(topPanel, BorderLayout.NORTH);
|
||||
outerTopPanel.add(panel, BorderLayout.CENTER);
|
||||
private Component buildUnmappedPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.add(buildInitializedRadioButtonPanel(), BorderLayout.NORTH);
|
||||
panel.add(buildVariableInitializedPanel());
|
||||
return panel;
|
||||
}
|
||||
|
||||
bottomPanel = new JPanel();
|
||||
BoxLayout layout = new BoxLayout(bottomPanel, BoxLayout.Y_AXIS);
|
||||
bottomPanel.setLayout(layout);
|
||||
bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 4, 5));
|
||||
bottomPanel.add(createComboBoxPanel());
|
||||
bottomPanel.add(viewPanel);
|
||||
private Component buildInitializedRadioButtonPanel() {
|
||||
JPanel panel = new JPanel(new HorizontalLayout(10));
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
layout = new BoxLayout(mainPanel, BoxLayout.Y_AXIS);
|
||||
mainPanel.setLayout(layout);
|
||||
mainPanel.add(outerTopPanel);
|
||||
mainPanel.add(bottomPanel);
|
||||
mainPanel.validate();
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
initializedRB = new GRadioButton("Initialized", false);
|
||||
initializedRB.setName(initializedRB.getText());
|
||||
initializedRB.addActionListener(ev -> initializeRBChanged());
|
||||
|
||||
JPanel mainPanel2 = new JPanel(new BorderLayout());
|
||||
mainPanel2.add(mainPanel, BorderLayout.NORTH);
|
||||
mainPanel2.add(new JPanel(), BorderLayout.CENTER);
|
||||
initializedFromFileBytesRB = new GRadioButton("File Bytes", false);
|
||||
initializedFromFileBytesRB.setName(initializedRB.getText());
|
||||
initializedFromFileBytesRB.addActionListener(ev -> initializeRBChanged());
|
||||
|
||||
createCardPanels();
|
||||
uninitializedRB = new GRadioButton("Uninitialized", true);
|
||||
uninitializedRB.setName(uninitializedRB.getText());
|
||||
uninitializedRB.addActionListener(ev -> initializeRBChanged());
|
||||
|
||||
addWorkPanel(mainPanel2);
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
radioGroup.add(initializedRB);
|
||||
radioGroup.add(initializedFromFileBytesRB);
|
||||
radioGroup.add(uninitializedRB);
|
||||
|
||||
panel.add(initializedRB);
|
||||
panel.add(initializedFromFileBytesRB);
|
||||
panel.add(uninitializedRB);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildVariableInitializedPanel() {
|
||||
initializedTypeCardLayout = new CardLayout();
|
||||
|
||||
inializedTypePanel = new JPanel(initializedTypeCardLayout);
|
||||
inializedTypePanel.add(new JPanel(), UNITIALIZED);
|
||||
inializedTypePanel.add(buildInitalValuePanel(), INITIALIZED);
|
||||
inializedTypePanel.add(buildFileBytesPanel(), FILE_BYTES);
|
||||
return inializedTypePanel;
|
||||
}
|
||||
|
||||
private Component buildInitalValuePanel() {
|
||||
initialValueLabel = new GDLabel("Initial Value");
|
||||
initialValueField = new RegisterField(8, null, false);
|
||||
initialValueField.setName("Initial Value");
|
||||
|
||||
initialValueField.setChangeListener(e -> initialValueChanged());
|
||||
|
||||
JPanel panel = new JPanel(new PairLayout(4, 10));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
panel.add(initialValueLabel);
|
||||
panel.add(initialValueField);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildFileBytesPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
|
||||
panel.add(new GLabel("File Bytes:"));
|
||||
panel.add(buildFileBytesCombo());
|
||||
panel.add(new GLabel("File Offset:"));
|
||||
panel.add(buildFileOffsetField());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildFileBytesCombo() {
|
||||
Memory memory = model.getProgram().getMemory();
|
||||
List<FileBytes> allFileBytes = memory.getAllFileBytes();
|
||||
FileBytes[] fileBytes = allFileBytes.toArray(new FileBytes[allFileBytes.size()]);
|
||||
|
||||
fileBytesComboBox = new GhidraComboBox<>(fileBytes) {
|
||||
public Dimension getPreferredSize() {
|
||||
Dimension preferredSize = super.getPreferredSize();
|
||||
preferredSize.width = 100;
|
||||
return preferredSize;
|
||||
}
|
||||
};
|
||||
fileBytesComboBox.addItemListener(e -> fileBytesChanged());
|
||||
if (!allFileBytes.isEmpty()) {
|
||||
model.setFileBytes(allFileBytes.get(0));
|
||||
}
|
||||
return fileBytesComboBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the dialog filled with default values.
|
||||
* Used to enter a new MemoryBlock.
|
||||
* @param nlines default value displayed in the text field.
|
||||
* @param tool the tool that owns this dialog
|
||||
*/
|
||||
void showDialog(PluginTool tool) {
|
||||
|
||||
|
@ -237,17 +299,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
lengthField.setValue(Long.valueOf(0));
|
||||
model.setLength(0);
|
||||
commentField.setText("");
|
||||
|
||||
readCB.setSelected(true);
|
||||
writeCB.setSelected(true);
|
||||
executeCB.setSelected(false);
|
||||
volatileCB.setSelected(false);
|
||||
|
||||
initialValueField.setValue(Long.valueOf(0));
|
||||
model.setBlockType(MemoryBlockType.DEFAULT);
|
||||
model.setIsInitialized(initializedRB.isSelected());
|
||||
model.setInitializedType(AddBlockModel.InitializedType.UNITIALIZED);
|
||||
model.setInitialValue(0);
|
||||
|
||||
readCB.setSelected(model.isRead());
|
||||
writeCB.setSelected(model.isWrite());
|
||||
executeCB.setSelected(model.isExecute());
|
||||
volatileCB.setSelected(model.isVolatile());
|
||||
|
||||
setOkEnabled(false);
|
||||
tool.showDialog(this, tool.getComponentProvider(PluginConstants.MEMORY_MAP));
|
||||
}
|
||||
|
@ -262,9 +323,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
*/
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
|
||||
if (model.execute(commentField.getText(), readCB.isSelected(), writeCB.isSelected(),
|
||||
executeCB.isSelected(), volatileCB.isSelected())) {
|
||||
if (model.execute()) {
|
||||
close();
|
||||
}
|
||||
else {
|
||||
|
@ -277,27 +336,17 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
if (updatingInitializedRB) {
|
||||
return;
|
||||
}
|
||||
boolean selected = initializedRB.isSelected();
|
||||
model.setIsInitialized(selected);
|
||||
initialValueField.setEnabled(selected);
|
||||
initialValueLabel.setEnabled(selected);
|
||||
if (!selected) {
|
||||
initialValueField.setValue(new Long(0));
|
||||
model.setInitialValue(0);
|
||||
if (initializedRB.isSelected()) {
|
||||
model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
|
||||
initializedTypeCardLayout.show(inializedTypePanel, INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
private void uninitializedRBChanged() {
|
||||
if (updatingInitializedRB) {
|
||||
return;
|
||||
else if (uninitializedRB.isSelected()) {
|
||||
model.setInitializedType(InitializedType.UNITIALIZED);
|
||||
initializedTypeCardLayout.show(inializedTypePanel, UNITIALIZED);
|
||||
}
|
||||
boolean selected = uninitializedRB.isSelected();
|
||||
model.setIsInitialized(!selected);
|
||||
initialValueField.setEnabled(!selected);
|
||||
initialValueLabel.setEnabled(!selected);
|
||||
if (!selected) {
|
||||
initialValueField.setValue(new Long(0));
|
||||
model.setInitialValue(0);
|
||||
else if (initializedFromFileBytesRB.isSelected()) {
|
||||
model.setInitializedType(InitializedType.INITIALIZED_FROM_FILE_BYTES);
|
||||
initializedTypeCardLayout.show(inializedTypePanel, FILE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,21 +367,40 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
model.setBlockName(name);
|
||||
}
|
||||
|
||||
private void commentChanged() {
|
||||
String comment = commentField.getText().trim();
|
||||
model.setComment(comment);
|
||||
}
|
||||
|
||||
private void lengthChanged() {
|
||||
int length = 0;
|
||||
long length = 0;
|
||||
Long val = lengthField.getValue();
|
||||
if (val != null) {
|
||||
length = val.intValue();
|
||||
length = val.longValue();
|
||||
}
|
||||
model.setLength(length);
|
||||
}
|
||||
|
||||
private void fileOffsetChanged() {
|
||||
long fileOffset = -1;
|
||||
Long val = fileOffsetField.getValue();
|
||||
if (val != null) {
|
||||
fileOffset = val.longValue();
|
||||
}
|
||||
model.setFileOffset(fileOffset);
|
||||
}
|
||||
|
||||
private void fileBytesChanged() {
|
||||
model.setFileBytes((FileBytes) fileBytesComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
private void addrChanged() {
|
||||
Address addr = null;
|
||||
try {
|
||||
addr = addrField.getAddress();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// just let it be null
|
||||
}
|
||||
model.setStartAddress(addr);
|
||||
}
|
||||
|
@ -342,90 +410,19 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
model.setBaseAddress(baseAddress);
|
||||
}
|
||||
|
||||
private JPanel createComboBoxPanel() {
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(BorderFactory.createTitledBorder("Block Types"));
|
||||
|
||||
MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT,
|
||||
MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
|
||||
|
||||
comboBox = new GhidraComboBox<>(items);
|
||||
comboBox.addItemListener(e -> blockTypeSelected());
|
||||
panel.add(comboBox);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void blockTypeSelected() {
|
||||
|
||||
MemoryBlockType blockType = (MemoryBlockType) comboBox.getSelectedItem();
|
||||
model.setBlockType(blockType);
|
||||
if (blockType == MemoryBlockType.DEFAULT) {
|
||||
cardLayout.show(viewPanel, OTHER);
|
||||
}
|
||||
else if (blockType == MemoryBlockType.OVERLAY) {
|
||||
cardLayout.show(viewPanel, OTHER);
|
||||
}
|
||||
else if (blockType == MemoryBlockType.BIT_MAPPED) {
|
||||
cardLayout.show(viewPanel, MAPPED);
|
||||
if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) {
|
||||
typeCardLayout.show(viewPanel, UNMAPPED);
|
||||
}
|
||||
else {
|
||||
// type is Byte mapped
|
||||
cardLayout.show(viewPanel, MAPPED);
|
||||
typeCardLayout.show(viewPanel, MAPPED);
|
||||
}
|
||||
}
|
||||
|
||||
private JPanel createRadioPanel() {
|
||||
JPanel panel = new JPanel();
|
||||
BoxLayout bl = new BoxLayout(panel, BoxLayout.X_AXIS);
|
||||
panel.setLayout(bl);
|
||||
|
||||
ButtonGroup radioGroup = new ButtonGroup();
|
||||
initializedRB = new GRadioButton("Initialized", false);
|
||||
initializedRB.setName(initializedRB.getText());
|
||||
initializedRB.addActionListener(ev -> initializeRBChanged());
|
||||
|
||||
uninitializedRB = new GRadioButton("Uninitialized", true);
|
||||
uninitializedRB.setName(uninitializedRB.getText());
|
||||
uninitializedRB.addActionListener(ev -> uninitializedRBChanged());
|
||||
|
||||
radioGroup.add(initializedRB);
|
||||
radioGroup.add(uninitializedRB);
|
||||
|
||||
panel.add(initializedRB);
|
||||
panel.add(uninitializedRB);
|
||||
|
||||
JPanel outerPanel = new JPanel();
|
||||
BoxLayout bl2 = new BoxLayout(outerPanel, BoxLayout.Y_AXIS);
|
||||
outerPanel.setLayout(bl2);
|
||||
outerPanel.add(panel);
|
||||
createInitializedPanel();
|
||||
outerPanel.add(initializedPanel);
|
||||
outerPanel.setBorder(BorderFactory.createEtchedBorder());
|
||||
return outerPanel;
|
||||
}
|
||||
|
||||
private void createCardPanels() {
|
||||
viewPanel.add(createAddressPanel(), MAPPED);
|
||||
viewPanel.add(createRadioPanel(), OTHER);
|
||||
cardLayout.show(viewPanel, OTHER);
|
||||
}
|
||||
|
||||
private void createInitializedPanel() {
|
||||
initialValueLabel = new GDLabel("Initial Value");
|
||||
initialValueField = new RegisterField(8, null, false);
|
||||
initialValueField.setName("Initial Value");
|
||||
initialValueField.setEnabled(false);
|
||||
|
||||
initialValueField.setChangeListener(e -> initialValueChanged());
|
||||
|
||||
initializedPanel = new JPanel(new PairLayout(4, 10));
|
||||
initializedPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
initializedPanel.add(initialValueLabel);
|
||||
initializedPanel.add(initialValueField);
|
||||
}
|
||||
|
||||
private JPanel createAddressPanel() {
|
||||
JPanel addressPanel = new JPanel(new PairLayout());
|
||||
private JPanel buildMappedPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout());
|
||||
|
||||
baseAddrField = new AddressInput();
|
||||
baseAddrField.setAddressFactory(addrFactory);
|
||||
|
@ -440,10 +437,77 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
}
|
||||
baseAddrField.setAddress(minAddr);
|
||||
model.setBaseAddress(minAddr);
|
||||
addressPanel.add(new GLabel("Source Addr:"));
|
||||
addressPanel.add(baseAddrField);
|
||||
addressPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
|
||||
return addressPanel;
|
||||
panel.add(new GLabel("Source Addr:"));
|
||||
panel.add(baseAddrField);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component buildCommentField() {
|
||||
commentField = new JTextField();
|
||||
commentField.setName("Comment");
|
||||
commentField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
commentChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
commentChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
commentChanged();
|
||||
}
|
||||
});
|
||||
return commentField;
|
||||
}
|
||||
|
||||
private Component buildLengthField() {
|
||||
lengthField = new RegisterField(36, null, false);
|
||||
lengthField.setName("Length");
|
||||
lengthField.setChangeListener(e -> lengthChanged());
|
||||
return lengthField;
|
||||
}
|
||||
|
||||
private Component buildFileOffsetField() {
|
||||
fileOffsetField = new RegisterField(60, null, false);
|
||||
fileOffsetField.setName("File Offset");
|
||||
fileOffsetField.setChangeListener(e -> fileOffsetChanged());
|
||||
return fileOffsetField;
|
||||
}
|
||||
|
||||
private Component buildAddressField() {
|
||||
addrField = new AddressInput();
|
||||
addrField.setName("Start Addr");
|
||||
addrFactory = model.getProgram().getAddressFactory();
|
||||
addrField.setAddressFactory(addrFactory, true);
|
||||
addrField.addChangeListener(ev -> addrChanged());
|
||||
return addrField;
|
||||
}
|
||||
|
||||
private Component buildNameField() {
|
||||
nameField = new JTextField();
|
||||
nameField.setName("Block Name");
|
||||
nameField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
nameChanged();
|
||||
}
|
||||
});
|
||||
return nameField;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ import ghidra.program.model.listing.Program;
|
|||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.util.ChangeManager;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.IntObjectHashtable;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
@ -48,20 +47,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
private ProgramDB program;
|
||||
private AddressMapDB addrMap;
|
||||
private MemoryMapDBAdapter adapter;
|
||||
private FileBytesAdapter fileBytesAdapter;
|
||||
|
||||
private static final DataConverter BIG_ENDIAN = BigEndianDataConverter.INSTANCE;
|
||||
private static final DataConverter LITTLE_ENDIAN = LittleEndianDataConverter.INSTANCE;
|
||||
|
||||
private DataConverter defaultEndian;
|
||||
private MemoryBlock[] blocks;// sorted list of blocks
|
||||
private IntObjectHashtable<MemoryBlock> blockMap = new IntObjectHashtable<>();// maps id to MemoryBlockDB objects
|
||||
private AddressSet addrSet;
|
||||
private AddressSet initializedLoadedAddrSet;
|
||||
private AddressSet allInitializedAddrSet;
|
||||
private List<MemoryBlockDB> blocks;// sorted list of blocks
|
||||
private AddressSet addrSet = new AddressSet();
|
||||
private AddressSet initializedLoadedAddrSet = new AddressSet();
|
||||
private AddressSet allInitializedAddrSet = new AddressSet();
|
||||
private MemoryBlock lastBlock;// the last accessed block
|
||||
private LiveMemoryHandler liveMemory;
|
||||
|
||||
Lock lock;
|
||||
private Set<MemoryBlock> potentialOverlappingBlocks;
|
||||
|
||||
private static Comparator<Object> BLOCK_ADDRESS_COMPARATOR = (o1, o2) -> {
|
||||
MemoryBlock block = (MemoryBlock) o1;
|
||||
|
@ -84,9 +84,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
Lock lock, TaskMonitor monitor) throws IOException, VersionException {
|
||||
this.addrMap = addrMap;
|
||||
this.lock = lock;
|
||||
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
|
||||
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
|
||||
init(false);
|
||||
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
|
||||
fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, this, monitor);
|
||||
initializeBlocks();
|
||||
buildAddressSets();
|
||||
}
|
||||
|
||||
// for testing
|
||||
MemoryMapDB(DBHandle handle, AddressMapDB addrMap, int openMode, boolean isBigEndian,
|
||||
Lock lock) {
|
||||
this.addrMap = addrMap;
|
||||
this.lock = lock;
|
||||
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
// for testing
|
||||
void init(MemoryMapDBAdapter memoryAdapter, FileBytesAdapter bytesAdapter) {
|
||||
this.adapter = memoryAdapter;
|
||||
this.fileBytesAdapter = bytesAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,55 +120,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
public void invalidateCache(boolean all) throws IOException {
|
||||
lock.acquire();
|
||||
try {
|
||||
init(true);
|
||||
reloadAll();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void init(boolean reload) throws IOException {
|
||||
private void buildAddressSets() {
|
||||
addrSet = new AddressSet();
|
||||
initializedLoadedAddrSet = new AddressSet();
|
||||
allInitializedAddrSet = new AddressSet();
|
||||
// we have to process the non-mapped blocks first because to process the mapped
|
||||
// blocks we need the address sets for the non-mapped blocks to be complete
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
if (!block.isMapped()) {
|
||||
addBlockAddresses(block);
|
||||
}
|
||||
}
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
if (block.isMapped()) {
|
||||
addBlockAddresses(block);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addBlockAddresses(MemoryBlockDB block) {
|
||||
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
|
||||
addrSet = addrSet.union(blockSet);
|
||||
if (block.isMapped()) {
|
||||
allInitializedAddrSet =
|
||||
allInitializedAddrSet.union(getMappedIntersection(block, allInitializedAddrSet));
|
||||
initializedLoadedAddrSet = initializedLoadedAddrSet.union(
|
||||
getMappedIntersection(block, initializedLoadedAddrSet));
|
||||
|
||||
}
|
||||
else if (block.isInitialized()) {
|
||||
allInitializedAddrSet = allInitializedAddrSet.union(blockSet);
|
||||
if (block.isLoaded()) {
|
||||
initializedLoadedAddrSet = initializedLoadedAddrSet.union(blockSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void reloadAll() throws IOException {
|
||||
synchronized (this) {
|
||||
if (reload) {
|
||||
adapter.refreshMemory();
|
||||
}
|
||||
|
||||
// Minimize chance of accessing field variables while in flux
|
||||
MemoryBlockDB[] newBlocks = adapter.getMemoryBlocks();
|
||||
AddressSet newAddrSet = new AddressSet();
|
||||
AddressSet newLoadedInitializedAddrSet = new AddressSet();
|
||||
AddressSet newAllInitializedAddrSet = new AddressSet();
|
||||
List<MappedMemoryBlock> mappedMemoryBlocks = new LinkedList<>();
|
||||
IntObjectHashtable<MemoryBlock> newBlockMap = new IntObjectHashtable<>();
|
||||
for (MemoryBlockDB block : newBlocks) {
|
||||
newBlockMap.put(block.getID(), block);
|
||||
newAddrSet.addRange(block.getStart(), block.getEnd());
|
||||
if (block.isInitialized()) {
|
||||
newAllInitializedAddrSet.addRange(block.getStart(), block.getEnd());
|
||||
if (block.isLoaded()) {
|
||||
newLoadedInitializedAddrSet.addRange(block.getStart(), block.getEnd());
|
||||
}
|
||||
}
|
||||
if (block.isMapped() && block instanceof MappedMemoryBlock) {
|
||||
mappedMemoryBlocks.add((MappedMemoryBlock) block);
|
||||
}
|
||||
}
|
||||
|
||||
// Mapped blocks are uninitialized, but ranges of them may map to initialized ranges.
|
||||
// Add in these mapped initialized ranges. Don't forget to keep them up to date as
|
||||
// initialized blocks that they map to come and go (this happens automatically because
|
||||
// this method gets called on every add/remove).
|
||||
newAllInitializedAddrSet.add(
|
||||
getMappedIntersection(mappedMemoryBlocks, newAllInitializedAddrSet));
|
||||
newLoadedInitializedAddrSet.add(
|
||||
getMappedIntersection(mappedMemoryBlocks, newLoadedInitializedAddrSet));
|
||||
|
||||
lastBlock = null;
|
||||
blocks = newBlocks;
|
||||
addrSet = newAddrSet;
|
||||
initializedLoadedAddrSet = newLoadedInitializedAddrSet;
|
||||
allInitializedAddrSet = newAllInitializedAddrSet;
|
||||
blockMap = newBlockMap;
|
||||
fileBytesAdapter.refresh();
|
||||
adapter.refreshMemory();
|
||||
initializeBlocks();
|
||||
buildAddressSets();
|
||||
}
|
||||
if (liveMemory != null) {
|
||||
liveMemory.clearCache();
|
||||
|
@ -160,6 +178,13 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
addrMap.memoryMapChanged(this);
|
||||
}
|
||||
|
||||
private synchronized void initializeBlocks() {
|
||||
List<MemoryBlockDB> newBlocks = adapter.getMemoryBlocks();
|
||||
lastBlock = null;
|
||||
blocks = newBlocks;
|
||||
addrMap.memoryMapChanged(this);
|
||||
}
|
||||
|
||||
public void setLanguage(Language newLanguage) {
|
||||
defaultEndian = newLanguage.isBigEndian() ? BIG_ENDIAN : LITTLE_ENDIAN;
|
||||
}
|
||||
|
@ -171,7 +196,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
public void setProgram(ProgramDB program) {
|
||||
this.program = program;
|
||||
try {
|
||||
init(true);
|
||||
reloadAll();
|
||||
}
|
||||
catch (IOException e) {
|
||||
dbError(e);
|
||||
|
@ -233,32 +258,36 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
return initializedLoadedAddrSet;
|
||||
}
|
||||
|
||||
void checkMemoryWrite(Address start, int length) throws MemoryAccessException {
|
||||
CodeManager codeManager = program.getCodeManager();
|
||||
Instruction instr = codeManager.getInstructionContaining(start);
|
||||
if (instr != null) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
}
|
||||
if (length > 1) {
|
||||
instr = codeManager.getInstructionAfter(start);
|
||||
if (instr != null) {
|
||||
Address end = start.add(length - 1);
|
||||
if (instr.getMinAddress().compareTo(end) <= 0) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
void checkMemoryWrite(MemoryBlockDB block, Address start, long length)
|
||||
throws MemoryAccessException {
|
||||
checkRangeForInstructions(start, start.add(length - 1));
|
||||
|
||||
Set<MemoryBlock> overlappingBlocks = getPotentialOverlappingBlocks();
|
||||
ByteSourceRangeList changeingByteSource = block.getByteSourceRangeList(start, length);
|
||||
if (overlappingBlocks.contains(block)) {
|
||||
for (MemoryBlock b : overlappingBlocks) {
|
||||
if (b.equals(block)) {
|
||||
continue;
|
||||
}
|
||||
ByteSourceRangeList set =
|
||||
((MemoryBlockDB) b).getByteSourceRangeList(b.getStart(), b.getSize());
|
||||
ByteSourceRangeList intersect = set.intersect(changeingByteSource);
|
||||
for (ByteSourceRange range : intersect) {
|
||||
checkRangeForInstructions(range.getStart(), range.getEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkMemoryWrite(Address addr) throws MemoryAccessException {
|
||||
CodeManager codeManager = program.getCodeManager();
|
||||
Instruction instr = codeManager.getInstructionContaining(addr);
|
||||
if (instr != null) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
private Set<MemoryBlock> getPotentialOverlappingBlocks() {
|
||||
if (potentialOverlappingBlocks == null) {
|
||||
ByteSourceRangeList byteSourceList = new ByteSourceRangeList();
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
byteSourceList.add(block.getByteSourceRangeList(block.getStart(), block.getSize()));
|
||||
}
|
||||
potentialOverlappingBlocks = byteSourceList.getOverlappingBlocks();
|
||||
}
|
||||
return potentialOverlappingBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,15 +317,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
return lastBlock;
|
||||
}
|
||||
}
|
||||
MemoryBlock[] tmpBlocks = blocks;
|
||||
int index = Arrays.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
|
||||
List<MemoryBlockDB> tmpBlocks = blocks;
|
||||
int index = Collections.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
|
||||
if (index >= 0) {
|
||||
lastBlock = tmpBlocks[index];
|
||||
lastBlock = tmpBlocks.get(index);
|
||||
return lastBlock;
|
||||
}
|
||||
index = -index - 2;
|
||||
if (index >= 0) {
|
||||
MemoryBlock block = tmpBlocks[index];
|
||||
MemoryBlock block = tmpBlocks.get(index);
|
||||
if (block.contains(addr)) {
|
||||
lastBlock = block;
|
||||
return block;
|
||||
|
@ -305,16 +334,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
return null;
|
||||
}
|
||||
|
||||
// MemoryBlockDB getBlock(int id) {
|
||||
// lock.acquire();
|
||||
// try {
|
||||
// return (MemoryBlockDB) blockMap.get(id);
|
||||
// }
|
||||
// finally {
|
||||
// lock.release();
|
||||
// }
|
||||
// }
|
||||
|
||||
void fireBlockAdded(MemoryBlock newBlock) {
|
||||
AddressRange range = new AddressRangeImpl(newBlock.getStart(), newBlock.getEnd());
|
||||
program.getTreeManager().addMemoryBlock(newBlock.getName(), range);
|
||||
|
@ -352,7 +371,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
newBlock);
|
||||
}
|
||||
|
||||
void fireBlockChanged(MemoryBlockDB block) {
|
||||
void fireBlockChanged(MemoryBlock block) {
|
||||
if (program != null) {
|
||||
program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null);
|
||||
}
|
||||
|
@ -468,9 +487,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
checkRange(start, length);
|
||||
}
|
||||
try {
|
||||
adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
|
||||
init(true);
|
||||
MemoryBlock newBlock = getBlockDB(start);
|
||||
MemoryBlockDB newBlock =
|
||||
adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
@ -486,6 +506,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
|
||||
long offset, long length, boolean overlay) throws LockException, DuplicateNameException,
|
||||
MemoryConflictException, AddressOverflowException {
|
||||
|
||||
lock.acquire();
|
||||
try {
|
||||
checkBlockSize(length, true);
|
||||
program.checkExclusiveAccess();
|
||||
checkFileBytesRange(fileBytes, offset, length);
|
||||
|
||||
if (overlay) {
|
||||
start = createOverlaySpace(name, start, length);
|
||||
}
|
||||
else {
|
||||
checkRange(start, length);
|
||||
}
|
||||
try {
|
||||
MemoryBlockDB newBlock =
|
||||
adapter.createFileBytesBlock(name, start, length, fileBytes, offset,
|
||||
MemoryBlock.READ);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFileBytesRange(FileBytes fileBytes, long offset, long length) {
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Length must be >= 0, got " + length);
|
||||
}
|
||||
if (offset < 0 || offset >= fileBytes.getSize()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Offset must be in range [0," + length + "], got " + offset);
|
||||
}
|
||||
if (offset + length > fileBytes.getSize()) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"Specified length extends beyond file bytes length");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -507,10 +578,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
checkRange(start, size);
|
||||
}
|
||||
try {
|
||||
adapter.createBlock(MemoryBlockType.DEFAULT, name, start, size, null, false,
|
||||
MemoryBlock.READ);
|
||||
init(true);
|
||||
MemoryBlock newBlock = getBlockDB(start);
|
||||
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.DEFAULT, name, start,
|
||||
size, null, false, MemoryBlock.READ);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
@ -534,10 +605,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
checkRange(start, length);
|
||||
overlayAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space
|
||||
try {
|
||||
adapter.createBlock(MemoryBlockType.BIT_MAPPED, name, start, length, overlayAddress,
|
||||
false, MemoryBlock.READ);
|
||||
init(true);
|
||||
MemoryBlock newBlock = getBlockDB(start);
|
||||
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BIT_MAPPED, name,
|
||||
start, length, overlayAddress, false, MemoryBlock.READ);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
@ -561,10 +632,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
checkRange(start, length);
|
||||
overlayAddress.addNoWrap(length - 1);// just to check if length fits in address space
|
||||
try {
|
||||
adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
|
||||
overlayAddress, false, MemoryBlock.READ);
|
||||
init(true);
|
||||
MemoryBlock newBlock = getBlockDB(start);
|
||||
MemoryBlockDB newBlock =
|
||||
adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
|
||||
overlayAddress, false, MemoryBlock.READ);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
@ -589,13 +661,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
try {
|
||||
Address overlayAddr = null;
|
||||
if (block instanceof MappedMemoryBlock) {
|
||||
overlayAddr = ((MappedMemoryBlock) block).getOverlayedMinAddress();
|
||||
if (block.isMapped()) {
|
||||
SourceInfo info = block.getSourceInfos().get(0);
|
||||
overlayAddr = info.getMappedRange().get().getMinAddress();
|
||||
}
|
||||
adapter.createBlock(block.getType(), name, start, length, overlayAddr,
|
||||
MemoryBlockDB newBlock =
|
||||
adapter.createBlock(block.getType(), name, start, length, overlayAddr,
|
||||
block.isInitialized(), block.getPermissions());
|
||||
init(true);
|
||||
MemoryBlock newBlock = getBlockDB(start);
|
||||
initializeBlocks();
|
||||
addBlockAddresses(newBlock);
|
||||
fireBlockAdded(newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
@ -625,7 +699,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
public MemoryBlock[] getBlocks() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return blocks.clone();
|
||||
return blocks.toArray(new MemoryBlock[blocks.size()]);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
|
@ -643,15 +717,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
throw new MemoryBlockException(
|
||||
"Memory move operation not permitted while live memory is active");
|
||||
}
|
||||
Address oldStartAddr = block.getStart();
|
||||
if (!(block instanceof MemoryBlockDB)) {
|
||||
throw new NotFoundException("Block does not belong to this program");
|
||||
}
|
||||
checkBlock(block);
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) block;
|
||||
if (memBlock.memMap != this) {
|
||||
throw new NotFoundException("Block does not belong to this program");
|
||||
}
|
||||
if (memBlock.getType() == MemoryBlockType.OVERLAY) {
|
||||
|
||||
Address oldStartAddr = block.getStart();
|
||||
if (block.getType() == MemoryBlockType.OVERLAY) {
|
||||
throw new IllegalArgumentException("Overlay blocks cannot be moved");
|
||||
}
|
||||
if (newStartAddr.getAddressSpace().isOverlaySpace()) {
|
||||
|
@ -662,7 +732,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
// events go out that would cause screen updates;
|
||||
// the code manager will be locked until the remove is done
|
||||
try {
|
||||
memBlock.checkValid();
|
||||
Address newEndAddr = newStartAddr.addNoWrap(block.getSize() - 1);
|
||||
AddressSet set = new AddressSet(addrSet);
|
||||
set.deleteRange(block.getStart(), block.getEnd());
|
||||
|
@ -672,12 +741,12 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
}
|
||||
try {
|
||||
memBlock.setStartAddress(newStartAddr);
|
||||
init(false);
|
||||
reloadAll();
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
}
|
||||
program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.length, monitor);
|
||||
program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.getSize(), monitor);
|
||||
}
|
||||
finally {
|
||||
program.invalidate();
|
||||
|
@ -701,9 +770,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
throw new MemoryBlockException(
|
||||
"Memory split operation not permitted while live memory is active");
|
||||
}
|
||||
if (!(block instanceof MemoryBlockDB)) {
|
||||
throw new NotFoundException("Block does not belong to this program");
|
||||
}
|
||||
checkBlock(block);
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) block;
|
||||
if (!memBlock.contains(addr)) {
|
||||
throw new IllegalArgumentException("Block must contain split address");
|
||||
|
@ -719,7 +786,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
}
|
||||
try {
|
||||
memBlock.split(addr);
|
||||
init(true);
|
||||
initializeBlocks();
|
||||
fireBlockSplit();
|
||||
}
|
||||
catch (IOException e) {
|
||||
|
@ -738,57 +805,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
throws MemoryBlockException, NotFoundException, LockException {
|
||||
lock.acquire();
|
||||
try {
|
||||
program.checkExclusiveAccess();
|
||||
if (liveMemory != null) {
|
||||
throw new MemoryBlockException(
|
||||
"Memory join operation not permitted while live memory is active");
|
||||
}
|
||||
if (!(blockOne instanceof MemoryBlockDB) || !(blockTwo instanceof MemoryBlockDB)) {
|
||||
throw new NotFoundException("Blocks do not belong to this program");
|
||||
}
|
||||
if (blockOne.getType() != blockTwo.getType()) {
|
||||
throw new MemoryBlockException("Blocks of different types can not be joined");
|
||||
}
|
||||
if (blockOne.isInitialized() != blockTwo.isInitialized()) {
|
||||
throw new MemoryBlockException(
|
||||
"Both blocks must be either initialized or uninitialized");
|
||||
}
|
||||
if (blockOne.getType() == MemoryBlockType.OVERLAY) {
|
||||
throw new IllegalArgumentException("Cannot join overlay blocks");
|
||||
}
|
||||
if (blockOne.getType() == MemoryBlockType.BIT_MAPPED) {
|
||||
throw new IllegalArgumentException("Cannot join bit mapped blocks");
|
||||
}
|
||||
if (blockOne.getType() == MemoryBlockType.BYTE_MAPPED) {
|
||||
throw new IllegalArgumentException("Cannot join byte mapped blocks");
|
||||
}
|
||||
long size1 = blockOne.getSize();
|
||||
long size2 = blockTwo.getSize();
|
||||
if (size1 + size2 > Integer.MAX_VALUE) {
|
||||
throw new MemoryBlockException("Blocks are too large to be joined");
|
||||
}
|
||||
// swap if second block is before first block
|
||||
if (blockOne.getStart().compareTo(blockTwo.getStart()) > 0) {
|
||||
MemoryBlock tmp = blockOne;
|
||||
blockOne = blockTwo;
|
||||
blockTwo = tmp;
|
||||
}
|
||||
|
||||
checkPreconditionsForJoining(blockOne, blockTwo);
|
||||
|
||||
MemoryBlockDB memBlock1 = (MemoryBlockDB) blockOne;
|
||||
MemoryBlockDB memBlock2 = (MemoryBlockDB) blockTwo;
|
||||
|
||||
Address block1Addr = blockOne.getStart();
|
||||
Address block2Addr = blockTwo.getStart();
|
||||
memBlock1.checkValid();
|
||||
memBlock2.checkValid();
|
||||
|
||||
if (memBlock1.memMap != this || memBlock2.memMap != this) {
|
||||
throw new NotFoundException("Blocks do not belong to this program");
|
||||
}
|
||||
if (!(memBlock1.getEnd().isSuccessor(memBlock2.getStart()))) {
|
||||
throw new MemoryBlockException("Blocks are not contiguous");
|
||||
}
|
||||
MemoryBlock newBlock = null;
|
||||
try {
|
||||
memBlock1.join(memBlock2);
|
||||
init(true);
|
||||
reloadAll();
|
||||
newBlock = getBlockDB(block1Addr);
|
||||
fireBlocksJoined(newBlock, block2Addr);
|
||||
|
||||
|
@ -804,15 +839,62 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
|
||||
}
|
||||
|
||||
private void checkPreconditionsForJoining(MemoryBlock block1, MemoryBlock block2)
|
||||
throws MemoryBlockException, NotFoundException, LockException {
|
||||
|
||||
program.checkExclusiveAccess();
|
||||
if (liveMemory != null) {
|
||||
throw new MemoryBlockException(
|
||||
"Memory join operation not permitted while live memory is active");
|
||||
}
|
||||
|
||||
checkBlockForJoining(block1);
|
||||
checkBlockForJoining(block2);
|
||||
|
||||
if (block1.isInitialized() != block2.isInitialized()) {
|
||||
throw new MemoryBlockException(
|
||||
"Both blocks must be either initialized or uninitialized");
|
||||
}
|
||||
|
||||
if (!(block1.getEnd().isSuccessor(block2.getStart()))) {
|
||||
throw new MemoryBlockException("Blocks are not contiguous");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void checkBlockForJoining(MemoryBlock block) {
|
||||
checkBlock(block);
|
||||
switch (block.getType()) {
|
||||
case BIT_MAPPED:
|
||||
throw new IllegalArgumentException("Cannot join bit mapped blocks");
|
||||
case BYTE_MAPPED:
|
||||
throw new IllegalArgumentException("Cannot join byte mapped blocks");
|
||||
case OVERLAY:
|
||||
throw new IllegalArgumentException("Cannot join overlay blocks");
|
||||
case DEFAULT:
|
||||
default:
|
||||
// do nothing, these types are ok for joining
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBlock(MemoryBlock block) {
|
||||
if (!(block instanceof MemoryBlockDB)) {
|
||||
throw new IllegalArgumentException("Blocks do not belong to this program");
|
||||
}
|
||||
MemoryBlockDB blockDB = (MemoryBlockDB) block;
|
||||
if (blockDB.memMap != this) {
|
||||
throw new IllegalArgumentException("Blocks do not belong to this program");
|
||||
}
|
||||
blockDB.checkValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryBlock convertToInitialized(MemoryBlock unitializedBlock, byte initialValue)
|
||||
throws MemoryBlockException, NotFoundException, LockException {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkBlock(unitializedBlock);
|
||||
program.checkExclusiveAccess();
|
||||
if (!(unitializedBlock instanceof MemoryBlockDB)) {
|
||||
throw new NotFoundException("Block does not belong to this program");
|
||||
}
|
||||
if (unitializedBlock.isInitialized()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Only an Uninitialized Block may be converted to an Initialized Block");
|
||||
|
@ -822,7 +904,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
throw new IllegalArgumentException("Block is of a type that cannot be initialized");
|
||||
}
|
||||
long size = unitializedBlock.getSize();
|
||||
if (size > MAX_INITIALIZED_BLOCK_SIZE) {
|
||||
if (size > MAX_BLOCK_SIZE) {
|
||||
throw new MemoryBlockException("Block too large to initialize");
|
||||
}
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) unitializedBlock;
|
||||
|
@ -852,9 +934,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
lock.acquire();
|
||||
try {
|
||||
program.checkExclusiveAccess();
|
||||
if (!(initializedBlock instanceof MemoryBlockDB)) {
|
||||
throw new NotFoundException("Block does not belong to this program");
|
||||
}
|
||||
checkBlock(initializedBlock);
|
||||
if (!initializedBlock.isInitialized()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Only an Initialized Block may be converted to an Uninitialized Block");
|
||||
|
@ -1667,14 +1747,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
lock.acquire();
|
||||
try {
|
||||
program.checkExclusiveAccess();
|
||||
|
||||
if (!(block instanceof MemoryBlockDB)) {
|
||||
throw new IllegalArgumentException("Block not in program");
|
||||
}
|
||||
checkBlock(block);
|
||||
MemoryBlockDB memBlock = (MemoryBlockDB) block;
|
||||
if (blockMap.get(memBlock.getID()) != memBlock) {
|
||||
throw new IllegalArgumentException("Block not in program");
|
||||
}
|
||||
|
||||
Address startAddress = block.getStart();
|
||||
|
||||
|
@ -1685,7 +1759,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
try {
|
||||
program.deleteAddressRange(startAddress, memBlock.getEnd(), monitor);
|
||||
memBlock.delete();
|
||||
init(true);
|
||||
reloadAll();
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
|
@ -1758,29 +1832,32 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
* Gets the intersected set of addresses between a list of mapped memory blocks, and some other
|
||||
* address set.
|
||||
*
|
||||
* @param mappedMemoryBlocks The mapped memory blocks to use in the intersection.
|
||||
* @param otherAddrSet Some other address set to use in the intersection.
|
||||
* @return The intersected set of addresses between 'mappedMemoryBlocks' and 'otherAddrSet'.
|
||||
* @param block The mapped memory block to use in the intersection.
|
||||
* @param set Some other address set to use in the intersection.
|
||||
* @return The intersected set of addresses between 'mappedMemoryBlock' and other address set
|
||||
*/
|
||||
private AddressSet getMappedIntersection(List<MappedMemoryBlock> mappedMemoryBlocks,
|
||||
AddressSet otherAddrSet) {
|
||||
private AddressSet getMappedIntersection(MemoryBlock block, AddressSet set) {
|
||||
AddressSet mappedIntersection = new AddressSet();
|
||||
for (MappedMemoryBlock mappedBlock : mappedMemoryBlocks) {
|
||||
AddressSet resolvedIntersection =
|
||||
otherAddrSet.intersect(new AddressSet(mappedBlock.getOverlayedAddressRange()));
|
||||
for (AddressRange resolvedRange : resolvedIntersection) {
|
||||
mappedIntersection.add(getMappedRange(mappedBlock, resolvedRange));
|
||||
}
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
// mapped blocks can only ever have one sourceInfo
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
AddressRange range = info.getMappedRange().get();
|
||||
AddressSet resolvedIntersection = set.intersect(new AddressSet(range));
|
||||
for (AddressRange resolvedRange : resolvedIntersection) {
|
||||
mappedIntersection.add(getMappedRange(block, resolvedRange));
|
||||
}
|
||||
|
||||
return mappedIntersection;
|
||||
}
|
||||
|
||||
private AddressRange getMappedRange(MappedMemoryBlock mappedBlock, AddressRange resolvedRange) {
|
||||
/**
|
||||
* Converts the given address range back from the source range back to the mapped range.
|
||||
*/
|
||||
private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange resolvedRange) {
|
||||
Address start, end;
|
||||
|
||||
SourceInfo info = mappedBlock.getSourceInfos().get(0);
|
||||
long startOffset =
|
||||
resolvedRange.getMinAddress().subtract(mappedBlock.getOverlayedMinAddress());
|
||||
resolvedRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress());
|
||||
boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED;
|
||||
if (isBitMapped) {
|
||||
start = mappedBlock.getStart().add(startOffset * 8);
|
||||
|
@ -1812,7 +1889,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
public final String toString() {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (blocks == null || blocks.length == 0) {
|
||||
if (blocks == null || blocks.isEmpty()) {
|
||||
return "[empty]\n";
|
||||
}
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
@ -1913,11 +1990,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
}
|
||||
|
||||
private void checkBlockSize(long newBlockLength, boolean initialized) {
|
||||
long limit = initialized ? MAX_INITIALIZED_BLOCK_SIZE : MAX_UNINITIALIZED_BLOCK_SIZE;
|
||||
if (newBlockLength > limit) {
|
||||
if (newBlockLength > MAX_BLOCK_SIZE) {
|
||||
throw new IllegalStateException(
|
||||
"New memory block NOT added: exceeds the maximum memory block byte size of " +
|
||||
(limit >> GBYTE_SHIFT_FACTOR) + " GByte(s)");
|
||||
MAX_BLOCK_SIZE_GB + " GByte(s)");
|
||||
}
|
||||
|
||||
long newSize = getNumAddresses() + newBlockLength;
|
||||
|
@ -1929,4 +2005,108 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
|
||||
throws IOException {
|
||||
lock.acquire();
|
||||
try {
|
||||
return fileBytesAdapter.createFileBytes(filename, offset, size, is);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileBytes> getAllFileBytes() {
|
||||
List<FileBytes> allFileBytes = fileBytesAdapter.getAllFileBytes();
|
||||
return Collections.unmodifiableList(allFileBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteFileBytes(FileBytes fileBytes) throws IOException {
|
||||
if (fileBytes.getMemMap() != this) {
|
||||
throw new IllegalArgumentException(
|
||||
"Attempted to delete FileBytes that doesn't belong to this program");
|
||||
}
|
||||
lock.acquire();
|
||||
try {
|
||||
if (inUse(fileBytes)) {
|
||||
return false;
|
||||
}
|
||||
return fileBytesAdapter.deleteFileBytes(fileBytes);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inUse(FileBytes fileBytes) {
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
if (block.uses(fileBytes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FileBytes getLayeredFileBytes(long fileBytesID) throws IOException {
|
||||
List<FileBytes> allFileBytes = fileBytesAdapter.getAllFileBytes();
|
||||
for (FileBytes layeredFileBytes : allFileBytes) {
|
||||
if (layeredFileBytes.getId() == fileBytesID) {
|
||||
return layeredFileBytes;
|
||||
}
|
||||
}
|
||||
throw new IOException("No File Bytes found for ID: " + fileBytesID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all memory blocks that contain any addresses in the given range
|
||||
* @param start the start address
|
||||
* @param end the end address
|
||||
* @return a list of all memory blocks that contain any addresses in the given range
|
||||
*/
|
||||
List<MemoryBlockDB> getBlocks(Address start, Address end) {
|
||||
List<MemoryBlockDB> list = new ArrayList<>();
|
||||
|
||||
List<MemoryBlockDB> tmpBlocks = blocks;
|
||||
int index = Collections.binarySearch(tmpBlocks, start, BLOCK_ADDRESS_COMPARATOR);
|
||||
if (index < 0) {
|
||||
index = -index - 2;
|
||||
}
|
||||
if (index >= 0) {
|
||||
MemoryBlockDB block = tmpBlocks.get(index);
|
||||
if (block.contains(start)) {
|
||||
list.add(block);
|
||||
}
|
||||
}
|
||||
|
||||
while (++index < tmpBlocks.size()) {
|
||||
MemoryBlockDB block = tmpBlocks.get(index);
|
||||
if (block.getStart().compareTo(end) > 0) {
|
||||
break;
|
||||
}
|
||||
list.add(block);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException {
|
||||
CodeManager codeManager = program.getCodeManager();
|
||||
Instruction instr = codeManager.getInstructionContaining(start);
|
||||
if (instr != null) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
}
|
||||
if (!end.equals(start)) {
|
||||
instr = codeManager.getInstructionAfter(start);
|
||||
if (instr != null) {
|
||||
if (instr.getMinAddress().compareTo(end) <= 0) {
|
||||
throw new MemoryAccessException(
|
||||
"Memory change conflicts with instruction at " + instr.getMinAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.program.database.mem;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -28,41 +29,40 @@ import ghidra.util.task.TaskMonitor;
|
|||
|
||||
abstract class MemoryMapDBAdapter {
|
||||
|
||||
static final String TABLE_NAME = "Memory Blocks";
|
||||
static final int CURRENT_VERSION = MemoryMapDBAdapterV3.V3_VERSION;
|
||||
|
||||
static final int CURRENT_VERSION = 2;
|
||||
static Schema BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_BLOCK_SCHEMA;
|
||||
static Schema SUB_BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_SUB_BLOCK_SCHEMA;
|
||||
|
||||
static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
|
||||
new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
|
||||
LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
|
||||
IntField.class },
|
||||
new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
|
||||
"Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
|
||||
public static final int NAME_COL = MemoryMapDBAdapterV3.V3_NAME_COL;
|
||||
public static final int COMMENTS_COL = MemoryMapDBAdapterV3.V3_COMMENTS_COL;
|
||||
public static final int SOURCE_COL = MemoryMapDBAdapterV3.V3_SOURCE_COL;
|
||||
public static final int PERMISSIONS_COL = MemoryMapDBAdapterV3.V3_PERMISSIONS_COL;
|
||||
public static final int START_ADDR_COL = MemoryMapDBAdapterV3.V3_START_ADDR_COL;
|
||||
public static final int LENGTH_COL = MemoryMapDBAdapterV3.V3_LENGTH_COL;
|
||||
public static final int SEGMENT_COL = MemoryMapDBAdapterV3.V3_SEGMENT_COL;
|
||||
|
||||
static final int NAME_COL = 0;
|
||||
static final int COMMENTS_COL = 1;
|
||||
static final int SOURCE_COL = 2;
|
||||
static final int PERMISSIONS_COL = 3;
|
||||
static final int START_ADDR_COL = 4;
|
||||
static final int BLOCK_TYPE_COL = 5;
|
||||
static final int OVERLAY_ADDR_COL = 6;
|
||||
static final int LENGTH_COL = 7;
|
||||
static final int CHAIN_BUF_COL = 8;
|
||||
static final int SEGMENT_COL = 9;
|
||||
public static final int SUB_PARENT_ID_COL = MemoryMapDBAdapterV3.V3_SUB_PARENT_ID_COL;
|
||||
public static final int SUB_TYPE_COL = MemoryMapDBAdapterV3.V3_SUB_TYPE_COL;
|
||||
public static final int SUB_LENGTH_COL = MemoryMapDBAdapterV3.V3_SUB_LENGTH_COL;
|
||||
public static final int SUB_START_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_START_OFFSET_COL;
|
||||
public static final int SUB_SOURCE_ID_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_ID_COL;
|
||||
public static final int SUB_SOURCE_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_OFFSET_COL;
|
||||
|
||||
static final int INITIALIZED = 0;
|
||||
static final int UNINITIALIZED = 1;
|
||||
static final int BIT_MAPPED = 2;
|
||||
static final int BYTE_MAPPED = 4;
|
||||
public static final byte SUB_TYPE_BIT_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BIT_MAPPED;
|
||||
public static final byte SUB_TYPE_BYTE_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BYTE_MAPPED;
|
||||
public static final byte SUB_TYPE_BUFFER = MemoryMapDBAdapterV3.V3_SUB_TYPE_BUFFER;
|
||||
public static final byte SUB_TYPE_UNITIALIZED = MemoryMapDBAdapterV3.V3_SUB_TYPE_UNITIALIZED;
|
||||
public static final byte SUB_TYPE_FILE_BYTES = MemoryMapDBAdapterV3.V3_SUB_TYPE_FILE_BYTES;
|
||||
|
||||
static MemoryMapDBAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap,
|
||||
TaskMonitor monitor) throws VersionException, IOException {
|
||||
|
||||
if (openMode == DBConstants.CREATE) {
|
||||
return new MemoryMapDBAdapterV2(handle, memMap, true);
|
||||
return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
|
||||
}
|
||||
try {
|
||||
return new MemoryMapDBAdapterV2(handle, memMap, false);
|
||||
return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, false);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
|
||||
|
@ -78,10 +78,17 @@ abstract class MemoryMapDBAdapter {
|
|||
|
||||
static MemoryMapDBAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap)
|
||||
throws VersionException, IOException {
|
||||
try {
|
||||
return new MemoryMapDBAdapterV2(handle, memMap);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
// try next oldest version
|
||||
}
|
||||
try {
|
||||
return new MemoryMapDBAdapterV1(handle, memMap);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
// try next oldest version
|
||||
}
|
||||
return new MemoryMapDBAdapterV0(handle, memMap);
|
||||
}
|
||||
|
@ -91,40 +98,34 @@ abstract class MemoryMapDBAdapter {
|
|||
|
||||
try {
|
||||
monitor.setMessage("Upgrading Memory Blocks...");
|
||||
MemoryBlockDB[] blocks = oldAdapter.getMemoryBlocks();
|
||||
monitor.initialize(blocks.length * 2);
|
||||
List<MemoryBlockDB> blocks = oldAdapter.getMemoryBlocks();
|
||||
oldAdapter.deleteTable(handle);
|
||||
|
||||
MemoryMapDBAdapter newAdapter = new MemoryMapDBAdapterV2(handle, memMap, true);
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
MemoryBlockDB block = blocks[i];
|
||||
monitor.initialize(blocks.size() * 2);
|
||||
|
||||
MemoryMapDBAdapter newAdapter =
|
||||
new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
MemoryBlock newBlock = null;
|
||||
if (blocks[i].isInitialized()) {
|
||||
if (block.isInitialized()) {
|
||||
DBBuffer buf = block.getBuffer();
|
||||
newBlock = newAdapter.createInitializedBlock(block.getName(), block.getStart(),
|
||||
buf, MemoryBlock.READ);
|
||||
buf, block.getPermissions());
|
||||
}
|
||||
else {
|
||||
Address mappedAddress = null;
|
||||
MemoryBlockType type = block.getType();
|
||||
if (type == MemoryBlockType.BIT_MAPPED || type == MemoryBlockType.BYTE_MAPPED) {
|
||||
mappedAddress = ((MappedMemoryBlock) block).getOverlayedMinAddress();
|
||||
|
||||
if (block.isMapped()) {
|
||||
SourceInfo info = block.getSourceInfos().get(0);
|
||||
mappedAddress = info.getMappedRange().get().getMinAddress();
|
||||
}
|
||||
newBlock = newAdapter.createBlock(block.getType(), block.getName(),
|
||||
block.getStart(), block.getSize(), mappedAddress, false, MemoryBlock.READ);
|
||||
newBlock =
|
||||
newAdapter.createBlock(block.getType(), block.getName(), block.getStart(),
|
||||
block.getSize(), mappedAddress, false, block.getPermissions());
|
||||
}
|
||||
newBlock.setComment(block.getComment());
|
||||
newBlock.setSourceName(block.getSourceName());
|
||||
if (block.isExecute()) {
|
||||
newBlock.setExecute(true);
|
||||
}
|
||||
if (block.isWrite()) {
|
||||
newBlock.setWrite(true);
|
||||
}
|
||||
if (block.isVolatile()) {
|
||||
newBlock.setVolatile(true);
|
||||
}
|
||||
}
|
||||
oldAdapter.deleteTable(handle);
|
||||
newAdapter.refreshMemory();
|
||||
return newAdapter;
|
||||
}
|
||||
|
@ -134,20 +135,13 @@ abstract class MemoryMapDBAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
static MemoryBlockDB getMemoryBlock(MemoryMapDBAdapter adapter, Record record, DBBuffer buf,
|
||||
MemoryMapDB memMap) throws IOException {
|
||||
|
||||
int blockType = record.getShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL);
|
||||
switch (blockType) {
|
||||
case INITIALIZED:
|
||||
case UNINITIALIZED:
|
||||
return new MemoryBlockDB(adapter, record, buf, memMap);
|
||||
case BIT_MAPPED:
|
||||
case BYTE_MAPPED:
|
||||
return new OverlayMemoryBlockDB(adapter, record, memMap);
|
||||
}
|
||||
throw new IllegalStateException("Bad block type");
|
||||
}
|
||||
/**
|
||||
* Returns a DBBuffer object for the given database buffer id
|
||||
* @param bufferID the id of the first buffer in the DBBuffer.
|
||||
* @return the DBBuffer for the given id.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract DBBuffer getBuffer(int bufferID) throws IOException;
|
||||
|
||||
abstract void deleteTable(DBHandle handle) throws IOException;
|
||||
|
||||
|
@ -159,8 +153,9 @@ abstract class MemoryMapDBAdapter {
|
|||
|
||||
/**
|
||||
* Returns an array of memory blocks sorted on start Address
|
||||
* @return all the memory blocks
|
||||
*/
|
||||
abstract MemoryBlockDB[] getMemoryBlocks();
|
||||
abstract List<MemoryBlockDB> getMemoryBlocks();
|
||||
|
||||
/**
|
||||
* Creates a new initialized block object using data provided from an
|
||||
|
@ -208,31 +203,12 @@ abstract class MemoryMapDBAdapter {
|
|||
long length, Address mappedAddress, boolean initializeBytes, int permissions)
|
||||
throws AddressOverflowException, IOException;
|
||||
|
||||
/**
|
||||
* Splits a memory block at the given offset and create a new block at the split location.
|
||||
* @param block the the split.
|
||||
* @param offset the offset within the block at which to split off into a new block
|
||||
* @return the new memory block created.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException;
|
||||
|
||||
/**
|
||||
* Combines two memory blocks into one.
|
||||
* @param block1 the first block
|
||||
* @param block2 the second block
|
||||
* @return the block that contains the bytes of block1 and block2.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Deletes the given memory block.
|
||||
* @param block the block to delete.
|
||||
* @param key the key for the memory block record
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract void deleteMemoryBlock(MemoryBlockDB block) throws IOException;
|
||||
abstract void deleteMemoryBlock(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Updates the memory block record.
|
||||
|
@ -245,15 +221,74 @@ abstract class MemoryMapDBAdapter {
|
|||
* Creates a new DBuffer object with the given length and initial value.
|
||||
* @param length block/chunk buffer length (length limited by ChainedBuffer implementation)
|
||||
* @param initialValue fill value
|
||||
* @return a new DBuffer object with the given length and initial value.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract DBBuffer createBuffer(int length, byte initialValue) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a DBBuffer object for the given database buffer id
|
||||
* @param bufferID the id of the first buffer in the DBBuffer.
|
||||
* Returns the MemoryMap that owns this adapter.
|
||||
* @return the MemoryMap that owns this adapter.
|
||||
*/
|
||||
abstract MemoryMapDB getMemoryMap();
|
||||
|
||||
/**
|
||||
* Deletes the sub block record for the given key.
|
||||
* @param key the record id of the sub block record to delete.
|
||||
* @throws IOException if a database error occurs.
|
||||
*/
|
||||
abstract void deleteSubBlock(long key) throws IOException;
|
||||
|
||||
/**
|
||||
* Updates the sub memory block record.
|
||||
* @param record the record to update.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
abstract DBBuffer getBuffer(int bufferID) throws IOException;
|
||||
protected abstract void updateSubBlockRecord(Record record) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a record for a new created sub block
|
||||
* @param memBlockId the id of the memory block that contains this sub block
|
||||
* @param startingOffset the starting offset relative to the containing memory block where this
|
||||
* sub block starts
|
||||
* @param length the length of this sub block
|
||||
* @param subType the type of the subBlock
|
||||
* @param sourceID if the type is a buffer, then this is the buffer id. If the type is file bytes,
|
||||
* then this is the FileBytes id.
|
||||
* @param sourceOffset if the type is file bytes, then this is the offset into the filebytes. If
|
||||
* the type is mapped, then this is the encoded mapped address.
|
||||
* @return the newly created record.
|
||||
* @throws IOException if a database error occurs
|
||||
*/
|
||||
abstract Record createSubBlockRecord(long memBlockId, long startingOffset, long length,
|
||||
byte subType, int sourceID, long sourceOffset) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a new memory block.
|
||||
* @param name the name of the block
|
||||
* @param startAddress the start address of the block
|
||||
* @param length the length of the block
|
||||
* @param permissions the permissions for the block
|
||||
* @param splitBlocks the list of subBlock objects that make up this block
|
||||
* @return the new MemoryBlock
|
||||
* @throws IOException if a database error occurs
|
||||
*/
|
||||
protected abstract MemoryBlockDB createBlock(String name, Address startAddress, long length,
|
||||
int permissions, List<SubMemoryBlock> splitBlocks) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a new memory block using a FileBytes
|
||||
* @param name the name of the block
|
||||
* @param startAddress the start address of the block
|
||||
* @param length the length of the block
|
||||
* @param fileBytes the {@link FileBytes} object that provides the bytes for this block
|
||||
* @param offset the offset into the {@link FileBytes} object
|
||||
* @param permissions the permissions for the block
|
||||
* @return the new MemoryBlock
|
||||
* @throws IOException if a database error occurs
|
||||
* @throws AddressOverflowException if block length is too large for the underlying space
|
||||
*/
|
||||
protected abstract MemoryBlockDB createFileBytesBlock(String name, Address startAddress,
|
||||
long length, FileBytes fileBytes, long offset, int permissions)
|
||||
throws IOException, AddressOverflowException;
|
||||
}
|
||||
|
|
|
@ -17,17 +17,11 @@ package ghidra.program.database.mem;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
|
||||
import db.DBBuffer;
|
||||
import db.DBHandle;
|
||||
import db.Record;
|
||||
import db.RecordIterator;
|
||||
import db.Table;
|
||||
import db.*;
|
||||
import ghidra.program.database.map.AddressMap;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.address.SegmentedAddress;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
@ -73,12 +67,10 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
// "Block Type", "Base Address",
|
||||
// "Source Block ID"});
|
||||
|
||||
private MemoryBlockDB[] blocks;
|
||||
private List<MemoryBlockDB> blocks;
|
||||
private DBHandle handle;
|
||||
private MemoryMapDB memMap;
|
||||
|
||||
/**
|
||||
* @param handle
|
||||
*/
|
||||
MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap) throws VersionException, IOException {
|
||||
this(handle, memMap, VERSION);
|
||||
}
|
||||
|
@ -86,6 +78,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
protected MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap, int expectedVersion)
|
||||
throws VersionException, IOException {
|
||||
this.handle = handle;
|
||||
this.memMap = memMap;
|
||||
AddressMap addrMap = memMap.getAddressMap();
|
||||
Table table = handle.getTable(V0_TABLE_NAME);
|
||||
if (table == null) {
|
||||
|
@ -97,10 +90,11 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
", got " + versionNumber);
|
||||
}
|
||||
int recCount = table.getRecordCount();
|
||||
blocks = new MemoryBlockDB[recCount];
|
||||
blocks = new ArrayList<MemoryBlockDB>(recCount);
|
||||
|
||||
AddressFactory addrFactory = memMap.getAddressFactory();
|
||||
int i = 0;
|
||||
int key = 0;
|
||||
|
||||
RecordIterator it = table.iterator();
|
||||
while (it.hasNext()) {
|
||||
Record rec = it.next();
|
||||
|
@ -116,130 +110,117 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
}
|
||||
Address start = addrFactory.oldGetAddressFromLong(rec.getLongValue(V0_START_ADDR_COL));
|
||||
long startAddr = addrMap.getKey(start, false);
|
||||
|
||||
long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
|
||||
try {
|
||||
Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
|
||||
overlayAddr = addrMap.getKey(ov, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
|
||||
long length = rec.getLongValue(V0_LENGTH_COL);
|
||||
long bufID = rec.getIntValue(V0_BUFFER_ID_COL);
|
||||
int segment = 0;
|
||||
if (expectedVersion == 1 && (start instanceof SegmentedAddress)) {
|
||||
segment = rec.getIntValue(V0_SEGMENT_COL);
|
||||
// ((SegmentedAddress)start).normalize(segment);
|
||||
}
|
||||
|
||||
// Convert to new record format
|
||||
Record blockRec = MemoryMapDBAdapter.BLOCK_SCHEMA.createRecord(i);
|
||||
Record blockRecord = BLOCK_SCHEMA.createRecord(key);
|
||||
Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
|
||||
|
||||
blockRec.setString(MemoryMapDBAdapter.NAME_COL, rec.getString(V0_NAME_COL));
|
||||
blockRec.setString(MemoryMapDBAdapter.COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
|
||||
blockRec.setString(MemoryMapDBAdapter.SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
|
||||
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, startAddr);
|
||||
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
|
||||
rec.getShortValue(V0_TYPE_COL));
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL, overlayAddr);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, rec.getLongValue(V0_LENGTH_COL));
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL,
|
||||
rec.getIntValue(V0_BUFFER_ID_COL));
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, segment);
|
||||
blockRecord.setString(NAME_COL, rec.getString(V0_NAME_COL));
|
||||
blockRecord.setString(COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
|
||||
blockRecord.setString(SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
|
||||
blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
|
||||
blockRecord.setLongValue(START_ADDR_COL, startAddr);
|
||||
blockRecord.setLongValue(LENGTH_COL, length);
|
||||
blockRecord.setIntValue(SEGMENT_COL, segment);
|
||||
|
||||
blocks[i++] = MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
|
||||
subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
|
||||
subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
|
||||
subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
|
||||
|
||||
int type = rec.getShortValue(V0_TYPE_COL);
|
||||
long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
|
||||
overlayAddr = updateOverlayAddr(addrMap, addrFactory, overlayAddr, type);
|
||||
|
||||
SubMemoryBlock subBlock = getSubBlock(memMap, bufID, subBlockRecord, type, overlayAddr);
|
||||
|
||||
blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
|
||||
}
|
||||
Arrays.sort(blocks);
|
||||
Collections.sort(blocks);
|
||||
}
|
||||
|
||||
private SubMemoryBlock getSubBlock(MemoryMapDB memMap, long bufID, Record record, int type,
|
||||
long overlayAddr) throws IOException {
|
||||
switch (type) {
|
||||
case MemoryMapDBAdapterV2.BIT_MAPPED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
|
||||
record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
|
||||
return new BitMappedSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.BYTE_MAPPED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
|
||||
record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
|
||||
return new ByteMappedSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.INITIALIZED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
|
||||
record.setLongValue(SUB_SOURCE_OFFSET_COL, bufID);
|
||||
return new BufferSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.UNINITIALIZED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
|
||||
return new UninitializedSubMemoryBlock(this, record);
|
||||
default:
|
||||
throw new IOException("Unknown memory block type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private long updateOverlayAddr(AddressMap addrMap, AddressFactory addrFactory, long overlayAddr,
|
||||
int type) {
|
||||
if (type == MemoryMapDBAdapterV2.BIT_MAPPED || type == MemoryMapDBAdapterV2.BYTE_MAPPED) {
|
||||
Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
|
||||
overlayAddr = addrMap.getKey(ov, false);
|
||||
}
|
||||
return overlayAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
|
||||
*/
|
||||
@Override
|
||||
void refreshMemory() throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB[] getMemoryBlocks() {
|
||||
List<MemoryBlockDB> getMemoryBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
|
||||
long length, int permissions)
|
||||
throws IOException {
|
||||
long length, int permissions) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
|
||||
int permissions)
|
||||
throws IOException {
|
||||
int permissions) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#setBlockSize(ghidra.program.database.mem2.MemoryBlockDB, long)
|
||||
*/
|
||||
void setBlockSize(MemoryBlockDB block, long size) {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.database.mem2.MemoryBlockDB)
|
||||
*/
|
||||
@Override
|
||||
void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
|
||||
void deleteMemoryBlock(long key) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
|
||||
*/
|
||||
@Override
|
||||
void deleteTable(DBHandle dbHandle) throws IOException {
|
||||
dbHandle.deleteTable(V0_TABLE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
|
||||
*/
|
||||
@Override
|
||||
void updateBlockRecord(Record record) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
|
||||
*/
|
||||
@Override
|
||||
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(ghidra.program.model.mem.MemoryBlockType, java.lang.String, ghidra.program.model.address.Address, long, ghidra.program.model.address.Address, boolean)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
|
||||
long length, Address overlayAddr, boolean initializeBytes, int permissions)
|
||||
|
@ -247,9 +228,6 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
|
||||
*/
|
||||
@Override
|
||||
DBBuffer getBuffer(int bufferID) throws IOException {
|
||||
if (bufferID >= 0) {
|
||||
|
@ -258,4 +236,38 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryMapDB getMemoryMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteSubBlock(long key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSubBlockRecord(Record record) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
|
||||
int sourceID, long sourceOffset) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
|
||||
List<SubMemoryBlock> splitBlocks) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
|
||||
FileBytes fileBytes, long offset, int permissions)
|
||||
throws IOException, AddressOverflowException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,12 +15,14 @@
|
|||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
/**
|
||||
* Adapter for version 1
|
||||
*/
|
||||
class MemoryMapDBAdapterV1 extends MemoryMapDBAdapterV0 {
|
||||
private final static int VERSION = 1;
|
||||
|
||||
|
|
|
@ -17,266 +17,180 @@ package ghidra.program.database.mem;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.database.map.AddressMap;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.util.exception.IOCancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
/**
|
||||
* Adapter for version 2
|
||||
*/
|
||||
class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
|
||||
private static final int V2_VERSION = 2;
|
||||
static final String V2_TABLE_NAME = "Memory Blocks";
|
||||
|
||||
private static final int VERSION = CURRENT_VERSION;
|
||||
static final int V2_NAME_COL = 0;
|
||||
static final int V2_COMMENTS_COL = 1;
|
||||
static final int V2_SOURCE_COL = 2;
|
||||
static final int V2_PERMISSIONS_COL = 3;
|
||||
static final int V2_START_ADDR_COL = 4;
|
||||
static final int V2_BLOCK_TYPE_COL = 5;
|
||||
static final int V2_OVERLAY_ADDR_COL = 6;
|
||||
static final int V2_LENGTH_COL = 7;
|
||||
static final int V2_CHAIN_BUF_COL = 8;
|
||||
static final int V2_SEGMENT_COL = 9;
|
||||
|
||||
static final int INITIALIZED = 0;
|
||||
static final int UNINITIALIZED = 1;
|
||||
static final int BIT_MAPPED = 2;
|
||||
static final int BYTE_MAPPED = 4;
|
||||
|
||||
private DBHandle handle;
|
||||
private Table blockTable;
|
||||
private MemoryMapDB memMap;
|
||||
private AddressMap addrMap;
|
||||
private MemoryBlockDB[] blocks = new MemoryBlockDB[0];
|
||||
|
||||
MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap, boolean create)
|
||||
private List<MemoryBlockDB> blocks = new ArrayList<>();
|
||||
|
||||
// The following schema definition documents the schema used in version 2No
|
||||
//
|
||||
// static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
|
||||
// new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
|
||||
// LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
|
||||
// IntField.class },
|
||||
// new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
|
||||
// "Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
|
||||
|
||||
|
||||
protected MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap)
|
||||
throws VersionException, IOException {
|
||||
this.handle = handle;
|
||||
this.memMap = memMap;
|
||||
this.addrMap = memMap.getAddressMap();
|
||||
if (create) {
|
||||
blockTable = handle.createTable(TABLE_NAME, BLOCK_SCHEMA);
|
||||
Table table = handle.getTable(V2_TABLE_NAME);
|
||||
if (table == null) {
|
||||
throw new VersionException("Memory Block table not found");
|
||||
}
|
||||
else {
|
||||
blockTable = handle.getTable(TABLE_NAME);
|
||||
if (blockTable == null) {
|
||||
throw new VersionException(
|
||||
handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
|
||||
}
|
||||
if (blockTable.getSchema().getVersion() != VERSION) {
|
||||
int version = blockTable.getSchema().getVersion();
|
||||
throw new VersionException(version < VERSION);
|
||||
}
|
||||
// refreshMemory();
|
||||
int versionNumber = table.getSchema().getVersion();
|
||||
if (versionNumber != V2_VERSION) {
|
||||
throw new VersionException(
|
||||
"Memory Block table: Expected Version " + V2_VERSION + ", got " + versionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
|
||||
*/
|
||||
@Override
|
||||
void refreshMemory() throws IOException {
|
||||
MemoryBlockDB[] updatedBlocks = new MemoryBlockDB[blockTable.getRecordCount()];
|
||||
RecordIterator it = blockTable.iterator();
|
||||
int index = 0;
|
||||
int recCount = table.getRecordCount();
|
||||
blocks = new ArrayList<>(recCount);
|
||||
|
||||
int key = 0;
|
||||
|
||||
RecordIterator it = table.iterator();
|
||||
while (it.hasNext()) {
|
||||
Record blockRec = it.next();
|
||||
long key = blockRec.getKey();
|
||||
for (int n = 0; n < blocks.length; n++) {
|
||||
if (blocks[n] != null && blocks[n].getID() == key) {
|
||||
updatedBlocks[index] = blocks[n];
|
||||
updatedBlocks[index].refresh(blockRec);
|
||||
blocks[n] = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (updatedBlocks[index] == null) {
|
||||
updatedBlocks[index] =
|
||||
MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
|
||||
}
|
||||
++index;
|
||||
Record rec = it.next();
|
||||
int permissions = rec.getByteValue(V2_PERMISSIONS_COL);
|
||||
|
||||
long startAddr = rec.getLongValue(V2_START_ADDR_COL);
|
||||
long length = rec.getLongValue(V2_LENGTH_COL);
|
||||
int bufID = rec.getIntValue(V2_CHAIN_BUF_COL);
|
||||
int segment = rec.getIntValue(V2_SEGMENT_COL);
|
||||
|
||||
Record blockRecord = BLOCK_SCHEMA.createRecord(key);
|
||||
Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
|
||||
|
||||
blockRecord.setString(NAME_COL, rec.getString(V2_NAME_COL));
|
||||
blockRecord.setString(COMMENTS_COL, rec.getString(V2_COMMENTS_COL));
|
||||
blockRecord.setString(SOURCE_COL, rec.getString(V2_SOURCE_COL));
|
||||
blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
|
||||
blockRecord.setLongValue(START_ADDR_COL, startAddr);
|
||||
blockRecord.setLongValue(LENGTH_COL, length);
|
||||
blockRecord.setIntValue(SEGMENT_COL, segment);
|
||||
|
||||
subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
|
||||
subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
|
||||
subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
|
||||
|
||||
int type = rec.getShortValue(V2_BLOCK_TYPE_COL);
|
||||
long overlayAddr = rec.getLongValue(V2_OVERLAY_ADDR_COL);
|
||||
|
||||
SubMemoryBlock subBlock = getSubBlock(bufID, subBlockRecord, type, overlayAddr);
|
||||
|
||||
blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
|
||||
|
||||
}
|
||||
Collections.sort(blocks);
|
||||
|
||||
}
|
||||
|
||||
private SubMemoryBlock getSubBlock(int bufID, Record record, int type, long overlayAddr)
|
||||
throws IOException {
|
||||
switch (type) {
|
||||
case MemoryMapDBAdapterV2.BIT_MAPPED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
|
||||
return new BitMappedSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.BYTE_MAPPED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
|
||||
return new ByteMappedSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.INITIALIZED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
|
||||
record.setIntValue(SUB_SOURCE_ID_COL, bufID);
|
||||
return new BufferSubMemoryBlock(this, record);
|
||||
case MemoryMapDBAdapterV2.UNINITIALIZED:
|
||||
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
|
||||
return new UninitializedSubMemoryBlock(this, record);
|
||||
default:
|
||||
throw new IOException("Unknown memory block type: " + type);
|
||||
}
|
||||
for (int i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i] != null) {
|
||||
blocks[i].invalidate();
|
||||
}
|
||||
}
|
||||
Arrays.sort(updatedBlocks);
|
||||
blocks = updatedBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB[] getMemoryBlocks() {
|
||||
List<MemoryBlockDB> getMemoryBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private int getSegment(Address addr) {
|
||||
if (addr instanceof SegmentedAddress) {
|
||||
// SegmentedAddress imageBase = (SegmentedAddress)addrMap.getImageBase();
|
||||
// int baseSegment = imageBase.getSegment();
|
||||
return ((SegmentedAddress) addr).getSegment();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(java.lang.String, ghidra.program.model.address.Address, db.DBBuffer)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
|
||||
int permissions) throws AddressOverflowException, IOException {
|
||||
|
||||
// Ensure that address key has been generated for end address
|
||||
Address endAddr = startAddr.addNoWrap(buf.length() - 1);
|
||||
addrMap.getKey(endAddr, true);
|
||||
|
||||
int blockID = (int) blockTable.getKey();
|
||||
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
|
||||
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
|
||||
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
|
||||
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, buf.length());
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
|
||||
|
||||
blockTable.putRecord(blockRec);
|
||||
|
||||
return new MemoryBlockDB(this, blockRec, buf, memMap);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createInitializedBlock(java.lang.String, ghidra.program.model.address.Address, java.io.InputStream, long)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
|
||||
long length, int permissions) throws AddressOverflowException, IOException {
|
||||
|
||||
// Ensure that address key has been generated for end address
|
||||
Address endAddr = startAddr.addNoWrap(length - 1);
|
||||
addrMap.getKey(endAddr, true);
|
||||
|
||||
int blockID = (int) blockTable.getKey();
|
||||
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
|
||||
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
|
||||
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
|
||||
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
|
||||
DBBuffer buf = createBuffer(length, is);
|
||||
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
|
||||
|
||||
blockTable.putRecord(blockRec);
|
||||
return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, buf, memMap);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(int, java.lang.String, ghidra.program.database.mem2.MemoryChunkDB[], ghidra.program.model.address.Address)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
|
||||
long length, Address mappedAddress, boolean initializeBytes, int permissions)
|
||||
throws AddressOverflowException, IOException {
|
||||
if (initializeBytes) {
|
||||
return createInitializedBlock(name, startAddr, null, length, permissions);
|
||||
}
|
||||
|
||||
// Ensure that address key has been generated for end address
|
||||
Address endAddr = startAddr.addNoWrap(length - 1);
|
||||
addrMap.getKey(endAddr, true);
|
||||
|
||||
int blockID = (int) blockTable.getKey();
|
||||
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
|
||||
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
|
||||
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
|
||||
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
|
||||
(short) encodeBlockType(blockType));
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
|
||||
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, -1);
|
||||
if (mappedAddress != null) {
|
||||
blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL,
|
||||
addrMap.getKey(mappedAddress, true));
|
||||
}
|
||||
|
||||
blockTable.putRecord(blockRec);
|
||||
return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
|
||||
}
|
||||
|
||||
private int encodeBlockType(MemoryBlockType blockType) {
|
||||
if (blockType == MemoryBlockType.BIT_MAPPED) {
|
||||
return BIT_MAPPED;
|
||||
}
|
||||
if (blockType == MemoryBlockType.BYTE_MAPPED) {
|
||||
return BYTE_MAPPED;
|
||||
}
|
||||
return UNINITIALIZED;
|
||||
}
|
||||
|
||||
private DBBuffer createBuffer(long length, InputStream is) throws IOException {
|
||||
|
||||
DBBuffer buf = handle.createBuffer((int) length);
|
||||
if (is != null) {
|
||||
try {
|
||||
buf.fill(is);
|
||||
}
|
||||
catch (IOCancelledException e) {
|
||||
buf.delete();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
|
||||
*/
|
||||
@Override
|
||||
MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.model.mem.MemoryBlock)
|
||||
*/
|
||||
@Override
|
||||
void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
|
||||
blockTable.deleteRecord(block.getID());
|
||||
block.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
|
||||
*/
|
||||
@Override
|
||||
void deleteTable(DBHandle dbHandle) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
|
||||
*/
|
||||
@Override
|
||||
void deleteMemoryBlock(long key) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteTable(DBHandle dbHandle) throws IOException {
|
||||
dbHandle.deleteTable(V2_TABLE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateBlockRecord(Record record) throws IOException {
|
||||
blockTable.putRecord(record);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
|
||||
*/
|
||||
@Override
|
||||
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
|
||||
DBBuffer buffer = handle.createBuffer(length);
|
||||
buffer.fill(0, length - 1, initialValue);
|
||||
return buffer;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void refreshMemory() throws IOException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
|
||||
*/
|
||||
@Override
|
||||
DBBuffer getBuffer(int bufferID) throws IOException {
|
||||
if (bufferID >= 0) {
|
||||
|
@ -285,4 +199,38 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryMapDB getMemoryMap() {
|
||||
return memMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteSubBlock(long key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSubBlockRecord(Record record) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
|
||||
int sourceID, long sourceOffset) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
|
||||
List<SubMemoryBlock> splitBlocks) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
|
||||
FileBytes fileBytes, long offset, int permissions)
|
||||
throws IOException, AddressOverflowException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.database.map.AddressMapDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.util.exception.*;
|
||||
|
||||
/**
|
||||
* MemoryMap adapter for version 3.
|
||||
* This version introduces the concept of sub memory blocks and FileBytes
|
||||
*/
|
||||
public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter {
|
||||
public static final int V3_VERSION = 3;
|
||||
public static final String TABLE_NAME = "Memory Blocks";
|
||||
public static final String SUB_BLOCK_TABLE_NAME = "Sub Memory Blocks";
|
||||
|
||||
public static final int V3_NAME_COL = 0;
|
||||
public static final int V3_COMMENTS_COL = 1;
|
||||
public static final int V3_SOURCE_COL = 2;
|
||||
public static final int V3_PERMISSIONS_COL = 3;
|
||||
public static final int V3_START_ADDR_COL = 4;
|
||||
public static final int V3_LENGTH_COL = 5;
|
||||
public static final int V3_SEGMENT_COL = 6;
|
||||
|
||||
public static final int V3_SUB_PARENT_ID_COL = 0;
|
||||
public static final int V3_SUB_TYPE_COL = 1;
|
||||
public static final int V3_SUB_LENGTH_COL = 2;
|
||||
public static final int V3_SUB_START_OFFSET_COL = 3;
|
||||
public static final int V3_SUB_SOURCE_ID_COL = 4;
|
||||
public static final int V3_SUB_SOURCE_OFFSET_COL = 5;
|
||||
|
||||
public static final byte V3_SUB_TYPE_BIT_MAPPED = 0;
|
||||
public static final byte V3_SUB_TYPE_BYTE_MAPPED = 1;
|
||||
public static final byte V3_SUB_TYPE_BUFFER = 2;
|
||||
public static final byte V3_SUB_TYPE_UNITIALIZED = 3;
|
||||
public static final byte V3_SUB_TYPE_FILE_BYTES = 4;
|
||||
|
||||
static Schema V3_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
|
||||
new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
|
||||
LongField.class, LongField.class, IntField.class },
|
||||
new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", "Length",
|
||||
"Segment" });
|
||||
|
||||
static Schema V3_SUB_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
|
||||
new Class[] { LongField.class, ByteField.class, LongField.class, LongField.class,
|
||||
IntField.class, LongField.class },
|
||||
new String[] { "Parent ID", "Type", "Length", "Starting Offset", "Source ID",
|
||||
"Source Address/Offset" });
|
||||
|
||||
private DBHandle handle;
|
||||
|
||||
private Table memBlockTable;
|
||||
private Table subBlockTable;
|
||||
private MemoryMapDB memMap;
|
||||
private AddressMapDB addrMap;
|
||||
|
||||
private List<MemoryBlockDB> memoryBlocks = new ArrayList<>();
|
||||
private long maxSubBlockSize;
|
||||
|
||||
public MemoryMapDBAdapterV3(DBHandle handle, MemoryMapDB memMap, long maxSubBlockSize,
|
||||
boolean create) throws VersionException, IOException {
|
||||
this.handle = handle;
|
||||
this.memMap = memMap;
|
||||
this.maxSubBlockSize = maxSubBlockSize;
|
||||
this.addrMap = memMap.getAddressMap();
|
||||
|
||||
if (create) {
|
||||
memBlockTable = handle.createTable(TABLE_NAME, V3_BLOCK_SCHEMA);
|
||||
subBlockTable = handle.createTable(SUB_BLOCK_TABLE_NAME, V3_SUB_BLOCK_SCHEMA);
|
||||
}
|
||||
else {
|
||||
memBlockTable = handle.getTable(TABLE_NAME);
|
||||
subBlockTable = handle.getTable(SUB_BLOCK_TABLE_NAME);
|
||||
|
||||
if (memBlockTable == null) {
|
||||
// the table name changed going from V1 to V2
|
||||
throw new VersionException(
|
||||
handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
|
||||
}
|
||||
if (subBlockTable == null || memBlockTable.getSchema().getVersion() != V3_VERSION) {
|
||||
int version = memBlockTable.getSchema().getVersion();
|
||||
throw new VersionException(version < V3_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteTable(DBHandle dbHandle) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void refreshMemory() throws IOException {
|
||||
Map<Long, List<SubMemoryBlock>> subBlockMap = getSubBlockMap();
|
||||
|
||||
Map<Long, MemoryBlockDB> blockMap = memoryBlocks.stream().collect(
|
||||
Collectors.toMap(MemoryBlockDB::getID, Function.identity()));
|
||||
|
||||
List<MemoryBlockDB> newBlocks = new ArrayList<>();
|
||||
RecordIterator it = memBlockTable.iterator();
|
||||
while (it.hasNext()) {
|
||||
Record record = it.next();
|
||||
long key = record.getKey();
|
||||
MemoryBlockDB block = blockMap.remove(key);
|
||||
if (block != null) {
|
||||
block.refresh(record, subBlockMap.get(key));
|
||||
}
|
||||
else {
|
||||
block = new MemoryBlockDB(this, record, subBlockMap.get(key));
|
||||
}
|
||||
newBlocks.add(block);
|
||||
}
|
||||
for (MemoryBlockDB block : blockMap.values()) {
|
||||
block.invalidate();
|
||||
}
|
||||
Collections.sort(newBlocks);
|
||||
memoryBlocks = newBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<MemoryBlockDB> getMemoryBlocks() {
|
||||
return memoryBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
|
||||
long length, int permissions) throws AddressOverflowException, IOException {
|
||||
updateAddressMapForAllAddresses(startAddr, length);
|
||||
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>();
|
||||
try {
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddr, length, permissions);
|
||||
long key = blockRecord.getKey();
|
||||
int numFullBlocks = (int) (length / maxSubBlockSize);
|
||||
int lastSubBlockSize = (int) (length % maxSubBlockSize);
|
||||
long blockOffset = 0;
|
||||
for (int i = 0; i < numFullBlocks; i++) {
|
||||
subBlocks.add(createBufferSubBlock(key, blockOffset, maxSubBlockSize, is));
|
||||
blockOffset += maxSubBlockSize;
|
||||
}
|
||||
if (lastSubBlockSize > 0) {
|
||||
subBlocks.add(createBufferSubBlock(key, blockOffset, lastSubBlockSize, is));
|
||||
}
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
|
||||
memoryBlocks.add(newBlock);
|
||||
Collections.sort(memoryBlocks);
|
||||
return newBlock;
|
||||
}
|
||||
catch (IOException e) {
|
||||
// clean up any created DBBufferss
|
||||
for (SubMemoryBlock subMemoryBlock : subBlocks) {
|
||||
BufferSubMemoryBlock bufferSubMemoryBlock = (BufferSubMemoryBlock) subMemoryBlock;
|
||||
subBlockTable.deleteRecord(bufferSubMemoryBlock.getKey());
|
||||
bufferSubMemoryBlock.buf.delete();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
|
||||
long length, Address mappedAddress, boolean initializeBytes, int permissions)
|
||||
throws AddressOverflowException, IOException {
|
||||
|
||||
if (initializeBytes) {
|
||||
return createInitializedBlock(name, startAddr, null, length, permissions);
|
||||
}
|
||||
else if (blockType == MemoryBlockType.BIT_MAPPED) {
|
||||
return createBitMappedBlock(name, startAddr, length, mappedAddress, permissions);
|
||||
}
|
||||
else if (blockType == MemoryBlockType.BYTE_MAPPED) {
|
||||
return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions);
|
||||
}
|
||||
return createUnitializedBlock(name, startAddr, length, permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
|
||||
int permissions) throws AddressOverflowException, IOException {
|
||||
updateAddressMapForAllAddresses(startAddr, buf.length());
|
||||
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>();
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddr, buf.length(), permissions);
|
||||
long key = blockRecord.getKey();
|
||||
|
||||
Record subRecord =
|
||||
createSubBlockRecord(key, 0, buf.length(), V3_SUB_TYPE_BUFFER, buf.getId(), 0);
|
||||
subBlockTable.putRecord(subRecord);
|
||||
subBlocks.add(new BufferSubMemoryBlock(this, subRecord));
|
||||
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
|
||||
memoryBlocks.add(newBlock);
|
||||
Collections.sort(memoryBlocks);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
MemoryBlockDB createUnitializedBlock(String name, Address startAddress, long length,
|
||||
int permissions) throws IOException, AddressOverflowException {
|
||||
updateAddressMapForAllAddresses(startAddress, length);
|
||||
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>();
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
|
||||
long key = blockRecord.getKey();
|
||||
|
||||
Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_UNITIALIZED, 0, 0);
|
||||
subBlocks.add(new UninitializedSubMemoryBlock(this, subRecord));
|
||||
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
|
||||
memoryBlocks.add(newBlock);
|
||||
Collections.sort(memoryBlocks);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createBlock(String name, Address startAddress, long length,
|
||||
int permissions, List<SubMemoryBlock> splitBlocks) throws IOException {
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
|
||||
long key = blockRecord.getKey();
|
||||
|
||||
long startingOffset = 0;
|
||||
for (SubMemoryBlock subMemoryBlock : splitBlocks) {
|
||||
subMemoryBlock.setParentIdAndStartingOffset(key, startingOffset);
|
||||
startingOffset += subMemoryBlock.length;
|
||||
}
|
||||
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, splitBlocks);
|
||||
int insertionIndex = Collections.binarySearch(memoryBlocks, newBlock);
|
||||
if (insertionIndex >= 0) { // should not find direct hit
|
||||
throw new AssertException("New memory block collides with existing block");
|
||||
}
|
||||
memoryBlocks.add(-insertionIndex - 1, newBlock);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
MemoryBlockDB createBitMappedBlock(String name, Address startAddress, long length,
|
||||
Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
|
||||
return createMappedBlock(V3_SUB_TYPE_BIT_MAPPED, name, startAddress, length, mappedAddress,
|
||||
permissions);
|
||||
}
|
||||
|
||||
MemoryBlockDB createByteMappedBlock(String name, Address startAddress, long length,
|
||||
Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
|
||||
return createMappedBlock(V3_SUB_TYPE_BYTE_MAPPED, name, startAddress, length, mappedAddress,
|
||||
permissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
|
||||
FileBytes fileBytes, long offset, int permissions)
|
||||
throws IOException, AddressOverflowException {
|
||||
|
||||
updateAddressMapForAllAddresses(startAddress, length);
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>();
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
|
||||
long key = blockRecord.getKey();
|
||||
|
||||
Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_FILE_BYTES,
|
||||
(int) fileBytes.getId(), offset);
|
||||
subBlocks.add(createSubBlock(subRecord));
|
||||
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
|
||||
memoryBlocks.add(newBlock);
|
||||
Collections.sort(memoryBlocks);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
private MemoryBlockDB createMappedBlock(byte type, String name, Address startAddress,
|
||||
long length, Address mappedAddress, int permissions)
|
||||
throws IOException, AddressOverflowException {
|
||||
updateAddressMapForAllAddresses(startAddress, length);
|
||||
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>();
|
||||
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
|
||||
long key = blockRecord.getKey();
|
||||
|
||||
long encoded = addrMap.getKey(mappedAddress, true);
|
||||
Record subRecord = createSubBlockRecord(key, 0, length, type, 0, encoded);
|
||||
subBlocks.add(createSubBlock(subRecord));
|
||||
|
||||
memBlockTable.putRecord(blockRecord);
|
||||
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
|
||||
memoryBlocks.add(newBlock);
|
||||
Collections.sort(memoryBlocks);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteMemoryBlock(long key) throws IOException {
|
||||
memBlockTable.deleteRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void deleteSubBlock(long key) throws IOException {
|
||||
subBlockTable.deleteRecord(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateBlockRecord(Record record) throws IOException {
|
||||
memBlockTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSubBlockRecord(Record record) throws IOException {
|
||||
subBlockTable.putRecord(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
DBBuffer getBuffer(int bufferID) throws IOException {
|
||||
if (bufferID >= 0) {
|
||||
return handle.getBuffer(bufferID);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
MemoryMapDB getMemoryMap() {
|
||||
return memMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
Record createSubBlockRecord(long parentKey, long startingOffset, long length, byte type,
|
||||
int sourceId, long sourceOffset) throws IOException {
|
||||
|
||||
Record record = V3_SUB_BLOCK_SCHEMA.createRecord(subBlockTable.getKey());
|
||||
record.setLongValue(V3_SUB_PARENT_ID_COL, parentKey);
|
||||
record.setByteValue(V3_SUB_TYPE_COL, type);
|
||||
record.setLongValue(V3_SUB_LENGTH_COL, length);
|
||||
record.setLongValue(V3_SUB_START_OFFSET_COL, startingOffset);
|
||||
record.setIntValue(V3_SUB_SOURCE_ID_COL, sourceId);
|
||||
record.setLongValue(V3_SUB_SOURCE_OFFSET_COL, sourceOffset);
|
||||
subBlockTable.putRecord(record);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
private Record createMemoryBlockRecord(String name, Address startAddr, long length,
|
||||
int permissions) {
|
||||
Record record = V3_BLOCK_SCHEMA.createRecord(memBlockTable.getKey());
|
||||
record.setString(V3_NAME_COL, name);
|
||||
record.setLongValue(V3_START_ADDR_COL, addrMap.getKey(startAddr, true));
|
||||
record.setLongValue(V3_LENGTH_COL, length);
|
||||
record.setByteValue(V3_PERMISSIONS_COL, (byte) permissions);
|
||||
record.setIntValue(V3_SEGMENT_COL, getSegment(startAddr));
|
||||
return record;
|
||||
}
|
||||
|
||||
private Map<Long, List<SubMemoryBlock>> getSubBlockMap() throws IOException {
|
||||
List<SubMemoryBlock> subBlocks = new ArrayList<>(subBlockTable.getRecordCount());
|
||||
RecordIterator it = subBlockTable.iterator();
|
||||
while (it.hasNext()) {
|
||||
Record record = it.next();
|
||||
subBlocks.add(createSubBlock(record));
|
||||
}
|
||||
return subBlocks.stream().collect(Collectors.groupingBy(SubMemoryBlock::getParentBlockID));
|
||||
}
|
||||
|
||||
private int getSegment(Address addr) {
|
||||
if (addr instanceof SegmentedAddress) {
|
||||
return ((SegmentedAddress) addr).getSegment();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void updateAddressMapForAllAddresses(Address startAddress, long length)
|
||||
throws AddressOverflowException {
|
||||
AddressSet set = new AddressSet(startAddress, startAddress.addNoWrap(length - 1));
|
||||
addrMap.getKeyRanges(set, true);
|
||||
}
|
||||
|
||||
private SubMemoryBlock createSubBlock(Record record) throws IOException {
|
||||
byte byteValue = record.getByteValue(V3_SUB_TYPE_COL);
|
||||
|
||||
switch (byteValue) {
|
||||
case V3_SUB_TYPE_BIT_MAPPED:
|
||||
return new BitMappedSubMemoryBlock(this, record);
|
||||
case V3_SUB_TYPE_BYTE_MAPPED:
|
||||
return new ByteMappedSubMemoryBlock(this, record);
|
||||
case V3_SUB_TYPE_BUFFER:
|
||||
return new BufferSubMemoryBlock(this, record);
|
||||
case V3_SUB_TYPE_UNITIALIZED:
|
||||
return new UninitializedSubMemoryBlock(this, record);
|
||||
case V3_SUB_TYPE_FILE_BYTES:
|
||||
return new FileBytesSubMemoryBlock(this, record);
|
||||
default:
|
||||
throw new AssertException("Unhandled sub block type: " + byteValue);
|
||||
}
|
||||
}
|
||||
|
||||
private SubMemoryBlock createBufferSubBlock(long parentKey, long offset, long length,
|
||||
InputStream is) throws IOException {
|
||||
DBBuffer buffer = createBuffer(length, is);
|
||||
Record record =
|
||||
createSubBlockRecord(parentKey, offset, length, V3_SUB_TYPE_BUFFER, buffer.getId(), 0);
|
||||
return new BufferSubMemoryBlock(this, record);
|
||||
}
|
||||
|
||||
private DBBuffer createBuffer(long length, InputStream is) throws IOException {
|
||||
DBBuffer buf = handle.createBuffer((int) length);
|
||||
if (is != null) {
|
||||
try {
|
||||
buf.fill(is);
|
||||
}
|
||||
catch (IOCancelledException e) {
|
||||
buf.delete();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
|
||||
DBBuffer buffer = handle.createBuffer(length);
|
||||
buffer.fill(0, length - 1, initialValue);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.Record;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
|
||||
/**
|
||||
* Class to handle memory blocks that are "overlayed" on other blocks. In other words, this
|
||||
* block just maps request for bytes from one address to another. It also handles bit
|
||||
* overlay blocks, in which case bit value requests are translated into a byte/bit into another
|
||||
* block.
|
||||
*/
|
||||
class OverlayMemoryBlockDB extends MemoryBlockDB implements MappedMemoryBlock {
|
||||
// ioPending is flag used to prevent cyclic memory access. Since this memory
|
||||
// block uses memory to resolve its reads and writes, we have to be careful that
|
||||
// some other block that this block uses, doesn't also use this block in return.
|
||||
|
||||
private boolean ioPending;
|
||||
private static final MemoryAccessException IOPENDING_EXCEPTION =
|
||||
new MemoryAccessException("Cyclic Access");
|
||||
|
||||
private static final MemoryAccessException MEMORY_ACCESS_EXCEPTION =
|
||||
new MemoryAccessException("No memory at address");
|
||||
|
||||
private Address overlayStart;
|
||||
private Address overlayEnd;
|
||||
private boolean bitOverlay;
|
||||
|
||||
/**
|
||||
* Constructs a new OverlayMemoryBlockDB
|
||||
* @param adapter the memory block database adapter
|
||||
* @param record the record for this block
|
||||
* @param memMap the memory map manager
|
||||
* @param bitOverlay if true this is a bit overlay memory.
|
||||
* @throws IOException if a database IO error occurs.
|
||||
*/
|
||||
OverlayMemoryBlockDB(MemoryMapDBAdapter adapter, Record record, MemoryMapDB memMap)
|
||||
throws IOException {
|
||||
super(adapter, record, null, memMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
void refresh(Record lRecord) throws IOException {
|
||||
super.refresh(lRecord);
|
||||
this.bitOverlay = blockType == MemoryBlockType.BIT_MAPPED;
|
||||
long base = record.getLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL);
|
||||
overlayStart = addrMap.decodeAddress(base);
|
||||
try {
|
||||
overlayEnd = overlayStart.addNoWrap((bitOverlay ? (length - 1) / 8 : (length - 1)));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new RuntimeException("Overlay range extends beyond address space");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.mem.MappedMemoryBlock#getOverlayedMinAddress()
|
||||
*/
|
||||
@Override
|
||||
public Address getOverlayedMinAddress() {
|
||||
return overlayStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
Address getOverlayAddress(long offset) {
|
||||
return overlayStart.add(bitOverlay ? offset / 8 : offset);
|
||||
}
|
||||
|
||||
private byte getBitOverlayByte(long blockOffset)
|
||||
throws AddressOverflowException, MemoryAccessException {
|
||||
Address otherAddr = overlayStart.addNoWrap(blockOffset / 8);
|
||||
byte b = memMap.getByte(otherAddr);
|
||||
return (byte) ((b >> (blockOffset % 8)) & 0x01);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.mem.MemoryBlock#getByte(ghidra.program.model.address.Address)
|
||||
*/
|
||||
@Override
|
||||
public byte getByte(Address addr) throws MemoryAccessException {
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
checkValid();
|
||||
if (ioPending) {
|
||||
throw IOPENDING_EXCEPTION;
|
||||
}
|
||||
ioPending = true;
|
||||
long offset = getBlockOffset(addr);
|
||||
if (bitOverlay) {
|
||||
return getBitOverlayByte(offset);
|
||||
}
|
||||
return memMap.getByte(overlayStart.addNoWrap(offset));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw MEMORY_ACCESS_EXCEPTION;
|
||||
}
|
||||
finally {
|
||||
ioPending = false;
|
||||
memMap.lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.mem.MemoryBlock#getBytes(ghidra.program.model.address.Address, byte, int, int)
|
||||
*/
|
||||
@Override
|
||||
public int getBytes(Address addr, byte[] bytes, int off, int len) throws MemoryAccessException {
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
checkValid();
|
||||
if (ioPending) {
|
||||
throw IOPENDING_EXCEPTION;
|
||||
}
|
||||
ioPending = true;
|
||||
long offset = getBlockOffset(addr);
|
||||
long size = getSize();
|
||||
if (len > size - (addr.subtract(startAddress))) {
|
||||
len = (int) (size - addr.subtract(startAddress));
|
||||
}
|
||||
if (bitOverlay) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
bytes[i + off] = getBitOverlayByte(offset++);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
return memMap.getBytes(overlayStart.addNoWrap(offset), bytes, off, len);
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw MEMORY_ACCESS_EXCEPTION;
|
||||
}
|
||||
finally {
|
||||
ioPending = false;
|
||||
memMap.lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.mem.MemoryBlock#putByte(ghidra.program.model.address.Address, byte)
|
||||
*/
|
||||
@Override
|
||||
public void putByte(Address addr, byte b) throws MemoryAccessException {
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
checkValid();
|
||||
if (ioPending) {
|
||||
throw IOPENDING_EXCEPTION;
|
||||
}
|
||||
ioPending = true;
|
||||
long offset = getBlockOffset(addr);
|
||||
if (bitOverlay) {
|
||||
checkValid();
|
||||
doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b);
|
||||
}
|
||||
else {
|
||||
memMap.setByte(overlayStart.add(offset), b);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
ioPending = false;
|
||||
memMap.lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.program.model.mem.MemoryBlock#putBytes(ghidra.program.model.address.Address, byte, int, int)
|
||||
*/
|
||||
@Override
|
||||
public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
|
||||
memMap.lock.acquire();
|
||||
try {
|
||||
checkValid();
|
||||
if (ioPending) {
|
||||
throw IOPENDING_EXCEPTION;
|
||||
}
|
||||
ioPending = true;
|
||||
long offset = getBlockOffset(addr);
|
||||
long size = getSize();
|
||||
if (len > size - (addr.subtract(startAddress))) {
|
||||
len = (int) (size - addr.subtract(startAddress));
|
||||
}
|
||||
if (bitOverlay) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b[off + i]);
|
||||
addr = addr.add(1);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
memMap.setBytes(overlayStart.add(offset), b, off, len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
finally {
|
||||
ioPending = false;
|
||||
memMap.lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void doPutByte(Address addr, int bitIndex, byte b) throws MemoryAccessException {
|
||||
ioPending = true;
|
||||
byte value = memMap.getByte(addr);
|
||||
int mask = 1 << (bitIndex % 8);
|
||||
if (b == 0) {
|
||||
value &= ~mask;
|
||||
}
|
||||
else {
|
||||
value |= mask;
|
||||
}
|
||||
memMap.setByte(addr, value);
|
||||
}
|
||||
|
||||
// void dataChanged(Address addr, int cnt) {
|
||||
// Address endAddr = addr.addWrap(cnt);
|
||||
// if (addr.compareTo(overlayEnd) > 0 || endAddr.compareTo(overlayStart) < 0) {
|
||||
// return;
|
||||
// }
|
||||
// if (ioPending) {
|
||||
// return;
|
||||
// }
|
||||
// try {
|
||||
// ioPending = true;
|
||||
// Address minAddr = addr.compareTo(overlayStart) > 0 ? addr : overlayStart;
|
||||
// Address maxAddr = endAddr.compareTo(overlayEnd) < 0 ? endAddr : overlayEnd;
|
||||
// Address myStartAddr =
|
||||
// startAddress.add(minAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
|
||||
// Address myEndAddr =
|
||||
// startAddress.add(maxAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
|
||||
// memMap.fireBytesChanged(myStartAddr, (int) myEndAddr.subtract(myStartAddr) + 1);
|
||||
// }
|
||||
// finally {
|
||||
// ioPending = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean isMapped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getOverlayedAddressRange() {
|
||||
return new AddressRangeImpl(overlayStart, overlayEnd);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
|
||||
/**
|
||||
* Class for describing the source of bytes for a memory block.
|
||||
*/
|
||||
public class SourceInfo {
|
||||
|
||||
final MemoryBlock block;
|
||||
final SubMemoryBlock subBlock;
|
||||
|
||||
SourceInfo(MemoryBlock block, SubMemoryBlock subBlock) {
|
||||
this.block = block;
|
||||
this.subBlock = subBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this block byte source.
|
||||
* @return the length of this block byte source.
|
||||
*/
|
||||
public long getLength() {
|
||||
return subBlock.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start address where this byte source is mapped.
|
||||
* @return the start address where this byte source is mapped.
|
||||
*/
|
||||
public Address getMinAddress() {
|
||||
return block.getStart().add(subBlock.startingOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end address where this byte source is mapped.
|
||||
* @return the end address where this byte source is mapped.
|
||||
*/
|
||||
public Address getMaxAddress() {
|
||||
return block.getStart().add(subBlock.startingOffset + subBlock.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of this SourceInfo object.
|
||||
* @return a description of this SourceInfo object.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return subBlock.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": StartAddress = " + getMinAddress() + ", length = " +
|
||||
getLength();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Optional} {@link FileBytes} object if a FileBytes object is the byte
|
||||
* source for this SourceInfo. Otherwise, the Optional will be empty.
|
||||
* @return the {@link FileBytes} object if it is the byte source for this section
|
||||
*/
|
||||
public Optional<FileBytes> getFileBytes() {
|
||||
if (subBlock instanceof FileBytesSubMemoryBlock) {
|
||||
return Optional.of(((FileBytesSubMemoryBlock) subBlock).getFileBytes());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset into the {@link FileBytes} object where this section starts getting its bytes or
|
||||
* -1 if this SourceInfo does not have an associated {@link FileBytes}
|
||||
* @return the offset into the {@link FileBytes} object where this section starts getting its bytes.
|
||||
*/
|
||||
public long getFileBytesOffset() {
|
||||
if (subBlock instanceof FileBytesSubMemoryBlock) {
|
||||
return ((FileBytesSubMemoryBlock) subBlock).getFileBytesOffset();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
|
||||
* memory block (bit mapped or byte mapped). Otherwise, the Optional is empty.
|
||||
* @return an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
|
||||
* memory block
|
||||
*/
|
||||
public Optional<AddressRange> getMappedRange() {
|
||||
if (subBlock instanceof BitMappedSubMemoryBlock) {
|
||||
BitMappedSubMemoryBlock bitMapped = (BitMappedSubMemoryBlock) subBlock;
|
||||
return Optional.of(bitMapped.getMappedRange());
|
||||
}
|
||||
if (subBlock instanceof ByteMappedSubMemoryBlock) {
|
||||
ByteMappedSubMemoryBlock byteMapped = (ByteMappedSubMemoryBlock) subBlock;
|
||||
return Optional.of(byteMapped.getMappedRange());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.Record;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.mem.*;
|
||||
|
||||
/**
|
||||
* Interface for the various types of memory block sections. They are used by a {@link MemoryBlockDB}
|
||||
* to do the actual storing and fetching of the bytes that make up a MemoryBlock
|
||||
*/
|
||||
abstract class SubMemoryBlock {
|
||||
|
||||
protected final MemoryMapDBAdapter adapter;
|
||||
protected final Record record;
|
||||
protected long length;
|
||||
protected long startingOffset;
|
||||
|
||||
protected SubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
|
||||
this.adapter = adapter;
|
||||
this.record = record;
|
||||
this.startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
|
||||
this.length = record.getLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this block has been initialized (has byte values)
|
||||
*
|
||||
* @return true if the block has associated byte values.
|
||||
*/
|
||||
public abstract boolean isInitialized();
|
||||
|
||||
/**
|
||||
* Returns the id of the MemoryBlockDB object that owns this sub block.
|
||||
* @return the id of the MemoryBlockDB object that owns this sub block.
|
||||
*/
|
||||
public final long getParentBlockID() {
|
||||
return record.getLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting offset for this sub block. In other words, the first byte in this sub
|
||||
* block is at this starting offset relative to the containing {@link MemoryBlockDB}
|
||||
*
|
||||
* @return the starting offset for this sub block.
|
||||
*/
|
||||
public final long getStartingOffset() {
|
||||
return startingOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this sub block
|
||||
* @return the length of this sub block
|
||||
*/
|
||||
public final long getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given {@link MemoryBlockDB} offset is in this sub block.
|
||||
*
|
||||
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
|
||||
* @return true if the offset is valid for this block
|
||||
*/
|
||||
public final boolean contains(long memBlockOffset) {
|
||||
return memBlockOffset >= startingOffset && memBlockOffset < startingOffset + length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte in this sub block corresponding to the given offset relative to the containing
|
||||
* {@link MemoryBlockDB}. In other words, the first byte in this sub block can be retrieved
|
||||
* using an offset equal to this blocks starting offset.
|
||||
*
|
||||
* @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
|
||||
* @return the byte at the given containing block offset.
|
||||
* @throws MemoryAccessException if the block is uninitialized.
|
||||
* @throws IOException if there is a problem reading from the database
|
||||
*/
|
||||
public abstract byte getByte(long memBlockOffset) throws MemoryAccessException, IOException;
|
||||
|
||||
/**
|
||||
* Tries to get len bytes from this block at the given offset (relative to the containing
|
||||
* {@link MemoryBlockDB} and put them into the given byte array at the specified offset.
|
||||
* May return fewer bytes if the requested length is beyond the end of the block.
|
||||
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
|
||||
* @param b the byte array to populate.
|
||||
* @param off the offset into the byte array.
|
||||
* @param len the number of bytes to get.
|
||||
* @return the number of bytes actually populated.
|
||||
* @throws MemoryAccessException if any of the requested bytes are
|
||||
* uninitialized.
|
||||
* @throws IOException if there is a problem reading from the database
|
||||
* @throws IllegalArgumentException if the offset is not in this block.
|
||||
*/
|
||||
public abstract int getBytes(long memBlockOffset, byte[] b, int off, int len)
|
||||
throws MemoryAccessException, IOException;
|
||||
|
||||
/**
|
||||
* Stores the byte in this sub block at the given offset relative to the containing
|
||||
* {@link MemoryBlockDB}. In other words, the first byte in this sub block can be targeted
|
||||
* using an offset equal to this blocks starting offset.
|
||||
*
|
||||
* @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
|
||||
* @param b the byte value to store at the given offset.
|
||||
* @throws MemoryAccessException if the block is uninitialized
|
||||
* @throws IOException if there is a problem writing to the database
|
||||
* @throws IllegalArgumentException if the offset is not in this block.
|
||||
*/
|
||||
public abstract void putByte(long memBlockOffset, byte b)
|
||||
throws MemoryAccessException, IOException;
|
||||
|
||||
/**
|
||||
* Tries to write len bytes to this block at the given offset (relative to the containing
|
||||
* {@link MemoryBlockDB} using the bytes contained in the given byte array at the specified byte
|
||||
* array offset.
|
||||
* May write fewer bytes if the requested length is beyond the end of the block.
|
||||
*
|
||||
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
|
||||
* @param b the byte array with the bytes to store.
|
||||
* @param off the offset into the byte array.
|
||||
* @param len the number of bytes to write.
|
||||
* @return the number of bytes actually written
|
||||
* @throws MemoryAccessException if this block is uninitialized.
|
||||
* @throws IOException if there is a problem writing to the database
|
||||
* @throws IllegalArgumentException if the offset is not in this block.
|
||||
*/
|
||||
public abstract int putBytes(long memBlockOffset, byte[] b, int off, int len)
|
||||
throws MemoryAccessException, IOException;
|
||||
|
||||
/**
|
||||
* Deletes this SumMemoryBlock
|
||||
* @throws IOException if a database error occurs
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
adapter.deleteSubBlock(record.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of a subblock (Used by the split command)
|
||||
* @param length the new length of the block
|
||||
* @throws IOException if a database error occurs
|
||||
*/
|
||||
protected void setLength(long length) throws IOException {
|
||||
this.length = length;
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
|
||||
adapter.updateSubBlockRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to join the given SubMemoryBlock with this block if possible
|
||||
*
|
||||
* @param other the SubMemoryBlock to join with this one.
|
||||
* @return true if the given SubMemoryBlock was successfully merged into this one
|
||||
* @throws IOException if a database error occurs.
|
||||
*/
|
||||
protected abstract boolean join(SubMemoryBlock other) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns true if this is either a bit-mapped or byte-mapped block.
|
||||
*
|
||||
* @return true if this is either a bit-mapped or byte-mapped block.
|
||||
*/
|
||||
protected boolean isMapped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MemoryBlockType} for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
|
||||
*
|
||||
* @return the type for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
|
||||
*/
|
||||
protected abstract MemoryBlockType getType();
|
||||
|
||||
/**
|
||||
* Returns the {@link SourceInfo} object for this SubMemoryBlock
|
||||
* @param block the {@link MemoryBlock} that this block belongs to.
|
||||
* @return the {@link SourceInfo} object for this SubMemoryBlock
|
||||
*/
|
||||
protected final SourceInfo getSourceInfo(MemoryBlock block) {
|
||||
return new SourceInfo(block, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits this SubMemoryBlock into two memory blocks
|
||||
* @param memBlockOffset the offset relative to the owning MemoryBlock (not this SubMemoryBlock)
|
||||
* To get the offset relative to this SubMemoryBlock, you have to subtract this sub blocks
|
||||
* starting offset.
|
||||
* @return the new SubMemoryBlock that contains the back half of this block
|
||||
* @throws IOException if a database error occurs.
|
||||
*/
|
||||
protected abstract SubMemoryBlock split(long memBlockOffset) throws IOException;
|
||||
|
||||
/**
|
||||
* Updates this SubMemoryBlock to have a new owning MemoryBlock and offset within that block.
|
||||
* This is used when splitting a block and entire sub blocks have to be moved to the new split
|
||||
* block.
|
||||
* @param key the id of the new owning memory block.
|
||||
* @param startingOffset the starting offset of this sub block in the new block.
|
||||
* @throws IOException if a database error occurs.
|
||||
*/
|
||||
protected void setParentIdAndStartingOffset(long key, long startingOffset) throws IOException {
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL, key);
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL, startingOffset);
|
||||
adapter.updateSubBlockRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of this SubMemoryBlock suitable to be displayed to the user.
|
||||
* @return a description of this SubMemoryBlock suitable to be displayed to the user.
|
||||
*/
|
||||
protected abstract String getDescription();
|
||||
|
||||
/**
|
||||
* Returns true if this subBlock uses the given fileBytes as its byte source.
|
||||
* @param fileBytes the {@link FileBytes} to check for use
|
||||
* @return true if this subBlock uses the given fileBytes as its byte source.
|
||||
*/
|
||||
protected boolean uses(FileBytes fileBytes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of BytesSourceRanges from this sub block for the given memBlockOffset and associates
|
||||
* it with the given {@link AddressRange}
|
||||
* @param block the {@link MemoryBlock} that generated the BytesSourceSet.
|
||||
* @param start the program address for which to get a ByteSourceSet
|
||||
* @param memBlockOffset the offset from the beginning of the containing MemoryBlock.
|
||||
* @param size the size of region to get byte sources
|
||||
* @return the set of ByteSourceRanges which maps program addresses to byte source locations.
|
||||
*/
|
||||
protected abstract ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
|
||||
long memBlockOffset, long size);
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import db.Record;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.*;
|
||||
|
||||
/**
|
||||
* Implementation of SubMemoryBlock for uninitialized blocks.
|
||||
*/
|
||||
class UninitializedSubMemoryBlock extends SubMemoryBlock {
|
||||
|
||||
UninitializedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
|
||||
super(adapter, record);
|
||||
startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(long offset) throws MemoryAccessException {
|
||||
if (offset < startingOffset || offset >= startingOffset + length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Offset " + offset + "is out of bounds. Should be in [" + startingOffset + "," +
|
||||
(startingOffset + length - 1));
|
||||
}
|
||||
throw new MemoryAccessException("Attempted to read from uninitialized block");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
|
||||
throw new MemoryAccessException("Attempted to read from uninitialized block");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putByte(long offset, byte b) throws MemoryAccessException {
|
||||
throw new MemoryAccessException("Attempted to read from uninitialized block");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
|
||||
throw new MemoryAccessException("Attempted to read from uninitialized block");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean join(SubMemoryBlock block) throws IOException {
|
||||
if (!(block instanceof UninitializedSubMemoryBlock)) {
|
||||
return false;
|
||||
}
|
||||
setLength(length + block.length);
|
||||
adapter.deleteSubBlock(block.record.getKey());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemoryBlockType getType() {
|
||||
return MemoryBlockType.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
|
||||
// convert from offset in block to offset in this sub block
|
||||
long offset = memBlockOffset - startingOffset;
|
||||
long newLength = length - offset;
|
||||
length = offset;
|
||||
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
|
||||
adapter.updateSubBlockRecord(record);
|
||||
|
||||
Record newSubRecord = adapter.createSubBlockRecord(-1, 0, newLength,
|
||||
MemoryMapDBAdapter.SUB_TYPE_UNITIALIZED, 0, 0);
|
||||
|
||||
return new UninitializedSubMemoryBlock(adapter, newSubRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
|
||||
long memBlockOffset,
|
||||
long size) {
|
||||
return new ByteSourceRangeList();
|
||||
}
|
||||
|
||||
}
|
|
@ -72,7 +72,7 @@ public class AddressRangeImpl implements AddressRange, Serializable {
|
|||
* @param length the length of the range.
|
||||
* @exception AddressOverflowException if the length would wrap.
|
||||
*/
|
||||
public AddressRangeImpl(Address start, int length) throws AddressOverflowException {
|
||||
public AddressRangeImpl(Address start, long length) throws AddressOverflowException {
|
||||
minAddress = start;
|
||||
maxAddress = start.addNoWrap(length - 1);
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
|
||||
/**
|
||||
* Additional methods for mapped memory blocks
|
||||
*/
|
||||
public interface MappedMemoryBlock extends MemoryBlock {
|
||||
/**
|
||||
* Get the address from the source block.
|
||||
*/
|
||||
public Address getOverlayedMinAddress();
|
||||
|
||||
/**
|
||||
* Returns the AddressRange of the memory this block is mapped onto.
|
||||
* @return the AddressRange of the memory this block is mapped onto.
|
||||
*/
|
||||
public AddressRange getOverlayedAddressRange();
|
||||
|
||||
}
|
|
@ -15,10 +15,12 @@
|
|||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import db.ChainedBuffer;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.*;
|
||||
|
@ -41,23 +43,10 @@ public interface Memory extends AddressSetView {
|
|||
public static final long MAX_BINARY_SIZE = (long) MAX_BINARY_SIZE_GB << GBYTE_SHIFT_FACTOR;
|
||||
|
||||
/**
|
||||
* Initialized blocks must be addressable by an int, 1-GByte.
|
||||
* This value has been established due to limitations of the
|
||||
* {@link ChainedBuffer} implementation use positive integers
|
||||
* to convey length.
|
||||
* The current max size of a memory block.
|
||||
*/
|
||||
public static final int MAX_INITIALIZED_BLOCK_SIZE_GB = 1;
|
||||
public static final long MAX_INITIALIZED_BLOCK_SIZE =
|
||||
(long) MAX_INITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
|
||||
|
||||
/**
|
||||
* Uninitialized blocks size limit, 12-GByte (limit number of 32-bit segments).
|
||||
* This restriction is somewhat arbitrary but is established to prevent an excessive
|
||||
* number of memory map segments ({@link #MAX_BINARY_SIZE_GB}).
|
||||
*/
|
||||
public static final int MAX_UNINITIALIZED_BLOCK_SIZE_GB = 12;
|
||||
public static final long MAX_UNINITIALIZED_BLOCK_SIZE =
|
||||
(long) MAX_UNINITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
|
||||
public static final int MAX_BLOCK_SIZE_GB = 16; // set to 16 because anything larger, ghidra bogs down
|
||||
public static final long MAX_BLOCK_SIZE = (long) MAX_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
|
||||
|
||||
/**
|
||||
* Returns the program that this memory belongs to.
|
||||
|
@ -123,9 +112,10 @@ public interface Memory extends AddressSetView {
|
|||
* @param start start address of the block
|
||||
* @param is source of the data used to fill the block.
|
||||
* @param length the size of the block
|
||||
* @param monitor task monitor
|
||||
* @param overlay if true, the block will be created as an OVERLAY which means that a new
|
||||
* overlay address space will be created and the block will have a starting address at the same
|
||||
* offset as the given start address paramaeter, but in the new address space.
|
||||
* offset as the given start address parameter, but in the new address space.
|
||||
* @return new Initialized Memory Block
|
||||
* @throws LockException if exclusive lock not in place (see haveLock())
|
||||
* @throws MemoryConflictException if the new block overlaps with a
|
||||
|
@ -133,6 +123,8 @@ public interface Memory extends AddressSetView {
|
|||
* @throws AddressOverflowException if the start is beyond the
|
||||
* address space
|
||||
* @throws CancelledException user cancelled operation
|
||||
* @throws DuplicateNameException if overlay is true and there is already an overlay address
|
||||
* space with the same name as this memory block
|
||||
*/
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
|
||||
long length, TaskMonitor monitor, boolean overlay)
|
||||
|
@ -150,6 +142,8 @@ public interface Memory extends AddressSetView {
|
|||
* overlay address space will be created and the block will have a starting address at the same
|
||||
* offset as the given start address paramaeter, but in the new address space.
|
||||
* @return new Initialized Memory Block
|
||||
* @throws DuplicateNameException if overlay is true and there is already an overlay address
|
||||
* space with the same name as this memory block
|
||||
* @throws LockException if exclusive lock not in place (see haveLock())
|
||||
* @throws MemoryConflictException if the new block overlaps with a
|
||||
* previous block
|
||||
|
@ -162,6 +156,30 @@ public interface Memory extends AddressSetView {
|
|||
throws LockException, DuplicateNameException, MemoryConflictException,
|
||||
AddressOverflowException, CancelledException;
|
||||
|
||||
/**
|
||||
* Create an initialized memory block using bytes from a {@link FileBytes} object.
|
||||
*
|
||||
* @param name block name
|
||||
* @param start starting address of the block
|
||||
* @param fileBytes the {@link FileBytes} object to use as the underlying source of bytes.
|
||||
* @param offset the offset into the FileBytes for the first byte of this memory block.
|
||||
* @param size block length
|
||||
* @param overlay if true, the block will be created as an OVERLAY which means that a new
|
||||
* overlay address space will be created and the block will have a starting address at the same
|
||||
* offset as the given start address parameter, but in the new address space.
|
||||
* @return new Initialized Memory Block
|
||||
* @throws LockException if exclusive lock not in place (see haveLock())
|
||||
* @throws DuplicateNameException if overlay is true and there is already an overlay address
|
||||
* space with the same name as this memory block
|
||||
* @throws MemoryConflictException if the new block overlaps with a
|
||||
* previous block
|
||||
* @throws AddressOverflowException if the start is beyond the
|
||||
* address space
|
||||
*/
|
||||
public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
|
||||
long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
|
||||
MemoryConflictException, AddressOverflowException;
|
||||
|
||||
/**
|
||||
* Create an uninitialized memory block and add it to this Memory.
|
||||
* @param name block name
|
||||
|
@ -176,6 +194,8 @@ public interface Memory extends AddressSetView {
|
|||
* previous block
|
||||
* @throws AddressOverflowException if the start is beyond the
|
||||
* address space
|
||||
* @throws DuplicateNameException if overlay is true and there is already an overlay address
|
||||
* space with the same name as this memory block
|
||||
*/
|
||||
public MemoryBlock createUninitializedBlock(String name, Address start, long size,
|
||||
boolean overlay) throws LockException, DuplicateNameException, MemoryConflictException,
|
||||
|
@ -192,6 +212,10 @@ public interface Memory extends AddressSetView {
|
|||
* @throws LockException if exclusive lock not in place (see haveLock())
|
||||
* @throws MemoryConflictException if the new block overlaps with a
|
||||
* previous block
|
||||
* @throws MemoryConflictException if the new block overlaps with a
|
||||
* previous block
|
||||
* @throws AddressOverflowException if the start is beyond the
|
||||
* address space
|
||||
*/
|
||||
public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress,
|
||||
long length) throws LockException, MemoryConflictException, AddressOverflowException;
|
||||
|
@ -695,4 +719,33 @@ public interface Memory extends AddressSetView {
|
|||
*/
|
||||
public void setLong(Address addr, long value, boolean bigEndian) throws MemoryAccessException;
|
||||
|
||||
/**
|
||||
* Stores a sequence of bytes into the program. Typically, this method is used by importers
|
||||
* to store the original raw program bytes.
|
||||
*
|
||||
* @param filename the name of the file from where the bytes originated
|
||||
* @param offset the offset into the file for the first byte in the input stream.
|
||||
* @param size the number of bytes to store from the input stream.
|
||||
* @param is the input stream that will supply the bytes to store in the program.
|
||||
* @return a FileBytes that was created to access the bytes.
|
||||
* @throws IOException if there was an IOException saving the bytes to the program database.
|
||||
*/
|
||||
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a list of all the stored original file bytes objects
|
||||
* @return a list of all the stored original file bytes objects
|
||||
*/
|
||||
public List<FileBytes> getAllFileBytes();
|
||||
|
||||
/**
|
||||
* Deletes a stored sequence of file bytes. The file bytes can only be deleted if there
|
||||
* are no memory block references to the file bytes.
|
||||
* @param fileBytes the FileBytes for the file bytes to be deleted.
|
||||
* @return true if the FileBytes was deleted. If any memory blNoocks are reference this FileBytes,
|
||||
* then it will not be deleted and false will be returned.
|
||||
* @throws IOException if there was an error updating the database.
|
||||
*/
|
||||
public boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ package ghidra.program.model.mem;
|
|||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.SourceInfo;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
@ -136,6 +138,14 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
|
|||
*/
|
||||
public void setExecute(boolean e);
|
||||
|
||||
/**
|
||||
* Sets the read, write, execute permissions on this block
|
||||
* @param read the read permission
|
||||
* @param write the write permission
|
||||
* @param execute the execute permission
|
||||
*/
|
||||
public void setPermissions(boolean read, boolean write, boolean execute);
|
||||
|
||||
/**
|
||||
* Returns the value of the volatile property associated with this block.
|
||||
* This attribute is generally associated with block of I/O regions of memory.
|
||||
|
@ -249,9 +259,19 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
|
|||
/**
|
||||
* Returns true if this memory block is a real loaded block (i.e. RAM) and not a special block
|
||||
* containing file header data such as debug sections.
|
||||
* @return true if this is a loaded block and not a "special" block such as a file header.
|
||||
*/
|
||||
public boolean isLoaded();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of {@link SourceInfo} objects for this block. A block may consist of
|
||||
* multiple sequences of bytes from different sources. Each such source of bytes is described
|
||||
* by its respective SourceInfo object. Blocks may have multiple sources after two or more
|
||||
* memory blocks have been joined together and the underlying byte sources can't be joined.
|
||||
* @return a list of SourceInfo objects, one for each different source of bytes in this block.
|
||||
*/
|
||||
public List<SourceInfo> getSourceInfos();
|
||||
|
||||
/**
|
||||
* Determine if the specified address is contained within the reserved EXTERNAL block.
|
||||
* @param address address of interest
|
||||
|
@ -266,4 +286,5 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
|
|||
MemoryBlock block = memory.getBlock(address);
|
||||
return block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.SourceInfo;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* MemoryBlockStub can be extended for use by tests. It throws an UnsupportedOperationException
|
||||
* for all methods in the MemoryBlock interface. Any method that is needed for your test can then
|
||||
|
@ -113,6 +115,11 @@ public class MemoryBlockStub implements MemoryBlock {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissions(boolean read, boolean write, boolean execute) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVolatile() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -183,4 +190,9 @@ public class MemoryBlockStub implements MemoryBlock {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SourceInfo> getSourceInfos() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,10 +15,13 @@
|
|||
*/
|
||||
package ghidra.program.model.mem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.exception.*;
|
||||
|
@ -464,4 +467,26 @@ public class MemoryStub implements Memory {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.mem.MappedMemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.pcode.PcodeOp;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
|
@ -83,9 +82,9 @@ public class RefTypeFactory {
|
|||
private static RefType[] memoryRefTypes = new RefType[] { RefType.INDIRECTION,
|
||||
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
|
||||
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
|
||||
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM, RefType.DATA,
|
||||
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
|
||||
RefType.READ_WRITE, RefType.READ_WRITE_IND };
|
||||
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM,
|
||||
RefType.DATA, RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE,
|
||||
RefType.WRITE_IND, RefType.READ_WRITE, RefType.READ_WRITE_IND };
|
||||
|
||||
private static HashSet<RefType> validMemRefTypes = new HashSet<>();
|
||||
static {
|
||||
|
@ -94,20 +93,19 @@ public class RefTypeFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private static RefType[] stackRefTypes = new RefType[] { RefType.DATA, RefType.READ,
|
||||
RefType.WRITE, RefType.READ_WRITE };
|
||||
private static RefType[] stackRefTypes =
|
||||
new RefType[] { RefType.DATA, RefType.READ, RefType.WRITE, RefType.READ_WRITE };
|
||||
|
||||
private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM, RefType.READ,
|
||||
RefType.WRITE, RefType.READ_WRITE, };
|
||||
private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM,
|
||||
RefType.READ, RefType.WRITE, RefType.READ_WRITE, };
|
||||
|
||||
private static RefType[] extRefTypes = new RefType[] {
|
||||
// TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
|
||||
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
|
||||
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
|
||||
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
|
||||
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
|
||||
RefType.READ_WRITE, RefType.READ_WRITE_IND
|
||||
};
|
||||
private static RefType[] extRefTypes = new RefType[] {
|
||||
// TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
|
||||
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
|
||||
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
|
||||
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
|
||||
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
|
||||
RefType.READ_WRITE, RefType.READ_WRITE_IND };
|
||||
|
||||
public static RefType[] getMemoryRefTypes() {
|
||||
return memoryRefTypes;
|
||||
|
@ -258,9 +256,8 @@ public class RefTypeFactory {
|
|||
Varnode[] inputs = op.getInputs();
|
||||
if (opCode == PcodeOp.COPY || opCode == PcodeOp.INT_ZEXT) {
|
||||
if (addrs.contains(inputs[0].getAddress())) {
|
||||
RefType rt =
|
||||
getLoadStoreRefType(instrOps, opSeq + 1, op.getOutput().getAddress(),
|
||||
refType);
|
||||
RefType rt = getLoadStoreRefType(instrOps, opSeq + 1,
|
||||
op.getOutput().getAddress(), refType);
|
||||
if (rt == RefType.READ) {
|
||||
if (refType == RefType.WRITE) {
|
||||
return RefType.READ_WRITE;
|
||||
|
@ -395,7 +392,7 @@ public class RefTypeFactory {
|
|||
|
||||
if (toAddr != null && toAddr.isMemoryAddress()) {
|
||||
MemoryBlock block = cu.getProgram().getMemory().getBlock(toAddr);
|
||||
if (block instanceof MappedMemoryBlock) {
|
||||
if (block != null && block.isMapped()) {
|
||||
ignoreExistingReferences = true;
|
||||
speculativeFlowNotAllowed = true;
|
||||
}
|
||||
|
@ -446,8 +443,8 @@ public class RefTypeFactory {
|
|||
}
|
||||
|
||||
if (!ignoreExistingReferences) {
|
||||
Reference[] refs =
|
||||
cu.getProgram().getReferenceManager().getReferencesFrom(cu.getMinAddress(), opIndex);
|
||||
Reference[] refs = cu.getProgram().getReferenceManager().getReferencesFrom(
|
||||
cu.getMinAddress(), opIndex);
|
||||
for (Reference ref : refs) {
|
||||
if (ref.getToAddress().equals(toAddr)) {
|
||||
return ref.getReferenceType();
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlockStub;
|
||||
|
||||
public class ByteSourceRangeListTest extends AbstractGenericTest {
|
||||
private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
|
||||
private MemoryBlock block = new MemoryBlockStub();
|
||||
|
||||
@Test
|
||||
public void testConstructor() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
|
||||
ByteSourceRangeList list2 = new ByteSourceRangeList();
|
||||
list2.add(range1);
|
||||
|
||||
assertTrue(list1.equals(list2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
|
||||
ByteSourceRangeList list2 = new ByteSourceRangeList(range2);
|
||||
list1.add(list2);
|
||||
assertEquals(2, list1.getRangeCount());
|
||||
assertEquals(range1, list1.get(0));
|
||||
assertEquals(range2, list1.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList();
|
||||
|
||||
assertTrue(list1.isEmpty());
|
||||
list1.add(range1);
|
||||
assertFalse(list1.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNullRange() {
|
||||
ByteSourceRange range = null;
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList();
|
||||
list1.add(range);
|
||||
assertTrue(list1.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
|
||||
list1.add(range2);
|
||||
|
||||
Iterator<ByteSourceRange> it = list1.iterator();
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range1, it.next());
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range2, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectSimple() {
|
||||
ByteSourceRangeList list1 = new ByteSourceRangeList();
|
||||
list1.add(new ByteSourceRange(block, addr(0), 0x100, 1, 0));
|
||||
|
||||
ByteSourceRangeList list2 = new ByteSourceRangeList();
|
||||
list2.add(new ByteSourceRange(block, addr(0x100), 0x100, 1, 0x10));
|
||||
|
||||
// note that list1.intersect(list2) is not equal to list2.intersect(list1).
|
||||
// The byte sources are the same but the corresponding real addresses are calling
|
||||
// objects byte sources.
|
||||
|
||||
ByteSourceRangeList result = list1.intersect(list2);
|
||||
assertEquals(1, result.getRangeCount());
|
||||
ByteSourceRange range = result.get(0);
|
||||
|
||||
assertEquals(0xf0, range.getSize());
|
||||
assertEquals(0x10, range.getOffset());
|
||||
assertEquals(block, range.getMemoryBlock());
|
||||
assertEquals(1, range.getSourceId());
|
||||
assertEquals(addr(0x10), range.getStart());
|
||||
assertEquals(addr(0xff), range.getEnd());
|
||||
|
||||
// now intersect from list2 perspective
|
||||
result = list2.intersect(list1);
|
||||
assertEquals(1, result.getRangeCount());
|
||||
range = result.get(0);
|
||||
|
||||
assertEquals(0xf0, range.getSize());
|
||||
assertEquals(0x10, range.getOffset());
|
||||
assertEquals(block, range.getMemoryBlock());
|
||||
assertEquals(1, range.getSourceId());
|
||||
|
||||
assertEquals(addr(0x100), range.getStart());
|
||||
assertEquals(addr(0x1ef), range.getEnd());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlappingBlocks() {
|
||||
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
|
||||
MemoryBlock block1 = new MemoryBlockStub();
|
||||
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
|
||||
|
||||
// create a byte source overlap with the first block
|
||||
MemoryBlock block2 = new MemoryBlockStub();
|
||||
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x50);
|
||||
|
||||
ByteSourceRangeList list = new ByteSourceRangeList();
|
||||
list.add(range);
|
||||
list.add(range1);
|
||||
list.add(range2);
|
||||
|
||||
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
|
||||
assertEquals(2, overlappingBlocks.size());
|
||||
assertTrue(overlappingBlocks.contains(block));
|
||||
assertTrue(overlappingBlocks.contains(block2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlappingBlocksBlocksWhereBlocksAreAdjacentButDontOverlap() {
|
||||
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
|
||||
MemoryBlock block1 = new MemoryBlockStub();
|
||||
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
|
||||
|
||||
// create a byte source overlap with the first block
|
||||
MemoryBlock block2 = new MemoryBlockStub();
|
||||
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x100);
|
||||
|
||||
ByteSourceRangeList list = new ByteSourceRangeList();
|
||||
list.add(range);
|
||||
list.add(range1);
|
||||
list.add(range2);
|
||||
|
||||
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
|
||||
assertEquals(0, overlappingBlocks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlappingBlocksBlocksWhereBlocksOverlapByExactlyOneByte() {
|
||||
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
|
||||
MemoryBlock block1 = new MemoryBlockStub();
|
||||
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
|
||||
|
||||
// create a byte source overlap with the first block
|
||||
MemoryBlock block2 = new MemoryBlockStub();
|
||||
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0xff);
|
||||
|
||||
ByteSourceRangeList list = new ByteSourceRangeList();
|
||||
list.add(range);
|
||||
list.add(range1);
|
||||
list.add(range2);
|
||||
|
||||
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
|
||||
assertEquals(2, overlappingBlocks.size());
|
||||
assertTrue(overlappingBlocks.contains(block));
|
||||
assertTrue(overlappingBlocks.contains(block2));
|
||||
}
|
||||
|
||||
private Address addr(long value) {
|
||||
return space.getAddress(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.MemoryBlockStub;
|
||||
|
||||
public class ByteSourceRangeTest extends AbstractGenericTest {
|
||||
private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
|
||||
private MemoryBlock block = new MemoryBlockStub();
|
||||
@Test
|
||||
public void testIntersectNotSameSource() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
|
||||
assertNull(range1.intersect(range2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectOneRangeSimpleOverlap() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 1, 0x60);
|
||||
|
||||
ByteSourceRange intersect = range1.intersect(range2);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(0x10), intersect.getStart());
|
||||
assertEquals(addr(0x1f), intersect.getEnd());
|
||||
assertEquals(0x10, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x60, intersect.getOffset());
|
||||
|
||||
intersect = range2.intersect(range1);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(0x100), intersect.getStart());
|
||||
assertEquals(addr(0x10f), intersect.getEnd());
|
||||
assertEquals(0x10, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x60, intersect.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectOneRangeButsAgainsAnother() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 2, 0x70);
|
||||
|
||||
assertNull(range1.intersect(range2));
|
||||
assertNull(range2.intersect(range1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIntersectOneRangeCompletelyInAnother() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x30, 1, 0x40);
|
||||
|
||||
ByteSourceRange intersect = range1.intersect(range2);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(0), intersect.getStart());
|
||||
assertEquals(addr(0xf), intersect.getEnd());
|
||||
assertEquals(0x10, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x50, intersect.getOffset());
|
||||
|
||||
intersect = range2.intersect(range1);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(0x110), intersect.getStart());
|
||||
assertEquals(addr(0x11f), intersect.getEnd());
|
||||
assertEquals(0x10, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x50, intersect.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitMappedIntersect() {
|
||||
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
|
||||
ByteSourceRange range2 = new BitMappedByteSourceRange(block, addr(0x100), 1, 0x55, 2);
|
||||
|
||||
ByteSourceRange intersect = range1.intersect(range2);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(5), intersect.getStart());
|
||||
assertEquals(addr(6), intersect.getEnd());
|
||||
assertEquals(2, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x55, intersect.getOffset());
|
||||
|
||||
intersect = range2.intersect(range1);
|
||||
assertNotNull(intersect);
|
||||
assertEquals(addr(0x100), intersect.getStart());
|
||||
assertEquals(addr(0x10f), intersect.getEnd());
|
||||
assertEquals(2, intersect.getSize());
|
||||
assertEquals(1, intersect.getSourceId());
|
||||
assertEquals(0x55, intersect.getOffset());
|
||||
|
||||
}
|
||||
|
||||
private Address addr(long value) {
|
||||
return space.getAddress(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.*;
|
||||
import db.buffers.BufferFile;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.store.db.PrivateDatabase;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class FileBytesTest extends AbstractGenericTest {
|
||||
|
||||
private static final int MAX_BUFFER_SIZE_FOR_TESTING = 200;
|
||||
private Program program;
|
||||
private Memory mem;
|
||||
private int transactionID;
|
||||
|
||||
public FileBytesTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreAndRetrieveFileBytes() throws IOException {
|
||||
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
|
||||
FileBytes fileBytes = createFileBytes("testFile", dataSize);
|
||||
|
||||
byte[] outBytes = new byte[200];
|
||||
assertEquals("testFile", fileBytes.getFilename());
|
||||
assertEquals(0L, fileBytes.getFileOffset());
|
||||
assertEquals(dataSize, fileBytes.getSize());
|
||||
int n = fileBytes.getOriginalBytes(0L, outBytes);
|
||||
assertEquals(dataSize, n);
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
assertEquals("Byte[" + i + "]", i, outBytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrieveAfterSavingAndReopeningProgram() throws Exception {
|
||||
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
|
||||
FileBytes fileBytes = createFileBytes("testFile", dataSize);
|
||||
|
||||
byte[] outBytes = new byte[200];
|
||||
|
||||
saveAndRestoreProgram();
|
||||
|
||||
List<FileBytes> list = program.getMemory().getAllFileBytes();
|
||||
fileBytes = list.get(0);
|
||||
|
||||
assertEquals("testFile", fileBytes.getFilename());
|
||||
assertEquals(0L, fileBytes.getFileOffset());
|
||||
assertEquals(dataSize, fileBytes.getSize());
|
||||
int n = fileBytes.getOriginalBytes(0L, outBytes);
|
||||
assertEquals(100, n);
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
assertEquals("Byte[" + i + "]", i, outBytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiresMultipleBuffers() throws Exception {
|
||||
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING + MAX_BUFFER_SIZE_FOR_TESTING / 2;
|
||||
FileBytes fileBytes = createFileBytes("testFile", dataSize);
|
||||
|
||||
saveAndRestoreProgram();
|
||||
|
||||
byte[] outBytes = new byte[400];
|
||||
List<FileBytes> list = program.getMemory().getAllFileBytes();
|
||||
fileBytes = list.get(0);
|
||||
|
||||
assertEquals("testFile", fileBytes.getFilename());
|
||||
assertEquals(0L, fileBytes.getFileOffset());
|
||||
assertEquals(dataSize, fileBytes.getSize());
|
||||
int n = fileBytes.getOriginalBytes(0L, outBytes);
|
||||
assertEquals(dataSize, n);
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
assertEquals("Byte[" + i + "]", (byte) i, outBytes[i]);
|
||||
}
|
||||
DBBuffer[] buffers = (DBBuffer[]) getInstanceField("originalBuffers", fileBytes);
|
||||
assertEquals(2, buffers.length);
|
||||
assertEquals(MAX_BUFFER_SIZE_FOR_TESTING, buffers[0].length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateMultipleFileBytes() throws Exception {
|
||||
createFileBytes("file1", 10);
|
||||
createFileBytes("file2", 20);
|
||||
createFileBytes("file3", 30);
|
||||
|
||||
saveAndRestoreProgram();
|
||||
List<FileBytes> fileBytesList = mem.getAllFileBytes();
|
||||
assertEquals(3, fileBytesList.size());
|
||||
assertEquals("file1", fileBytesList.get(0).getFilename());
|
||||
assertEquals(10, fileBytesList.get(0).getSize());
|
||||
assertEquals("file2", fileBytesList.get(1).getFilename());
|
||||
assertEquals(20, fileBytesList.get(1).getSize());
|
||||
assertEquals("file3", fileBytesList.get(2).getFilename());
|
||||
assertEquals(30, fileBytesList.get(2).getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFileBytesDescriptors() throws Exception {
|
||||
createFileBytes("file1", 10);
|
||||
createFileBytes("file2", 20);
|
||||
createFileBytes("file3", 30);
|
||||
|
||||
saveAndRestoreProgram();
|
||||
List<FileBytes> fileBytes = mem.getAllFileBytes();
|
||||
|
||||
mem.deleteFileBytes(fileBytes.get(1));
|
||||
|
||||
saveAndRestoreProgram();
|
||||
List<FileBytes> fileBytesList = mem.getAllFileBytes();
|
||||
assertEquals(2, fileBytesList.size());
|
||||
assertEquals("file1", fileBytesList.get(0).getFilename());
|
||||
assertEquals(10, fileBytesList.get(0).getSize());
|
||||
assertEquals("file3", fileBytesList.get(1).getFilename());
|
||||
assertEquals(30, fileBytesList.get(1).getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByte() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes("file1", 10);
|
||||
assertEquals(5, fileBytes.getOriginalByte(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLayeredByte() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes("file1", 10);
|
||||
incrementFileBytes(fileBytes, 0, 10);
|
||||
|
||||
// check that the layered bytes are changed, but you can still get the originals
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(i, fileBytes.getOriginalByte(i));
|
||||
assertEquals(i + 1, fileBytes.getModifiedByte(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void incrementFileBytes(FileBytes fileBytes, int offset, int n) throws IOException {
|
||||
for (int i = offset; i < offset + n; i++) {
|
||||
fileBytes.putByte(i, (byte) (fileBytes.getModifiedByte(i) + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLayeredBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes("file1", 10);
|
||||
incrementFileBytes(fileBytes, 0, 10);
|
||||
|
||||
// check that the layered bytes are changed, but you can still get the originals
|
||||
byte[] original = new byte[10];
|
||||
byte[] modified = new byte[10];
|
||||
fileBytes.getOriginalBytes(0, original);
|
||||
fileBytes.getModifiedBytes(0, modified);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(i, original[i]);
|
||||
assertEquals(i + 1, modified[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private FileBytes createFileBytes(String name, int size) throws IOException {
|
||||
byte[] bytes = new byte[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
bytes[i] = (byte) i;
|
||||
}
|
||||
try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) {
|
||||
return mem.createFileBytes(name, 0, size, is);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAndRestoreProgram() throws Exception {
|
||||
program.endTransaction(transactionID, true);
|
||||
PrivateDatabase privateDatabase = saveProgram(program);
|
||||
program = restoreProgram(privateDatabase);
|
||||
mem = program.getMemory();
|
||||
transactionID = program.startTransaction("test");
|
||||
}
|
||||
|
||||
private PrivateDatabase saveProgram(Program program) throws Exception {
|
||||
File dir = createTempDirectory("program");
|
||||
File dbDir = new File(dir, "program.db");
|
||||
|
||||
DBHandle dbh = ((ProgramDB) program).getDBHandle();
|
||||
BufferFile bfile = PrivateDatabase.createDatabase(dbDir, null, dbh.getBufferSize());
|
||||
dbh.saveAs(bfile, true, TaskMonitor.DUMMY);
|
||||
return new PrivateDatabase(dbDir);
|
||||
}
|
||||
|
||||
private Program restoreProgram(PrivateDatabase db) throws Exception {
|
||||
DBHandle dbh = db.open(TaskMonitor.DUMMY);
|
||||
return new ProgramDB(dbh, DBConstants.UPDATE, null, this);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
FileBytesAdapter.setMaxBufferSize(MAX_BUFFER_SIZE_FOR_TESTING);
|
||||
Language language = getLanguage("Toy:BE:64:default");
|
||||
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
||||
program = new ProgramDB("Test", language, compilerSpec, this);
|
||||
|
||||
mem = program.getMemory();
|
||||
transactionID = program.startTransaction("Test");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(transactionID, true);
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
private Language getLanguage(String languageName) throws Exception {
|
||||
|
||||
ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
|
||||
if (ldefFile != null) {
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
|
||||
Language language = languageService.getLanguage(new LanguageID(languageName));
|
||||
return language;
|
||||
}
|
||||
throw new LanguageNotFoundException("Unsupported test language: " + languageName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,857 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.database.mem;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.DBConstants;
|
||||
import db.DBHandle;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.database.map.AddressMapDB;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.util.Lock;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class MemBlockDBTest extends AbstractGenericTest {
|
||||
private static final long MAX_SUB_BLOCK_SIZE = 16;
|
||||
private MemoryMapDB mem;
|
||||
private long txID;
|
||||
private DBHandle handle;
|
||||
private AddressFactory addressFactory;
|
||||
private ProgramDB program;
|
||||
private int ptxID;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Language language = getLanguage("Toy:BE:64:default");
|
||||
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
|
||||
program = new ProgramDB("Test", language, compilerSpec, this);
|
||||
ptxID = program.startTransaction("test");
|
||||
|
||||
handle = new DBHandle();
|
||||
|
||||
txID = handle.startTransaction();
|
||||
|
||||
addressFactory = language.getAddressFactory();
|
||||
AddressMapDB addrMap = (AddressMapDB) program.getAddressMap();
|
||||
Lock lock = new Lock("Test");
|
||||
int openMode = DBConstants.CREATE;
|
||||
mem = new MemoryMapDB(handle, addrMap, openMode, true, lock);
|
||||
|
||||
MemoryMapDBAdapter adapter =
|
||||
new MemoryMapDBAdapterV3(handle, mem, MAX_SUB_BLOCK_SIZE, true);
|
||||
FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, mem, true);
|
||||
|
||||
mem.init(adapter, fileBytesAdapter);
|
||||
mem.setProgram(program);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(ptxID, true);
|
||||
handle.endTransaction(txID, true);
|
||||
handle.close();
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInitializedBlock() throws Exception {
|
||||
MemoryBlock block =
|
||||
mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
|
||||
assertEquals(10, block.getSize());
|
||||
assertEquals("test", block.getName());
|
||||
assertEquals(addr(0), block.getStart());
|
||||
assertEquals(addr(9), block.getEnd());
|
||||
assertEquals(MemoryBlockType.DEFAULT, block.getType());
|
||||
assertEquals(true, block.isInitialized());
|
||||
assertEquals(false, block.isMapped());
|
||||
assertNull(block.getComment());
|
||||
assertNull(block.getSourceName());
|
||||
assertEquals(MemoryBlock.READ, block.getPermissions());
|
||||
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(10, info.getLength());
|
||||
assertEquals(addr(0), info.getMinAddress());
|
||||
assertEquals(addr(9), info.getMaxAddress());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(1, block.getByte(addr(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUninitializedBlock() throws Exception {
|
||||
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, false);
|
||||
|
||||
assertEquals(10, block.getSize());
|
||||
assertEquals("test", block.getName());
|
||||
assertEquals(addr(0), block.getStart());
|
||||
assertEquals(addr(9), block.getEnd());
|
||||
assertEquals(MemoryBlockType.DEFAULT, block.getType());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(10, info.getLength());
|
||||
assertEquals(addr(0), info.getMinAddress());
|
||||
assertEquals(addr(9), info.getMaxAddress());
|
||||
try {
|
||||
block.getByte(addr(0));
|
||||
fail("expected exception trying to read bytes on unitialized block");
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUnitializedOverlayBlock() throws Exception {
|
||||
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, true);
|
||||
|
||||
assertEquals(10, block.getSize());
|
||||
assertEquals("test", block.getName());
|
||||
assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
|
||||
assertEquals(0, block.getStart().getOffset());
|
||||
assertEquals(9, block.getEnd().getOffset());
|
||||
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
|
||||
assertEquals(MemoryBlockType.OVERLAY, block.getType());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(10, info.getLength());
|
||||
try {
|
||||
block.getByte(block.getStart());
|
||||
fail("expected exception trying to read bytes on unitialized block");
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInitializedOverlayBlock() throws Exception {
|
||||
MemoryBlock block =
|
||||
mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, true);
|
||||
|
||||
assertEquals(10, block.getSize());
|
||||
assertEquals("test", block.getName());
|
||||
assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
|
||||
assertEquals(0, block.getStart().getOffset());
|
||||
assertEquals(9, block.getEnd().getOffset());
|
||||
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
|
||||
assertEquals(MemoryBlockType.OVERLAY, block.getType());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(10, info.getLength());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(1, block.getByte(block.getStart().add(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateByteMappedBlock() throws Exception {
|
||||
mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
mem.createUninitializedBlock("test2", addr(50), 50, false);
|
||||
MemoryBlock block = mem.createByteMappedBlock("mapped", addr(1000), addr(40), 20);
|
||||
|
||||
assertEquals(20, block.getSize());
|
||||
assertEquals("mapped", block.getName());
|
||||
assertEquals(addr(1000), block.getStart());
|
||||
assertEquals(addr(1019), block.getEnd());
|
||||
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
|
||||
assertEquals(false, block.isInitialized());
|
||||
assertEquals(true, block.isMapped());
|
||||
assertNull(block.getComment());
|
||||
assertNull(block.getSourceName());
|
||||
assertEquals(MemoryBlock.READ, block.getPermissions());
|
||||
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(20, info.getLength());
|
||||
assertEquals(new AddressRangeImpl(addr(40), addr(59)), info.getMappedRange().get());
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertEquals(1, mem.getByte(block.getStart().add(i)));
|
||||
}
|
||||
try {
|
||||
mem.getByte(block.getStart().add(10));
|
||||
fail("expected exception trying to read bytes on mapped unitialized block");
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBitMappedBlock() throws Exception {
|
||||
mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
mem.createUninitializedBlock("test2", addr(50), 50, false);
|
||||
MemoryBlock block = mem.createBitMappedBlock("mapped", addr(1000), addr(49), 16);
|
||||
|
||||
assertEquals(16, block.getSize());
|
||||
assertEquals("mapped", block.getName());
|
||||
assertEquals(addr(1000), block.getStart());
|
||||
assertEquals(addr(1015), block.getEnd());
|
||||
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
|
||||
assertEquals(false, block.isInitialized());
|
||||
assertEquals(true, block.isMapped());
|
||||
assertNull(block.getComment());
|
||||
assertNull(block.getSourceName());
|
||||
assertEquals(MemoryBlock.READ, block.getPermissions());
|
||||
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(16, info.getLength());
|
||||
assertEquals(new AddressRangeImpl(addr(49), addr(50)), info.getMappedRange().get());
|
||||
|
||||
assertEquals(1, mem.getByte(block.getStart()));
|
||||
for (int i = 1; i < 8; i++) {
|
||||
assertEquals(0, mem.getByte(block.getStart().add(i)));
|
||||
}
|
||||
try {
|
||||
mem.getByte(block.getStart().add(8));
|
||||
fail("expected exception trying to read bytes on mapped unitialized block");
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFileBytesBlock() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block = mem.createInitializedBlock("test", addr(100), fileBytes, 10, 50, false);
|
||||
|
||||
assertEquals(50, block.getSize());
|
||||
assertEquals("test", block.getName());
|
||||
assertEquals(addr(100), block.getStart());
|
||||
assertEquals(addr(149), block.getEnd());
|
||||
assertEquals(MemoryBlockType.DEFAULT, block.getType());
|
||||
assertEquals(true, block.isInitialized());
|
||||
assertEquals(false, block.isMapped());
|
||||
assertNull(block.getComment());
|
||||
assertNull(block.getSourceName());
|
||||
assertEquals(MemoryBlock.READ, block.getPermissions());
|
||||
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo info = sourceInfos.get(0);
|
||||
assertEquals(50, info.getLength());
|
||||
assertEquals(addr(100), info.getMinAddress());
|
||||
assertEquals(addr(149), info.getMaxAddress());
|
||||
|
||||
for (int i = 0; i < block.getSize(); i++) {
|
||||
assertEquals(i + 10, block.getByte(addr(100 + i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFileBytesBlockOutSideRange() throws Exception {
|
||||
byte[] bytes = new byte[256];
|
||||
FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
|
||||
try {
|
||||
mem.createInitializedBlock("test", addr(100), fileBytes, 10, 100, false);
|
||||
fail(
|
||||
"Expected create filebytes block to fail because the offset+blockLength > fileBytesLength");
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializedBlockAcrossSubBlocks() throws Exception {
|
||||
mem.createInitializedBlock("test", addr(0), 100, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
assertEquals(0x0101010101010101L, mem.getLong(addr(MAX_SUB_BLOCK_SIZE - 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializedBlockAcrossMutlitipleSubBlocks() throws Exception {
|
||||
byte[] bytes = new byte[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
bytes[i] = (byte) i;
|
||||
}
|
||||
mem.createInitializedBlock("test", addr(0), new ByteArrayInputStream(bytes), 256,
|
||||
TaskMonitor.DUMMY, false);
|
||||
byte[] b = new byte[100];
|
||||
assertEquals(100, mem.getBytes(addr(10), b));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assertEquals(i + 10, b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinFileBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
|
||||
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 35, 10);
|
||||
MemoryBlock block = mem.join(block1, block2);
|
||||
assertEquals(1, mem.getBlocks().length);
|
||||
assertEquals(20, block.getSize());
|
||||
assertEquals(addr(10), block.getStart());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(25, sourceInfo.getFileBytesOffset());
|
||||
byte[] bytes = new byte[30];
|
||||
assertEquals(20, block.getBytes(addr(10), bytes));
|
||||
for (int i = 0; i < 20; i++) {
|
||||
assertEquals(i + 25, bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinNonConsecutiveFileBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
|
||||
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 70, 10);
|
||||
|
||||
MemoryBlock block = mem.join(block1, block2);
|
||||
assertEquals(1, mem.getBlocks().length);
|
||||
assertEquals(20, block.getSize());
|
||||
assertEquals(addr(10), block.getStart());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(2, sourceInfos.size());
|
||||
SourceInfo sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(25, sourceInfo.getFileBytesOffset());
|
||||
assertEquals(10, sourceInfo.getLength());
|
||||
|
||||
sourceInfo = sourceInfos.get(1);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(70, sourceInfo.getFileBytesOffset());
|
||||
assertEquals(10, sourceInfo.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinFileBytesBlockAndBufferBlock() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
|
||||
MemoryBlock block2 =
|
||||
mem.createInitializedBlock("test", addr(20), 10, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
|
||||
MemoryBlock block = mem.join(block1, block2);
|
||||
assertEquals(1, mem.getBlocks().length);
|
||||
assertEquals(20, block.getSize());
|
||||
assertEquals(addr(10), block.getStart());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(2, sourceInfos.size());
|
||||
SourceInfo sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(25, sourceInfo.getFileBytesOffset());
|
||||
assertEquals(10, sourceInfo.getLength());
|
||||
|
||||
SourceInfo sourceInfo2 = sourceInfos.get(1);
|
||||
assertEquals(10, sourceInfo2.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinBlocksFromDifferentFileBytes() throws Exception {
|
||||
FileBytes fileBytes1 = createFileBytes();
|
||||
FileBytes fileBytes2 = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes1, addr(10), 25, 10);
|
||||
MemoryBlock block2 = createFileBytesBlock(fileBytes2, addr(20), 35, 10);
|
||||
|
||||
MemoryBlock block = mem.join(block1, block2);
|
||||
assertEquals(1, mem.getBlocks().length);
|
||||
assertEquals(20, block.getSize());
|
||||
assertEquals(addr(10), block.getStart());
|
||||
List<SourceInfo> sourceInfos = block.getSourceInfos();
|
||||
assertEquals(2, sourceInfos.size());
|
||||
|
||||
SourceInfo sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes1, sourceInfo.getFileBytes().get());
|
||||
assertEquals(25, sourceInfo.getFileBytesOffset());
|
||||
assertEquals(10, sourceInfo.getLength());
|
||||
|
||||
sourceInfo = sourceInfos.get(1);
|
||||
assertEquals(fileBytes2, sourceInfo.getFileBytes().get());
|
||||
assertEquals(35, sourceInfo.getFileBytesOffset());
|
||||
assertEquals(10, sourceInfo.getLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitFileBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
|
||||
mem.split(block1, addr(30));
|
||||
|
||||
MemoryBlock[] blocks = mem.getBlocks();
|
||||
assertEquals(2, blocks.length);
|
||||
|
||||
assertEquals(20, blocks[0].getSize());
|
||||
assertEquals(30, blocks[1].getSize());
|
||||
|
||||
assertEquals(addr(10), blocks[0].getStart());
|
||||
assertEquals(addr(30), blocks[1].getStart());
|
||||
|
||||
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
SourceInfo sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(25, sourceInfo.getFileBytesOffset());
|
||||
|
||||
sourceInfos = blocks[1].getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size());
|
||||
sourceInfo = sourceInfos.get(0);
|
||||
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
|
||||
assertEquals(45, sourceInfo.getFileBytesOffset());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFileBytesBlock() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
|
||||
mem.removeBlock(block1, TaskMonitor.DUMMY);
|
||||
assertEquals(0, mem.getBlocks().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutByteToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
byte[] bytes = new byte[20];
|
||||
block.getBytes(addr(0), bytes);
|
||||
checkBytes(bytes, 0);
|
||||
block.getBytes(addr(20), bytes);
|
||||
checkBytes(bytes, 20);
|
||||
|
||||
block.putBytes(addr(0), bytes);
|
||||
block.getBytes(addr(0), bytes);
|
||||
checkBytes(bytes, 20);
|
||||
fileBytes.getOriginalBytes(0, bytes);
|
||||
checkBytes(bytes, 0);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitAndJoinUnitializedBlock() throws Exception {
|
||||
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 40, false);
|
||||
mem.split(block, addr(10));
|
||||
MemoryBlock[] blocks = mem.getBlocks();
|
||||
assertEquals(2, blocks.length);
|
||||
assertEquals(addr(0), blocks[0].getStart());
|
||||
assertEquals(addr(10), blocks[1].getStart());
|
||||
assertEquals(10, blocks[0].getSize());
|
||||
assertEquals(30, blocks[1].getSize());
|
||||
assertTrue(!blocks[0].isInitialized());
|
||||
assertTrue(!blocks[1].isInitialized());
|
||||
|
||||
mem.join(blocks[0], blocks[1]);
|
||||
blocks = mem.getBlocks();
|
||||
assertEquals(1, blocks.length);
|
||||
assertEquals(addr(0), blocks[0].getStart());
|
||||
assertEquals(40, blocks[0].getSize());
|
||||
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
|
||||
assertEquals(1, sourceInfos.size()); // make sure the sub blocks were merged
|
||||
}
|
||||
|
||||
private void checkBytes(byte[] bytes, int startingValue) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
assertEquals(startingValue + i, bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutBytesToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
assertEquals(0, block.getByte(addr(0)));
|
||||
block.putByte(addr(0), (byte) 55);
|
||||
assertEquals(55, block.getByte(addr(0)));
|
||||
assertEquals(0, fileBytes.getOriginalByte(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitOnSubBlockBoundary() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
|
||||
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(10), 30, 10);
|
||||
mem.join(block1, block2);
|
||||
MemoryBlock[] blocks = mem.getBlocks();
|
||||
assertEquals(1, blocks.length);
|
||||
mem.split(blocks[0], addr(10));
|
||||
blocks = mem.getBlocks();
|
||||
assertEquals(2, blocks.length);
|
||||
assertEquals(1, blocks[0].getSourceInfos().size());
|
||||
assertEquals(1, blocks[1].getSourceInfos().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteMappedGetPutByte() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
|
||||
MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
|
||||
assertEquals(5, mappedBlock.getByte(addr(105)));
|
||||
assertEquals(5, block1.getByte(addr(5)));
|
||||
mappedBlock.putByte(addr(105), (byte) 87);
|
||||
assertEquals(87, block1.getByte(addr(5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteMappedGetPutBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
|
||||
byte[] bytes = new byte[10];
|
||||
mappedBlock.getBytes(addr(100), bytes);
|
||||
checkBytes(bytes, 0);
|
||||
|
||||
mappedBlock.putBytes(addr(105), bytes);
|
||||
block1.getBytes(addr(5), bytes);
|
||||
checkBytes(bytes, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteMappedJoin() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 10);
|
||||
MemoryBlock mappedBlock2 = mem.createByteMappedBlock("mapped2", addr(110), addr(10), 10);
|
||||
try {
|
||||
mem.join(mappedBlock1, mappedBlock2);
|
||||
fail("Expected exception when joining byte mapped blocks");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteMappedSplit() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 20);
|
||||
mem.split(mappedBlock1, addr(110));
|
||||
MemoryBlock[] blocks = mem.getBlocks();
|
||||
assertEquals(3, blocks.length);
|
||||
assertEquals(addr(110), blocks[2].getStart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitMappedGetPutByte() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 20);
|
||||
|
||||
assertEquals(0, mappedBlock.getByte(addr(100)));
|
||||
assertEquals(0, mappedBlock.getByte(addr(101)));
|
||||
assertEquals(0, mappedBlock.getByte(addr(114)));
|
||||
assertEquals(1, mappedBlock.getByte(addr(108)));
|
||||
assertEquals(0, mappedBlock.getByte(addr(116)));
|
||||
|
||||
mappedBlock.putByte(addr(100), (byte) 4);
|
||||
assertEquals(1, block.getByte(addr(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitMappedGetPutBytes() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 50);
|
||||
|
||||
byte[] bytes = new byte[8];
|
||||
|
||||
mappedBlock.getBytes(addr(108), bytes);
|
||||
assertEquals(1, bytes[0]);
|
||||
for (int i = 1; i < 8; i++) {
|
||||
assertEquals(0, bytes[i]);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bytes[i] = 1;
|
||||
}
|
||||
mappedBlock.putBytes(addr(100), bytes);
|
||||
assertEquals(-1, block.getByte(addr(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitMappedJoin() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
|
||||
MemoryBlock mappedBlock2 = mem.createBitMappedBlock("mapped2", addr(116), addr(2), 16);
|
||||
try {
|
||||
mem.join(mappedBlock1, mappedBlock2);
|
||||
fail("Expected exception when joining bit mapped blocks");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitMappedSplit() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
|
||||
try {
|
||||
mem.split(mappedBlock1, addr(108));
|
||||
fail("Expected exception when joining bit mapped blocks");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceSetForFileBytesBlock() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
MemoryBlockDB block = (MemoryBlockDB) createFileBytesBlock(fileBytes, addr(0), 10, 50);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(5), 10);
|
||||
|
||||
// we expect to get a single range ByteSourceSet pointing into the filebytes at offset
|
||||
// 15 (10 because block was created at filebytes:10 and 5 because we start at the 5th byte
|
||||
// in the block)
|
||||
|
||||
assertEquals(1, ranges.getRangeCount());
|
||||
assertEquals(10, ranges.get(0).getSize());
|
||||
assertEquals(5, ranges.get(0).getStart().getOffset());
|
||||
assertEquals(14, ranges.get(0).getEnd().getOffset());
|
||||
assertEquals(fileBytes.getId(), ranges.get(0).getSourceId());
|
||||
assertEquals(15, ranges.get(0).getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceSetForBufferBlock() throws Exception {
|
||||
MemoryBlockDB block = (MemoryBlockDB) mem.createInitializedBlock("test", addr(0), 30,
|
||||
(byte) 1, TaskMonitor.DUMMY, false);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
|
||||
|
||||
// We expect to get to ranges because we made the buffer size small (16) so when we
|
||||
// created a 30 size block, it had to make two separate sub blocks each with its own
|
||||
// DBBuffer. The first range should contain the first 6 bytes of the requested range
|
||||
// and the second buffer should contain the last 4 bytes of request range.
|
||||
|
||||
assertEquals(2, ranges.getRangeCount()); // we have two sublocks so two distinct ranges
|
||||
assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
|
||||
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(10, range.getStart().getOffset());
|
||||
assertEquals(15, range.getEnd().getOffset());
|
||||
assertEquals(6, range.getSize());
|
||||
assertEquals(10, range.getOffset());
|
||||
|
||||
range = ranges.get(1);
|
||||
assertEquals(16, range.getStart().getOffset());
|
||||
assertEquals(19, range.getEnd().getOffset());
|
||||
assertEquals(4, range.getSize());
|
||||
assertEquals(0, range.getOffset());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForUndefinedBlock() throws Exception {
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createUninitializedBlock("test", addr(0), 30, false);
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
|
||||
// undefined blocks have no source bytes
|
||||
assertTrue(ranges.isEmpty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForByteMappedBlock() throws Exception {
|
||||
mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
mem.createUninitializedBlock("test2", addr(15), 20, false);
|
||||
mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), addr(5), 40);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 30);
|
||||
|
||||
// Uninitialized blocks don't contribute, so we should have 10 address (5 from first and last blocks each).
|
||||
assertEquals(2, ranges.getRangeCount());
|
||||
assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(1005), range.getStart());
|
||||
assertEquals(addr(1009), range.getEnd());
|
||||
assertEquals(5, range.getSize());
|
||||
assertEquals(10, range.getOffset());
|
||||
|
||||
range = ranges.get(1);
|
||||
assertEquals(addr(1030), range.getStart());
|
||||
assertEquals(addr(1034), range.getEnd());
|
||||
assertEquals(5, range.getSize());
|
||||
assertEquals(0, range.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForBitMappedBlock() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1000), 0x14);
|
||||
|
||||
assertEquals(1, ranges.getRangeCount());
|
||||
assertEquals(3, ranges.get(0).getSize());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(0x1000), range.getStart());
|
||||
assertEquals(addr(0x1017), range.getEnd());
|
||||
assertEquals(3, range.getSize());
|
||||
assertEquals(5, range.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForBitMappedBlockOffcutStart() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1005), 8);
|
||||
|
||||
assertEquals(1, ranges.getRangeCount());
|
||||
assertEquals(2, ranges.get(0).getSize());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(0x1000), range.getStart());
|
||||
assertEquals(addr(0x100f), range.getEnd());
|
||||
assertEquals(2, range.getSize());
|
||||
assertEquals(5, range.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForBitMappedBlockOffcutStartNotAtStart() throws Exception {
|
||||
FileBytes fileBytes = createFileBytes();
|
||||
createFileBytesBlock(fileBytes, addr(0), 0, 50);
|
||||
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x44);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1015), 8);
|
||||
|
||||
assertEquals(1, ranges.getRangeCount());
|
||||
assertEquals(2, ranges.get(0).getSize());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(0x1010), range.getStart());
|
||||
assertEquals(addr(0x101f), range.getEnd());
|
||||
assertEquals(2, range.getSize());
|
||||
assertEquals(7, range.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForBitMappedBlock2() throws Exception {
|
||||
mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
mem.createUninitializedBlock("test2", addr(0x4), 4, false);
|
||||
mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1008), 0x30);
|
||||
|
||||
assertEquals(2, ranges.getRangeCount());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(0x1008), range.getStart());
|
||||
assertEquals(addr(0x100f), range.getEnd());
|
||||
assertEquals(1, range.getSize());
|
||||
assertEquals(3, range.getOffset());
|
||||
|
||||
range = ranges.get(1);
|
||||
assertEquals(addr(0x1030), range.getStart());
|
||||
assertEquals(addr(0x1037), range.getEnd());
|
||||
assertEquals(1, range.getSize());
|
||||
assertEquals(0, range.getOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetByteSourceForBitMappedBlock2Offcut() throws Exception {
|
||||
mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
mem.createUninitializedBlock("test2", addr(0x4), 4, false);
|
||||
mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
|
||||
MemoryBlockDB block =
|
||||
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
|
||||
|
||||
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1006), 0x34);
|
||||
|
||||
assertEquals(2, ranges.getRangeCount());
|
||||
|
||||
ByteSourceRange range = ranges.get(0);
|
||||
assertEquals(addr(0x1000), range.getStart());
|
||||
assertEquals(addr(0x100f), range.getEnd());
|
||||
assertEquals(2, range.getSize());
|
||||
assertEquals(2, range.getOffset());
|
||||
|
||||
range = ranges.get(1);
|
||||
assertEquals(addr(0x1030), range.getStart());
|
||||
assertEquals(addr(0x103f), range.getEnd());
|
||||
assertEquals(2, range.getSize());
|
||||
assertEquals(0, range.getOffset());
|
||||
}
|
||||
private MemoryBlock createFileBytesBlock(FileBytes fileBytes, Address addr, int offset,
|
||||
int length) throws Exception {
|
||||
return mem.createInitializedBlock("test" + addr.toString(), addr, fileBytes, offset, length,
|
||||
false);
|
||||
}
|
||||
|
||||
private FileBytes createFileBytes() throws IOException {
|
||||
byte[] bytes = new byte[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
bytes[i] = (byte) i;
|
||||
}
|
||||
FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
|
||||
return fileBytes;
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return addressFactory.getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
private Language getLanguage(String languageName) throws Exception {
|
||||
|
||||
ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
|
||||
if (ldefFile != null) {
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
|
||||
Language language = languageService.getLanguage(new LanguageID(languageName));
|
||||
return language;
|
||||
}
|
||||
throw new LanguageNotFoundException("Unsupported test language: " + languageName);
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package help.screenshot;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Window;
|
||||
|
||||
|
@ -26,14 +26,14 @@ import docking.widgets.combobox.GhidraComboBox;
|
|||
import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.threaded.GThreadedTablePanel;
|
||||
import generic.test.TestUtils;
|
||||
import ghidra.app.cmd.memory.AddMemoryBlockCmd;
|
||||
import ghidra.app.cmd.memory.AddInitializedMemoryBlockCmd;
|
||||
import ghidra.app.cmd.memory.AddUninitializedMemoryBlockCmd;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
||||
import ghidra.app.plugin.core.table.TableComponentProvider;
|
||||
import ghidra.app.plugin.core.table.TableServicePlugin;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.util.navigation.GoToAddressLabelDialog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
import ghidra.util.table.GhidraProgramTableModel;
|
||||
|
||||
public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
||||
|
@ -54,12 +54,12 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
public void testGoto_Ambiguous() {
|
||||
|
||||
//add fake memory blocks to mimic a multiple address space program
|
||||
tool.execute(new AddMemoryBlockCmd("CODE", "comments", "test1", addr(0), 0x100, true, true,
|
||||
true, false, (byte) 0x69, MemoryBlockType.OVERLAY, null, true), program);
|
||||
tool.execute(new AddMemoryBlockCmd("INTMEM", "comments", "test2", addr(0), 0x100, true,
|
||||
true, true, false, (byte) 1, MemoryBlockType.OVERLAY, null, false), program);
|
||||
tool.execute(new AddMemoryBlockCmd("DUMMY", "comments", "test3", addr(20), 0x100, true,
|
||||
true, true, false, (byte) 1, MemoryBlockType.OVERLAY, null, true), program);
|
||||
tool.execute(new AddInitializedMemoryBlockCmd("CODE", "comments", "test1", addr(0), 0x100,
|
||||
true, true, true, false, (byte) 0x69, true), program);
|
||||
tool.execute(new AddUninitializedMemoryBlockCmd("INTMEM", "comments", "test2", addr(0),
|
||||
0x100, true, true, true, false, false), program);
|
||||
tool.execute(new AddInitializedMemoryBlockCmd("DUMMY", "comments", "test3", addr(20), 0x100,
|
||||
true, true, true, false, (byte) 1, true), program);
|
||||
|
||||
// go to an address outside the blocks that will be displayed
|
||||
goToAddress(program.getMemory().getBlock("DUMMY").getStart());
|
||||
|
@ -98,7 +98,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressOkOnDialog();
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
@ -107,7 +107,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressOkOnDialog();
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
@ -116,7 +116,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressOkOnDialog();
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
@ -125,7 +125,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressOkOnDialog();
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
@ -134,7 +134,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressOkOnDialog();
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
@ -147,7 +147,7 @@ public class NavigationScreenShots extends GhidraScreenShotGenerator {
|
|||
pressButtonByText(window, "OK");
|
||||
waitForSwing();
|
||||
waitForModel();
|
||||
|
||||
|
||||
waitForSwing();
|
||||
|
||||
performAction("Go To Address/Label", "GoToAddressLabelPlugin", false);
|
||||
|
|
|
@ -8,4 +8,5 @@ LICENSE||GHIDRA||||END|
|
|||
NOTICE||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
ghidra.repos.config||GHIDRA||||END|
|
||||
settings.gradle||GHIDRA||||END|
|
||||
|
|