diff --git a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm index 18ca350367..d9799704d8 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/AutoAnalysisPlugin/AutoAnalysis.htm @@ -238,8 +238,8 @@
  • Search Only in Accessible Memory Blocks - 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.
  • + to true. Enabling this option ensures strings are not created in areas such as non-loaded + overlays or debug sections.
  • String End Alignment - 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 diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm index c763f9d573..14170b74b3 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Glossary/glossary.htm @@ -898,7 +898,11 @@ xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-

    Overlay

    -

    A memory block that occupies the same memory address range as some other block.

    +

    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.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm b/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm index fc4ea223d5..feb8f0a400 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/ImporterPlugin/importer.htm @@ -374,7 +374,7 @@

    Overlay

    -

    If selected, the bytes will be loaded as an overlay. A new overlay space will be +

    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.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm index 548206be92..4122b8f3f6 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm @@ -20,40 +20,54 @@

    The Memory Map window displays a list of memory blocks that make up the memory structure of the current program.  The component provides actions for adding, renaming, moving, splitting, extending, joining, and deleting memory blocks.

    + +

    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.

    -

    Ghidra supports four different block types through the Memory Map window:

    +

    Ghidra supports three different block types through the Memory Map window:

    1. Default - The normal block type that can be - initialized or uninitialized. + Initialized, File Bytes or Uninitialized.
        -
      • Initialized - The block has an initial value - specified for the bytes
      • +
      • Initialized - The block has an initial value + specified for all bytes
      • + +
      • File Bytes - An initialized block whose data corresponds + to a specified range within an existing loaded File Bytes instance.
      • -
      • Uninitialized - The block has no initial +
      • Uninitialized - The block has no initial value specified for the bytes
    2. Bit Mapped - 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.
    3. + 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..
    4. Byte Mapped - The block provides a - byte-addressable map onto other blocks.  This can be useful when the same bytes can be - accessed via two or more addresses.
    5. - -
    6. Overlay - The block is created in a new - overlay address space. Overlay blocks can be initialized or - unitialized. 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.  Overlay blocks contain - code that would get swapped in when the program needs to execute it.   Note that - Overlay blocks are fixed and may not be moved, split or expanded.  In addition, Overlays - do not relocate with image base changes.
      -
    7. + byte-addressable map onto other blocks.  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).
    + +

    File Bytes 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.

    + +

    Overlay - Each of the above memory block types may optionally be specified as an Overlay at the + time of creation. If this option is selected, the block is created in a new + overlay address space.  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.   Note that + overlay blocks are fixed and may not be moved, split, merged or expanded.  In addition, Overlays + do not relocate with image base changes and have significant limitations in conjunction with + decompilation and analysis.

    To view the Memory Map, select Window Memory Map from the main tool menu, or click on the  W - Indicates write permission.

    -

    X - Indicates execute permission.
    -

    +

    X - Indicates execute permission.

    + +

    Volatile - Indicates a region of volatile I/O Memory.

    -

    Volatile - Indicates a region of volatile I/O - Memory.
    -

    +

    Overlay - Indicates if block is defined as a memory overlay.

    Type - Indicates whether the block is a Default, - Bit Mapped, Byte Mapped or Overlay type of block.

    + Bit Mapped or Byte Mapped type of block.

    Initialized - Indicates whether the block has been initialized with values; this property applies to Default and Overlay blocks.

    @@ -107,10 +119,7 @@ sources. In that case, source information about the first several regions will be d displayed.

    -

    Source - The name of the file that produced the bytes that make up this - block as set by the file importer; for Bit Mapped or Byte Mapped blocks, the Source shows the mapped source - address.

    +

    Source - Description of block origination.

    Comment - User added comment about this memory block.

    @@ -221,15 +230,14 @@

    Write - Sets the write permission.

    -

    Execute - Sets the execute permission.
    -

    +

    Execute - Sets the execute permission.

    -

    Volatile - Marks this block as volatile I/O - memory.
    -

    +

    Volatile - Marks this block as volatile I/O memory.

    + +

    Overlay - Creates the block as an overlay within a corresponding overlay address space.

    Block Types - Select the block type from the combo box: Default, Bit - Mapped, Byte Mapped, or Overlay.

    + Mapped or Byte Mapped.

    -
  • Overlay - An overlay block is used to give an alternative set of - bytes (and related information) for a range in memory.  This is achieved by - creating a new address space related to the actual processor address space and placing - the block in the new space at the same offsets as the start address in the processor - space.  Overlay blocks can be either initialized or uninitialized. If you select - Initialized you can enter a byte value that will be used to fill all the bytes - in the new memory block.
  • - -
  • Bit Mapped - This is a block that allow bit addressing of a section +
  • Bit Mapped - This is a block that allows bit addressing of a section of bytes in memory.  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.
  • @@ -261,10 +261,9 @@
  • The illustration below depicts a Bit Mapped block of Length 16 with a Start Addr of (BIT:) 0000, and a Source Address of 00008100.  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.

    -
  • - + @@ -274,16 +273,21 @@
    - - + +
    @@ -517,9 +521,9 @@ be created.  Disregarding the warning may cause Ghidra to fail with an "out of memory" error.

    -

    Only blocks of the same type can be - merged. For example, default blocks can only be merged with - another default block. 

    +

    Only adjacent Default blocks of the same + initialization state can be merged.

    +

    Overlay type blocks cannot be merged.

    diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png index e8fa300a22..4d198fc7f7 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png index 94073d9ac8..0812e81550 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandDown.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandDown.png index ea72f5933a..03b7fd38bb 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandDown.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandDown.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandUp.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandUp.png index 221585c08e..34af1c9895 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandUp.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryExpandUp.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png index 9346e1cc33..ce072eaed7 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png index 90bbc79d05..8b74e22745 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png index 938bbaf393..34c1ad8587 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png index 53c86882bf..f6cf4bff4e 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png and b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java index 4ddaf1a515..eaedd0479f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java index 9c00677354..ad20f640b4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java @@ -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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java index 8ebfd7ac39..dd4b3b5279 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java @@ -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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java index c4b9ff1542..fa8813f783 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java index 050aa5bbe5..ccfe65a8a4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java index 2e7b5df4c4..6ec60220c0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java index d5f09e2a18..627ee782b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java @@ -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 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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java index 4bcd1447e9..fd5ca0f09e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java index c2d5ca23ca..19d50b74db 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java @@ -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; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java index d80785781c..17acddcd01 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java @@ -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 { @@ -50,11 +49,12 @@ class MemoryMapModel extends AbstractSortedTableModel { 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 { 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 { 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 { @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 { @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 { "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 { } 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 { 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 { 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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java index 693fb74a67..e3af5d4c9e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java @@ -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."); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/SplitBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/SplitBlockDialog.java index 5d80c923d0..b53dd9bd42 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/SplitBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/SplitBlockDialog.java @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java index 0e4af5c40e..5f3ca5bd81 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/CommentFieldMouseHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/CommentFieldMouseHandler.java index 6a6de33e5b..3073a40ca0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/CommentFieldMouseHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/CommentFieldMouseHandler.java @@ -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() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java index 7257f2510d..a825d0fc0c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/MemoryBlockStartFieldFactory.java @@ -111,8 +111,15 @@ public class MemoryBlockStartFieldFactory extends FieldFactory { return null; } CodeUnit cu = (CodeUnit) proxyObject; + + List 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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java index a2560fee0b..d41c30ae83 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java index db1f7dc45e..c19caceffe 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,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; - /** * MemoryDiff 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 rangeDiffs = new ArrayList(); - 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; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java index 735b27d5d4..03eaf1567b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/ProgramMemoryUtil.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java index 689e1a616e..2a06a2a7c4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/AddBlockModelTest.java @@ -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()); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java index de72b1987f..3f3c4c960d 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java @@ -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"); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java index 5b8956ba35..93b4db7080 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider2Test.java @@ -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)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java index 29e1be8b37..ef75614c84 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressIndexPrimaryKeyIteratorTest.java @@ -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 diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java index 65660db65e..288a60c2c1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/map/AddressKeyIteratorTest.java @@ -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 diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMemoryBlockTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMappedMemoryBlockTest.java similarity index 91% rename from Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMemoryBlockTest.java rename to Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMappedMemoryBlockTest.java index 20233e02bb..7268baf6cb 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMemoryBlockTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/BitMappedMemoryBlockTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java new file mode 100644 index 0000000000..b0dc85c139 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/ByteMappedMemoryBlockTest.java @@ -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)); + } +} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java index 5873a532ed..9b956086ef 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryWriteCheckTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryWriteCheckTest.java new file mode 100644 index 0000000000..4c46198c04 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryWriteCheckTest.java @@ -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); + + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java index 95ab232f5b..233be23748 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java @@ -22,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); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java index 33d1aa09c7..17d6ec8604 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java @@ -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(); } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java index 13d09169c1..206a050d8c 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java @@ -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(); diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java index 9895bd85a6..6080754741 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java @@ -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"); diff --git a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java index d15e2c2049..f6e3ad9679 100644 --- a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java +++ b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java index 66f78e654a..d24eb7c288 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java deleted file mode 100644 index eb21fffcae..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedByteSourceRange.java +++ /dev/null @@ -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); - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java index b1b77645ad..51600d349e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BitMappedSubMemoryBlock.java @@ -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 blocks = memMap.getBlocks(startMappedAddress, endMappedAddress); - - // for each block, get its ByteSourceSet and then translate that set back into this block's - // addresses - for (MemoryBlockDB mappedBlock : blocks) { - Address startInBlock = max(mappedBlock.getStart(), startMappedAddress); - Address endInBlock = min(mappedBlock.getEnd(), endMappedAddress); - long blockSize = endInBlock.subtract(startInBlock) + 1; - ByteSourceRangeList ranges = - mappedBlock.getByteSourceRangeList(startInBlock, blockSize); - for (ByteSourceRange bsRange : ranges) { - result.add(translate(block, bsRange, start, memBlockOffset, size)); - } - } - return result; - } - - // translates the ByteSourceRange back to addresse - private ByteSourceRange translate(MemoryBlock block, ByteSourceRange bsRange, Address start, - long offset, - long bitLength) { - Address startMappedAddress = mappedAddress.add(offset / 8); - Address normalizedStart = start.subtract(offset % 8); - long mappedOffsetFromStart = bsRange.getStart().subtract(startMappedAddress); - long offsetFromStart = mappedOffsetFromStart * 8; - Address startAddress = normalizedStart.add(offsetFromStart); - - return new BitMappedByteSourceRange(block, startAddress, bsRange.getSourceId(), - bsRange.getOffset(), bsRange.getSize()); - } - - Address min(Address a1, Address a2) { - return a1.compareTo(a2) <= 0 ? a1 : a2; - } - - Address max(Address a1, Address a2) { - return a1.compareTo(a2) >= 0 ? a1 : a2; - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java index 0c9ffd6ba3..2b65260cd8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/BufferSubMemoryBlock.java @@ -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); - } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java index b2e7c9936c..b46c4302cc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappedSubMemoryBlock.java @@ -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 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; } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java new file mode 100644 index 0000000000..dbbc7dc825 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java @@ -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.*; + +/** + * ByteMappingScheme 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; + } + +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java deleted file mode 100644 index 92264286b2..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRange.java +++ /dev/null @@ -1,126 +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 ByteSourceRange { - protected final Address start; - protected final long size; - protected final long sourceId; - protected final long byteSourceOffset; - protected MemoryBlock block; - - public ByteSourceRange(MemoryBlock block, Address start, long size, long sourceId, - long offset) { - this.block = block; - this.start = start; - this.size = size; - this.sourceId = sourceId; - this.byteSourceOffset = offset; - } - - public Address getStart() { - return start; - } - - public Address getEnd() { - return start.add(size - 1); - } - - public long getSize() { - return size; - } - - public long getSourceId() { - return sourceId; - } - - public long getOffset() { - return byteSourceOffset; - } - - public ByteSourceRange intersect(ByteSourceRange range) { - if (sourceId != range.sourceId) { - return null; - } - long maxOffset = Math.max(byteSourceOffset, range.byteSourceOffset); - long minEndOffset = - Math.min(byteSourceOffset + size - 1, range.byteSourceOffset + range.size - 1); - if (maxOffset > minEndOffset) { - return null; - } - return new ByteSourceRange(block, start.add(maxOffset - byteSourceOffset), - minEndOffset - maxOffset + 1, sourceId, maxOffset); - } - - public MemoryBlock getMemoryBlock() { - return block; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (byteSourceOffset ^ (byteSourceOffset >>> 32)); - result = prime * result + (int) (size ^ (size >>> 32)); - result = prime * result + (int) (sourceId ^ (sourceId >>> 32)); - result = prime * result + ((start == null) ? 0 : start.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ByteSourceRange other = (ByteSourceRange) obj; - if (block == null) { - if (other.block != null) { - return false; - } - } - else if (!block.equals(other.block)) { - return false; - } - if (byteSourceOffset != other.byteSourceOffset) { - return false; - } - if (size != other.size) { - return false; - } - if (sourceId != other.sourceId) { - return false; - } - if (start == null) { - if (other.start != null) { - return false; - } - } - else if (!start.equals(other.start)) { - return false; - } - return true; - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java deleted file mode 100644 index 9bc1f5660b..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteSourceRangeList.java +++ /dev/null @@ -1,220 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.program.database.mem; - -import java.util.*; - -import ghidra.program.model.mem.MemoryBlock; - -public class ByteSourceRangeList implements Iterable { - List ranges; - - public ByteSourceRangeList(ByteSourceRange bsRange) { - this(); - ranges.add(bsRange); - } - - public ByteSourceRangeList() { - ranges = new ArrayList<>(); - } - - @Override - public Iterator iterator() { - return ranges.iterator(); - } - - public void add(ByteSourceRange range) { - if (range != null) { - ranges.add(range); - } - } - - public void add(ByteSourceRangeList byteSourceList) { - ranges.addAll(byteSourceList.ranges); - } - - public int getRangeCount() { - return ranges.size(); - } - - public ByteSourceRange get(int i) { - return ranges.get(i); - } - - public boolean isEmpty() { - return ranges.isEmpty(); - } - - public Set getOverlappingBlocks() { - List entries = new ArrayList<>(); - for (ByteSourceRange range : ranges) { - entries.add(new BlockRangeStart(this, range)); - entries.add(new BlockRangeEnd(this, range)); - } - Collections.sort(entries); - return findOverlappingBlocks(entries); - } - - public ByteSourceRangeList intersect(ByteSourceRangeList rangeList) { - List entries = new ArrayList<>(); - for (ByteSourceRange range : ranges) { - entries.add(new BlockRangeStart(this, range)); - entries.add(new BlockRangeEnd(this, range)); - } - for (ByteSourceRange range : rangeList) { - entries.add(new BlockRangeStart(rangeList, range)); - entries.add(new BlockRangeEnd(rangeList, range)); - } - Collections.sort(entries); - return getIntersectingRanges(entries); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((ranges == null) ? 0 : ranges.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ByteSourceRangeList other = (ByteSourceRangeList) obj; - if (ranges == null) { - if (other.ranges != null) { - return false; - } - } - else if (!ranges.equals(other.ranges)) { - return false; - } - return true; - } - - private ByteSourceRangeList getIntersectingRanges(List entries) { - ByteSourceRangeList result = new ByteSourceRangeList(); - - Set currentSet = new HashSet<>(); - - for (BlockRangeEntry entry : entries) { - if (entry.isStart()) { - currentSet.add(entry.range); - } - else { - currentSet.remove(entry.range); - addIntersections(result, entry, currentSet); - } - } - - return result; - } - - private void addIntersections(ByteSourceRangeList set, BlockRangeEntry entry, - Set currentSet) { - - if (currentSet.isEmpty()) { - return; - } - for (ByteSourceRange byteSourceRange : currentSet) { - if (entry.owner == this) { - set.add(entry.range.intersect(byteSourceRange)); - } - else { - set.add(byteSourceRange.intersect(entry.range)); - } - } - } - - private Set findOverlappingBlocks(List entries) { - Set overlappingBlocks = new HashSet<>(); - Set currentSet = new HashSet<>(); - - for (BlockRangeEntry entry : entries) { - if (entry.isStart()) { - currentSet.add(entry.range); - } - else { - currentSet.remove(entry.range); - if (!currentSet.isEmpty()) { - overlappingBlocks.add(entry.range.block); - for (ByteSourceRange byteSourceRange : currentSet) { - overlappingBlocks.add(byteSourceRange.block); - } - } - } - } - return overlappingBlocks; - } - - abstract class BlockRangeEntry implements Comparable { - private ByteSourceRange range; - private long sourceId; - private long offset; - private ByteSourceRangeList owner; - - BlockRangeEntry(ByteSourceRangeList owner, ByteSourceRange range, long offset) { - this.owner = owner; - this.range = range; - this.offset = offset; - this.sourceId = range.getSourceId(); - } - - abstract boolean isStart(); - - @Override - public int compareTo(BlockRangeEntry o) { - if (sourceId != o.sourceId) { - return sourceId > o.sourceId ? 1 : -1; - } - if (offset == o.offset) { - return (isStart() == o.isStart()) ? 0 : (isStart() ? -1 : 1); - } - return offset > o.offset ? 1 : -1; - } - } - - class BlockRangeStart extends BlockRangeEntry { - BlockRangeStart(ByteSourceRangeList owner, ByteSourceRange range) { - super(owner, range, range.getOffset()); - } - @Override - boolean isStart() { - return true; - } - - } - - class BlockRangeEnd extends BlockRangeEntry { - BlockRangeEnd(ByteSourceRangeList owner, ByteSourceRange range) { - super(owner, range, range.getOffset() + range.size - 1); - } - - @Override - boolean isStart() { - return false; - } - - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java index 6e807e06d0..a3c8192d1f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java @@ -18,8 +18,7 @@ package ghidra.program.database.mem; import java.io.IOException; import db.Record; -import ghidra.program.model.address.Address; -import ghidra.program.model.mem.*; +import ghidra.program.model.mem.MemoryAccessException; /** * Class for handling {@link FileBytes} memory sub blocks (blocks whose bytes are backed by a FileBytes object @@ -30,8 +29,8 @@ class FileBytesSubMemoryBlock extends SubMemoryBlock { FileBytesSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException { super(adapter, record); - long fileBytesID = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL); - fileBytesOffset = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL); + long fileBytesID = record.getLongValue(MemoryMapDBAdapter.SUB_INT_DATA1_COL); + fileBytesOffset = record.getLongValue(MemoryMapDBAdapter.SUB_LONG_DATA2_COL); fileBytes = adapter.getMemoryMap().getLayeredFileBytes(fileBytesID); } @@ -95,11 +94,6 @@ class FileBytesSubMemoryBlock extends SubMemoryBlock { return fileBytesOffset; } - @Override - protected MemoryBlockType getType() { - return MemoryBlockType.DEFAULT; - } - @Override protected SubMemoryBlock split(long memBlockOffset) throws IOException { // convert from offset in block to offset in this sub block @@ -109,7 +103,7 @@ class FileBytesSubMemoryBlock extends SubMemoryBlock { record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, subBlockLength); adapter.updateSubBlockRecord(record); - int fileBytesID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL); + int fileBytesID = record.getIntValue(MemoryMapDBAdapter.SUB_INT_DATA1_COL); Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength, MemoryMapDBAdapter.SUB_TYPE_FILE_BYTES, fileBytesID, fileBytesOffset + offset); @@ -129,14 +123,4 @@ class FileBytesSubMemoryBlock extends SubMemoryBlock { return fileBytes.equals(fb); } - @Override - protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start, - long memBlockOffset, - long size) { - long sourceId = fileBytes.getId(); - ByteSourceRange bsRange = new ByteSourceRange(block, start, size, sourceId, - fileBytesOffset + memBlockOffset - subBlockOffset); - return new ByteSourceRangeList(bsRange); - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java index 34d35c9300..9ce03f9dde 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java @@ -30,6 +30,7 @@ import ghidra.util.exception.AssertException; import ghidra.util.exception.DuplicateNameException; public class MemoryBlockDB implements MemoryBlock { + private MemoryMapDBAdapter adapter; protected Record record; private Address startAddress; @@ -40,6 +41,8 @@ public class MemoryBlockDB implements MemoryBlock { private long id; private SubMemoryBlock lastSubBlock; + private List mappedBlocks; // list of mapped blocks which map onto this block + MemoryBlockDB(MemoryMapDBAdapter adapter, Record record, List subBlocks) { this.adapter = adapter; this.record = record; @@ -74,6 +77,33 @@ public class MemoryBlockDB implements MemoryBlock { lastSubBlock = null; Collections.sort(list); subBlocks = list; + mappedBlocks = null; + } + + /** + * Add a block which is mapped onto this block + * @param mappedBlock mapped memory block + */ + void addMappedBlock(MemoryBlockDB mappedBlock) { + if (mappedBlocks == null) { + mappedBlocks = new ArrayList<>(); + } + mappedBlocks.add(mappedBlock); + } + + /** + * Clear list of blocks mapped onto this block + */ + void clearMappedBlockList() { + mappedBlocks = null; + } + + /** + * Get collection of blocks which map onto this block. + * @return collection of blocks which map onto this block or null if none identified + */ + Collection getMappedBlocks() { + return mappedBlocks; } @Override @@ -130,8 +160,12 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); + if (oldName.equals(name)) { + return; + } + memMap.checkBlockName(name); try { - if (getStart().getAddressSpace().isOverlaySpace()) { + if (isOverlay()) { memMap.overlayBlockRenamed(oldName, name); } record.setString(MemoryMapDBAdapter.NAME_COL, name); @@ -394,12 +428,14 @@ public class MemoryBlockDB implements MemoryBlock { @Override public MemoryBlockType getType() { - if (startAddress.getAddressSpace().isOverlaySpace()) { - return MemoryBlockType.OVERLAY; - } return subBlocks.get(0).getType(); } + @Override + public boolean isOverlay() { + return startAddress.getAddressSpace().isOverlaySpace(); + } + public byte getByte(long offset) throws MemoryAccessException { SubMemoryBlock subBlock = getSubBlock(offset); try { @@ -585,7 +621,7 @@ public class MemoryBlockDB implements MemoryBlock { throw new IllegalArgumentException("offset " + offset + " not in this block"); } - public void initializeBlock(byte initialValue) throws IOException { + void initializeBlock(byte initialValue) throws IOException { lastSubBlock = null; for (SubMemoryBlock subBlock : subBlocks) { subBlock.delete(); @@ -687,30 +723,4 @@ public class MemoryBlockDB implements MemoryBlock { return false; } - ByteSourceRangeList getByteSourceRangeList(Address address, long size) { - long blockOffset = address.subtract(startAddress); - size = Math.min(size, length - blockOffset); - - SubMemoryBlock subBlock = getSubBlock(blockOffset); - long subBlockOffset = blockOffset - subBlock.getStartingOffset(); - long available = subBlock.subBlockLength - subBlockOffset; - long subSize = Math.min(size, available); - if (subSize == size) { - return subBlock.getByteSourceRangeList(this, address, blockOffset, size); - } - Address start = address; - ByteSourceRangeList set = - subBlock.getByteSourceRangeList(this, start, blockOffset, subSize); - - long total = subSize; - while (total < size) { - subBlock = getSubBlock(blockOffset + total); - subSize = Math.min(size - total, subBlock.subBlockLength); - start = address.add(total); - set.add(subBlock.getByteSourceRangeList(this, start, blockOffset + total, subSize)); - total += subSize; - } - return set; - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfoDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfoDB.java index 185157c900..94b862b469 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfoDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfoDB.java @@ -35,33 +35,21 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { this.subBlock = subBlock; } - /** - * @return - */ @Override public long getLength() { return subBlock.subBlockLength; } - /** - * @return - */ @Override public Address getMinAddress() { return block.getStart().add(subBlock.subBlockOffset); } - /** - * @return - */ @Override public Address getMaxAddress() { return block.getStart().add(subBlock.subBlockOffset + subBlock.subBlockLength - 1); } - /** - * @return - */ @Override public String getDescription() { return subBlock.getDescription(); @@ -74,9 +62,6 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { } - /** - * @return - */ @Override public Optional getFileBytes() { if (subBlock instanceof FileBytesSubMemoryBlock) { @@ -85,9 +70,6 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { return Optional.empty(); } - /** - * @return - */ @Override public long getFileBytesOffset() { if (subBlock instanceof FileBytesSubMemoryBlock) { @@ -96,10 +78,6 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { return -1; } - /** - * @param address - * @return - */ @Override public long getFileBytesOffset(Address address) { if (subBlock instanceof FileBytesSubMemoryBlock && contains(address)) { @@ -110,9 +88,6 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { return -1; } - /** - * @return - */ @Override public Optional getMappedRange() { if (subBlock instanceof BitMappedSubMemoryBlock) { @@ -126,18 +101,20 @@ class MemoryBlockSourceInfoDB implements MemoryBlockSourceInfo { return Optional.empty(); } - /** - * @return - */ + @Override + public Optional getByteMappingScheme() { + if (subBlock instanceof ByteMappedSubMemoryBlock) { + ByteMappedSubMemoryBlock byteMapped = (ByteMappedSubMemoryBlock) subBlock; + return Optional.of(byteMapped.getByteMappingScheme()); + } + return Optional.empty(); + } + @Override public MemoryBlock getMemoryBlock() { return block; } - /** - * @param address - * @return - */ @Override public boolean contains(Address address) { return address.compareTo(getMinAddress()) >= 0 && address.compareTo(getMaxAddress()) <= 0; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java index f6f0e6741b..f57c32cc68 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java @@ -37,7 +37,6 @@ import ghidra.program.util.ChangeManager; import ghidra.util.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; -import ghidra.util.task.TaskMonitorAdapter; /** * The database memory map manager. @@ -67,7 +66,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { private final static MemoryBlock NoBlock = new MemoryBlockStub(); // placeholder for no block, not given out Lock lock; - private Set potentialOverlappingBlocks; private static Comparator BLOCK_ADDRESS_COMPARATOR = (o1, o2) -> { MemoryBlock block = (MemoryBlock) o1; @@ -134,33 +132,97 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { // we have to process the non-mapped blocks first because to process the mapped // blocks we need the address sets for the non-mapped blocks to be complete for (MemoryBlockDB block : blocks) { + block.clearMappedBlockList(); if (!block.isMapped()) { - addBlockAddresses(block); + addBlockAddresses(block, false); } } + // process all mapped blocks after non-mapped-blocks above for (MemoryBlockDB block : blocks) { if (block.isMapped()) { - addBlockAddresses(block); + addBlockAddresses(block, false); } } - } - private void addBlockAddresses(MemoryBlockDB block) { + /** + * Update the allInitializedAddrSet and initializedLoadedAddrSet + * with relevant initialized addresses from the specified memory block. If block is not + * a mapped-block and it may be a source to existing mapped-blocks then + * scanAllMappedBlocksIfNeeded should be passed as true unless + * all mapped blocks will be processed separately. + * @param block memory block + * @param scanAllMappedBlocksIfNeeded if true and block is initialized and not a mapped block all + * mapped blocks will be processed for possible introduction of newly initialized mapped regions. + */ + private void addBlockAddresses(MemoryBlockDB block, boolean scanAllMappedBlocksIfNeeded) { AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd()); addrSet = addrSet.union(blockSet); if (block.isMapped()) { - allInitializedAddrSet = - allInitializedAddrSet.union(getMappedIntersection(block, allInitializedAddrSet)); + + // Identify source-blocks which block maps onto and add as a mapped-block to each of these + AddressRange mappedRange = block.getSourceInfos().get(0).getMappedRange().get(); + for (MemoryBlockDB b : getBlocks(mappedRange.getMinAddress(), + mappedRange.getMaxAddress())) { + b.addMappedBlock(block); + } + + AddressSet mappedSet = getMappedIntersection(block, allInitializedAddrSet); + allInitializedAddrSet = allInitializedAddrSet.union(mappedSet); initializedLoadedAddrSet = initializedLoadedAddrSet.union( getMappedIntersection(block, initializedLoadedAddrSet)); - } else if (block.isInitialized()) { allInitializedAddrSet = allInitializedAddrSet.union(blockSet); if (block.isLoaded()) { initializedLoadedAddrSet = initializedLoadedAddrSet.union(blockSet); } + if (scanAllMappedBlocksIfNeeded) { + // If only adding one initialized non-mapped-block we must scan all mapped-blocks + // which may utilize block as a byte source + for (MemoryBlockDB b : blocks) { + b.clearMappedBlockList(); + } + for (MemoryBlockDB b : blocks) { + if (b.isMapped()) { + addBlockAddresses(b, false); + } + } + } + } + } + + /** + * Update initialized address set for those mapped blocks which map onto the + * specified block which has just completed a transition of its' initialized state. + * @param block block whose initialized state has changed + * @param isInitialized true if block transitioned from uninitialized to initialized, + * else transition is from initialized to uninitialized. + */ + private void updateMappedAddresses(MemoryBlockDB block, boolean isInitialized) { + + Collection mappedBlocks = block.getMappedBlocks(); + if (mappedBlocks == null) { + return; + } + + AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd()); + boolean isLoaded = block.getStart().isLoadedMemoryAddress(); + + for (MemoryBlockDB mappedBlock : block.getMappedBlocks()) { + AddressSet mappedSet = getMappedIntersection(mappedBlock, blockSet); + if (isInitialized) { + allInitializedAddrSet = allInitializedAddrSet.union(mappedSet); + if (isLoaded) { + initializedLoadedAddrSet = initializedLoadedAddrSet.union(mappedSet); + } + } + else { + allInitializedAddrSet = allInitializedAddrSet.subtract(mappedSet); + if (isLoaded) { + initializedLoadedAddrSet = initializedLoadedAddrSet.union(mappedSet); + } + } } } @@ -181,9 +243,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { List newBlocks = adapter.getMemoryBlocks(); lastBlock = null; blocks = newBlocks; - addrMap.memoryMapChanged(this); nameBlockMap = new HashMap<>(); executeSet = null; + addrMap.memoryMapChanged(this); } public void setLanguage(Language newLanguage) { @@ -223,6 +285,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { /** * Returns the address factory for the program. + * @return program address factory */ AddressFactory getAddressFactory() { return addrMap.getAddressFactory(); @@ -230,6 +293,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { /** * Returns the AddressMap from the program. + * @return program address map */ AddressMapDB getAddressMap() { return addrMap; @@ -255,34 +319,90 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { void checkMemoryWrite(MemoryBlockDB block, Address start, long length) throws MemoryAccessException { - checkRangeForInstructions(start, start.add(length - 1)); - Set overlappingBlocks = getPotentialOverlappingBlocks(); - ByteSourceRangeList changeingByteSource = block.getByteSourceRangeList(start, length); - if (overlappingBlocks.contains(block)) { - for (MemoryBlock b : overlappingBlocks) { - if (b.equals(block)) { - continue; - } - ByteSourceRangeList set = - ((MemoryBlockDB) b).getByteSourceRangeList(b.getStart(), b.getSize()); - ByteSourceRangeList intersect = set.intersect(changeingByteSource); - for (ByteSourceRange range : intersect) { - checkRangeForInstructions(range.getStart(), range.getEnd()); - } + if (!block.contains(start)) { + throw new MemoryAccessException( + block.getName() + " does not contain address " + start.toString(true)); + } + + try { + Address endAddr = start.addNoWrap(length - 1); + if (!block.contains(start)) { + throw new MemoryAccessException(block.getName() + " does not contain range " + + start.toString(true) + "-" + endAddr); } + + if (block.isMapped()) { + checkMemoryWriteMappedBlock(block, start, endAddr); + } + else { + checkMemoryWriteNonMappedBlock(block, start, endAddr); + } + } + catch (AddressOverflowException e) { + throw new MemoryAccessException("invalid address range specified for address " + + start.toString(true) + " (length: " + length + ")"); } } - private Set getPotentialOverlappingBlocks() { - if (potentialOverlappingBlocks == null) { - ByteSourceRangeList byteSourceList = new ByteSourceRangeList(); - for (MemoryBlockDB block : blocks) { - byteSourceList.add(block.getByteSourceRangeList(block.getStart(), block.getSize())); - } - potentialOverlappingBlocks = byteSourceList.getOverlappingBlocks(); + private void checkMemoryWriteMappedBlock(MemoryBlockDB mappedBlock, Address start, + Address endAddr) + throws AddressOverflowException, MemoryAccessException { + long startOffset = start.subtract(mappedBlock.getStart()); + long endOffset = endAddr.subtract(mappedBlock.getStart()); + + // determine source block(s) for mapped block + MemoryBlockSourceInfo info = mappedBlock.getSourceInfos().get(0); + AddressRange mappedRange = info.getMappedRange().get(); + Address mappedRangeMinAddr = mappedRange.getMinAddress(); + + Address mappedStartAddress, mappedEndAddress; + if (mappedBlock.getType() == MemoryBlockType.BIT_MAPPED) { + mappedStartAddress = mappedRangeMinAddr.addNoWrap(startOffset / 8); + mappedEndAddress = mappedRangeMinAddr.addNoWrap(endOffset / 8); + } + else { // BYTE_MAPPED + ByteMappingScheme byteMappingScheme = info.getByteMappingScheme().get(); + mappedStartAddress = + byteMappingScheme.getMappedSourceAddress(mappedRangeMinAddr, startOffset); + mappedEndAddress = + byteMappingScheme.getMappedSourceAddress(mappedRangeMinAddr, endOffset); + } + + for (MemoryBlockDB b : getBlocks(mappedStartAddress, mappedEndAddress)) { + Address minAddr = Address.min(b.getEnd(), mappedEndAddress); + Address maxAddr = Address.max(b.getStart(), mappedStartAddress); + checkMemoryWrite(b, minAddr, maxAddr.subtract(minAddr) + 1); + } + } + + private void checkMemoryWriteNonMappedBlock(MemoryBlockDB nonMappedBlock, Address start, + Address endAddr) + throws MemoryAccessException { + // TODO: could contain uninitialized region which is illegal to write to although block.isInitialized + // may not be of much help since it reflects the first sub-block only - seems like mixing is a bad idea + + checkRangeForInstructions(start, endAddr); + + // Check all mapped-block address ranges which map onto the range to be modified + Collection mappedBlocks = nonMappedBlock.getMappedBlocks(); + if (mappedBlocks != null) { + for (MemoryBlockDB mappedBlock : mappedBlocks) { + + // Determine source intersection with mapped block + MemoryBlockSourceInfo info = mappedBlock.getSourceInfos().get(0); + AddressRange mappedRange = info.getMappedRange().get(); + mappedRange = mappedRange.intersectRange(start, endAddr); + if (mappedRange == null) { + continue; // no intersection with range of interest + } + AddressRange range = getMappedRange(mappedBlock, mappedRange); + if (range == null) { + continue; // unexpected + } + checkRangeForInstructions(range.getMinAddress(), range.getMaxAddress()); + } } - return potentialOverlappingBlocks; } @Override @@ -362,8 +482,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { /** * Two blocks have been joined producing newBlock. The block which was * eliminated can be identified using the oldBlockStartAddr. - * @param newBlock - * @param oldBlockStartAddr + * @param newBlock new joined memory block + * @param oldBlockStartAddr original start address of affected block */ void fireBlocksJoined(MemoryBlock newBlock, Address oldBlockStartAddr) { program.setChanged(ChangeManager.DOCR_MEMORY_BLOCKS_JOINED, oldBlockStartAddr, newBlock); @@ -477,7 +597,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { public MemoryBlock createInitializedBlock(String name, Address start, InputStream is, long length, TaskMonitor monitor, boolean overlay) throws MemoryConflictException, AddressOverflowException, CancelledException, LockException, DuplicateNameException { - Objects.requireNonNull(name); + checkBlockName(name); lock.acquire(); try { checkBlockSize(length, true); @@ -496,7 +616,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { MemoryBlockDB newBlock = adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, !overlay); fireBlockAdded(newBlock); return newBlock; } @@ -523,7 +643,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { long offset, long length, boolean overlay) throws LockException, DuplicateNameException, MemoryConflictException, AddressOverflowException, IndexOutOfBoundsException { - Objects.requireNonNull(name); + checkBlockName(name); lock.acquire(); try { checkBlockSize(length, true); @@ -540,7 +660,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { MemoryBlockDB newBlock = adapter.createFileBytesBlock(name, start, length, fileBytes, offset, MemoryBlock.READ); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, !overlay); fireBlockAdded(newBlock); return newBlock; } @@ -576,7 +696,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { boolean overlay) throws MemoryConflictException, AddressOverflowException, LockException, DuplicateNameException { - Objects.requireNonNull(name); + checkBlockName(name); lock.acquire(); try { checkBlockSize(size, false); @@ -591,9 +711,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } try { MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.DEFAULT, name, start, - size, null, false, MemoryBlock.READ); + size, null, false, MemoryBlock.READ, 0); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, false); fireBlockAdded(newBlock); return newBlock; } @@ -608,21 +728,27 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } @Override - public MemoryBlock createBitMappedBlock(String name, Address start, Address overlayAddress, - long length) throws MemoryConflictException, AddressOverflowException, LockException { + public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress, + long length, boolean overlay) throws MemoryConflictException, AddressOverflowException, + LockException, IllegalArgumentException, DuplicateNameException { - Objects.requireNonNull(name); + checkBlockName(name); lock.acquire(); try { checkBlockSize(length, false); program.checkExclusiveAccess(); - checkRange(start, length); - overlayAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space + mappedAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space + if (overlay) { + start = createOverlaySpace(name, start, length); + } + else { + checkRange(start, length); + } try { MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BIT_MAPPED, name, - start, length, overlayAddress, false, MemoryBlock.READ); + start, length, mappedAddress, false, MemoryBlock.READ, 0); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, false); fireBlockAdded(newBlock); return newBlock; } @@ -637,21 +763,37 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } @Override - public MemoryBlock createByteMappedBlock(String name, Address start, Address overlayAddress, - long length) throws MemoryConflictException, AddressOverflowException, LockException { + public MemoryBlock createByteMappedBlock(String name, Address start, Address mappedAddress, + long length, ByteMappingScheme byteMappingScheme, boolean overlay) + throws MemoryConflictException, AddressOverflowException, LockException, + DuplicateNameException { + + checkBlockName(name); + + int mappingScheme = 0; // use for 1:1 mapping + if (byteMappingScheme == null) { + byteMappingScheme = new ByteMappingScheme(mappingScheme); // 1:1 mapping + } + else if (!byteMappingScheme.isOneToOneMapping()) { + mappingScheme = byteMappingScheme.getEncodedMappingScheme(); + } - Objects.requireNonNull(name); lock.acquire(); try { checkBlockSize(length, false); program.checkExclusiveAccess(); - checkRange(start, length); - overlayAddress.addNoWrap(length - 1);// just to check if length fits in address space + byteMappingScheme.getMappedSourceAddress(mappedAddress, length - 1); // source fit check + if (overlay) { + start = createOverlaySpace(name, start, length); + } + else { + checkRange(start, length); + } try { MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, - start, length, overlayAddress, false, MemoryBlock.READ); + start, length, mappedAddress, false, MemoryBlock.READ, mappingScheme); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, false); fireBlockAdded(newBlock); return newBlock; } @@ -665,27 +807,46 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { return null; } + /** + * Check new block name for validity + * @param name new block name + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an address space name + */ + void checkBlockName( + String name) + throws IllegalArgumentException, DuplicateNameException { + if (!Memory.isValidAddressSpaceName(name)) { + throw new IllegalArgumentException("Invalid block name: " + name); + } + if (getAddressFactory().getAddressSpace(name) != null) { + throw new DuplicateNameException( + "Block name conflicts with existing address space: " + name); + } + } + @Override public MemoryBlock createBlock(MemoryBlock block, String name, Address start, long length) - throws MemoryConflictException, AddressOverflowException, LockException { - - Objects.requireNonNull(name); + throws MemoryConflictException, AddressOverflowException, LockException, + DuplicateNameException { + checkBlockName(name); lock.acquire(); try { checkBlockSize(length, block.isInitialized()); program.checkExclusiveAccess(); checkRange(start, length); - try { - Address overlayAddr = null; + Address mappedAddr = null; + int mappingScheme = 0; if (block.isMapped()) { MemoryBlockSourceInfo info = block.getSourceInfos().get(0); - overlayAddr = info.getMappedRange().get().getMinAddress(); + mappingScheme = info.getByteMappingScheme().get().getEncodedMappingScheme(); + mappedAddr = info.getMappedRange().get().getMinAddress(); } MemoryBlockDB newBlock = adapter.createBlock(block.getType(), name, start, length, - overlayAddr, block.isInitialized(), block.getPermissions()); + mappedAddr, block.isInitialized(), block.getPermissions(), mappingScheme); initializeBlocks(); - addBlockAddresses(newBlock); + addBlockAddresses(newBlock, !block.isMapped() && block.isInitialized()); fireBlockAdded(newBlock); return newBlock; } @@ -731,7 +892,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { MemoryBlockDB memBlock = (MemoryBlockDB) block; Address oldStartAddr = block.getStart(); - if (block.getType() == MemoryBlockType.OVERLAY) { + if (block.isOverlay()) { throw new IllegalArgumentException("Overlay blocks cannot be moved"); } if (newStartAddr.getAddressSpace().isOverlaySpace()) { @@ -788,9 +949,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { if (addr.equals(memBlock.getStart())) { throw new IllegalArgumentException("Split cannot be done on block start address"); } - if (memBlock.getType() == MemoryBlockType.OVERLAY) { + if (memBlock.isOverlay()) { throw new IllegalArgumentException("Split cannot be done on an overlay block"); } + if (memBlock.isMapped()) { + if (memBlock.getType() == MemoryBlockType.BIT_MAPPED) { + throw new IllegalArgumentException( + "Split cannot be done on a bit-mapped block"); + } + ByteMappingScheme byteMappingScheme = + memBlock.getSourceInfos().get(0).getByteMappingScheme().get(); + if (!byteMappingScheme.isOneToOneMapping()) { + throw new IllegalArgumentException( + "Split cannot be done on a byte-mapped block with " + byteMappingScheme); + } + } if (memBlock.getType() == MemoryBlockType.BIT_MAPPED) { throw new IllegalArgumentException("Split cannot be done on a bit mapped block"); } @@ -850,7 +1023,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } private void checkPreconditionsForJoining(MemoryBlock block1, MemoryBlock block2) - throws MemoryBlockException, NotFoundException, LockException { + throws MemoryBlockException, LockException { program.checkExclusiveAccess(); if (liveMemory != null) { @@ -874,16 +1047,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { private void checkBlockForJoining(MemoryBlock block) { checkBlock(block); - switch (block.getType()) { - case BIT_MAPPED: - throw new IllegalArgumentException("Cannot join bit mapped blocks"); - case BYTE_MAPPED: - throw new IllegalArgumentException("Cannot join byte mapped blocks"); - case OVERLAY: - throw new IllegalArgumentException("Cannot join overlay blocks"); - case DEFAULT: - default: - // do nothing, these types are ok for joining + if (block.isOverlay()) { + throw new IllegalArgumentException("Cannot join overlay blocks"); + } + if (block.isMapped()) { + throw new IllegalArgumentException("Cannot join mapped blocks"); } } @@ -909,8 +1077,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { throw new IllegalArgumentException( "Only an Uninitialized Block may be converted to an Initialized Block"); } - MemoryBlockType type = unitializedBlock.getType(); - if (!((type == MemoryBlockType.DEFAULT) || (type == MemoryBlockType.OVERLAY))) { + if (unitializedBlock.getType() != MemoryBlockType.DEFAULT) { throw new IllegalArgumentException("Block is of a type that cannot be initialized"); } long size = unitializedBlock.getSize(); @@ -922,6 +1089,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { memBlock.initializeBlock(initialValue); allInitializedAddrSet.addRange(memBlock.getStart(), memBlock.getEnd()); initializedLoadedAddrSet.addRange(memBlock.getStart(), memBlock.getEnd()); + if (!memBlock.isMapped()) { + // update initialized sets for all blocks mapped to memBlock + updateMappedAddresses(memBlock, true); + } fireBlockChanged(memBlock); fireBytesChanged(memBlock.getStart(), (int) memBlock.getSize()); return memBlock; @@ -949,16 +1120,20 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { throw new IllegalArgumentException( "Only an Initialized Block may be converted to an Uninitialized Block"); } - MemoryBlockType type = initializedBlock.getType(); - if (!((type == MemoryBlockType.DEFAULT) || (type == MemoryBlockType.OVERLAY))) { + if (initializedBlock.getType() != MemoryBlockType.DEFAULT) { throw new IllegalArgumentException( "Block is of a type that cannot be uninitialized"); } MemoryBlockDB memBlock = (MemoryBlockDB) initializedBlock; try { +// FIXME: clear instructions in initializedBlock or any block which maps to it memBlock.uninitializeBlock(); allInitializedAddrSet.deleteRange(memBlock.getStart(), memBlock.getEnd()); initializedLoadedAddrSet.deleteRange(memBlock.getStart(), memBlock.getEnd()); + if (!memBlock.isMapped()) { + // update initialized sets for all blocks mapped to memBlock + updateMappedAddresses(memBlock, false); + } fireBlockChanged(memBlock); fireBytesChanged(memBlock.getStart(), (int) memBlock.getSize()); return memBlock; @@ -979,7 +1154,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { public Address findBytes(Address addr, byte[] bytes, byte[] masks, boolean forward, TaskMonitor monitor) { if (monitor == null) { - monitor = TaskMonitorAdapter.DUMMY_MONITOR; + monitor = TaskMonitor.DUMMY; } AddressIterator it = initializedLoadedAddrSet.getAddresses(addr, forward); @@ -997,6 +1172,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { monitor.incrementProgress(-moffset); } catch (AddressOverflowException e) { + // ignore } continue; } @@ -1025,7 +1201,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { public Address findBytes(Address startAddr, Address endAddr, byte[] bytes, byte[] masks, boolean forward, TaskMonitor monitor) { if (monitor == null) { - monitor = TaskMonitorAdapter.DUMMY_MONITOR; + monitor = TaskMonitor.DUMMY; } AddressIterator it = allInitializedAddrSet.getAddresses(startAddr, forward); byte[] b = new byte[bytes.length]; @@ -1722,7 +1898,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { /** * Tests if the given addressSpace (overlay space) is used by any blocks. If not, it * removes the space. - * @param addressSpace + * @param addressSpace overlay address space to be removed */ private void checkRemoveAddressSpace(AddressSpace addressSpace) { lock.acquire(); @@ -1771,43 +1947,72 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { } /** - * Gets the intersected set of addresses between a list of mapped memory blocks, and some other + * Gets the intersected set of addresses between a mapped memory block, and some other * address set. * - * @param block The mapped memory block to use in the intersection. + * @param mappedBlock The mapped memory block to use in the intersection. * @param set Some other address set to use in the intersection. * @return The intersected set of addresses between 'mappedMemoryBlock' and other address set */ - private AddressSet getMappedIntersection(MemoryBlock block, AddressSet set) { + private AddressSet getMappedIntersection(MemoryBlock mappedBlock, AddressSet set) { AddressSet mappedIntersection = new AddressSet(); - List sourceInfos = block.getSourceInfos(); + List sourceInfos = mappedBlock.getSourceInfos(); // mapped blocks can only ever have one sourceInfo MemoryBlockSourceInfo info = sourceInfos.get(0); AddressRange range = info.getMappedRange().get(); AddressSet resolvedIntersection = set.intersect(new AddressSet(range)); for (AddressRange resolvedRange : resolvedIntersection) { - mappedIntersection.add(getMappedRange(block, resolvedRange)); + AddressRange mappedRange = getMappedRange(mappedBlock, resolvedRange); + if (mappedRange != null) { + mappedIntersection.add(mappedRange); + } } return mappedIntersection; } /** * Converts the given address range back from the source range back to the mapped range. + * NOTE: It is important that the specified mappedSourceRange is restricted to the + * mapped source area of the specified mappedBlock. + * @param mappedBlock mapped memory block + * @param mappedSourceRange source range which maps into mappedBlock. + * @return mapped range or null if source range not mapped to block */ - private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange resolvedRange) { + private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange mappedSourceRange) { Address start, end; - MemoryBlockSourceInfo info = mappedBlock.getSourceInfos().get(0); - long startOffset = - resolvedRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress()); - boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED; - if (isBitMapped) { - start = mappedBlock.getStart().add(startOffset * 8); - end = start.add((resolvedRange.getLength() * 8) - 1); + long sourceRangeLength = mappedSourceRange.getLength(); + if (sourceRangeLength <= 0) { + throw new AssertException("invalid mapped source range length"); } - else { - start = mappedBlock.getStart().add(startOffset); - end = start.add(resolvedRange.getLength() - 1); + MemoryBlockSourceInfo info = mappedBlock.getSourceInfos().get(0); + + long startOffset = + mappedSourceRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress()); + boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED; + try { + if (isBitMapped) { + startOffset *= 8; + start = mappedBlock.getStart().addNoWrap(startOffset); + long endOffset = startOffset + (sourceRangeLength * 8) - 1; + // since end may only partially consume a byte we must limit end address + end = (endOffset < mappedBlock.getSize()) + ? mappedBlock.getStart().addNoWrap(endOffset) + : mappedBlock.getEnd(); + } + else { // Byte mapped + ByteMappingScheme byteMappingScheme = info.getByteMappingScheme().get(); + start = + byteMappingScheme.getMappedAddress(mappedBlock, startOffset, false); + long endOffset = startOffset + sourceRangeLength - 1; + end = byteMappingScheme.getMappedAddress(mappedBlock, endOffset, true); + if (start == null || start.compareTo(end) > 0) { + return null; // mappedSourceRange corresponds to non-mapped/skipped bytes + } + } + } + catch (AddressOverflowException e) { + throw new AddressOutOfBoundsException(e.getMessage()); } return new AddressRangeImpl(start, end); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java index f0cbc98124..3cc6ae4ded 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java @@ -46,8 +46,30 @@ abstract class MemoryMapDBAdapter { public static final int SUB_TYPE_COL = MemoryMapDBAdapterV3.V3_SUB_TYPE_COL; public static final int SUB_LENGTH_COL = MemoryMapDBAdapterV3.V3_SUB_LENGTH_COL; public static final int SUB_START_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_START_OFFSET_COL; - public static final int SUB_SOURCE_ID_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_ID_COL; - public static final int SUB_SOURCE_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_OFFSET_COL; + + /** + * Subblock record int data1 usage: + *
      + *
    • {@link BufferSubMemoryBlock} - data buffer ID
    • + *
    • {@link FileBytesSubMemoryBlock} - file bytes layered data buffer ID
    • + *
    • {@link ByteMappedSubMemoryBlock} - encoded byte mapping scheme
    • + *
    • {@link BitMappedSubMemoryBlock} - (not used) 0
    • + *
    • {@link UninitializedSubMemoryBlock} - (not used) 0
    • + *
    + */ + public static final int SUB_INT_DATA1_COL = MemoryMapDBAdapterV3.V3_SUB_INT_DATA1_COL; + + /** + * Subblock record long data2 usage: + *
      + *
    • {@link BufferSubMemoryBlock} - (not used) 0
    • + *
    • {@link FileBytesSubMemoryBlock} - starting byte offset within file bytes buffer
    • + *
    • {@link ByteMappedSubMemoryBlock} - encoded mapped source address
    • + *
    • {@link BitMappedSubMemoryBlock} - encoded mapped source address
    • + *
    • {@link UninitializedSubMemoryBlock} - (not used) 0
    • + *
    + */ + public static final int SUB_LONG_DATA2_COL = MemoryMapDBAdapterV3.V3_SUB_LONG_DATA2_COL; public static final byte SUB_TYPE_BIT_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BIT_MAPPED; public static final byte SUB_TYPE_BYTE_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BYTE_MAPPED; @@ -121,7 +143,7 @@ abstract class MemoryMapDBAdapter { } newBlock = newAdapter.createBlock(block.getType(), block.getName(), block.getStart(), - block.getSize(), mappedAddress, false, block.getPermissions()); + block.getSize(), mappedAddress, false, block.getPermissions(), 0); } newBlock.setComment(block.getComment()); newBlock.setSourceName(block.getSourceName()); @@ -192,16 +214,19 @@ abstract class MemoryMapDBAdapter { * @param name the name of the block. * @param startAddr the start address of the block * @param length the size of the block - * @param mappedAddress the address at which to overlay this block. (If the type is overlay) - * @param initializeBytes if true, creates a database buffer for the bytes in the block + * @param mappedAddress the starting byte source address at which to map + * the block. (used for bit/byte-mapped blocks only) + * @param initializeBytes if true, creates a database buffer for storing the + * bytes in the block (applies to initialized default blocks only) * @param permissions the new block permissions + * @param encodedMappingScheme byte mapping scheme (used by byte-mapped blocks only) * @return new memory block * @throws IOException if a database IO error occurs. * @throws AddressOverflowException if block length is too large for the underlying space */ abstract MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions) - throws AddressOverflowException, IOException; + long length, Address mappedAddress, boolean initializeBytes, int permissions, + int encodedMappingScheme) throws AddressOverflowException, IOException; /** * Deletes the given memory block. @@ -253,15 +278,13 @@ abstract class MemoryMapDBAdapter { * sub block starts * @param length the length of this sub block * @param subType the type of the subBlock - * @param sourceID if the type is a buffer, then this is the buffer id. If the type is file bytes, - * then this is the FileBytes id. - * @param sourceOffset if the type is file bytes, then this is the offset into the filebytes. If - * the type is mapped, then this is the encoded mapped address. + * @param data1 subblock implementation specific integer data + * @param data2 subblock implementation specific long data * @return the newly created record. * @throws IOException if a database error occurs */ abstract Record createSubBlockRecord(long memBlockId, long startingOffset, long length, - byte subType, int sourceID, long sourceOffset) throws IOException; + byte subType, int data1, long data2) throws IOException; /** * Creates a new memory block. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java index 96c87fa3db..30decfc948 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java @@ -156,7 +156,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { return new ByteMappedSubMemoryBlock(this, record); case MemoryMapDBAdapterV2.INITIALIZED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER); - record.setLongValue(SUB_SOURCE_OFFSET_COL, bufID); + record.setLongValue(SUB_LONG_DATA2_COL, bufID); return new BufferSubMemoryBlock(this, record); case MemoryMapDBAdapterV2.UNINITIALIZED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED); @@ -223,7 +223,8 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address overlayAddr, boolean initializeBytes, int permissions) + long length, Address overlayAddr, boolean initializeBytes, int permissions, + int mappingScheme) throws IOException { throw new UnsupportedOperationException(); } @@ -253,7 +254,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { @Override Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType, - int sourceID, long sourceOffset) throws IOException { + int data1, long data2) throws IOException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java index 69b67e6086..7a27671123 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java @@ -124,15 +124,15 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { switch (type) { case MemoryMapDBAdapterV2.BIT_MAPPED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED); - record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr); + record.setLongValue(MemoryMapDBAdapter.SUB_LONG_DATA2_COL, overlayAddr); return new BitMappedSubMemoryBlock(this, record); case MemoryMapDBAdapterV2.BYTE_MAPPED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED); - record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr); + record.setLongValue(MemoryMapDBAdapter.SUB_LONG_DATA2_COL, overlayAddr); return new ByteMappedSubMemoryBlock(this, record); case MemoryMapDBAdapterV2.INITIALIZED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER); - record.setIntValue(SUB_SOURCE_ID_COL, bufID); + record.setIntValue(SUB_INT_DATA1_COL, bufID); return new BufferSubMemoryBlock(this, record); case MemoryMapDBAdapterV2.UNINITIALIZED: record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED); @@ -161,7 +161,8 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions) + long length, Address mappedAddress, boolean initializeBytes, int permissions, + int mappingScheme) throws AddressOverflowException, IOException { throw new UnsupportedOperationException(); } @@ -216,7 +217,7 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { @Override Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType, - int sourceID, long sourceOffset) throws IOException { + int data1, long data2) throws IOException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java index bc462f073d..fa5afc1f6e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java @@ -48,8 +48,8 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { public static final int V3_SUB_TYPE_COL = 1; public static final int V3_SUB_LENGTH_COL = 2; public static final int V3_SUB_START_OFFSET_COL = 3; - public static final int V3_SUB_SOURCE_ID_COL = 4; - public static final int V3_SUB_SOURCE_OFFSET_COL = 5; + public static final int V3_SUB_INT_DATA1_COL = 4; + public static final int V3_SUB_LONG_DATA2_COL = 5; public static final byte V3_SUB_TYPE_BIT_MAPPED = 0; public static final byte V3_SUB_TYPE_BYTE_MAPPED = 1; @@ -186,17 +186,19 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions) - throws AddressOverflowException, IOException { + long length, Address mappedAddress, boolean initializeBytes, int permissions, + int encodedMappingScheme) throws AddressOverflowException, IOException { - if (initializeBytes) { - return createInitializedBlock(name, startAddr, null, length, permissions); - } - else if (blockType == MemoryBlockType.BIT_MAPPED) { + if (blockType == MemoryBlockType.BIT_MAPPED) { return createBitMappedBlock(name, startAddr, length, mappedAddress, permissions); } - else if (blockType == MemoryBlockType.BYTE_MAPPED) { - return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions); + if (blockType == MemoryBlockType.BYTE_MAPPED) { + return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions, + encodedMappingScheme); + } + // DEFAULT block type + if (initializeBytes) { + return createInitializedBlock(name, startAddr, null, length, permissions); } return createUnitializedBlock(name, startAddr, length, permissions); } @@ -265,13 +267,14 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { MemoryBlockDB createBitMappedBlock(String name, Address startAddress, long length, Address mappedAddress, int permissions) throws IOException, AddressOverflowException { return createMappedBlock(V3_SUB_TYPE_BIT_MAPPED, name, startAddress, length, mappedAddress, - permissions); + permissions, 0); } MemoryBlockDB createByteMappedBlock(String name, Address startAddress, long length, - Address mappedAddress, int permissions) throws IOException, AddressOverflowException { + Address mappedAddress, int permissions, int mappingScheme) + throws IOException, AddressOverflowException { return createMappedBlock(V3_SUB_TYPE_BYTE_MAPPED, name, startAddress, length, mappedAddress, - permissions); + permissions, mappingScheme); } @Override @@ -296,7 +299,8 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } private MemoryBlockDB createMappedBlock(byte type, String name, Address startAddress, - long length, Address mappedAddress, int permissions) + long length, Address mappedAddress, int permissions, + int mappingScheme) throws IOException, AddressOverflowException { updateAddressMapForAllAddresses(startAddress, length); @@ -305,7 +309,7 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { long key = blockRecord.getKey(); long encoded = addrMap.getKey(mappedAddress, true); - Record subRecord = createSubBlockRecord(key, 0, length, type, 0, encoded); + Record subRecord = createSubBlockRecord(key, 0, length, type, mappingScheme, encoded); subBlocks.add(createSubBlock(subRecord)); memBlockTable.putRecord(blockRecord); @@ -350,15 +354,15 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { @Override Record createSubBlockRecord(long parentKey, long startingOffset, long length, byte type, - int sourceId, long sourceOffset) throws IOException { + int data1, long data2) throws IOException { Record record = V3_SUB_BLOCK_SCHEMA.createRecord(subBlockTable.getKey()); record.setLongValue(V3_SUB_PARENT_ID_COL, parentKey); record.setByteValue(V3_SUB_TYPE_COL, type); record.setLongValue(V3_SUB_LENGTH_COL, length); record.setLongValue(V3_SUB_START_OFFSET_COL, startingOffset); - record.setIntValue(V3_SUB_SOURCE_ID_COL, sourceId); - record.setLongValue(V3_SUB_SOURCE_OFFSET_COL, sourceOffset); + record.setIntValue(V3_SUB_INT_DATA1_COL, data1); + record.setLongValue(V3_SUB_LONG_DATA2_COL, data2); subBlockTable.putRecord(record); return record; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java index 03aae05b7d..eb655c95a3 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java @@ -18,8 +18,6 @@ package ghidra.program.database.mem; import java.io.IOException; import db.Record; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressRange; import ghidra.program.model.mem.*; /** @@ -183,11 +181,13 @@ abstract class SubMemoryBlock implements Comparable { } /** - * Get the {@link MemoryBlockType} for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED + * Get the {@link MemoryBlockType} for this block: DEFAULT, BIT_MAPPED, or BYTE_MAPPED * - * @return the type for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED + * @return the type for this block: DEFAULT, BIT_MAPPED, or BYTE_MAPPED */ - protected abstract MemoryBlockType getType(); + protected MemoryBlockType getType() { + return MemoryBlockType.DEFAULT; + } /** * Returns the {@link MemoryBlockSourceInfo} object for this SubMemoryBlock @@ -238,18 +238,6 @@ abstract class SubMemoryBlock implements Comparable { return false; } - /** - * Gets the list of BytesSourceRanges from this sub block for the given memBlockOffset and associates - * it with the given {@link AddressRange} - * @param block the {@link MemoryBlock} that generated the BytesSourceSet. - * @param start the program address for which to get a ByteSourceSet - * @param memBlockOffset the offset from the beginning of the containing MemoryBlock. - * @param size the size of region to get byte sources - * @return the set of ByteSourceRanges which maps program addresses to byte source locations. - */ - protected abstract ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start, - long memBlockOffset, long size); - @Override public int compareTo(SubMemoryBlock o) { long result = getStartingOffset() - o.getStartingOffset(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java index b3cee0938d..e30c47c253 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/UninitializedSubMemoryBlock.java @@ -18,8 +18,7 @@ package ghidra.program.database.mem; import java.io.IOException; import db.Record; -import ghidra.program.model.address.Address; -import ghidra.program.model.mem.*; +import ghidra.program.model.mem.MemoryAccessException; /** * Implementation of SubMemoryBlock for uninitialized blocks. @@ -28,7 +27,6 @@ class UninitializedSubMemoryBlock extends SubMemoryBlock { UninitializedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) { super(adapter, record); - subBlockOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL); } @Override @@ -53,12 +51,12 @@ class UninitializedSubMemoryBlock extends SubMemoryBlock { @Override public void putByte(long offset, byte b) throws MemoryAccessException { - throw new MemoryAccessException("Attempted to read from uninitialized block"); + throw new MemoryAccessException("Attempted to write to an uninitialized block"); } @Override public int putBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException { - throw new MemoryAccessException("Attempted to read from uninitialized block"); + throw new MemoryAccessException("Attempted to write to an uninitialized block"); } @Override @@ -71,11 +69,6 @@ class UninitializedSubMemoryBlock extends SubMemoryBlock { return true; } - @Override - protected MemoryBlockType getType() { - return MemoryBlockType.DEFAULT; - } - @Override protected SubMemoryBlock split(long memBlockOffset) throws IOException { // convert from offset in block to offset in this sub block @@ -96,11 +89,4 @@ class UninitializedSubMemoryBlock extends SubMemoryBlock { return ""; } - @Override - protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start, - long memBlockOffset, - long size) { - return new ByteSourceRangeList(); - } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java index 5b6aa846a7..f5a3372aa0 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/Address.java @@ -406,4 +406,23 @@ public interface Address extends Comparable
    { */ public boolean isExternalAddress(); + /** + * Return the minimum of two addresses using Address.compareTo + * @param a first address + * @param b second address + * @return minimum of two addresses + */ + public static Address min(Address a, Address b) { + return a.compareTo(b) <= 0 ? a : b; + } + + /** + * Return the maximum of two addresses using Address.compareTo + * @param a first address + * @param b second address + * @return maximum of two addresses + */ + public static Address max(Address a, Address b) { + return a.compareTo(b) > 0 ? a : b; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetViewAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetViewAdapter.java index 6b3d46db40..c11d250fae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetViewAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetViewAdapter.java @@ -172,4 +172,9 @@ public class AddressSetViewAdapter implements AddressSetView { public Address findFirstAddressInCommon(AddressSetView otherSet) { return set.findFirstAddressInCommon(otherSet); } + + @Override + public String toString() { + return set.toString(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java index 6f0f759431..3e137e4f41 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java @@ -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.util.exception.*; @@ -109,7 +108,8 @@ public interface Memory extends AddressSetView { /** * Create an initialized memory block and add it to this Memory. - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start start address of the block * @param is source of the data used to fill the block or null for zero initialization. * @param length the size of the block @@ -124,43 +124,45 @@ public interface Memory extends AddressSetView { * @throws AddressOverflowException if the start is beyond the * address space * @throws CancelledException user cancelled operation - * @throws DuplicateNameException if overlay is true and there is already an overlay address - * space with the same name as this memory block + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ public MemoryBlock createInitializedBlock(String name, Address start, InputStream is, long length, TaskMonitor monitor, boolean overlay) throws LockException, MemoryConflictException, AddressOverflowException, - CancelledException, DuplicateNameException; + CancelledException, IllegalArgumentException, DuplicateNameException; /** * Create an initialized memory block and add it to this Memory. - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start start of the block * @param size block length (positive non-zero value required) * @param initialValue initialization value for every byte in the block. * @param monitor progress monitor, may be null. * @param overlay if true, the block will be created as an OVERLAY which means that a new * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address paramaeter, but in the new address space. + * offset as the given start address parameter, but in the new address space. * @return new Initialized Memory Block - * @throws DuplicateNameException if overlay is true and there is already an overlay address - * space with the same name as this memory block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a * previous block * @throws AddressOverflowException if the start is beyond the * address space + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name * @throws CancelledException user cancelled operation */ public MemoryBlock createInitializedBlock(String name, Address start, long size, byte initialValue, TaskMonitor monitor, boolean overlay) - throws LockException, DuplicateNameException, MemoryConflictException, - AddressOverflowException, CancelledException; + throws LockException, IllegalArgumentException, DuplicateNameException, + MemoryConflictException, AddressOverflowException, CancelledException; /** * Create an initialized memory block using bytes from a {@link FileBytes} object. * - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start starting address of the block * @param fileBytes the {@link FileBytes} object to use as the underlying source of bytes. * @param offset the offset into the FileBytes for the first byte of this memory block. @@ -170,86 +172,138 @@ public interface Memory extends AddressSetView { * offset as the given start address parameter, but in the new address space. * @return new Initialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) - * @throws DuplicateNameException if overlay is true and there is already an overlay address - * space with the same name as this memory block * @throws MemoryConflictException if the new block overlaps with a * previous block * @throws AddressOverflowException if the start is beyond the address space * @throws IndexOutOfBoundsException if file bytes range specified by offset and size * is out of bounds for the specified fileBytes. + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes, - long offset, long size, boolean overlay) throws LockException, DuplicateNameException, - MemoryConflictException, AddressOverflowException; + long offset, long size, boolean overlay) throws LockException, IllegalArgumentException, + DuplicateNameException, MemoryConflictException, AddressOverflowException; /** * Create an uninitialized memory block and add it to this Memory. - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start start of the block * @param size block length * @param overlay if true, the block will be created as an OVERLAY which means that a new * overlay address space will be created and the block will have a starting address at the same - * offset as the given start address paramaeter, but in the new address space. + * offset as the given start address parameter, but in the new address space. * @return new Uninitialized Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a * previous block * @throws AddressOverflowException if the start is beyond the * address space - * @throws DuplicateNameException if overlay is true and there is already an overlay address - * space with the same name as this memory block + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ public MemoryBlock createUninitializedBlock(String name, Address start, long size, - boolean overlay) throws LockException, DuplicateNameException, MemoryConflictException, - AddressOverflowException; + boolean overlay) throws LockException, IllegalArgumentException, DuplicateNameException, + MemoryConflictException, AddressOverflowException; /** * Create a bit overlay memory block and add it to this Memory. - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start start of the block * @param mappedAddress start address in the source block for the * beginning of this block * @param length block length + * @param overlay if true, the block will be created as an OVERLAY which means that a new + * overlay address space will be created and the block will have a starting address at the same + * offset as the given start address parameter, but in the new address space. * @return new Bit Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) * @throws MemoryConflictException if the new block overlaps with a * previous block * @throws MemoryConflictException if the new block overlaps with a * previous block - * @throws AddressOverflowException if the start is beyond the - * address space + * @throws AddressOverflowException if block specification exceeds bounds of address space + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress, - long length) throws LockException, MemoryConflictException, AddressOverflowException; + long length, boolean overlay) throws LockException, MemoryConflictException, + AddressOverflowException, + IllegalArgumentException, DuplicateNameException; /** - * Create a memory block that uses the bytes located at a different location. - * @param name block name + * Create a memory block that uses the bytes located at a different location with a 1:1 + * byte mapping scheme. + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) * @param start start of the block * @param mappedAddress start address in the source block for the * beginning of this block * @param length block length + * @param byteMappingScheme byte mapping scheme (may be null for 1:1 mapping) + * @param overlay if true, the block will be created as an OVERLAY which means that a new + * overlay address space will be created and the block will have a starting address at the same + * offset as the given start address parameter, but in the new address space. * @return new Bit Memory Block * @throws LockException if exclusive lock not in place (see haveLock()) - * @throws MemoryConflictException if the new block overlaps with a - * previous block + * @throws MemoryConflictException if the new block overlaps with a previous block + * @throws AddressOverflowException if block specification exceeds bounds of address space + * @throws IllegalArgumentException if invalid block name + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ 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, DuplicateNameException; + + /** + * Create a memory block that uses the bytes located at a different location with a 1:1 + * byte mapping scheme. + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules) + * @param start start of the block + * @param mappedAddress start address in the source block for the + * beginning of this block + * @param length block length + * @param overlay if true, the block will be created as an OVERLAY which means that a new + * overlay address space will be created and the block will have a starting address at the same + * offset as the given start address parameter, but in the new address space. + * @return new Bit Memory Block + * @throws LockException if exclusive lock not in place (see haveLock()) + * @throws MemoryConflictException if the new block overlaps with a previous block + * @throws AddressOverflowException if block specification exceeds bounds of address space + * @throws IllegalArgumentException if invalid block name + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name + */ + default public MemoryBlock createByteMappedBlock(String name, Address start, + Address mappedAddress, long length, boolean overlay) throws LockException, + MemoryConflictException, + AddressOverflowException, IllegalArgumentException, DuplicateNameException { + return createByteMappedBlock(name, start, mappedAddress, length, null, overlay); + } /** * Creates a MemoryBlock at the given address with the same properties - * as block, and adds it to this Memory. + * as block, and adds it to this Memory. Initialized Default blocks will + * have block filled with 0's. Method will only create physical space blocks + * and will not create an overlay block. * @param block source block - * @param name block name + * @param name block name (See {@link Memory#isValidAddressSpaceName(String)} for + * naming rules). * @param start start of the block * @param length the size of the new block. + * @return new block * @throws LockException if exclusive lock not in place (see haveLock()) + * @throws MemoryConflictException if block specification conflicts with an existing block * @throws AddressOverflowException if the new memory block would extend * beyond the end of the address space. + * @throws IllegalArgumentException if invalid block name specified + * @throws DuplicateNameException if name conflicts with an existing address space/overlay name */ public MemoryBlock createBlock(MemoryBlock block, String name, Address start, long length) - throws LockException, MemoryConflictException, AddressOverflowException; + throws LockException, IllegalArgumentException, MemoryConflictException, + AddressOverflowException, DuplicateNameException; /** * Remove the memory block. @@ -760,4 +814,23 @@ public interface Memory extends AddressSetView { * null if the address is not in memory. */ public AddressSourceInfo getAddressSourceInfo(Address address); + + /** + * Validate the given address space or block name: cannot be null, cannot be an empty string, cannot contain blank + * or reserved characters (e.g., colon). + * @return true if name is valid else false + */ + public static boolean isValidAddressSpaceName(String name) { + if (name == null || name.length() == 0) { + return false; + } + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (c < 0x20 || c >= 0x7f || c == ':') { + return false; + } + } + return true; + } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java index bb48bf53b9..92350f4ca4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java @@ -22,6 +22,7 @@ import java.util.List; import ghidra.framework.store.LockException; import ghidra.program.model.address.Address; import ghidra.program.model.listing.Program; +import ghidra.util.NamingUtilities; import ghidra.util.exception.DuplicateNameException; /** @@ -86,12 +87,15 @@ public interface MemoryBlock extends Serializable, Comparable { public String getName(); /** - * Set the name for this block. + * Set the name for this block (See {@link NamingUtilities#isValidName(String)} for + * naming rules). Specified name must not conflict with an address space name. * @param name the new name for this block. - * @throws DuplicateNameException + * @throws DuplicateNameException if name conflicts with an address space name + * @throws IllegalArgumentException if invalid name specified * @throws LockException renaming an Overlay block without exclusive access */ - public void setName(String name) throws DuplicateNameException, LockException; + public void setName(String name) + throws IllegalArgumentException, DuplicateNameException, LockException; /** * Get the comment associated with this block. @@ -241,7 +245,7 @@ public interface MemoryBlock extends Serializable, Comparable { public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException; /** - * Get the type for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED + * Get the type for this block: DEFAULT, BIT_MAPPED, or BYTE_MAPPED */ public MemoryBlockType getType(); @@ -255,6 +259,12 @@ public interface MemoryBlock extends Serializable, Comparable { */ public boolean isMapped(); + /** + * Returns true if this is an overlay block (i.e., contained within overlay space). + * @return true if this is an overlay block + */ + public boolean isOverlay(); + /** * Returns true if this memory block is a real loaded block (i.e. RAM) and not a special block * containing file header data such as debug sections. @@ -285,5 +295,4 @@ public interface MemoryBlock extends Serializable, Comparable { MemoryBlock block = memory.getBlock(address); return block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName()); } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockSourceInfo.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockSourceInfo.java index 703b58172a..8cfe596307 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockSourceInfo.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockSourceInfo.java @@ -17,6 +17,7 @@ package ghidra.program.model.mem; import java.util.Optional; +import ghidra.program.database.mem.ByteMappingScheme; import ghidra.program.database.mem.FileBytes; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; @@ -75,13 +76,20 @@ public interface MemoryBlockSourceInfo { long getFileBytesOffset(Address address); /** - * Returns an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped + * Returns an {@link Optional} {@link AddressRange} for the mapped addresses if this is a mapped * memory block (bit mapped or byte mapped). Otherwise, the Optional is empty. - * @return an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped + * @return an {@link Optional} {@link AddressRange} for the mapped addresses if this is a mapped * memory block */ Optional getMappedRange(); + /** + * Returns an {@link Optional} {@link ByteMappingScheme} employed if this is a byte-mapped + * memory block. Otherwise, the Optional is empty. + * @return an {@link Optional} {@link ByteMappingScheme} employed if this is a byte-mapped memory block. + */ + Optional getByteMappingScheme(); + /** * Returns the containing Memory Block * @return the containing Memory Block diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java index 536aa8fffb..83fec861ba 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java @@ -129,6 +129,11 @@ public class MemoryBlockStub implements MemoryBlock { throw new UnsupportedOperationException(); } + @Override + public boolean isOverlay() { + throw new UnsupportedOperationException(); + } + @Override public String getSourceName() { throw new UnsupportedOperationException(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockType.java index 100fa32ad9..c14e229457 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockType.java @@ -20,8 +20,7 @@ public enum MemoryBlockType { //@formatter:off DEFAULT("Default"), BIT_MAPPED("Bit Mapped"), - BYTE_MAPPED("Byte Mapped"), - OVERLAY("Overlay"); + BYTE_MAPPED("Byte Mapped"); //@formatter:on private String name; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java index f047d6a929..b0a30f33fe 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java @@ -21,8 +21,7 @@ import java.util.Iterator; 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.util.exception.*; @@ -235,13 +234,16 @@ public class MemoryStub 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 { throw new UnsupportedOperationException(); } @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 { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java deleted file mode 100644 index 2546e0a66c..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeListTest.java +++ /dev/null @@ -1,194 +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 static org.junit.Assert.*; - -import java.util.Iterator; -import java.util.Set; - -import org.junit.Test; - -import generic.test.AbstractGenericTest; -import ghidra.program.model.address.*; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.mem.MemoryBlockStub; - -public class ByteSourceRangeListTest extends AbstractGenericTest { - private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0); - private MemoryBlock block = new MemoryBlockStub(); - - @Test - public void testConstructor() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - - ByteSourceRangeList list1 = new ByteSourceRangeList(range1); - ByteSourceRangeList list2 = new ByteSourceRangeList(); - list2.add(range1); - - assertTrue(list1.equals(list2)); - } - - @Test - public void testAdd() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50); - ByteSourceRangeList list1 = new ByteSourceRangeList(range1); - ByteSourceRangeList list2 = new ByteSourceRangeList(range2); - list1.add(list2); - assertEquals(2, list1.getRangeCount()); - assertEquals(range1, list1.get(0)); - assertEquals(range2, list1.get(1)); - } - - @Test - public void testIsEmpty() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRangeList list1 = new ByteSourceRangeList(); - - assertTrue(list1.isEmpty()); - list1.add(range1); - assertFalse(list1.isEmpty()); - } - - @Test - public void testAddNullRange() { - ByteSourceRange range = null; - ByteSourceRangeList list1 = new ByteSourceRangeList(); - list1.add(range); - assertTrue(list1.isEmpty()); - } - - @Test - public void testIterator() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50); - ByteSourceRangeList list1 = new ByteSourceRangeList(range1); - list1.add(range2); - - Iterator it = list1.iterator(); - - assertTrue(it.hasNext()); - assertEquals(range1, it.next()); - assertTrue(it.hasNext()); - assertEquals(range2, it.next()); - assertFalse(it.hasNext()); - } - - @Test - public void testIntersectSimple() { - ByteSourceRangeList list1 = new ByteSourceRangeList(); - list1.add(new ByteSourceRange(block, addr(0), 0x100, 1, 0)); - - ByteSourceRangeList list2 = new ByteSourceRangeList(); - list2.add(new ByteSourceRange(block, addr(0x100), 0x100, 1, 0x10)); - - // note that list1.intersect(list2) is not equal to list2.intersect(list1). - // The byte sources are the same but the corresponding real addresses are calling - // objects byte sources. - - ByteSourceRangeList result = list1.intersect(list2); - assertEquals(1, result.getRangeCount()); - ByteSourceRange range = result.get(0); - - assertEquals(0xf0, range.getSize()); - assertEquals(0x10, range.getOffset()); - assertEquals(block, range.getMemoryBlock()); - assertEquals(1, range.getSourceId()); - assertEquals(addr(0x10), range.getStart()); - assertEquals(addr(0xff), range.getEnd()); - - // now intersect from list2 perspective - result = list2.intersect(list1); - assertEquals(1, result.getRangeCount()); - range = result.get(0); - - assertEquals(0xf0, range.getSize()); - assertEquals(0x10, range.getOffset()); - assertEquals(block, range.getMemoryBlock()); - assertEquals(1, range.getSourceId()); - - assertEquals(addr(0x100), range.getStart()); - assertEquals(addr(0x1ef), range.getEnd()); - - } - - @Test - public void testGetOverlappingBlocks() { - ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00); - MemoryBlock block1 = new MemoryBlockStub(); - ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00); - - // create a byte source overlap with the first block - MemoryBlock block2 = new MemoryBlockStub(); - ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x50); - - ByteSourceRangeList list = new ByteSourceRangeList(); - list.add(range); - list.add(range1); - list.add(range2); - - Set overlappingBlocks = list.getOverlappingBlocks(); - assertEquals(2, overlappingBlocks.size()); - assertTrue(overlappingBlocks.contains(block)); - assertTrue(overlappingBlocks.contains(block2)); - } - - @Test - public void testGetOverlappingBlocksBlocksWhereBlocksAreAdjacentButDontOverlap() { - ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00); - MemoryBlock block1 = new MemoryBlockStub(); - ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00); - - // create a byte source overlap with the first block - MemoryBlock block2 = new MemoryBlockStub(); - ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x100); - - ByteSourceRangeList list = new ByteSourceRangeList(); - list.add(range); - list.add(range1); - list.add(range2); - - Set overlappingBlocks = list.getOverlappingBlocks(); - assertEquals(0, overlappingBlocks.size()); - } - - @Test - public void testGetOverlappingBlocksBlocksWhereBlocksOverlapByExactlyOneByte() { - ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00); - MemoryBlock block1 = new MemoryBlockStub(); - ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00); - - // create a byte source overlap with the first block - MemoryBlock block2 = new MemoryBlockStub(); - ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0xff); - - ByteSourceRangeList list = new ByteSourceRangeList(); - list.add(range); - list.add(range1); - list.add(range2); - - Set overlappingBlocks = list.getOverlappingBlocks(); - assertEquals(2, overlappingBlocks.size()); - assertTrue(overlappingBlocks.contains(block)); - assertTrue(overlappingBlocks.contains(block2)); - } - - private Address addr(long value) { - return space.getAddress(value); - } - -} diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java deleted file mode 100644 index 3cbf620c0e..0000000000 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/ByteSourceRangeTest.java +++ /dev/null @@ -1,119 +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 static org.junit.Assert.*; - -import org.junit.Test; - -import generic.test.AbstractGenericTest; -import ghidra.program.model.address.*; -import ghidra.program.model.mem.MemoryBlock; -import ghidra.program.model.mem.MemoryBlockStub; - -public class ByteSourceRangeTest extends AbstractGenericTest { - private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0); - private MemoryBlock block = new MemoryBlockStub(); - @Test - public void testIntersectNotSameSource() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50); - assertNull(range1.intersect(range2)); - } - - @Test - public void testIntersectOneRangeSimpleOverlap() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 1, 0x60); - - ByteSourceRange intersect = range1.intersect(range2); - assertNotNull(intersect); - assertEquals(addr(0x10), intersect.getStart()); - assertEquals(addr(0x1f), intersect.getEnd()); - assertEquals(0x10, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x60, intersect.getOffset()); - - intersect = range2.intersect(range1); - assertNotNull(intersect); - assertEquals(addr(0x100), intersect.getStart()); - assertEquals(addr(0x10f), intersect.getEnd()); - assertEquals(0x10, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x60, intersect.getOffset()); - } - - @Test - public void testIntersectOneRangeButsAgainsAnother() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 2, 0x70); - - assertNull(range1.intersect(range2)); - assertNull(range2.intersect(range1)); - } - - - @Test - public void testIntersectOneRangeCompletelyInAnother() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x30, 1, 0x40); - - ByteSourceRange intersect = range1.intersect(range2); - assertNotNull(intersect); - assertEquals(addr(0), intersect.getStart()); - assertEquals(addr(0xf), intersect.getEnd()); - assertEquals(0x10, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x50, intersect.getOffset()); - - intersect = range2.intersect(range1); - assertNotNull(intersect); - assertEquals(addr(0x110), intersect.getStart()); - assertEquals(addr(0x11f), intersect.getEnd()); - assertEquals(0x10, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x50, intersect.getOffset()); - } - - @Test - public void testBitMappedIntersect() { - ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50); - ByteSourceRange range2 = new BitMappedByteSourceRange(block, addr(0x100), 1, 0x55, 2); - - ByteSourceRange intersect = range1.intersect(range2); - assertNotNull(intersect); - assertEquals(addr(5), intersect.getStart()); - assertEquals(addr(6), intersect.getEnd()); - assertEquals(2, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x55, intersect.getOffset()); - - intersect = range2.intersect(range1); - assertNotNull(intersect); - assertEquals(addr(0x100), intersect.getStart()); - assertEquals(addr(0x10f), intersect.getEnd()); - assertEquals(2, intersect.getSize()); - assertEquals(1, intersect.getSourceId()); - assertEquals(0x55, intersect.getOffset()); - - } - - private Address addr(long value) { - return space.getAddress(value); - } - -} - diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java index f2225cdd91..075e62afb4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java @@ -142,7 +142,8 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(0, block.getStart().getOffset()); assertEquals(9, block.getEnd().getOffset()); assertTrue(block.getStart().getAddressSpace().isOverlaySpace()); - assertEquals(MemoryBlockType.OVERLAY, block.getType()); + assertEquals(MemoryBlockType.DEFAULT, block.getType()); + assertTrue(block.isOverlay()); List sourceInfos = block.getSourceInfos(); assertEquals(1, sourceInfos.size()); MemoryBlockSourceInfo info = sourceInfos.get(0); @@ -167,7 +168,8 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(0, block.getStart().getOffset()); assertEquals(9, block.getEnd().getOffset()); assertTrue(block.getStart().getAddressSpace().isOverlaySpace()); - assertEquals(MemoryBlockType.OVERLAY, block.getType()); + assertEquals(MemoryBlockType.DEFAULT, block.getType()); + assertTrue(block.isOverlay()); List sourceInfos = block.getSourceInfos(); assertEquals(1, sourceInfos.size()); MemoryBlockSourceInfo info = sourceInfos.get(0); @@ -181,7 +183,7 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testCreateByteMappedBlock() throws Exception { mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false); mem.createUninitializedBlock("test2", addr(50), 50, false); - MemoryBlock block = mem.createByteMappedBlock("mapped", addr(1000), addr(40), 20); + MemoryBlock block = mem.createByteMappedBlock("mapped", addr(1000), addr(40), 20, false); assertEquals(20, block.getSize()); assertEquals("mapped", block.getName()); @@ -217,7 +219,7 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testCreateBitMappedBlock() throws Exception { mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false); mem.createUninitializedBlock("test2", addr(50), 50, false); - MemoryBlock block = mem.createBitMappedBlock("mapped", addr(1000), addr(49), 16); + MemoryBlock block = mem.createBitMappedBlock("mapped", addr(1000), addr(49), 16, false); assertEquals(16, block.getSize()); assertEquals("mapped", block.getName()); @@ -570,7 +572,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testByteMappedGetPutByte() throws Exception { FileBytes fileBytes = createFileBytes(); MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10); - MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20); + MemoryBlock mappedBlock = + mem.createByteMappedBlock("mapped", addr(100), addr(0), 20, false); assertEquals(5, mappedBlock.getByte(addr(105))); assertEquals(5, block1.getByte(addr(5))); mappedBlock.putByte(addr(105), (byte) 87); @@ -581,7 +584,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testByteMappedGetPutBytes() throws Exception { FileBytes fileBytes = createFileBytes(); MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20); + MemoryBlock mappedBlock = + mem.createByteMappedBlock("mapped", addr(100), addr(0), 20, false); byte[] bytes = new byte[10]; mappedBlock.getBytes(addr(100), bytes); checkBytes(bytes, 0); @@ -595,8 +599,10 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testByteMappedJoin() throws Exception { FileBytes fileBytes = createFileBytes(); createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 10); - MemoryBlock mappedBlock2 = mem.createByteMappedBlock("mapped2", addr(110), addr(10), 10); + MemoryBlock mappedBlock1 = + mem.createByteMappedBlock("mapped1", addr(100), addr(0), 10, false); + MemoryBlock mappedBlock2 = + mem.createByteMappedBlock("mapped2", addr(110), addr(10), 10, false); try { mem.join(mappedBlock1, mappedBlock2); fail("Expected exception when joining byte mapped blocks"); @@ -610,7 +616,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testByteMappedSplit() throws Exception { FileBytes fileBytes = createFileBytes(); createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 20); + MemoryBlock mappedBlock1 = + mem.createByteMappedBlock("mapped1", addr(100), addr(0), 20, false); mem.split(mappedBlock1, addr(110)); MemoryBlock[] blocks = mem.getBlocks(); assertEquals(3, blocks.length); @@ -621,7 +628,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testBitMappedGetPutByte() throws Exception { FileBytes fileBytes = createFileBytes(); MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 20); + MemoryBlock mappedBlock = + mem.createBitMappedBlock("mapped1", addr(100), addr(0), 20, false); assertEquals(0, mappedBlock.getByte(addr(100))); assertEquals(0, mappedBlock.getByte(addr(101))); @@ -637,7 +645,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testBitMappedGetPutBytes() throws Exception { FileBytes fileBytes = createFileBytes(); MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 50); + MemoryBlock mappedBlock = + mem.createBitMappedBlock("mapped1", addr(100), addr(0), 50, false); byte[] bytes = new byte[8]; @@ -668,8 +677,10 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testBitMappedJoin() throws Exception { FileBytes fileBytes = createFileBytes(); createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16); - MemoryBlock mappedBlock2 = mem.createBitMappedBlock("mapped2", addr(116), addr(2), 16); + MemoryBlock mappedBlock1 = + mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16, false); + MemoryBlock mappedBlock2 = + mem.createBitMappedBlock("mapped2", addr(116), addr(2), 16, false); try { mem.join(mappedBlock1, mappedBlock2); fail("Expected exception when joining bit mapped blocks"); @@ -683,7 +694,8 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testBitMappedSplit() throws Exception { FileBytes fileBytes = createFileBytes(); createFileBytesBlock(fileBytes, addr(0), 0, 50); - MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16); + MemoryBlock mappedBlock1 = + mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16, false); try { mem.split(mappedBlock1, addr(108)); fail("Expected exception when joining bit mapped blocks"); @@ -693,200 +705,229 @@ public class MemBlockDBTest extends AbstractGenericTest { } } - @Test - public void testGetByteSourceSetForFileBytesBlock() throws Exception { - FileBytes fileBytes = createFileBytes(); - MemoryBlockDB block = (MemoryBlockDB) createFileBytesBlock(fileBytes, addr(0), 10, 50); +// @Test +// public void testGetByteSourceSetForFileBytesBlock() throws Exception { +// FileBytes fileBytes = createFileBytes(); +// MemoryBlockDB block = (MemoryBlockDB) createFileBytesBlock(fileBytes, addr(0), 10, 50); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(5), 10); +// +// // we expect to get a single range ByteSourceSet pointing into the filebytes at offset +// // 15 (10 because block was created at filebytes:10 and 5 because we start at the 5th byte +// // in the block) +// +// assertEquals(1, ranges.getRangeCount()); +// assertEquals(10, ranges.get(0).getSize()); +// assertEquals(5, ranges.get(0).getStart().getOffset()); +// assertEquals(14, ranges.get(0).getEnd().getOffset()); +// assertEquals(fileBytes.getId(), ranges.get(0).getSourceId()); +// assertEquals(15, ranges.get(0).getOffset()); +// } - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(5), 10); - - // we expect to get a single range ByteSourceSet pointing into the filebytes at offset - // 15 (10 because block was created at filebytes:10 and 5 because we start at the 5th byte - // in the block) - - assertEquals(1, ranges.getRangeCount()); - assertEquals(10, ranges.get(0).getSize()); - assertEquals(5, ranges.get(0).getStart().getOffset()); - assertEquals(14, ranges.get(0).getEnd().getOffset()); - assertEquals(fileBytes.getId(), ranges.get(0).getSourceId()); - assertEquals(15, ranges.get(0).getOffset()); - } - - @Test - public void testGetByteSourceSetForBufferBlock() throws Exception { - MemoryBlockDB block = (MemoryBlockDB) mem.createInitializedBlock("test", addr(0), 30, - (byte) 1, TaskMonitor.DUMMY, false); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10); - - // We expect to get to ranges because we made the buffer size small (16) so when we - // created a 30 size block, it had to make two separate sub blocks each with its own - // DBBuffer. The first range should contain the first 6 bytes of the requested range - // and the second buffer should contain the last 4 bytes of request range. - - assertEquals(2, ranges.getRangeCount()); // we have two sublocks so two distinct ranges - assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize()); - - ByteSourceRange range = ranges.get(0); - assertEquals(10, range.getStart().getOffset()); - assertEquals(15, range.getEnd().getOffset()); - assertEquals(6, range.getSize()); - assertEquals(10, range.getOffset()); - - range = ranges.get(1); - assertEquals(16, range.getStart().getOffset()); - assertEquals(19, range.getEnd().getOffset()); - assertEquals(4, range.getSize()); - assertEquals(0, range.getOffset()); - - } - - @Test - public void testGetByteSourceForUndefinedBlock() throws Exception { - MemoryBlockDB block = - (MemoryBlockDB) mem.createUninitializedBlock("test", addr(0), 30, false); - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10); - // undefined blocks have no source bytes - assertTrue(ranges.isEmpty()); - - } - - @Test - public void testGetByteSourceForByteMappedBlock() throws Exception { - mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false); - mem.createUninitializedBlock("test2", addr(15), 20, false); - mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false); - MemoryBlockDB block = - (MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), addr(5), 40); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 30); - - // Uninitialized blocks don't contribute, so we should have 10 address (5 from first and last blocks each). - assertEquals(2, ranges.getRangeCount()); - assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(1005), range.getStart()); - assertEquals(addr(1009), range.getEnd()); - assertEquals(5, range.getSize()); - assertEquals(10, range.getOffset()); - - range = ranges.get(1); - assertEquals(addr(1030), range.getStart()); - assertEquals(addr(1034), range.getEnd()); - assertEquals(5, range.getSize()); - assertEquals(0, range.getOffset()); - } - - @Test - public void testGetByteSourceForBitMappedBlock() throws Exception { - FileBytes fileBytes = createFileBytes(); - createFileBytesBlock(fileBytes, addr(0), 0, 50); - - MemoryBlockDB block = - (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1000), 0x14); - - assertEquals(1, ranges.getRangeCount()); - assertEquals(3, ranges.get(0).getSize()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(0x1000), range.getStart()); - assertEquals(addr(0x1017), range.getEnd()); - assertEquals(3, range.getSize()); - assertEquals(5, range.getOffset()); - } - - @Test - public void testGetByteSourceForBitMappedBlockOffcutStart() throws Exception { - FileBytes fileBytes = createFileBytes(); - createFileBytesBlock(fileBytes, addr(0), 0, 50); - - MemoryBlockDB block = - (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1005), 8); - - assertEquals(1, ranges.getRangeCount()); - assertEquals(2, ranges.get(0).getSize()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(0x1000), range.getStart()); - assertEquals(addr(0x100f), range.getEnd()); - assertEquals(2, range.getSize()); - assertEquals(5, range.getOffset()); - } - - @Test - public void testGetByteSourceForBitMappedBlockOffcutStartNotAtStart() throws Exception { - FileBytes fileBytes = createFileBytes(); - createFileBytesBlock(fileBytes, addr(0), 0, 50); - - MemoryBlockDB block = - (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x44); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1015), 8); - - assertEquals(1, ranges.getRangeCount()); - assertEquals(2, ranges.get(0).getSize()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(0x1010), range.getStart()); - assertEquals(addr(0x101f), range.getEnd()); - assertEquals(2, range.getSize()); - assertEquals(7, range.getOffset()); - } - - @Test - public void testGetByteSourceForBitMappedBlock2() throws Exception { - mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false); - mem.createUninitializedBlock("test2", addr(0x4), 4, false); - mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false); - MemoryBlockDB block = - (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1008), 0x30); - - assertEquals(2, ranges.getRangeCount()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(0x1008), range.getStart()); - assertEquals(addr(0x100f), range.getEnd()); - assertEquals(1, range.getSize()); - assertEquals(3, range.getOffset()); - - range = ranges.get(1); - assertEquals(addr(0x1030), range.getStart()); - assertEquals(addr(0x1037), range.getEnd()); - assertEquals(1, range.getSize()); - assertEquals(0, range.getOffset()); - } - - @Test - public void testGetByteSourceForBitMappedBlock2Offcut() throws Exception { - mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false); - mem.createUninitializedBlock("test2", addr(0x4), 4, false); - mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false); - MemoryBlockDB block = - (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40); - - ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1006), 0x34); - - assertEquals(2, ranges.getRangeCount()); - - ByteSourceRange range = ranges.get(0); - assertEquals(addr(0x1000), range.getStart()); - assertEquals(addr(0x100f), range.getEnd()); - assertEquals(2, range.getSize()); - assertEquals(2, range.getOffset()); - - range = ranges.get(1); - assertEquals(addr(0x1030), range.getStart()); - assertEquals(addr(0x103f), range.getEnd()); - assertEquals(2, range.getSize()); - assertEquals(0, range.getOffset()); - } +// @Test +// public void testGetByteSourceSetForBufferBlock() throws Exception { +// MemoryBlockDB block = (MemoryBlockDB) mem.createInitializedBlock("test", addr(0), 30, +// (byte) 1, TaskMonitor.DUMMY, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10); +// +// // We expect to get to ranges because we made the buffer size small (16) so when we +// // created a 30 size block, it had to make two separate sub blocks each with its own +// // DBBuffer. The first range should contain the first 6 bytes of the requested range +// // and the second buffer should contain the last 4 bytes of request range. +// +// assertEquals(2, ranges.getRangeCount()); // we have two sublocks so two distinct ranges +// assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(10, range.getStart().getOffset()); +// assertEquals(15, range.getEnd().getOffset()); +// assertEquals(6, range.getSize()); +// assertEquals(10, range.getOffset()); +// +// range = ranges.get(1); +// assertEquals(16, range.getStart().getOffset()); +// assertEquals(19, range.getEnd().getOffset()); +// assertEquals(4, range.getSize()); +// assertEquals(0, range.getOffset()); +// +// } +// +// @Test +// public void testGetByteSourceForUndefinedBlock() throws Exception { +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createUninitializedBlock("test", addr(0), 30, false); +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10); +// // undefined blocks have no source bytes +// assertTrue(ranges.isEmpty()); +// +// } +// +// @Test +// public void testGetByteSourceForByteMappedBlock() throws Exception { +// mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false); +// mem.createUninitializedBlock("test2", addr(15), 20, false); +// mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false); +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), addr(5), 40, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 30); // 5, 20, 5 +// +// // Uninitialized blocks don't contribute, so we should have 10 address (5 from first and last blocks each). +// assertEquals(2, ranges.getRangeCount()); +// assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(1005), range.getStart()); +// assertEquals(addr(1009), range.getEnd()); +// assertEquals(5, range.getSize()); +// assertEquals(10, range.getOffset()); +// +// range = ranges.get(1); +// assertEquals(addr(1030), range.getStart()); +// assertEquals(addr(1034), range.getEnd()); +// assertEquals(5, range.getSize()); +// assertEquals(0, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForByteMappedBlockWithScheme() throws Exception { +// mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false); // mapped bytes: 5, 6, .. 9, 10, .. 13, (14 +// mem.createUninitializedBlock("test2", addr(15), 20, false); // mapped bytes: 17, 18, 21, 22, 25, 26, 29, 30, 33, 34 +// mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false); // mapped bytes: .. 37, 38, .. 41, 42), .. 45, 46, .. 49, 50 ... +// MemoryBlockDB block = (MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), +// addr(5), 40, new ByteMappingScheme(2, 4), false); +// +// // NOTE: source range includes skipped bytes within mapped range +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 15); +//// FIXME XXX Expected something different than previous test !! +// // Uninitialized blocks don't contribute, so we should have 16 address (1 from first and 4 from last block each, plus 4 skipped bytes in last block). +//// assertEquals(2, ranges.getRangeCount()); +//// assertEquals(8, ranges.get(0).getSize() + ranges.get(1).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(1005), range.getStart()); +// assertEquals(addr(1005), range.getEnd()); +// assertEquals(1, range.getSize()); +// assertEquals(14, range.getOffset()); +// +// range = ranges.get(1); +// assertEquals(addr(1016), range.getStart()); +// assertEquals(addr(1019), range.getEnd()); +// assertEquals(5, range.getSize()); +// assertEquals(0, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForBitMappedBlock() throws Exception { +// FileBytes fileBytes = createFileBytes(); +// createFileBytesBlock(fileBytes, addr(0), 0, 50); +// +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1000), 0x14); +// +// assertEquals(1, ranges.getRangeCount()); +// assertEquals(3, ranges.get(0).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(0x1000), range.getStart()); +// assertEquals(addr(0x1017), range.getEnd()); +// assertEquals(3, range.getSize()); +// assertEquals(5, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForBitMappedBlockOffcutStart() throws Exception { +// FileBytes fileBytes = createFileBytes(); +// createFileBytesBlock(fileBytes, addr(0), 0, 50); +// +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1005), 8); +// +// assertEquals(1, ranges.getRangeCount()); +// assertEquals(2, ranges.get(0).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(0x1000), range.getStart()); +// assertEquals(addr(0x100f), range.getEnd()); +// assertEquals(2, range.getSize()); +// assertEquals(5, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForBitMappedBlockOffcutStartNotAtStart() throws Exception { +// FileBytes fileBytes = createFileBytes(); +// createFileBytesBlock(fileBytes, addr(0), 0, 50); +// +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x44, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1015), 8); +// +// assertEquals(1, ranges.getRangeCount()); +// assertEquals(2, ranges.get(0).getSize()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(0x1010), range.getStart()); +// assertEquals(addr(0x101f), range.getEnd()); +// assertEquals(2, range.getSize()); +// assertEquals(7, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForBitMappedBlock2() throws Exception { +// mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false); +// mem.createUninitializedBlock("test2", addr(0x4), 4, false); +// mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false); +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1008), 0x30); +// +// assertEquals(2, ranges.getRangeCount()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(0x1008), range.getStart()); +// assertEquals(addr(0x100f), range.getEnd()); +// assertEquals(1, range.getSize()); +// assertEquals(3, range.getOffset()); +// +// range = ranges.get(1); +// assertEquals(addr(0x1030), range.getStart()); +// assertEquals(addr(0x1037), range.getEnd()); +// assertEquals(1, range.getSize()); +// assertEquals(0, range.getOffset()); +// } +// +// @Test +// public void testGetByteSourceForBitMappedBlock2Offcut() throws Exception { +// mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false); +// mem.createUninitializedBlock("test2", addr(0x4), 4, false); +// mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false); +// MemoryBlockDB block = +// (MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40, false); +// +// ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1006), 0x34); +// +// assertEquals(2, ranges.getRangeCount()); +// +// ByteSourceRange range = ranges.get(0); +// assertEquals(addr(0x1000), range.getStart()); +// assertEquals(addr(0x100f), range.getEnd()); +// assertEquals(2, range.getSize()); +// assertEquals(2, range.getOffset()); +// +// range = ranges.get(1); +// assertEquals(addr(0x1030), range.getStart()); +// assertEquals(addr(0x103f), range.getEnd()); +// assertEquals(2, range.getSize()); +// assertEquals(0, range.getOffset()); +// } @Test public void testAddressSourceInfoForFileBytesBlock() throws Exception { @@ -934,7 +975,7 @@ public class MemBlockDBTest extends AbstractGenericTest { public void testAddressSourceInfoForMappedBlock() throws Exception { FileBytes fileBytes = createFileBytes(); mem.createInitializedBlock("block", addr(0), fileBytes, 10, 50, false); - mem.createByteMappedBlock("mapped", addr(1000), addr(0), 20); + mem.createByteMappedBlock("mapped", addr(1000), addr(0), 20, false); AddressSourceInfo info = mem.getAddressSourceInfo(addr(1000)); assertEquals(addr(1000), info.getAddress()); diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java index 657847c984..646804c486 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/MemoryMapPluginScreenShots.java @@ -45,7 +45,7 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator { moveProviderToItsOwnWindow(provider); JComponent component = getDockableComponent(provider); - captureIsolatedComponent(component, 650, 225); + captureIsolatedComponent(component, 800, 225); } @Test diff --git a/GhidraDocs/GhidraClass/Intermediate/Intermediate_Ghidra_Student_Guide.html b/GhidraDocs/GhidraClass/Intermediate/Intermediate_Ghidra_Student_Guide.html index 6f8291400b..29ed6fc16a 100644 --- a/GhidraDocs/GhidraClass/Intermediate/Intermediate_Ghidra_Student_Guide.html +++ b/GhidraDocs/GhidraClass/Intermediate/Intermediate_Ghidra_Student_Guide.html @@ -279,14 +279,16 @@
    • The Memory Map
    • Allows users to add, delete, move, split, merge, or expand memory blocks in their program.
    • -
    • Memory Blocks can be byte-mapped, bit-mapped (if supported by processor) or overlays
    • +
    • Default memory blocks may be uninitialized or initialized using specified data
    • +
    • Other memory block types include byte-mapped and bit-mapped
    • +
    • Any memory block may be created as an overlay
    • Allows users to rename memory blocks
    • Allows users to change the image base of their program
    • Allows users to edit settings on individual memory blocks
      • Read/Write/Execute
      • Volatile/non-volatile
      • -
      • Initialized/non-initialized
      • +
      • Initialized/non-initialized (default blocks only)
    diff --git a/GhidraDocs/InstallationGuide.html b/GhidraDocs/InstallationGuide.html index 1cde614e67..fcbdcd7063 100644 --- a/GhidraDocs/InstallationGuide.html +++ b/GhidraDocs/InstallationGuide.html @@ -528,7 +528,7 @@ be installed in a pre-existing Eclipse installation.

    lookups.
  • - Image base can not be changed if overlays have been defined. + Image base may not be changed to an address which falls within an existing memory block.
  • Language versioning and migration does not handle complex changes in the use of the context