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

This commit is contained in:
ghidravore 2019-05-06 12:44:51 -04:00 committed by Ryan Kurtz
parent d95fd43762
commit 0792417979
73 changed files with 7129 additions and 2575 deletions

View file

@ -99,6 +99,13 @@
<P><I><B>Initialized -</B></I> Indicates whether the block has been initialized with values;
this property applies to Default and Overlay blocks.</P>
<P><I><B>Byte Source -</B></I> Provides information about the source of the bytes in this
block. If the bytes were originally imported from a file, then this will indicate which file
and the offset into that file. If the bytes are mapped to another region of memory, it will
provide the address for the mapping. Blocks may consist of regions that have different
sources. In that case, source information about the first several regions will be d
displayed.</P>
<P><I><B>Source -</B></I> The name of the file that produced the bytes that make up this
block as set by the file importer; for <A href="#BitMappedType">Bit Mapped</A> or <A href=
@ -228,10 +235,16 @@
<UL>
<LI><I><B>Default&nbsp;</B></I> - A normal memory block within the processor's address
space.&nbsp; These blocks cannot overlap any other default block.&nbsp; Default blocks
can be either initialized or uninitialized. If you select <I>Initialized</I> you can
enter a byte value that will be used to fill all the bytes in the new memory
block.</LI>
can be one of the following types:</LI>
<UL>
<LI><B>Initialized</B> - Specify a value and a new block will be created
using that value for every byte in the block. </LI>
<LI><B>Uninitialized</B> - An unitialized block will be created.</LI>
<LI><B>File Bytes</B> - Select from a list of imported files and enter
a starting offset for that file. Those bytes will be the initial value for the block.</LI>
<P><I><IMG src="../../shared/note.png" border="0"> You can use the "Add To Program"
using "Binary Import" to create new FileBytes that you can use here.</I></P>
</UL>
<LI><B><I>Overlay -</I></B> An overlay block is used to give an alternative set of
bytes (and related information) for a range in memory.&nbsp; This is achieved by
creating a new address space related to the actual processor address space and placing

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -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);
}
}
}

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,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;

View file

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

View file

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

View file

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

View file

@ -0,0 +1,117 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.database.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();
}
}

View file

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

View file

@ -0,0 +1,106 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.database.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();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,249 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.program.database.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);
}
}

View file

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

View file

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

View file

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