Modified Memory API for creating Overlay blocks allow for

byte/bit-mapped overlays.  Added ByteMappingScheme for byte-mapped
blocks.
This commit is contained in:
ghidra1
2020-04-29 15:27:35 -04:00
parent 1df6fa79da
commit 6ff98a4098
76 changed files with 2351 additions and 1618 deletions
@@ -238,8 +238,8 @@
<LI><B>Search Only in Accessible Memory Blocks</B> - if checked, searches only in memory
blocks that have at least one of the Read (R), Write (W), or Execute (X) permissions set
to true. Enabling this option ensures strings are not created in areas such as overlays
or debug sections.</LI>
to true. Enabling this option ensures strings are not created in areas such as non-loaded
overlays or debug sections.</LI>
<LI><B>String End Alignment</B> - specifies the byte alignment requirement for the end of
the string. An alignment of 1 means the string can end at any address. Alignments greater
@@ -898,7 +898,11 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-
<H2><A name="Overlay"></A>Overlay</H2>
<BLOCKQUOTE>
<P>A memory block that occupies the same memory address range as some other block.</P>
<P>A memory block which corresponds to a physical memory space address within a corresponding
overlay address space identified by the block name. This allows multiple memory blocks to be defined which correspond
to the same physical address region. While the use of overlay blocks are useful to
represent a memory range its' use has significant limitations for the decompiler and
analysis which may be unable to determine when an overlay should be referenced.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
@@ -374,7 +374,7 @@
<H4>Overlay</H4>
<BLOCKQUOTE>
<P>If selected, the bytes will be loaded as an overlay. A new overlay space will be
<P>If selected, the bytes will be loaded as an initiailized overlay block. A new overlay space will be
created with the same name as the Block Name.</P>
</BLOCKQUOTE>
@@ -20,40 +20,54 @@
<P>The <I>Memory Map</I> window displays a list of memory blocks that make up the memory
structure of the current program.&nbsp; The component provides actions for adding, renaming,
moving, splitting, extending, joining, and deleting memory blocks.</P>
<P><IMG src="../../shared/note.png" border="0">When working with a versioned program within a
shared project an exclusive checkout of the program project file is required to perform any
modifications to the memory map.</P>
<P>Ghidra supports four different block types through the Memory Map window:</P>
<P>Ghidra supports three different block types through the Memory Map window:</P>
<OL>
<LI>
<A name="DefaultType"></A><I><B>Default</B> -</I> The normal block type that can be
<I>initialized</I> or <I>uninitialized</I>.
<I>Initialized</I>, <I>File Bytes</I> or <I>Uninitialized</I>.
<UL>
<LI><A name="InitializeBlockType"></A><I>Initialized</I> - The block has an initial value
specified for the bytes</LI>
<LI><A name="InitializedBlock"></A><I>Initialized</I> - The block has an initial value
specified for all bytes</LI>
<LI><A name="FileBytesBlock"></A><I>File Bytes</I> - An initialized block whose data corresponds
to a specified range within an existing loaded File Bytes instance.</LI>
<LI><A name="UninitializedBlockType"></A><I>Uninitialized</I> - The block has no initial
<LI><A name="UninitializedBlock"></A><I>Uninitialized</I> - The block has no initial
value specified for the bytes</LI>
</UL>
</LI>
<LI><A name="BitMappedType"></A><I><B>Bit Mapped</B></I> - The block provides a
bit-addressable map onto other blocks. This is useful when a processor can access some or all
of the bits in memory directly using an alternative addressing space.</LI>
bit-addressable map onto other blocks. This is useful when a processor can indirectly access
individual bits within memory using an alternative byte address. Such blocks have a fixed
mapping of 8-bytes to 1-source-byte..</LI>
<LI><A name="ByteMappedType"></A><I><B>Byte Mapped</B></I> - The block provides a
byte-addressable map onto other blocks.&nbsp; This can be useful when the same bytes can be
accessed via two or more addresses.</LI>
<LI><A name="OverlayType"></A><I><B>Overlay</B></I> - The block is created in a new
<I>overlay</I> address space. Overlay blocks can be <I>initialized</I> or
<I>unitialized</I>.&nbsp;Using Overlays is a way to get around the problem where the program
is too large to fit completely in the target system's memory.&nbsp; Overlay blocks contain
code that would get swapped in when the program needs to execute it.&nbsp;&nbsp; Note that
Overlay blocks are fixed and may not be moved, split or expanded.&nbsp; In addition, Overlays
do not relocate with image base changes.<BR>
</LI>
byte-addressable map onto other blocks.&nbsp; This can be useful when a range of
bytes can be accessed via an alternative address range. While the default mapping
is 1-byte to 1-source-byte (1:1), other decimations are permitted specified using a
mapping ratio (e.g., 2:4).</LI>
</OL>
<P><IMG src="../../shared/note.png" border="0"><I>File Bytes</I> are currently only created
by importers. At this point in time there is no capability provided by the Memory Map provider to create a
new File Bytes instance.</P>
<P><B>Overlay</B> - Each of the above memory block types may optionally be specified as an <I>Overlay</I> at the
time of creation. If this option is selected, the block is created in a new
overlay address space.&nbsp; Overlay blocks can serve various
purposes where a memory range may contain different data/code or map to different areas of memory
at any given point in time or processor state. &nbsp; Note that
overlay blocks are fixed and may not be moved, split, merged or expanded.&nbsp; In addition, Overlays
do not relocate with image base changes and have significant limitations in conjunction with
decompilation and analysis.</P>
<P>To view the <I>Memory Map</I>, select <B>Window<IMG src="../../shared/arrow.gif" border="0">
Memory Map</B> from the main tool menu, or click on the&nbsp; <IMG src="images/memory16.gif"
@@ -86,16 +100,14 @@
<P><I><B>W -</B></I> Indicates write permission.</P>
<P><I><B>X -</B></I> Indicates execute permission.<BR>
</P>
<P><I><B>X -</B></I> Indicates execute permission.</P>
<P><B>Volatile</B> - Indicates a region of volatile I/O Memory.</P>
<P><SPAN style="font-weight: bold;">Volatile</SPAN> - Indicates a region of volatile I/O
Memory.<BR>
</P>
<P><I><B>Overlay -</B></I> Indicates if block is defined as a memory overlay.</P>
<P><I><B>Type -</B></I> Indicates whether the block is a <A href="#DefaultType">Default</A>,
<A href="#BitMappedType">Bit Mapped</A>, <A href="#ByteMappedType">Byte Mapped</A> or <A
href="#OverlayType">Overlay</A> type of block.</P>
<A href="#BitMappedType">Bit Mapped</A> or <A href="#ByteMappedType">Byte Mapped</A> type of block.</P>
<P><I><B>Initialized -</B></I> Indicates whether the block has been initialized with values;
this property applies to Default and Overlay blocks.</P>
@@ -107,10 +119,7 @@
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=
"#ByteMappedType">Byte Mapped</A> blocks, the <I>Source</I> shows the mapped source
address.</P>
<P><I><B>Source -</B></I> Description of block origination.</P>
<P><I><B>Comment -</B></I> User added comment about this memory block.</P>
@@ -221,15 +230,14 @@
<P><I><B>Write</B></I> - Sets the write permission.</P>
<P><B><I>Execute</I></B> - Sets the execute permission.<BR>
</P>
<P><B><I>Execute</I></B> - Sets the execute permission.</P>
<P><SPAN style="font-weight: bold;">Volatile</SPAN> - Marks this block as volatile I/O
memory.<BR>
</P>
<P><B>Volatile</B> - Marks this block as volatile I/O memory.</P>
<P><B>Overlay</B> - Creates the block as an overlay within a corresponding overlay address space.</P>
<P><B><I>Block Types</I></B> - Select the block type from the combo box: <I><B>Default, Bit
Mapped, Byte Mapped, or Overlay</B></I>.</P>
Mapped or Byte Mapped</B></I>.</P>
<BLOCKQUOTE>
<UL>
@@ -245,15 +253,7 @@
<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
the block in the new space at the same offsets as the start address in the processor
space.&nbsp; Overlay 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>
<LI><B><I>Bit Mapped -</I></B> This is a block that allow bit addressing of a section
<LI><B><I>Bit Mapped -</I></B> This is a block that allows bit addressing of a section
of bytes in memory.&nbsp; For example, the first bit of the byte at memory location
0x1000 might also be addressed as BIT:0. The second bit at the same byte would then be
addressed as BIT:1 and so on.</LI>
@@ -261,10 +261,9 @@
<LI style="list-style: none">
<P>The illustration below depicts a Bit Mapped block of <I>Length</I> 16 with a
<I>Start Addr</I> of (BIT:) 0000, and a <I>Source Address</I> of 00008100.&nbsp; Note
that Bit Overlay addresses are assigned from least significant bit to most
that bit-mapped addresses are assigned from least significant bit to most
significant bit.</P>
</LI>
</UL>
<TABLE x-use-null-cells="" width="100%">
<TBODY>
@@ -274,16 +273,21 @@
</TR>
</TBODY>
</TABLE>
<UL>
<LI>This is used to model certain processors that allow this sort of addressing such as
<BR>This is used to model certain processors that allow this sort of addressing such as
the INTEL 8051. When a Bit Mapped block is created you must specify the byte address on
which the bit addressing will be based.</LI>
</UL>
<UL>
<LI>&nbsp;<B><I>Byte Mapped</I></B> - This is a block that allows access to a range of
bytes in memory using an alternative address.&nbsp; In other words, it allows the same
set of bytes to be accessed by two different logical addresses. A source address must
be specified that contains the actual bytes for this block.</LI>
bytes in memory using an alternative address.&nbsp; A <I>Source Address</I> must
be specified which corresponds to the source of the actual bytes for this block, although all or part of the
mapping may correspond to an uninitialized block or no block at all. The default mapping ratio
is 1-byte to 1-source-byte (1:1), although other decimations may be specified using a mapping ratio. When specifying a <I>Mapping
Ratio</I> both values must be in the range 1..127 where the right (source-byte count) value must be greater-than-or-equal
to the left value (e.g., 2:4).</LI>
</UL>
</BLOCKQUOTE>
</BLOCKQUOTE>
@@ -517,9 +521,9 @@
be created.&nbsp; Disregarding the warning may cause Ghidra to fail with an "out of memory"
error.</P>
<P><I><IMG src="../../shared/note.png" border="0"></I> Only blocks of the same type can be
merged. For example, <A href="#DefaultType">default</A> blocks can only be merged with
another default block.&nbsp;</P>
<P><I><IMG src="../../shared/note.png" border="0"></I> Only adjacent <I>Default</I> blocks of the same
initialization state can be merged.</P>
<P><I><IMG src="../../shared/note.png" border="0"></I>Overlay type blocks cannot be merged.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

@@ -40,9 +40,11 @@ abstract class AbstractAddMemoryBlockCmd implements Command {
protected final boolean write;
protected final boolean execute;
protected final boolean isVolatile;
protected final boolean isOverlay;
AbstractAddMemoryBlockCmd(String name, String comment, String source, Address start,
long length, boolean read, boolean write, boolean execute, boolean isVolatile) {
long length, boolean read, boolean write, boolean execute, boolean isVolatile,
boolean isOverlay) {
this.name = name;
this.comment = comment;
this.source = source;
@@ -52,6 +54,7 @@ abstract class AbstractAddMemoryBlockCmd implements Command {
this.write = write;
this.execute = execute;
this.isVolatile = isVolatile;
this.isOverlay = isOverlay;
}
@Override
@@ -19,9 +19,13 @@ 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 Bit-mapped memory blocks
* Command for adding Bit-mapped memory blocks.
* The resulting mapped block will derive its' byte values (1 or 0) from the mapped source bits.
* Example: 8 bytes in the resulting block will be derived from 1-byte
* in the underlying source region.
*/
public class AddBitMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
@@ -33,25 +37,27 @@ public class AddBitMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
* @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 length the length of the new block in number of bits to be mapped
* @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
* @param isOverlay if true, the block will be created in a new overlay address space.
*/
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);
Address mappedAddress, boolean isOverlay) {
super(name, comment, source, start, length, read, write, execute, isVolatile, isOverlay);
this.mappedAddress = mappedAddress;
}
@Override
protected MemoryBlock createMemoryBlock(Memory memory)
throws LockException, MemoryConflictException, AddressOverflowException {
return memory.createBitMappedBlock(name, start, mappedAddress, length);
throws LockException, MemoryConflictException, AddressOverflowException,
IllegalArgumentException, DuplicateNameException {
return memory.createBitMappedBlock(name, start, mappedAddress, length, isOverlay);
}
}
@@ -16,9 +16,11 @@
package ghidra.app.cmd.memory;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.ByteMappingScheme;
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 byte-mapped memory blocks
@@ -26,9 +28,16 @@ import ghidra.program.model.mem.*;
public class AddByteMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
private final Address mappedAddress;
private final ByteMappingScheme byteMappingScheme;
/**
* Create a new AddByteMappedMemoryBlockCmd
* Create a new AddByteMappedMemoryBlockCmd with a specified byte mapping scheme.
* Byte mapping scheme is specified by two values schemeDestByteCount and schemeSrcByteCount which
* may be viewed as a ratio of number of destination bytes to number of mapped source bytes.
* When the destination consumes bytes from the mapped source it consume schemeDestByteCount bytes then
* skips (schemeSrcByteCount - schemeDestByteCount) bytes before repeating the mapping sequence over
* the extent of the destination block. The block start address and source mappedAddress must
* be chosen carefully as they relate to the mapping scheme when it is anything other than 1:1.
* @param name the name for the new memory block.
* @param comment the comment for the block
* @param source indicates what is creating the block
@@ -39,19 +48,45 @@ public class AddByteMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
* @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
* @param byteMappingScheme byte mapping scheme (may be null for 1:1 mapping)
* @param isOverlay if true, the block will be created in a new overlay address space.
*/
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);
Address mappedAddress, ByteMappingScheme byteMappingScheme, boolean isOverlay) {
super(name, comment, source, start, length, read, write, execute, isVolatile, isOverlay);
this.mappedAddress = mappedAddress;
this.byteMappingScheme = byteMappingScheme;
}
/**
* Create a new AddByteMappedMemoryBlockCmd with 1:1 byte mapping scheme
* @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
* @param isOverlay if true, the block will be created in a new overlay address space.
*/
public AddByteMappedMemoryBlockCmd(String name, String comment, String source, Address start,
long length, boolean read, boolean write, boolean execute, boolean isVolatile,
Address mappedAddress, boolean isOverlay) {
this(name, comment, source, start, length, read, write, execute, isVolatile, mappedAddress,
null, isOverlay);
}
@Override
protected MemoryBlock createMemoryBlock(Memory memory)
throws LockException, MemoryConflictException, AddressOverflowException {
return memory.createByteMappedBlock(name, start, mappedAddress, length);
throws LockException, MemoryConflictException, AddressOverflowException,
IllegalArgumentException, DuplicateNameException {
return memory.createByteMappedBlock(name, start, mappedAddress, length,
byteMappingScheme,
isOverlay);
}
}
@@ -29,7 +29,6 @@ public class AddFileBytesMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
private final FileBytes fileBytes;
private final long offset;
private final boolean isOverlay;
/**
* Create a new AddFileBytesMemoryBlockCmd
@@ -49,10 +48,9 @@ public class AddFileBytesMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
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);
super(name, comment, source, start, length, read, write, execute, isVolatile, isOverlay);
this.fileBytes = fileBytes;
this.offset = offset;
this.isOverlay = isOverlay;
}
@Override
@@ -28,7 +28,6 @@ import ghidra.util.exception.DuplicateNameException;
public class AddInitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
private final byte initialValue;
private final boolean isOverlay;
/**
* Create a new AddFileBytesMemoryBlockCmd
@@ -47,10 +46,9 @@ public class AddInitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
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);
super(name, comment, source, start, length, read, write, execute, isVolatile, isOverlay);
this.initialValue = initialValue;
this.isOverlay = isOverlay;
}
@Override
@@ -26,8 +26,6 @@ import ghidra.util.exception.DuplicateNameException;
*/
public class AddUninitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
private final boolean isOverlay;
/**
* Create a new AddUninitializedMemoryBlockCmd
* @param name the name for the new memory block.
@@ -44,9 +42,7 @@ public class AddUninitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd {
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;
super(name, comment, source, start, length, read, write, execute, isVolatile, isOverlay);
}
@Override
@@ -27,6 +27,7 @@ import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel;
import docking.widgets.textfield.IntegerTextField;
import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType;
import ghidra.app.plugin.core.misc.RegisterField;
import ghidra.app.util.*;
@@ -63,11 +64,14 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
private JCheckBox writeCB;
private JCheckBox executeCB;
private JCheckBox volatileCB;
private JCheckBox overlayCB;
private RegisterField initialValueField;
private JLabel initialValueLabel;
private AddressFactory addrFactory;
private AddressInput baseAddrField; // used for BitMemoryBlocks
private Address baseAddress;
private AddressInput baseAddrField; // used for Bit and Byte mapped blocks
private IntegerTextField schemeDestByteCountField; // used for Byte mapped blocks
private IntegerTextField schemeSrcByteCountField; // used for Byte mapped blocks
private AddBlockModel model;
private GhidraComboBox<MemoryBlockType> comboBox;
private boolean updatingInitializedRB;
@@ -103,6 +107,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
writeCB.setSelected(model.isWrite());
executeCB.setSelected(model.isExecute());
volatileCB.setSelected(model.isVolatile());
overlayCB.setSelected(model.isOverlay());
}
/**
@@ -163,12 +168,18 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
volatileCB.setSelected(model.isVolatile());
volatileCB.addActionListener(e -> model.setVolatile(volatileCB.isSelected()));
overlayCB = new GCheckBox("Overlay");
overlayCB.setName("Overlay");
overlayCB.setSelected(model.isOverlay());
overlayCB.addActionListener(e -> model.setOverlay(overlayCB.isSelected()));
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);
panel.add(overlayCB);
return panel;
}
@@ -178,7 +189,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
panel.setBorder(BorderFactory.createTitledBorder("Block Types"));
MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT,
MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED };
comboBox = new GhidraComboBox<>(items);
comboBox.addItemListener(e -> blockTypeSelected());
@@ -308,6 +319,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
writeCB.setSelected(model.isWrite());
executeCB.setSelected(model.isExecute());
volatileCB.setSelected(model.isVolatile());
overlayCB.setSelected(model.isOverlay());
setOkEnabled(false);
tool.showDialog(this, tool.getComponentProvider(PluginConstants.MEMORY_MAP));
@@ -406,29 +418,66 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
}
private void baseAddressChanged() {
baseAddress = baseAddrField.getAddress();
Address baseAddress = baseAddrField.getAddress();
model.setBaseAddress(baseAddress);
}
private void schemeSrcByteCountChanged() {
int value = schemeSrcByteCountField.getIntValue();
model.setSchemeSrcByteCount(value);
}
private void schemeDestByteCountChanged() {
int value = schemeDestByteCountField.getIntValue();
model.setSchemeDestByteCount(value);
}
private void blockTypeSelected() {
MemoryBlockType blockType = (MemoryBlockType) comboBox.getSelectedItem();
model.setBlockType(blockType);
if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) {
if (blockType == MemoryBlockType.DEFAULT) {
typeCardLayout.show(viewPanel, UNMAPPED);
}
else {
enableByteMappingSchemeControls(blockType == MemoryBlockType.BYTE_MAPPED);
schemeDestByteCountField.setValue(model.getSchemeDestByteCount());
schemeSrcByteCountField.setValue(model.getSchemeSrcByteCount());
typeCardLayout.show(viewPanel, MAPPED);
}
}
private void enableByteMappingSchemeControls(boolean b) {
schemeDestByteCountField.setValue(1);
schemeDestByteCountField.setEnabled(b);
schemeSrcByteCountField.setValue(1);
schemeSrcByteCountField.setEnabled(b);
}
private JPanel buildMappedPanel() {
JPanel panel = new JPanel(new PairLayout());
baseAddrField = new AddressInput();
baseAddrField.setAddressFactory(addrFactory);
baseAddrField.setName("Source Addr");
baseAddrField.addChangeListener(ev -> baseAddressChanged());
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
schemeDestByteCountField = new IntegerTextField(4, 1);
schemeDestByteCountField.setAllowNegativeValues(false);
schemeDestByteCountField.setAllowsHexPrefix(false);
schemeDestByteCountField.setDecimalMode();
schemeDestByteCountField.addChangeListener(ev -> schemeDestByteCountChanged());
schemeSrcByteCountField = new IntegerTextField(4, 1);
schemeSrcByteCountField.setAllowNegativeValues(false);
schemeSrcByteCountField.setAllowsHexPrefix(false);
schemeSrcByteCountField.setDecimalMode();
schemeSrcByteCountField.addChangeListener(ev -> schemeSrcByteCountChanged());
schemePanel.add(schemeDestByteCountField.getComponent());
schemePanel.add(new GLabel(" : "));
schemePanel.add(schemeSrcByteCountField.getComponent());
Program program = model.getProgram();
Address minAddr = program.getMinAddress();
@@ -437,8 +486,12 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
}
baseAddrField.setAddress(minAddr);
model.setBaseAddress(minAddr);
panel.add(new GLabel("Source Addr:"));
panel.add(new GLabel("Source Address:"));
panel.add(baseAddrField);
panel.add(new GLabel("Mapping Ratio:"));
panel.add(schemePanel);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
return panel;
}
@@ -20,6 +20,7 @@ import javax.swing.event.ChangeListener;
import ghidra.app.cmd.memory.*;
import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.mem.ByteMappingScheme;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@@ -42,8 +43,11 @@ class AddBlockModel {
private String blockName;
private Address startAddr;
private Address baseAddr;
private int schemeDestByteCount;
private int schemeSrcByteCount;
private long length;
private MemoryBlockType blockType;
private boolean isOverlay;
private int initialValue;
private String message;
private ChangeListener listener;
@@ -121,6 +125,9 @@ class AddBlockModel {
isWrite = true;
isExecute = false;
isVolatile = false;
isOverlay = false;
schemeDestByteCount = blockType == MemoryBlockType.BIT_MAPPED ? 8 : 1;
schemeSrcByteCount = 1;
initializedType = InitializedType.UNITIALIZED;
validateInfo();
listener.stateChanged(null);
@@ -142,6 +149,10 @@ class AddBlockModel {
this.isVolatile = b;
}
void setOverlay(boolean b) {
this.isOverlay = b;
}
void setInitializedType(InitializedType type) {
this.initializedType = type;
validateInfo();
@@ -154,6 +165,26 @@ class AddBlockModel {
listener.stateChanged(null);
}
void setSchemeSrcByteCount(int value) {
this.schemeSrcByteCount = value;
validateInfo();
listener.stateChanged(null);
}
int getSchemeSrcByteCount() {
return schemeSrcByteCount;
}
void setSchemeDestByteCount(int value) {
this.schemeDestByteCount = value;
validateInfo();
listener.stateChanged(null);
}
int getSchemeDestByteCount() {
return schemeDestByteCount;
}
Address getStartAddress() {
return startAddr;
}
@@ -194,6 +225,10 @@ class AddBlockModel {
return isVolatile;
}
boolean isOverlay() {
return isOverlay;
}
InitializedType getInitializedType() {
return initializedType;
}
@@ -217,21 +252,21 @@ class AddBlockModel {
switch (blockType) {
case BIT_MAPPED:
return new AddBitMappedMemoryBlockCmd(blockName, comment, source, startAddr, length,
isRead, isWrite, isExecute, isVolatile, baseAddr);
isRead, isWrite, isExecute, isVolatile, baseAddr, isOverlay);
case BYTE_MAPPED:
ByteMappingScheme byteMappingScheme =
new ByteMappingScheme(schemeDestByteCount, schemeSrcByteCount);
return new AddByteMappedMemoryBlockCmd(blockName, comment, source, startAddr,
length, isRead, isWrite, isExecute, isVolatile, baseAddr);
length, isRead, isWrite, isExecute, isVolatile, baseAddr, byteMappingScheme,
isOverlay);
case DEFAULT:
return createNonMappedMemoryBlock(source, false);
case OVERLAY:
return createNonMappedMemoryBlock(source, true);
return createNonMappedMemoryBlock(source);
default:
throw new AssertException("Encountered unexpected block type: " + blockType);
}
}
private Command createNonMappedMemoryBlock(String source, boolean isOverlay) {
private Command createNonMappedMemoryBlock(String source) {
switch (initializedType) {
case INITIALIZED_FROM_FILE_BYTES:
return new AddFileBytesMemoryBlockCmd(blockName, comment, source, startAddr, length,
@@ -299,7 +334,7 @@ class AddBlockModel {
}
private boolean hasUniqueNameIfOverlay() {
if (blockType != MemoryBlockType.OVERLAY) {
if (!isOverlay) {
return true;
}
AddressFactory factory = program.getAddressFactory();
@@ -315,7 +350,7 @@ class AddBlockModel {
private boolean isOverlayIfOtherSpace() {
if (startAddr.getAddressSpace().equals(AddressSpace.OTHER_SPACE)) {
if (blockType != MemoryBlockType.OVERLAY) {
if (!isOverlay) {
message = "Blocks defined in the " + AddressSpace.OTHER_SPACE.getName() +
" space must be overlay blocks";
return false;
@@ -327,16 +362,27 @@ class AddBlockModel {
private boolean hasMappedAddressIfNeeded() {
if (blockType == MemoryBlockType.BIT_MAPPED || blockType == MemoryBlockType.BYTE_MAPPED) {
if (baseAddr == null) {
String blockTypeStr = (blockType == MemoryBlockType.BIT_MAPPED) ? "bit" : "overlay";
String blockTypeStr =
(blockType == MemoryBlockType.BIT_MAPPED) ? "bit-mapped" : "byte-mapped";
message = "Please enter a source address for the " + blockTypeStr + " block";
return false;
}
if (schemeDestByteCount <= 0 || schemeDestByteCount > Byte.MAX_VALUE ||
schemeSrcByteCount <= 0 || schemeSrcByteCount > Byte.MAX_VALUE) {
message = "Mapping ratio values must be within range: 1 to 127";
return false;
}
if (schemeDestByteCount > schemeSrcByteCount) {
message =
"Mapping ratio destination byte count (left-value) must be less than or equal the source byte count (right-value)";
return false;
}
}
return true;
}
private boolean hasNoMemoryConflicts() {
if (blockType == MemoryBlockType.OVERLAY) {
if (isOverlay) {
return true;
}
Address endAddr = startAddr.add(length - 1);
@@ -81,11 +81,11 @@ class MemoryMapManager {
Listing listing = program.getListing();
String[] treeNames = listing.getTreeNames();
for (int i = 0; i < treeNames.length; i++) {
for (String treeName : treeNames) {
boolean duplicate = false;
int index = 0;
ProgramFragment frag = listing.getFragment(treeNames[i], start);
ProgramFragment frag = listing.getFragment(treeName, start);
do {
try {
frag.setName("Frag" + index + "-" + name);
@@ -121,6 +121,11 @@ class MemoryMapManager {
// make sure that the block after the first block is the second block
Address nextStart = blockA.getEnd();
AddressSpace space = nextStart.getAddressSpace();
if (space.isOverlaySpace()) {
Msg.showError(this, plugin.getMemoryMapProvider().getComponent(),
"Merge Blocks Failed", "Can't merge overlay blocks");
return false;
}
Address blockBstart = blockB.getStart();
if (!space.isSuccessor(nextStart, blockBstart)) {
@@ -171,19 +176,9 @@ class MemoryMapManager {
return true;
}
boolean isValidBlockName(String name) {
if (name == null || name.length() == 0) {
return false;
}
Memory memory = program.getMemory();
MemoryBlock[] blocks = memory.getBlocks();
for (int i = 0; i < blocks.length; i++) {
if (blocks[i].getName().equals(name)) {
return false;
}
}
return true;
boolean isDuplicateName(String name) {
// block names may not duplicate existing address spaces (includes overlay blocks)
return program.getAddressFactory().getAddressSpace(name) != null;
}
void setProgram(Program program) {
@@ -332,8 +327,7 @@ class MemoryMapManager {
return false;
}
for (int i = 0; i < blocks.size(); i++) {
MemoryBlock nextBlock = blocks.get(i);
for (MemoryBlock nextBlock : blocks) {
if (min == null || nextBlock.getStart().compareTo(min) < 0) {
min = nextBlock.getStart();
}
@@ -412,8 +406,8 @@ class MemoryMapManager {
private boolean allBlocksInSameSpace() {
AddressSpace lastSpace = null;
for (int i = 0; i < blocks.size(); i++) {
Address start = blocks.get(i).getStart();
for (MemoryBlock block : blocks) {
Address start = block.getStart();
AddressSpace space = start.getAddressSpace();
if (lastSpace != null && !lastSpace.equals(space)) {
return false;
@@ -37,7 +37,6 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
import ghidra.util.NamingUtilities;
import ghidra.util.exception.DuplicateNameException;
class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
@@ -50,11 +49,12 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
final static byte WRITE = 5;
final static byte EXECUTE = 6;
final static byte VOLATILE = 7;
final static byte BLOCK_TYPE = 8;
final static byte INIT = 9;
final static byte BYTE_SOURCE = 10;
final static byte SOURCE = 11;
final static byte COMMENT = 12;
final static byte OVERLAY = 8;
final static byte BLOCK_TYPE = 9;
final static byte INIT = 10;
final static byte BYTE_SOURCE = 11;
final static byte SOURCE = 12;
final static byte COMMENT = 13;
final static String NAME_COL = "Name";
final static String START_COL = "Start";
@@ -64,6 +64,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
final static String WRITE_COL = "W";
final static String EXECUTE_COL = "X";
final static String VOLATILE_COL = "Volatile";
final static String OVERLAY_COL = "Overlay";
final static String BLOCK_TYPE_COL = "Type";
final static String INIT_COL = "Initialized";
final static String BYTE_SOURCE_COL = "Byte Source";
@@ -77,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, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL };
OVERLAY_COL, BLOCK_TYPE_COL, INIT_COL, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL };
MemoryMapModel(MemoryMapProvider provider, Program program) {
super(START);
@@ -115,7 +116,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
@Override
public boolean isSortable(int columnIndex) {
if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE ||
columnIndex == VOLATILE || columnIndex == INIT) {
columnIndex == VOLATILE || columnIndex == OVERLAY || columnIndex == INIT) {
return false;
}
return true;
@@ -163,7 +164,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE ||
columnIndex == VOLATILE || columnIndex == INIT) {
columnIndex == VOLATILE || columnIndex == OVERLAY || columnIndex == INIT) {
return Boolean.class;
}
return String.class;
@@ -263,17 +264,17 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
"Please enter a label name.");
break;
}
if (!NamingUtilities.isValidName(name)) {
if (name.equals(block.getName())) {
break;
}
if (Memory.isValidAddressSpaceName(name)) {
Msg.showError(this, provider.getComponent(), "Invalid Name",
"Invalid Memory Block Name: " + name);
break;
}
if (name.equals(block.getName())) {
break;
}
if (!provider.getMemoryMapManager().isValidBlockName(name)) {
if (provider.getMemoryMapManager().isDuplicateName(name)) {
Msg.showError(this, provider.getComponent(), "Duplicate Name",
"Block named " + name + " already exists.");
"Address space/overlay named " + name + " already exists.");
break;
}
if (!name.equals(block.getName())) {
@@ -417,7 +418,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
}
private boolean verifyRenameAllowed(MemoryBlock block, String newName) {
if ((block.getType() != MemoryBlockType.OVERLAY) || block.getName().equals(newName)) {
if (!block.isOverlay() || block.getName().equals(newName)) {
return true;
}
if (!program.hasExclusiveAccess()) {
@@ -492,6 +493,8 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
return block.isExecute() ? Boolean.TRUE : Boolean.FALSE;
case VOLATILE:
return block.isVolatile() ? Boolean.TRUE : Boolean.FALSE;
case OVERLAY:
return block.isOverlay() ? Boolean.TRUE : Boolean.FALSE;
case INIT:
MemoryBlockType blockType = block.getType();
if (blockType == MemoryBlockType.BIT_MAPPED) {
@@ -581,6 +584,10 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
int b1v = (b1.isVolatile() ? 1 : -1);
int b2v = (b2.isVolatile() ? 1 : -1);
return (b1v - b2v);
case OVERLAY:
int b1o = (b1.isOverlay() ? 1 : -1);
int b2o = (b2.isOverlay() ? 1 : -1);
return (b1o - b2o);
case INIT:
int b1init = (b1.isInitialized() ? 1 : -1);
int b2init = (b2.isInitialized() ? 1 : -1);
@@ -150,6 +150,8 @@ class MemoryMapProvider extends ComponentProviderAdapter {
column.setCellRenderer(new GBooleanCellRenderer());
column = memTable.getColumn(MemoryMapModel.VOLATILE_COL);
column.setCellRenderer(new GBooleanCellRenderer());
column = memTable.getColumn(MemoryMapModel.OVERLAY_COL);
column.setCellRenderer(new GBooleanCellRenderer());
column = memTable.getColumn(MemoryMapModel.INIT_COL);
column.setCellRenderer(new GBooleanCellRenderer());
@@ -435,13 +437,22 @@ class MemoryMapProvider extends ComponentProviderAdapter {
column.setResizable(false);
column = memTable.getColumn(MemoryMapModel.VOLATILE_COL);
column.setMaxWidth(50);
column.setMinWidth(50);
column.setMaxWidth(57);
column.setMinWidth(57);
column.setResizable(false);
column = memTable.getColumn(MemoryMapModel.INIT_COL);
column.setMaxWidth(60);
column = memTable.getColumn(MemoryMapModel.OVERLAY_COL);
column.setMaxWidth(55);
column.setMinWidth(55);
column.setResizable(false);
column = memTable.getColumn(MemoryMapModel.BLOCK_TYPE_COL);
column.setMinWidth(60);
// column.setResizable(true);
column = memTable.getColumn(MemoryMapModel.INIT_COL);
column.setMaxWidth(68);
column.setMinWidth(68);
column.setResizable(false);
}
@@ -539,7 +550,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
if (block == null) {
return;
}
if (block.getType() == MemoryBlockType.OVERLAY) {
if (block.isOverlay()) {
Msg.showInfo(getClass(), getComponent(), "Expand Overlay Block Not Allowed",
"Overlay blocks cannot be expanded.");
}
@@ -558,7 +569,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
return;
}
if (block.getType() == MemoryBlockType.OVERLAY) {
if (block.isOverlay()) {
Msg.showInfo(getClass(), getComponent(), "Move Overlay Block Not Allowed",
"Overlay blocks cannot be moved.");
}
@@ -575,7 +586,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
if (block == null) {
return;
}
if (block.getType() == MemoryBlockType.OVERLAY) {
if (block.isOverlay()) {
Msg.showInfo(getClass(), getComponent(), "Split Overlay Block Not Allowed",
"Overlay blocks cannot be split.");
}
@@ -29,9 +29,9 @@ import ghidra.app.plugin.core.misc.RegisterField;
import ghidra.app.util.AddressInput;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.HelpLocation;
import ghidra.util.NamingUtilities;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.layout.PairLayout;
@@ -85,15 +85,14 @@ class SplitBlockDialog extends DialogComponentProvider {
newBlockName = block.getName() + ".split";
blockTwoNameField.setText(newBlockName);
}
if (!plugin.getMemoryMapManager().isValidBlockName(newBlockName)) {
setStatusText("Block name already exists");
return;
}
if (!NamingUtilities.isValidName(newBlockName)) {
if (!Memory.isValidAddressSpaceName(newBlockName)) {
setStatusText("Invalid Block Name: " + newBlockName);
return;
}
if (plugin.getMemoryMapManager().isDuplicateName(newBlockName)) {
setStatusText("Address space/overlay named " + newBlockName + " already exists.");
return;
}
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
plugin.getMemoryMapManager().splitBlock(block, blockTwoStart.getAddress(), newBlockName);
close();
@@ -121,17 +121,18 @@ public class MemoryBlockUtils {
* @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 overlay create overlay block if true otherwise a normal mapped block will be created
* @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) {
boolean x, boolean overlay, MessageLog log) {
Memory memory = program.getMemory();
try {
MemoryBlock block = memory.createBitMappedBlock(name, start, base, length);
MemoryBlock block = memory.createBitMappedBlock(name, start, base, length, overlay);
setBlockAttributes(block, comment, source, r, w, x);
adjustFragment(program, start, name);
@@ -148,7 +149,8 @@ public class MemoryBlockUtils {
}
/**
* Creates a new byte mapped memory block. (A byte mapped block is a block where each byte value
* Creates a new byte mapped memory block with a 1:1 byte mapping scheme.
* (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.
@@ -161,17 +163,18 @@ public class MemoryBlockUtils {
* @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 overlay create overlay block if true otherwise a normal mapped block will be created
* @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) {
boolean x, boolean overlay, MessageLog log) {
Memory memory = program.getMemory();
try {
MemoryBlock block = memory.createByteMappedBlock(name, start, base, length);
MemoryBlock block = memory.createByteMappedBlock(name, start, base, length, overlay);
setBlockAttributes(block, comment, source, r, w, x);
adjustFragment(program, start, name);
@@ -31,7 +31,8 @@ public class CommentFieldMouseHandler implements FieldMouseHandlerExtension {
private final static Class<?>[] SUPPORTED_CLASSES =
new Class[] { CommentFieldLocation.class, EolCommentFieldLocation.class,
PlateFieldLocation.class, AutomaticCommentFieldLocation.class };
PlateFieldLocation.class, AutomaticCommentFieldLocation.class,
MemoryBlockStartFieldLocation.class };
@Override
public Class<?>[] getSupportedProgramLocations() {
@@ -111,8 +111,15 @@ public class MemoryBlockStartFieldFactory extends FieldFactory {
return null;
}
CodeUnit cu = (CodeUnit) proxyObject;
List<AttributedString> attributedStrings = createBlockStartText(cu);
String[] comments = new String[attributedStrings.size()];
for (int i = 0; i < comments.length; i++) {
comments[i] = attributedStrings.get(i).getText();
}
return new MemoryBlockStartFieldLocation(cu.getProgram(), cu.getMinAddress(), null, row,
col, null, 0);
col, comments, 0);
}
/**
@@ -199,11 +206,20 @@ public class MemoryBlockStartFieldFactory extends FieldFactory {
return null;
}
String type = block.getType() == MemoryBlockType.DEFAULT ? "" : "(" + block.getType() + ")";
MemoryBlockType blockType = block.getType();
String type = "";
if (blockType != MemoryBlockType.DEFAULT) {
if (block.isMapped()) {
type = "(" + block.getSourceInfos().get(0).getDescription() + ")";
}
else {
type = "(" + blockType + ")";
}
}
String line1 = block.getName() + " " + type;
String line2 = block.getComment();
String line3 = cu.getMemory().getMinAddress().getAddressSpace().toString() + " " +
block.getStart() + "-" + block.getEnd();
String line3 = block.getStart().toString(true) + "-" + block.getEnd().toString(true);
AttributedString borderAS = new AttributedString("//", color, getMetrics());
lines.add(borderAS);
@@ -149,7 +149,7 @@ class MemoryMapXmlMgr {
Address sourceAddr = factory.getAddress(element.getAttribute("SOURCE_ADDRESS"));
MemoryBlock block = MemoryBlockUtils.createBitMappedBlock(program, overlayName,
addr, sourceAddr, length, comment, comment, r, w, x, log);
addr, sourceAddr, length, comment, comment, r, w, x, false, log);
if (block != null) {
block.setVolatile(isVolatile);
}
@@ -159,7 +159,7 @@ class MemoryMapXmlMgr {
Address sourceAddr = factory.getAddress(element.getAttribute("SOURCE_ADDRESS"));
MemoryBlock block = MemoryBlockUtils.createByteMappedBlock(program, overlayName,
addr, sourceAddr, length, comment, comment, r, w, x, log);
addr, sourceAddr, length, comment, comment, r, w, x, false, log);
if (block != null) {
block.setVolatile(isVolatile);
}
@@ -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,6 +15,8 @@
*/
package ghidra.program.util;
import java.util.ArrayList;
import ghidra.framework.store.LockException;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@@ -26,8 +27,6 @@ import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskMonitor;
import java.util.ArrayList;
/**
* <CODE>MemoryDiff</CODE> determines where the memory differs between two programs as well as the
* types of differences.
@@ -179,12 +178,12 @@ public class MemoryDiff {
*/
public AddressRange[] getDifferentAddressRanges() {
ArrayList<AddressRange> rangeDiffs = new ArrayList<AddressRange>();
for (int i = 0; i < ranges.length; i++) {
Address addr = ranges[i].getMinAddress();
for (AddressRange range : ranges) {
Address addr = range.getMinAddress();
MemoryBlock block1 = memory1.getBlock(addr);
MemoryBlock block2 = memory2.getBlock(addr);
if (!sameMemoryBlock(block1, block2)) {
rangeDiffs.add(ranges[i]);
rangeDiffs.add(range);
}
}
return rangeDiffs.toArray(new AddressRange[rangeDiffs.size()]);
@@ -271,15 +270,8 @@ public class MemoryDiff {
memory1.join(firstBlock, secondBlock);
}
return true;
} catch (MemoryBlockException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} catch (LockException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} catch (MemoryConflictException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} catch (AddressOverflowException e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
} catch (NotFoundException e) {
}
catch (Exception e) {
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
return false;
@@ -225,7 +225,7 @@ public class ProgramMemoryUtil {
AddressSet addrSet = new AddressSet();
MemoryBlock[] memBlocks = program.getMemory().getBlocks();
for (MemoryBlock memoryBlock : memBlocks) {
if (memoryBlock.getType() == MemoryBlockType.OVERLAY) {
if (memoryBlock.isOverlay()) {
AddressRange addressRange =
new AddressRangeImpl(memoryBlock.getStart(), memoryBlock.getEnd());
addrSet.add(addressRange);
@@ -143,7 +143,10 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setLength(100);
assertTrue(model.isValidInfo());
model.setBlockType(MemoryBlockType.OVERLAY);
model.setBlockType(MemoryBlockType.DEFAULT);
assertTrue(model.isValidInfo());
model.setOverlay(true);
assertTrue(model.isValidInfo());
model.setBaseAddress(getAddr(0x2000));
@@ -181,7 +184,8 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setBlockName(".test");
model.setStartAddress(getAddr(0x100));
model.setLength(100);
model.setBlockType(MemoryBlockType.OVERLAY);
model.setBlockType(MemoryBlockType.DEFAULT);
model.setOverlay(true);
model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
model.setInitialValue(0xa);
assertTrue(model.execute());
@@ -206,7 +210,8 @@ public class AddBlockModelTest extends AbstractGhidraHeadedIntegrationTest
model.setBlockName(".test");
model.setStartAddress(getAddr(0x01001000));
model.setLength(100);
model.setBlockType(MemoryBlockType.OVERLAY);
model.setBlockType(MemoryBlockType.DEFAULT);
model.setOverlay(true);
model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE);
model.setInitialValue(0xa);
assertTrue(model.execute());
@@ -482,7 +482,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
// add a bit overlay block, live block, and an unitialized block
int transactionID = program.startTransaction("test");
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100);
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100, false);
memory.createUninitializedBlock(".Uninit", getAddr(0x3000), 0x200, false);
program.endTransaction(transactionID, true);
@@ -508,7 +508,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
public void testSortBlockTypeDescending() throws Exception {
// add a bit overlay block, live block, and an unitialized block
int transactionID = program.startTransaction("test");
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100);
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100, false);
memory.createUninitializedBlock(".Uninit", getAddr(0x3000), 0x200, false);
program.endTransaction(transactionID, true);
@@ -540,7 +540,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
//
int transactionID = program.startTransaction("test");
MemoryBlock block =
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100);
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100, false);
block.setSourceName("this is a test");
block = memory.createUninitializedBlock(".Uninit", getAddr(0x3000), 0x200, false);
block.setSourceName("other source");
@@ -581,7 +581,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
//
int transactionID = program.startTransaction("test");
MemoryBlock block =
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100);
memory.createBitMappedBlock(".Bit", getAddr(0), getAddr(0x01001000), 0x100, false);
block.setSourceName("this is a test");
block = memory.createUninitializedBlock(".Uninit", getAddr(0x3000), 0x200, false);
block.setSourceName("other source");
@@ -215,6 +215,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT));
assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
@@ -292,6 +293,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.INIT));
assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
@@ -578,6 +580,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY));
assertEquals("Default", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.INIT));
assertEquals("", model.getValueAt(0, MemoryMapModel.SOURCE));
@@ -611,6 +614,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
final JCheckBox readCB = (JCheckBox) findComponentByName(d.getComponent(), "Read");
final JCheckBox writeCB = (JCheckBox) findComponentByName(d.getComponent(), "Write");
final JCheckBox executeCB = (JCheckBox) findComponentByName(d.getComponent(), "Execute");
final JCheckBox overlayCB = (JCheckBox) findComponentByName(d.getComponent(), "Overlay");
final JRadioButton initializedRB =
(JRadioButton) findComponentByName(d.getComponent(), "Initialized");
final RegisterField initialValue =
@@ -623,11 +627,16 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
final JButton okButton = findButton(d.getComponent(), "OK");
SwingUtilities.invokeAndWait(() -> {
comboBox.setSelectedItem(MemoryBlockType.OVERLAY);
comboBox.setSelectedItem(MemoryBlockType.DEFAULT);
overlayCB.setSelected(true);
overlayCB.getActionListeners()[0].actionPerformed(null);
nameField.setText(".test");
lengthField.setText("0x100");
commentField.setText("this is a block test");
initialValue.setText("0xa");
});
SwingUtilities.invokeAndWait(() -> {
pressButton(executeCB);
});
@@ -666,7 +675,9 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.WRITE));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.EXECUTE));
assertEquals(MemoryBlockType.OVERLAY.toString(),
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.OVERLAY));
assertEquals(
MemoryBlockType.DEFAULT.toString(),
model.getValueAt(row, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.INIT));
assertEquals("", model.getValueAt(row, MemoryMapModel.SOURCE));
@@ -693,6 +704,8 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
final JCheckBox readCB = (JCheckBox) findComponentByName(d.getComponent(), "Read");
final JCheckBox writeCB = (JCheckBox) findComponentByName(d.getComponent(), "Write");
final JCheckBox executeCB = (JCheckBox) findComponentByName(d.getComponent(), "Execute");
final JCheckBox overlayCB = (JCheckBox) findComponentByName(d.getComponent(), "Overlay");
final JRadioButton uninitRB =
(JRadioButton) findComponentByName(d.getComponent(), "Uninitialized");
final AddressInput addrField =
@@ -703,7 +716,9 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
final JButton okButton = findButton(d.getComponent(), "OK");
SwingUtilities.invokeAndWait(() -> {
comboBox.setSelectedItem(MemoryBlockType.OVERLAY);
comboBox.setSelectedItem(MemoryBlockType.DEFAULT);
overlayCB.setSelected(true);
overlayCB.getActionListeners()[0].actionPerformed(null);
nameField.setText(".test");
lengthField.setText("0x100");
commentField.setText("this is a block test");
@@ -743,7 +758,9 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.WRITE));
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.EXECUTE));
assertEquals(MemoryBlockType.OVERLAY.toString(),
assertEquals(Boolean.TRUE, model.getValueAt(row, MemoryMapModel.OVERLAY));
assertEquals(
MemoryBlockType.DEFAULT.toString(),
model.getValueAt(row, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.FALSE, model.getValueAt(row, MemoryMapModel.INIT));
assertEquals("", model.getValueAt(row, MemoryMapModel.SOURCE));
@@ -822,6 +839,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY));
assertEquals("Bit Mapped", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertNull(model.getValueAt(0, MemoryMapModel.INIT));
assertEquals("01001000", model.getValueAt(0, MemoryMapModel.SOURCE));
@@ -900,6 +918,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.READ));
assertEquals(Boolean.TRUE, model.getValueAt(0, MemoryMapModel.WRITE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.EXECUTE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.OVERLAY));
assertEquals("Byte Mapped", model.getValueAt(0, MemoryMapModel.BLOCK_TYPE));
assertEquals(Boolean.FALSE, model.getValueAt(0, MemoryMapModel.INIT));
assertEquals("01001000", model.getValueAt(0, MemoryMapModel.SOURCE));
@@ -15,8 +15,7 @@
*/
package ghidra.program.database.map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import org.junit.*;
@@ -62,7 +61,7 @@ public class AddressIndexPrimaryKeyIteratorTest extends AbstractGhidraHeadedInte
// Create fragmented memory
memMap.createInitializedBlock("Block1", addr(0x8000), 0x10, (byte) 0, null, false);// startKey: 0x0
memMap.createUninitializedBlock("Block2", addr(0x5000), 0x10, false);// startKey: 0x10000
memMap.createBitMappedBlock("Block3", addr(0x9000), addr(0x5000), 0x10);// startKey: 0x20000
memMap.createBitMappedBlock("Block3", addr(0x9000), addr(0x5000), 0x10, false);// startKey: 0x20000
memMap.createUninitializedBlock("Block4", addr(0x3000), 0x10, false);// startKey: 0x30000
// Create table with indexed address column
@@ -15,8 +15,7 @@
*/
package ghidra.program.database.map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import java.util.NoSuchElementException;
@@ -65,7 +64,7 @@ public class AddressKeyIteratorTest extends AbstractGhidraHeadedIntegrationTest
// Create fragmented memory
memMap.createInitializedBlock("Block1", addr(0x8000), 0x10, (byte) 0, null, false);// startKey: 0x0
memMap.createUninitializedBlock("Block2", addr(0x5000), 0x10, false);// startKey: 0x10000
memMap.createBitMappedBlock("Block3", addr(0x9000), addr(0x5000), 0x10);// startKey: 0x20000
memMap.createBitMappedBlock("Block3", addr(0x9000), addr(0x5000), 0x10, false);// startKey: 0x20000
memMap.createUninitializedBlock("Block4", addr(0x3000), 0x10, false);// startKey: 0x30000
// Create table keyed on address
@@ -15,8 +15,7 @@
*/
package ghidra.program.database.mem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.*;
import org.junit.*;
@@ -31,7 +30,7 @@ import ghidra.util.task.TaskMonitorAdapter;
/**
* Test for the BitMemoryBlock for the database implementation.
*/
public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
public class BitMappedMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
private AddressSpace byteSpace;
private AddressSpace bitSpace;
private MemoryBlock block;
@@ -43,7 +42,7 @@ public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
* Constructor for BitMemoryBlockTest.
* @param name
*/
public BitMemoryBlockTest() {
public BitMappedMemoryBlockTest() {
super();
}
@@ -74,11 +73,12 @@ public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testCreateNewBlock() throws Exception {
memory.createBitMappedBlock("BIT_BLOCK", bitSpace.getAddress(0), bitSpace.getAddress(0x20),
0x20);
0x20, false);
Address newStart = bitSpace.getAddress(0x40);
MemoryBlock newblock =
memory.createBitMappedBlock("BitTest", newStart, bitSpace.getAddress(0x20), 0x50);
memory.createBitMappedBlock("BitTest", newStart, bitSpace.getAddress(0x20), 0x50,
false);
assertNotNull(newblock);
assertEquals(newStart, newblock.getStart());
}
@@ -86,7 +86,7 @@ public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testNoUnderlyingMemory() throws Exception {
MemoryBlock bitBlock = memory.createBitMappedBlock("BIT_BLOCK", bitSpace.getAddress(0),
bitSpace.getAddress(0x20), 0x20);
bitSpace.getAddress(0x20), 0x20, false);
Address addr = bitSpace.getAddress(0x40);
MemoryBlock newblock = memory.createBlock(bitBlock, "BitTest", addr, 0x50);
@@ -101,7 +101,7 @@ public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testGetByte() throws Exception {
MemoryBlock bitBlock = memory.createBitMappedBlock("BIT_BLOCK", bitSpace.getAddress(0),
byteSpace.getAddress(0x20), 256);
byteSpace.getAddress(0x20), 256, false);
for (int i = 0; i < 256; i += 2) {
assertEquals(0, bitBlock.getByte(bitSpace.getAddress(i)));
@@ -113,7 +113,7 @@ public class BitMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testPutByte() throws Exception {
MemoryBlock bitBlock = memory.createBitMappedBlock("BIT_BLOCK", bitSpace.getAddress(0),
byteSpace.getAddress(0x20), 256);
byteSpace.getAddress(0x20), 256, false);
for (int i = 0; i < 256; i += 2) {
bitBlock.putByte(bitSpace.getAddress(i), (byte) 1);
bitBlock.putByte(bitSpace.getAddress(i + 1), (byte) 0);
@@ -0,0 +1,354 @@
/* ###
* 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.Arrays;
import org.junit.*;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitor;
public class ByteMappedMemoryBlockTest extends AbstractGhidraHeadedIntegrationTest {
private AddressSpace space;
private MemoryBlock block;
private Memory memory;
private Program program;
private int transactionID;
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY64_LE, this);
memory = program.getMemory();
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) i;
}
space = program.getAddressFactory().getDefaultAddressSpace();
transactionID = program.startTransaction("Test");
block = memory.createInitializedBlock("BYTE_BLOCK", space.getAddress(0),
bytes.length, (byte) 0, TaskMonitor.DUMMY, false);
memory.setBytes(block.getStart(), bytes);
}
@After
public void tearDown() {
program.endTransaction(transactionID, true);
program.release(this);
}
private Address addr(long offset) {
return space.getAddress(offset);
}
@Test
public void testCreateNewBlock1to1() throws Exception {
MemoryBlock byteMappedBlock =
memory.createByteMappedBlock("test", addr(0x1000), addr(0x80), 0x100, false);
assertEquals(0x100, byteMappedBlock.getSize());
assertEquals(addr(0x1000), byteMappedBlock.getStart());
assertEquals(addr(0x10FF), byteMappedBlock.getEnd());
AddressSet set = new AddressSet(addr(0), addr(0xFF));
set.add(addr(0x1000), addr(0x107F));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
MemoryBlockSourceInfo info = byteMappedBlock.getSourceInfos().get(0);
ByteMappingScheme scheme = info.getByteMappingScheme().get();
assertEquals(1, scheme.getMappedByteCount());
assertEquals(1, scheme.getMappedSourceByteCount());
assertEquals(addr(0x80), scheme.getMappedSourceAddress(addr(0), 0x80));
for (int i = 0; i < 0x80; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
assertEquals(0x80 + i, b & 0xff);
}
try {
byteMappedBlock.getByte(addr(0x1100));
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
// expected
}
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ~i;
}
MemoryBlock block2 = memory.createInitializedBlock("BYTE_BLOCK2", space.getAddress(0x100),
bytes.length,
(byte) 0, TaskMonitor.DUMMY, false);
set.add(addr(0x100), addr(0x1FF));
set.add(addr(0x1080), addr(0x10FF));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
assertEquals(0, byteMappedBlock.getByte(addr(0x1080)));
memory.setBytes(block2.getStart(), bytes);
for (int i = 0; i < 0x80; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
assertEquals(0x80 + i, b & 0xff);
}
for (int i = 0; i < 0x7F; i++) {
byte b = byteMappedBlock.getByte(addr(0x1080 + i));
assertEquals(~i & 0xff, b & 0xff);
}
byte[] data1 = new byte[] { 1, 2, 3 };
byteMappedBlock.putBytes(addr(0x1080), data1);
byte[] data2 = new byte[3];
assertEquals(3, byteMappedBlock.getBytes(addr(0x1080), data2));
assertTrue(Arrays.equals(data1, data2));
assertEquals(3, block2.getBytes(addr(0x100), data2));
assertTrue(Arrays.equals(data1, data2));
}
@Test
public void testCreateNewBlock1to2() throws Exception {
MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80),
0x100, new ByteMappingScheme(1, 2), false);
assertEquals(0x100, byteMappedBlock.getSize());
assertEquals(addr(0x1000), byteMappedBlock.getStart());
assertEquals(addr(0x10FF), byteMappedBlock.getEnd());
AddressSet set = new AddressSet(addr(0), addr(0xFF));
set.add(addr(0x1000), addr(0x103F));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
MemoryBlockSourceInfo info = byteMappedBlock.getSourceInfos().get(0);
ByteMappingScheme scheme = info.getByteMappingScheme().get();
assertEquals(1, scheme.getMappedByteCount());
assertEquals(2, scheme.getMappedSourceByteCount());
assertEquals(addr(0x100), scheme.getMappedSourceAddress(addr(0), 0x80));
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
assertEquals(0x80 + (2 * i), b & 0xff);
}
try {
byteMappedBlock.getByte(addr(0x1100));
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
// expected
}
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ~i;
}
MemoryBlock block2 = memory.createInitializedBlock("BYTE_BLOCK2", space.getAddress(0x100),
bytes.length, (byte) 0, TaskMonitor.DUMMY, false);
set.add(addr(0x100), addr(0x1FF));
set.add(addr(0x1040), addr(0x10BF));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
assertEquals(0, byteMappedBlock.getByte(addr(0x1080)));
memory.setBytes(block2.getStart(), bytes);
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
assertEquals(0x80 + (2 * i), b & 0xff);
}
for (int i = 0; i < 0x7F; i++) {
byte b = byteMappedBlock.getByte(addr(0x1040 + i));
assertEquals(~(2 * i) & 0xff, b & 0xff);
}
byte[] data1 = new byte[] { 1, 2, 3, 4 };
byteMappedBlock.putBytes(addr(0x1040), data1);
byte[] data2 = new byte[4];
assertEquals(4, byteMappedBlock.getBytes(addr(0x1040), data2));
assertTrue(Arrays.equals(data1, data2));
assertEquals(4, block2.getBytes(addr(0x100), data2));
assertTrue(Arrays.equals(new byte[] { 1, -2, 2, -4 }, data2));
}
@Test
public void testCreateNewBlock2to4() throws Exception {
MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80),
0x100, new ByteMappingScheme(2, 4), false);
assertEquals(0x100, byteMappedBlock.getSize());
assertEquals(addr(0x1000), byteMappedBlock.getStart());
assertEquals(addr(0x10FF), byteMappedBlock.getEnd());
AddressSet set = new AddressSet(addr(0), addr(0xFF));
set.add(addr(0x1000), addr(0x103E));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
MemoryBlockSourceInfo info = byteMappedBlock.getSourceInfos().get(0);
ByteMappingScheme scheme = info.getByteMappingScheme().get();
assertEquals(2, scheme.getMappedByteCount());
assertEquals(4, scheme.getMappedSourceByteCount());
assertEquals(addr(0x100), scheme.getMappedSourceAddress(addr(0), 0x80));
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
int val = 0x80 + (4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
try {
byteMappedBlock.getByte(addr(0x1100));
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
// expected
}
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ~i;
}
MemoryBlock block2 = memory.createInitializedBlock("BYTE_BLOCK2", space.getAddress(0x100),
bytes.length, (byte) 0, TaskMonitor.DUMMY, false);
set.add(addr(0x100), addr(0x1FF));
set.add(addr(0x103F), addr(0x10BE));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
assertEquals(0, byteMappedBlock.getByte(addr(0x1080)));
memory.setBytes(block2.getStart(), bytes);
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(addr(0x1000 + i));
int val = 0x80 + (4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
for (int i = 0; i < 0x7F; i++) {
byte b = byteMappedBlock.getByte(addr(0x1040 + i));
int val = ~(4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
byte[] data1 = new byte[] { 1, 2, 3, 4 };
byteMappedBlock.putBytes(addr(0x1040), data1);
byte[] data2 = new byte[4];
assertEquals(4, byteMappedBlock.getBytes(addr(0x1040), data2));
assertTrue(Arrays.equals(data1, data2));
assertEquals(4, block2.getBytes(addr(0x100), data2));
assertTrue(Arrays.equals(new byte[] { 1, 2, -3, -4 }, data2));
}
@Test
public void testCreateNewBlock2to4Overlay() throws Exception {
MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80),
0x100, new ByteMappingScheme(2, 4), true);
assertTrue(byteMappedBlock.isOverlay());
AddressSpace testSpace = program.getAddressFactory().getAddressSpace("test");
assertNotNull(testSpace);
assertEquals(space, testSpace.getPhysicalSpace());
assertEquals(testSpace.getAddress(0x1000), testSpace.getMinAddress());
assertEquals(testSpace.getAddress(0x10FF), testSpace.getMaxAddress());
assertEquals(0x100, byteMappedBlock.getSize());
assertEquals(testSpace.getAddress(0x1000), byteMappedBlock.getStart());
assertEquals(testSpace.getAddress(0x10FF), byteMappedBlock.getEnd());
AddressSet set = new AddressSet(addr(0), addr(0xFF));
set.add(testSpace.getAddress(0x1000), testSpace.getAddress(0x103E));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
MemoryBlockSourceInfo info = byteMappedBlock.getSourceInfos().get(0);
ByteMappingScheme scheme = info.getByteMappingScheme().get();
assertEquals(2, scheme.getMappedByteCount());
assertEquals(4, scheme.getMappedSourceByteCount());
assertEquals(addr(0x100), scheme.getMappedSourceAddress(addr(0), 0x80));
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(testSpace.getAddress(0x1000 + i));
int val = 0x80 + (4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
try {
byteMappedBlock.getByte(testSpace.getAddress(0x1100));
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
// expected
}
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) ~i;
}
MemoryBlock block2 = memory.createInitializedBlock("BYTE_BLOCK2", space.getAddress(0x100),
bytes.length, (byte) 0, TaskMonitor.DUMMY, false);
set.add(addr(0x100), addr(0x1FF));
set.add(testSpace.getAddress(0x103F), testSpace.getAddress(0x10BE));
assertEquals(set, memory.getAllInitializedAddressSet());
assertEquals(set, memory.getLoadedAndInitializedAddressSet());
assertEquals(0, byteMappedBlock.getByte(testSpace.getAddress(0x1080)));
memory.setBytes(block2.getStart(), bytes);
for (int i = 0; i < 0x40; i++) {
byte b = byteMappedBlock.getByte(testSpace.getAddress(0x1000 + i));
int val = 0x80 + (4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
for (int i = 0; i < 0x7F; i++) {
byte b = byteMappedBlock.getByte(testSpace.getAddress(0x1040 + i));
int val = ~(4 * (i / 2) + (i % 2));
assertEquals(val & 0xff, b & 0xff);
}
byte[] data1 = new byte[] { 1, 2, 3, 4 };
byteMappedBlock.putBytes(testSpace.getAddress(0x1040), data1);
byte[] data2 = new byte[4];
assertEquals(4, byteMappedBlock.getBytes(testSpace.getAddress(0x1040), data2));
assertTrue(Arrays.equals(data1, data2));
assertEquals(4, block2.getBytes(addr(0x100), data2));
assertTrue(Arrays.equals(new byte[] { 1, 2, -3, -4 }, data2));
}
}
@@ -15,51 +15,20 @@
*/
package ghidra.program.database.mem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import java.util.Iterator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.*;
import ghidra.app.plugin.core.memory.UninitializedBlockCmd;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.ByteDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.ProgramFragment;
import ghidra.program.model.listing.ProgramModule;
import ghidra.program.model.mem.LiveMemoryHandler;
import ghidra.program.model.mem.LiveMemoryListener;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockException;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import ghidra.program.model.mem.MemoryBlockStub;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.program.model.mem.MemoryConflictException;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceManager;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.ToyProgramBuilder;
import ghidra.util.task.TaskMonitor;
@@ -255,7 +224,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
public void testCreateBitBlock() throws Exception {
createBlock("Test", addr(0), 100);
createBlock("Test", addr(500), 100);
MemoryBlock bitBlock = mem.createBitMappedBlock("BitBlock", addr(600), addr(30), 20);
MemoryBlock bitBlock = mem.createBitMappedBlock("BitBlock", addr(600), addr(30), 20, false);
MemoryBlock block = mem.getBlock(addr(610));
assertNotNull(block);
assertEquals(bitBlock, block);
@@ -321,7 +290,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
MemoryBlock block2 = createBlock("Test2", addr(500), 100);
MemoryBlock block3 = mem.createUninitializedBlock("Test3", addr(1500), 200, false);
MemoryBlock block4 = mem.createUninitializedBlock("Test4", addr(2500), 100, false);
mem.createBitMappedBlock("BitBlock", addr(3000), addr(550), 2000);
mem.createBitMappedBlock("BitBlock", addr(3000), addr(550), 2000, false);
MemoryBlock[] blocks = mem.getBlocks();
assertEquals(5, blocks.length);
@@ -487,7 +456,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
MemoryBlock block2 = createBlock("Test2", addr(500), 100);
MemoryBlock block3 = mem.createUninitializedBlock("Test3", addr(1500), 200, false);
mem.createUninitializedBlock("Test4", addr(2500), 100, false);
MemoryBlock block5 = mem.createBitMappedBlock("BitBlock", addr(3000), addr(550), 20);
MemoryBlock block5 = mem.createBitMappedBlock("BitBlock", addr(3000), addr(550), 20, false);
block1.setComment("Hello!");
block2.setName("NewTest2");
block3.setWrite(false);
@@ -670,7 +639,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
public void testMoveBitBlock() throws Exception {
createBlock("Test", addr(0), 100);
MemoryBlock bitBlock = mem.createBitMappedBlock("BitBlock", addr(200), addr(50), 20);
MemoryBlock bitBlock = mem.createBitMappedBlock("BitBlock", addr(200), addr(50), 20, false);
assertEquals(0, bitBlock.getByte(addr(200)));
bitBlock.putByte(addr(200), (byte) 5);
assertEquals(1, bitBlock.getByte(addr(200)));
@@ -1034,13 +1003,15 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
public void testCreateOverlayBlock() throws Exception {
MemoryBlock block = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
TaskMonitor.DUMMY, true);
assertEquals(MemoryBlockType.OVERLAY, block.getType());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertTrue(block.isOverlay());
}
@Test
public void testCreateBitMappedBlock() throws Exception {
mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa, TaskMonitor.DUMMY, false);
MemoryBlock bitBlock = mem.createBitMappedBlock("bit", addr(0x2000), addr(0xf00), 0x1000);
MemoryBlock bitBlock =
mem.createBitMappedBlock("bit", addr(0x2000), addr(0xf00), 0x1000, false);
assertEquals(MemoryBlockType.BIT_MAPPED, bitBlock.getType());
@@ -1056,7 +1027,8 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testCreateByteMappedBlock() throws Exception {
mem.createInitializedBlock("mem", addr(0), 0x1000, (byte) 0xa, TaskMonitor.DUMMY, false);
MemoryBlock byteBlock = mem.createByteMappedBlock("byte", addr(0x2000), addr(0xf00), 0x200);
MemoryBlock byteBlock =
mem.createByteMappedBlock("byte", addr(0x2000), addr(0xf00), 0x200, false);
assertEquals(MemoryBlockType.BYTE_MAPPED, byteBlock.getType());
@@ -1066,14 +1038,14 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
expectedInitializedSet.add(addr(0), addr(0xfff));
expectedInitializedSet.add(addr(0x2000), addr(0x20ff));
assertEquals(expectedInitializedSet, mem.getAllInitializedAddressSet());
}
@Test
public void testCreateRemoveCreateOverlayBlock() throws Exception {
MemoryBlock block = mem.createInitializedBlock(".overlay", addr(0), 0x1000, (byte) 0xa,
TaskMonitor.DUMMY, true);
assertEquals(MemoryBlockType.OVERLAY, block.getType());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertTrue(block.isOverlay());
mem.removeBlock(block, TaskMonitor.DUMMY);
block =
mem.createInitializedBlock("ov2", addr(0), 0x2000, (byte) 0xa, TaskMonitor.DUMMY, true);
@@ -0,0 +1,193 @@
/* ###
* 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.*;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitor;
public class MemoryWriteCheckTest extends AbstractGhidraHeadedIntegrationTest {
private AddressSpace space;
private MemoryBlock block;
private Memory memory;
private Program program;
private int transactionID;
@Before
public void setUp() throws Exception {
program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY64_LE, this);
memory = program.getMemory();
byte[] bytes = new byte[0x100];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) i;
}
space = program.getAddressFactory().getDefaultAddressSpace();
transactionID = program.startTransaction("Test");
block = memory.createInitializedBlock("BYTE_BLOCK", space.getAddress(0), bytes.length,
(byte) 0, TaskMonitor.DUMMY, false);
memory.setBytes(block.getStart(), bytes);
}
@After
public void tearDown() {
program.endTransaction(transactionID, true);
program.release(this);
}
private Address addr(long offset) {
return space.getAddress(offset);
}
@Test
public void testByteMappedMemoryCheck() throws Exception {
AddressSet set = new AddressSet(addr(0), addr(0xd7));
DisassembleCommand cmd = new DisassembleCommand(set, set);
cmd.applyTo(program); // range 0x0000 to 0x00d7 disassembled
MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80),
0x100, new ByteMappingScheme(2, 4), true);
AddressSpace testSpace = program.getAddressFactory().getAddressSpace("test");
try {
byteMappedBlock.putByte(testSpace.getAddress(0x1000), (byte) 1);
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at 00000080", e.getMessage());
}
try {
byteMappedBlock.putBytes(testSpace.getAddress(0x1002), new byte[] { 1, 2 });
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at 00000084", e.getMessage());
}
program.getListing().clearCodeUnits(addr(0), addr(0xd7), true);
byteMappedBlock.putByte(testSpace.getAddress(0x1000), (byte) 1);
assertEquals(1, byteMappedBlock.getByte(testSpace.getAddress(0x1000)));
byteMappedBlock.putBytes(testSpace.getAddress(0x1002), new byte[] { 1, 2 });
byte[] data = new byte[2];
assertEquals(2, byteMappedBlock.getBytes(testSpace.getAddress(0x1002), data));
assertArrayEquals(new byte[] { 1, 2 }, data);
}
@Test
public void testByteMappedMemoryCheck1() throws Exception {
// NOTE: disassembling in a 2:4 byte-mapped block is rather inappropriate and may be disallowed in the future
MemoryBlock byteMappedBlock = memory.createByteMappedBlock("test", addr(0x1000), addr(0x80),
0x100, new ByteMappingScheme(2, 4), true);
AddressSpace testSpace = program.getAddressFactory().getAddressSpace("test");
AddressSet set = new AddressSet(testSpace.getAddress(0x1000), testSpace.getAddress(0x1011));
DisassembleCommand cmd = new DisassembleCommand(set, set);
cmd.applyTo(program); // range test:0x1000 to test::0x1011 disassembled
try {
block.putByte(addr(0x80), (byte) 1);
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at test::00001000",
e.getMessage());
}
// small modification within filler byte region for mapped block allowed
block.putBytes(addr(0x82), new byte[] { 1, 2 });
byte[] data = new byte[2];
assertEquals(2, block.getBytes(addr(0x82), data));
assertArrayEquals(new byte[] { 1, 2 }, data);
try {
block.putBytes(addr(0x84), new byte[] { 1, 2 });
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at test::00001002",
e.getMessage());
}
program.getListing().clearCodeUnits(set.getMinAddress(), set.getMaxAddress(), true);
block.putByte(addr(0x80), (byte) 1);
assertEquals(1, byteMappedBlock.getByte(testSpace.getAddress(0x1000)));
block.putBytes(addr(0x84), new byte[] { 1, 2 });
assertEquals(2, byteMappedBlock.getBytes(testSpace.getAddress(0x1002), data));
assertArrayEquals(new byte[] { 1, 2 }, data);
}
@Test
public void testBitMappedMemoryCheck() throws Exception {
AddressSet set = new AddressSet(addr(0), addr(0xd7));
DisassembleCommand cmd = new DisassembleCommand(set, set);
cmd.applyTo(program); // range 0x0000 to 0x00d7 disassembled
MemoryBlock bitMappedBlock =
memory.createBitMappedBlock("test", addr(0x1000), addr(0x80), 0x100, true);
AddressSpace testSpace = program.getAddressFactory().getAddressSpace("test");
try {
bitMappedBlock.putByte(testSpace.getAddress(0x1000), (byte) 1);
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at 00000080", e.getMessage());
}
try {
bitMappedBlock.putBytes(testSpace.getAddress(0x1010), new byte[] { 1, 0, 1, 0 });
fail("expected MemoryAccessException");
}
catch (MemoryAccessException e) {
assertEquals("Memory change conflicts with instruction at 00000082", e.getMessage());
}
program.getListing().clearCodeUnits(addr(0), addr(0xd7), true);
bitMappedBlock.putByte(testSpace.getAddress(0x1000), (byte) 1);
assertEquals(1, bitMappedBlock.getByte(testSpace.getAddress(0x1000)));
bitMappedBlock.putBytes(testSpace.getAddress(0x1010), new byte[] { 1, 0, 1, 0 });
byte[] data = new byte[4];
assertEquals(4, bitMappedBlock.getBytes(testSpace.getAddress(0x1010), data));
assertArrayEquals(new byte[] { 1, 0, 1, 0 }, data);
}
}
@@ -22,7 +22,8 @@ import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.framework.cmd.Command;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
import ghidra.program.database.mem.ByteMappingScheme;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.util.exception.RollbackException;
@@ -47,7 +48,7 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
notepad = notepadBuilder.getProgram();
ProgramBuilder x08Builder = new ProgramBuilder("x08", ProgramBuilder._8051);
x08Builder.createMemory("test1", "0x0", 1);
x08Builder.createMemory("test1", "0x0", 400);
x08 = x08Builder.getProgram();
}
@@ -59,6 +60,8 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
assertTrue(applyCmd(notepad, command));
MemoryBlock block = notepad.getMemory().getBlock(getNotepadAddr(0x100));
assertNotNull(block);
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertFalse(block.isOverlay());
byte b = block.getByte(getNotepadAddr(0x100));
assertEquals((byte) 0xa, b);
@@ -103,29 +106,139 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
public void testAddBitBlock() {
Address addr = getX08Addr(0x3000);
command = new AddBitMappedMemoryBlockCmd(".testBit", "A Test", "new block", addr, 100, true,
true, true, false, getX08Addr(0));
true, true, false, getX08Addr(0), false);
assertTrue(applyCmd(x08, command));
// map 100 byte block from source of 12-bytes (96 bits) + partial byte (4 bits)
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
assertEquals(100, block.getSize());
assertEquals(getX08Addr(0x3000), block.getStart());
assertEquals(getX08Addr(0x3063), block.getEnd());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
AddressRange mappedRange = info.getMappedRange().get();
assertEquals(13, mappedRange.getLength());
assertEquals(getX08Addr(0), mappedRange.getMinAddress());
assertEquals(getX08Addr(12), mappedRange.getMaxAddress());
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
assertFalse(block.isOverlay());
}
@Test
public void testAddBitOverlayBlock() {
Address addr = getX08Addr(0x3000);
command = new AddBitMappedMemoryBlockCmd(".testBit", "A Test", "new block", addr, 100, true,
true, true, false, getX08Addr(0), true);
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNull(block);
block = x08.getMemory().getBlock(".testBit");
assertNotNull(block);
assertEquals(100, block.getSize());
AddressSpace space = x08.getAddressFactory().getAddressSpace(".testBit");
assertNotNull(space);
assertTrue(space.isOverlaySpace());
assertEquals(space.getAddress(0x3000), block.getStart());
assertEquals(space.getAddress(0x3063), block.getEnd());
assertEquals(block.getStart(), space.getMinAddress());
assertEquals(block.getEnd(), space.getMaxAddress());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
AddressRange mappedRange = info.getMappedRange().get();
assertEquals(13, mappedRange.getLength());
assertEquals(getX08Addr(0), mappedRange.getMinAddress());
assertEquals(getX08Addr(12), mappedRange.getMaxAddress());
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
assertTrue(block.isOverlay());
}
@Test
public void testAddByteBlock() {
Address addr = getX08Addr(0x3000);
command = new AddByteMappedMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100,
true, true, true, false, getX08Addr(0));
true, true, true, false, getX08Addr(0), false);
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
assertEquals(100, block.getSize());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(getX08Addr(99), info.getMappedRange().get().getMaxAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
assertFalse(block.isOverlay());
}
@Test
public void testAddByteBlockWithScheme() {
Address addr = getX08Addr(0x3000);
command = new AddByteMappedMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100,
true, true, true, false, getX08Addr(0), new ByteMappingScheme(2, 4), false);
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
assertEquals(100, block.getSize());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(getX08Addr(197), info.getMappedRange().get().getMaxAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
assertFalse(block.isOverlay());
}
@Test
public void testAddByteOverlayBlock() {
Address addr = getX08Addr(0x3000);
command = new AddByteMappedMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100,
true, true, true, false, getX08Addr(0), true);
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNull(block);
block = x08.getMemory().getBlock(".testByte");
assertNotNull(block);
assertEquals(100, block.getSize());
AddressSpace space = x08.getAddressFactory().getAddressSpace(".testByte");
assertNotNull(space);
assertTrue(space.isOverlaySpace());
assertEquals(space.getAddress(0x3000), block.getStart());
assertEquals(space.getAddress(0x3063), block.getEnd());
assertEquals(block.getStart(), space.getMinAddress());
assertEquals(block.getEnd(), space.getMaxAddress());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
AddressRange mappedRange = info.getMappedRange().get();
assertEquals(100, mappedRange.getLength());
assertEquals(getX08Addr(0), mappedRange.getMinAddress());
assertEquals(getX08Addr(99), mappedRange.getMaxAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
assertTrue(block.isOverlay());
}
@Test
public void testAddByteOverlayBlockWithScheme() {
Address addr = getX08Addr(0x3000);
command = new AddByteMappedMemoryBlockCmd(".testByte", "A Test", "new block", addr, 100,
true, true, true, false, getX08Addr(0), new ByteMappingScheme(2, 4), true);
assertTrue(applyCmd(x08, command));
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNull(block);
block = x08.getMemory().getBlock(".testByte");
assertNotNull(block);
assertEquals(100, block.getSize());
AddressSpace space = x08.getAddressFactory().getAddressSpace(".testByte");
assertNotNull(space);
assertTrue(space.isOverlaySpace());
assertEquals(space.getAddress(0x3000), block.getStart());
assertEquals(space.getAddress(0x3063), block.getEnd());
assertEquals(block.getStart(), space.getMinAddress());
assertEquals(block.getEnd(), space.getMaxAddress());
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
AddressRange mappedRange = info.getMappedRange().get();
assertEquals(198, mappedRange.getLength());
assertEquals(getX08Addr(0), mappedRange.getMinAddress());
assertEquals(getX08Addr(197), mappedRange.getMaxAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
assertTrue(block.isOverlay());
}
@Test
@@ -144,7 +257,8 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
}
}
assertNotNull(block);
assertEquals(MemoryBlockType.OVERLAY, block.getType());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertTrue(block.isOverlay());
byte b = block.getByte(block.getStart().getNewAddress(0x3000));
assertEquals((byte) 0xa, b);
}
@@ -20,8 +20,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.database.mem.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@@ -103,13 +102,15 @@ class MyTestMemory extends AddressSet implements Memory {
@Override
public MemoryBlock createBitMappedBlock(String name, Address start, Address overlayAddress,
long length) throws MemoryConflictException, AddressOverflowException {
long length, boolean overlay) throws MemoryConflictException, AddressOverflowException {
throw new UnsupportedOperationException();
}
@Override
public MemoryBlock createByteMappedBlock(String name, Address start, Address overlayAddress,
long length) throws MemoryConflictException, AddressOverflowException {
public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress,
long length, ByteMappingScheme byteMappingScheme, boolean overlay)
throws LockException,
MemoryConflictException, AddressOverflowException, IllegalArgumentException {
throw new UnsupportedOperationException();
}
@@ -170,6 +170,11 @@ class MyTestMemoryBlock implements MemoryBlock {
return MemoryBlockType.DEFAULT;
}
@Override
public boolean isOverlay() {
return false;
}
@Override
public int compareTo(MemoryBlock block) {
throw new UnsupportedOperationException();
@@ -19,6 +19,7 @@ import java.math.BigInteger;
import ghidra.app.plugin.core.format.ByteBlock;
import ghidra.app.plugin.core.format.ByteBlockAccessException;
import ghidra.program.database.mem.ByteMappingScheme;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
@@ -139,9 +140,7 @@ public class MemoryByteBlock implements ByteBlock {
@Override
public boolean hasValue(BigInteger index) {
Address addr = getAddress(index);
MemoryBlock memBlock = memory.getBlock(addr);
return (memBlock != null) && memBlock.isInitialized();
return memory.getAllInitializedAddressSet().contains(addr);
}
/**
@@ -260,6 +259,23 @@ public class MemoryByteBlock implements ByteBlock {
return (int) (start.getOffset() % radix);
}
private Address getMappedAddress(Address addr) {
MemoryBlock memBlock = memory.getBlock(addr);
if (memBlock != null && memBlock.getType() == MemoryBlockType.BYTE_MAPPED) {
try {
MemoryBlockSourceInfo info = memBlock.getSourceInfos().get(0);
AddressRange mappedRange = info.getMappedRange().get();
ByteMappingScheme byteMappingScheme = info.getByteMappingScheme().get();
addr = byteMappingScheme.getMappedSourceAddress(mappedRange.getMinAddress(),
addr.subtract(memBlock.getStart()));
}
catch (AddressOverflowException e) {
// ignore
}
}
return addr;
}
/**
* Get the address based on the index.
*/
@@ -268,7 +284,6 @@ public class MemoryByteBlock implements ByteBlock {
mAddr = start;
mAddr = mAddr.addNoWrap(index);
return mAddr;
}
catch (AddressOverflowException e) {
throw new IndexOutOfBoundsException("Index " + index + " is not in this block");
@@ -20,8 +20,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.database.mem.*;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@@ -48,7 +47,8 @@ public class MemoryTestDummy extends AddressSet implements Memory {
@Override
public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress,
long length) throws LockException, MemoryConflictException, AddressOverflowException {
long length, boolean overlay)
throws LockException, MemoryConflictException, AddressOverflowException {
return null;
}
@@ -60,7 +60,9 @@ public class MemoryTestDummy extends AddressSet implements Memory {
@Override
public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress,
long length) throws LockException, MemoryConflictException, AddressOverflowException {
long length, ByteMappingScheme byteMappingScheme, boolean overlay)
throws LockException, MemoryConflictException, AddressOverflowException,
IllegalArgumentException {
return null;
}
@@ -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.
@@ -23,7 +22,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.XmlProgramUtilities;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitorAdapter;
import ghidra.util.task.TaskMonitor;
import ghidra.util.xml.XmlAttributeException;
import ghidra.util.xml.XmlUtilities;
import ghidra.xml.XmlElement;
@@ -92,13 +91,13 @@ public class MemoryBlockDefinition {
if (bitMappedAddress != null) {
Address mappedAddr =
XmlProgramUtilities.parseAddress(program.getAddressFactory(), bitMappedAddress);
block = mem.createBitMappedBlock(blockName, addr, mappedAddr, length);
block = mem.createBitMappedBlock(blockName, addr, mappedAddr, length, false);
}
else if (initialized) {
try {
block =
mem.createInitializedBlock(blockName, addr, length, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, false);
TaskMonitor.DUMMY, false);
}
catch (CancelledException e) {
throw new AssertException(e); // unexpected
@@ -1,49 +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 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);
}
}
@@ -16,12 +16,12 @@
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.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlockType;
/**
* Class for handling bit mapped memory sub blocks
@@ -36,7 +36,7 @@ class BitMappedSubMemoryBlock extends SubMemoryBlock {
this.memMap = adapter.getMemoryMap();
AddressMapDB addressMap = memMap.getAddressMap();
mappedAddress = addressMap.decodeAddress(
record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
record.getLongValue(MemoryMapDBAdapter.SUB_LONG_DATA2_COL), false);
}
@Override
@@ -62,7 +62,7 @@ class BitMappedSubMemoryBlock extends SubMemoryBlock {
}
}
public AddressRange getMappedRange() {
AddressRange getMappedRange() {
Address endMappedAddress = mappedAddress.add((subBlockLength - 1) / 8);
return new AddressRangeImpl(mappedAddress, endMappedAddress);
}
@@ -182,55 +182,4 @@ class BitMappedSubMemoryBlock extends SubMemoryBlock {
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;
}
}
@@ -19,8 +19,7 @@ import java.io.IOException;
import db.DBBuffer;
import db.Record;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
import ghidra.program.model.mem.Memory;
/**
* Implementation of SubMemoryBlock for blocks that store bytes in their own private database
@@ -31,7 +30,7 @@ class BufferSubMemoryBlock extends SubMemoryBlock {
BufferSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException {
super(adapter, record);
int bufferID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
int bufferID = record.getIntValue(MemoryMapDBAdapter.SUB_INT_DATA1_COL);
buf = adapter.getBuffer(bufferID);
}
@@ -95,11 +94,6 @@ class BufferSubMemoryBlock extends SubMemoryBlock {
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
@@ -121,14 +115,4 @@ class BufferSubMemoryBlock extends SubMemoryBlock {
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 - subBlockOffset);
return new ByteSourceRangeList(bsRange);
}
}
@@ -16,12 +16,12 @@
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.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlockType;
/**
* Class for handling byte mapped memory sub blocks
@@ -30,14 +30,23 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
private final MemoryMapDB memMap;
private final Address mappedAddress;
private final ByteMappingScheme byteMappingScheme;
private boolean ioPending;
ByteMappedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
super(adapter, record);
this.memMap = adapter.getMemoryMap();
AddressMapDB addressMap = memMap.getAddressMap();
// TODO: ensure that mappedAddress is aligned with addressMask (trailing 0's of mask should be 0 in mappedAddress)
mappedAddress = addressMap.decodeAddress(
record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
record.getLongValue(MemoryMapDBAdapter.SUB_LONG_DATA2_COL), false);
int encodedMappingScheme = record.getIntValue(MemoryMapDBAdapter.SUB_INT_DATA1_COL);
byteMappingScheme = new ByteMappingScheme(encodedMappingScheme);
}
ByteMappingScheme getByteMappingScheme() {
return byteMappingScheme;
}
@Override
@@ -53,7 +62,9 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
}
try {
ioPending = true;
return memMap.getByte(mappedAddress.addNoWrap(offsetInSubBlock));
Address sourceAddr =
byteMappingScheme.getMappedSourceAddress(mappedAddress, offsetInSubBlock);
return memMap.getByte(sourceAddr);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
@@ -68,13 +79,14 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
throws MemoryAccessException, IOException {
long offsetInSubBlock = offsetInMemBlock - subBlockOffset;
long available = subBlockLength - offsetInSubBlock;
// TODO: should array length be considered?
len = (int) Math.min(len, available);
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
try {
ioPending = true;
return memMap.getBytes(mappedAddress.addNoWrap(offsetInSubBlock), b, off, len);
return byteMappingScheme.getBytes(memMap, mappedAddress, offsetInSubBlock, b, off, len);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
@@ -92,7 +104,9 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
memMap.setByte(mappedAddress.addNoWrap(offsetInSubBlock), b);
Address sourceAddr =
byteMappingScheme.getMappedSourceAddress(mappedAddress, offsetInSubBlock);
memMap.setByte(sourceAddr, b);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
@@ -114,8 +128,7 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
memMap.setBytes(mappedAddress.addNoWrap(offsetInSubBlock), b, off,
len);
byteMappingScheme.setBytes(memMap, mappedAddress, offsetInSubBlock, b, off, len);
return len;
}
catch (AddressOverflowException e) {
@@ -126,8 +139,16 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
}
}
public AddressRange getMappedRange() {
Address endMappedAddress = mappedAddress.add(subBlockLength - 1);
AddressRange getMappedRange() {
Address endMappedAddress;
try {
endMappedAddress =
byteMappingScheme.getMappedSourceAddress(mappedAddress, subBlockLength - 1);
}
catch (AddressOverflowException e) {
// keep things happy
endMappedAddress = mappedAddress.getAddressSpace().getMaxAddress();
}
return new AddressRangeImpl(mappedAddress, endMappedAddress);
}
@@ -148,6 +169,17 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
@Override
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
// NOTE - GUI does not support any split of any byte-mapped blocks although API does.
// Not sure we really need to support it for byte-mapped block.
if (!byteMappingScheme.isOneToOneMapping()) {
// byte-mapping scheme alignment restrictions would apply to split
// boundary if we were to support
throw new UnsupportedOperationException(
"split not supported for byte-mapped block with " + byteMappingScheme);
}
// convert from offset in block to offset in this sub block
int offset = (int) (memBlockOffset - subBlockOffset);
long newLength = subBlockLength - offset;
@@ -167,45 +199,7 @@ class ByteMappedSubMemoryBlock extends SubMemoryBlock {
@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 - subBlockOffset;
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;
return "Byte Mapped: " + mappedAddress + ", " + byteMappingScheme;
}
}
@@ -0,0 +1,329 @@
/* ###
* 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.address.AddressOverflowException;
import ghidra.program.model.mem.*;
/**
* <code>ByteMappingScheme</code> facilitate byte mapping/decimation scheme for a mapped sub-block to
* an underlying source memory region.
*/
public class ByteMappingScheme {
// Repeating byte mapping pattern defined by number of source bytes mapped (mappedByteCount) followed
// by number of non-mapped source bytes (nonMappedByteCount). The sum of these two values is
// mappedSourceByteCount. The first byte of this block must correspond to the first mapped
// byte of this mapping sequence.
private final int mappedByteCount;
private final int nonMappedByteCount;
private final int mappedSourceByteCount;
/**
* Construct byte mapping scheme from an encoded mappingScheme value.
* @param encodedMappingScheme encoded mapping scheme value or 0 for a 1:1 default mapping.
* A zero value is accepted to ensure backward compatibility with pre-existing byte-mapped blocks
* where a 1:1 mapping was employed.
* @throws IllegalArgumentException if packed mapping scheme produces an invalid mapping ratio
*/
ByteMappingScheme(int encodedMappingScheme) throws IllegalArgumentException {
if (encodedMappingScheme == 0) {
// default mode implies 1:1 mapping
mappedByteCount = 1;
mappedSourceByteCount = 1;
nonMappedByteCount = 0;
}
else {
mappedByteCount = getMappedByteCount(encodedMappingScheme);
mappedSourceByteCount = getMappedSourceByteCount(encodedMappingScheme);
nonMappedByteCount = mappedSourceByteCount - mappedByteCount;
validateMappingScheme(mappedByteCount, mappedSourceByteCount);
}
}
/**
* Construct byte mapping scheme specified as a ratio of mapped bytes to source bytes.
* @param mappedByteCount number of mapped bytes per mappedSourcebyteCount (1..127). This
* value must be less-than or equal to schemeSrcByteCount.
* @param mappedSourceByteCount number of source bytes for mapping ratio (1..127)
* @throws IllegalArgumentException if invalid mapping scheme specified
*/
public ByteMappingScheme(int mappedByteCount, int mappedSourceByteCount) {
validateMappingScheme(mappedByteCount, mappedSourceByteCount);
this.mappedByteCount = mappedByteCount;
this.mappedSourceByteCount = mappedSourceByteCount;
this.nonMappedByteCount = mappedSourceByteCount - mappedByteCount;
}
@Override
public String toString() {
String ratioStr = "1:1";
if (!isOneToOneMapping()) {
ratioStr = mappedByteCount + ":" + mappedSourceByteCount;
}
return ratioStr + " mapping";
}
/**
* Get byte mapping scheme as single 14-bit packed value for storage and reconstruction use.
* @return mapping scheme as single 14-bit integer value
*/
int getEncodedMappingScheme() {
if (isOneToOneMapping()) {
// for legacy reasons continue to use 0 to indicate 1:1 default mapping
return 0;
}
return getEncodedMappingScheme(mappedByteCount, mappedSourceByteCount);
}
/**
* Determine this scheme corresponds to a 1:1 byte mapping
* @return true if 1:1 mapping else false
*/
public boolean isOneToOneMapping() {
return mappedSourceByteCount <= 1;
}
/**
* Get the mapped-byte-count (left-hand value in mapping ratio)
* @return mapped-byte-count
*/
public int getMappedByteCount() {
if (isOneToOneMapping()) {
return 1;
}
return mappedByteCount;
}
/**
* Get the mapped-source-byte-count (right-hand value in mapping ratio)
* @return mapped-source-byte-count
*/
public int getMappedSourceByteCount() {
if (isOneToOneMapping()) {
return 1;
}
return mappedSourceByteCount;
}
/**
* Calculate the mapped source address for a specified offset with the mapped sub-block.
* @param mappedSourceBaseAddress mapped source base address for sub-block
* @param offsetInSubBlock byte offset within sub-block to be mapped into source
* @return mapped source address
* @throws AddressOverflowException if offset in sub-block produces a wrap condition in
* the mapped source address space.
*/
public Address getMappedSourceAddress(Address mappedSourceBaseAddress,
long offsetInSubBlock)
throws AddressOverflowException {
if (offsetInSubBlock < 0) {
throw new IllegalArgumentException("negative offset");
}
long sourceOffset = offsetInSubBlock;
if (!isOneToOneMapping()) {
sourceOffset = (mappedSourceByteCount * (offsetInSubBlock / mappedByteCount)) +
(offsetInSubBlock % mappedByteCount);
}
return mappedSourceBaseAddress.addNoWrap(sourceOffset);
}
/**
* Calculate the address within a mapped block for a specified mapped source offset.
* If the specified mappedSourceOffset corresponds to a non-mapped (i.e., skipped) byte
* the address returned will correspond to the last mapped byte. Care must be used
* when using this method.
* @param mappedBlock mapped block
* @param mappedSourceOffset byte offset within mapped source relative to mapped base source address.
* @param skipBack controls return address when mappedSourceOffset corresponds to a non-mapped/skipped byte.
* If true the returned address will correspond to the previous mapped address, if false the next mapped
* address will be returned.
* @return mapped address within block or null if skipBack is false and unable to map within block limits
* @throws AddressOverflowException thrown for 1:1 mapping when mappedSourceOffset exceeds length of mappedBlock
*/
Address getMappedAddress(MemoryBlock mappedBlock, long mappedSourceOffset, boolean skipBack)
throws AddressOverflowException {
if (mappedSourceOffset < 0) {
throw new IllegalArgumentException("negative source offset");
}
long mappedOffset = mappedSourceOffset;
if (!isOneToOneMapping()) {
mappedOffset = (mappedByteCount * (mappedSourceOffset / mappedSourceByteCount));
long offsetLimit = mappedBlock.getSize() - 1;
long mod = mappedSourceOffset % mappedSourceByteCount;
if (mod < mappedByteCount) {
mappedOffset += mod;
}
else if (!skipBack) {
mappedOffset += mappedByteCount;
if (mappedOffset > offsetLimit) {
return null;
}
}
}
return mappedBlock.getStart().addNoWrap(mappedOffset);
}
/**
* Read bytes into an array from memory utilizing this mapping scheme.
* @param memory program memory
* @param mappedSourceBaseAddress base source memory address for byte-mapped subblock
* @param offsetInSubBlock byte offset from start of subblock where reading should begin
* @param b byte array to be filled
* @param off offset within byte array b where filling should start
* @param len number of bytes to be read
* @return actual number of bytes read
* @throws MemoryAccessException if read of uninitialized or non-existing memory occurs
* @throws AddressOverflowException if address computation error occurs
*/
int getBytes(Memory memory, Address mappedSourceBaseAddress, long offsetInSubBlock, byte[] b,
int off, int len) throws MemoryAccessException, AddressOverflowException {
if (isOneToOneMapping()) {
return memory.getBytes(mappedSourceBaseAddress.addNoWrap(offsetInSubBlock), b, off,
len);
}
// NOTE: approach avoids incremental reading by including unmapped bytes in
// bulk read and filters as needed based upon mapping scheme ratio
long patternCount = offsetInSubBlock / mappedByteCount;
int partialByteCount = (int) (offsetInSubBlock % mappedByteCount);
long mappedOffset = (mappedSourceByteCount * patternCount) + partialByteCount;
int bufSize = mappedSourceByteCount * ((len / mappedByteCount) + 1);
byte[] buf = new byte[bufSize];
int bufCnt = memory.getBytes(mappedSourceBaseAddress.addNoWrap(mappedOffset), buf);
int bufIndex = 0;
int cnt = 0;
int index = off;
int i = mappedByteCount - partialByteCount;
boolean skip = false;
while (bufIndex < bufCnt && cnt < len) {
if (!skip) {
b[index++] = buf[bufIndex];
++cnt;
if (--i == 0) {
skip = true;
i = nonMappedByteCount;
}
}
else if (--i == 0) {
skip = false;
i = mappedByteCount;
}
++bufIndex;
}
return cnt;
}
/**
* Write an array of bytes to memory utilizing this mapping scheme.
* @param memory program memory
* @param mappedSourceBaseAddress base source memory address for byte-mapped subblock
* @param offsetInSubBlock byte offset from start of subblock where writing should begin
* @param b an array to get bytes from
* @param off start source index within byte array b where bytes should be read
* @param len number of bytes to be written
* @throws MemoryAccessException if write of uninitialized or non-existing memory occurs
* @throws AddressOverflowException if address computation error occurs
*/
void setBytes(Memory memory, Address mappedSourceBaseAddress, long offsetInSubBlock,
byte[] b,
int off, int len) throws MemoryAccessException, AddressOverflowException {
if (isOneToOneMapping()) {
memory.setBytes(mappedSourceBaseAddress.addNoWrap(offsetInSubBlock), b, off, len);
return;
}
long patternCount = offsetInSubBlock / mappedByteCount;
int partialByteCount = (int) (offsetInSubBlock % mappedByteCount);
long mappedOffset = (mappedSourceByteCount * patternCount) + partialByteCount;
Address destAddr = mappedSourceBaseAddress.addNoWrap(mappedOffset);
int index = off;
int cnt = 0;
int i = mappedByteCount - partialByteCount;
while (cnt < len) {
memory.setBytes(destAddr, b, index, i);
index += i;
cnt += i;
destAddr = destAddr.addNoWrap(i + nonMappedByteCount);
i = mappedByteCount;
}
}
/**
* Validate mapping scheme. This scheme is specified as a ratio of mapped bytes to source bytes.
* @param schemeDestByteCount number of mapped bytes per mappedSourcebyteCount (1..127). This
* value must be less-than or equal to schemeSrcByteCount.
* @param schemeSrcByteCount number of source bytes for mapping ratio (1..127)
* @throws IllegalArgumentException if invalid mapping scheme specified
*/
static void validateMappingScheme(int schemeDestByteCount, int schemeSrcByteCount) {
if (schemeDestByteCount <= 0 || schemeDestByteCount > 0x7F || schemeSrcByteCount <= 0 ||
schemeSrcByteCount > 0x7F ||
schemeDestByteCount > schemeSrcByteCount) {
throw new IllegalArgumentException(
"invalid byte mapping ratio: " + schemeDestByteCount + ":" + schemeSrcByteCount);
}
}
/**
* Get encoded mapping scheme as a single value for storage purposes. This scheme value
* identifies the ratio of mapped bytes to source bytes. Value is encoded as two 7-bit
* values corresponding to the destination and source byte counts.
* @param schemeDestByteCount number of mapped bytes per mappedSourcebyteCount (1..127). This
* value must be less-than or equal to schemeSrcByteCount.
* @param schemeSrcByteCount number of source bytes for mapping ratio (1..127)
* @return mapping scheme value
* @throws IllegalArgumentException if invalid mapping scheme specified
*/
static int getEncodedMappingScheme(int schemeDestByteCount, int schemeSrcByteCount) {
validateMappingScheme(schemeDestByteCount, schemeSrcByteCount);
return (schemeDestByteCount << 7) | (schemeSrcByteCount & 0x7F);
}
/**
* Extract the mapping scheme mapped-byte-count from a mappingScheme value.
* @param mappingScheme mapping scheme
* @return mapped-byte-count (aka schemeDestByteCount)
*/
static int getMappedByteCount(int mappingScheme) {
int mappedByteCount = 1;
if (mappingScheme != 0) {
mappedByteCount = (mappingScheme >> 7) & 0x7F;
}
return mappedByteCount;
}
/**
* Extract the mapping ratio mapped-source-byte-count from a mappingScheme value.
* @param mappingScheme mapping scheme
* @return mapped-source-byte-count (aka schemeSrcByteCount)
*/
static int getMappedSourceByteCount(int mappingScheme) {
int mappedSourceByteCount = 1;
if (mappingScheme != 0) {
mappedSourceByteCount = mappingScheme & 0x7F;
}
return mappedSourceByteCount;
}
}

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