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 57e9d68bfa..548206be92 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 @@ -99,6 +99,13 @@

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

+ +

Byte Source - Provides information about the source of the bytes in this + block. If the bytes were originally imported from a file, then this will indicate which file + and the offset into that file. If the bytes are mapped to another region of memory, it will + provide the address for the mapping. Blocks may consist of regions that have different + sources. In that case, source information about the first several regions will be d + displayed.

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

  • Default  - A normal memory block within the processor's address space.  These blocks cannot overlap any other default block.  Default blocks - can be either initialized or uninitialized. If you select Initialized you can - enter a byte value that will be used to fill all the bytes in the new memory - block.
  • - + can be one of the following types: +
  • 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 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 4272b752fa..e8fa300a22 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 d615a569cf..94073d9ac8 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/MemoryMap.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png index 06218d1ee5..9346e1cc33 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/SplitMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png index 5ba0d563f8..53c86882bf 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 new file mode 100644 index 0000000000..4ddaf1a515 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java @@ -0,0 +1,135 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.cmd.Command; +import ghidra.framework.model.DomainObject; +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; +import ghidra.util.Msg; +import ghidra.util.exception.*; + +/** + * Base command class for adding memory blocks. + */ +abstract class AbstractAddMemoryBlockCmd implements Command { + protected String message; + protected final String name; + protected final String comment; + protected final String source; + protected final Address start; + protected final long length; + + protected final boolean read; + protected final boolean write; + protected final boolean execute; + protected final boolean isVolatile; + + AbstractAddMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile) { + this.name = name; + this.comment = comment; + this.source = source; + this.start = start; + this.length = length; + this.read = read; + this.write = write; + this.execute = execute; + this.isVolatile = isVolatile; + } + + @Override + public String getStatusMsg() { + return message; + } + + @Override + public String getName() { + return "Add Memory Block"; + } + + protected abstract MemoryBlock createMemoryBlock(Memory memory) + throws LockException, MemoryConflictException, AddressOverflowException, + DuplicateNameException, CancelledException; + + @Override + public boolean applyTo(DomainObject obj) { + Program program = (Program) obj; + try { + Memory memory = program.getMemory(); + MemoryBlock block = createMemoryBlock(memory); + block.setComment(comment); + block.setRead(read); + block.setWrite(write); + block.setExecute(execute); + block.setVolatile(isVolatile); + block.setSourceName(source); + renameFragment(program, block.getStart()); + return true; + } + catch (IllegalArgumentException e) { + message = e.getMessage(); + } + catch (AddressOverflowException e) { + message = e.getMessage(); + } + catch (MemoryConflictException e) { + message = e.getMessage(); + } + catch (DuplicateNameException e) { + message = "Duplicate Name: " + e.getMessage(); + } + catch (IllegalStateException e) { + message = e.getMessage(); + } + catch (Throwable t) { + message = "Create block failed"; + Msg.showError(this, null, "Create Block Failed", t.getMessage(), t); + } + throw new RollbackException(message); + } + + private void renameFragment(Program program, Address blockStartAddr) { + Listing listing = program.getListing(); + String[] treeNames = listing.getTreeNames(); + for (String treeName : treeNames) { + ProgramFragment frag = listing.getFragment(treeName, blockStartAddr); + renameFragment(frag, name); + } + } + + private void renameFragment(ProgramFragment fragment, String fragmentName) { + String newName = fragmentName; + int count = 1; + while (!doRenameFragment(fragment, newName)) { + newName = fragmentName + "_" + count; + count++; + } + } + + private boolean doRenameFragment(ProgramFragment fragment, String fragmentName) { + try { + fragment.setName(fragmentName); + return true; + } + catch (DuplicateNameException e) { + return false; + } + } +} 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 new file mode 100644 index 0000000000..9c00677354 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddBitMappedMemoryBlockCmd.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.mem.*; + +/** + * Command for adding Bit-mapped memory blocks + */ +public class AddBitMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd { + + private final Address mappedAddress; + + /** + * Create a new AddBitMappedMemoryBlockCmd + * @param name the name for the new memory block. + * @param comment the comment for the block + * @param source indicates what is creating the block + * @param start the start address for the the block + * @param length the length of the new block + * @param read sets the block's read permission flag + * @param write sets the block's write permission flag + * @param execute sets the block's execute permission flag + * @param isVolatile sets the block's volatile flag + * @param mappedAddress the address in memory that will serve as the bytes source for the block + */ + public AddBitMappedMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile, + Address mappedAddress) { + super(name, comment, source, start, length, read, write, execute, isVolatile); + this.mappedAddress = mappedAddress; + + } + + @Override + protected MemoryBlock createMemoryBlock(Memory memory) + throws LockException, MemoryConflictException, AddressOverflowException { + return memory.createBitMappedBlock(name, start, mappedAddress, length); + } + +} 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 new file mode 100644 index 0000000000..8ebfd7ac39 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddByteMappedMemoryBlockCmd.java @@ -0,0 +1,57 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.mem.*; + +/** + * Command for adding byte-mapped memory blocks + */ +public class AddByteMappedMemoryBlockCmd extends AbstractAddMemoryBlockCmd { + + private final Address mappedAddress; + + /** + * Create a new AddByteMappedMemoryBlockCmd + * @param name the name for the new memory block. + * @param comment the comment for the block + * @param source indicates what is creating the block + * @param start the start address for the the block + * @param length the length of the new block + * @param read sets the block's read permission flag + * @param write sets the block's write permission flag + * @param execute sets the block's execute permission flag + * @param isVolatile sets the block's volatile flag + * @param mappedAddress the address in memory that will serve as the bytes source for the block + */ + public AddByteMappedMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile, + Address mappedAddress) { + super(name, comment, source, start, length, read, write, execute, isVolatile); + this.mappedAddress = mappedAddress; + + } + + @Override + protected MemoryBlock createMemoryBlock(Memory memory) + throws LockException, MemoryConflictException, AddressOverflowException { + return memory.createByteMappedBlock(name, start, mappedAddress, length); + } + +} 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 new file mode 100644 index 0000000000..c4b9ff1542 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddFileBytesMemoryBlockCmd.java @@ -0,0 +1,65 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.store.LockException; +import ghidra.program.database.mem.FileBytes; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.mem.*; +import ghidra.util.exception.DuplicateNameException; + +/** + * Command for adding a new memory block using bytes from an imported {@link FileBytes} object. + */ +public class AddFileBytesMemoryBlockCmd extends AbstractAddMemoryBlockCmd { + + private final FileBytes fileBytes; + private final long offset; + private final boolean isOverlay; + + /** + * Create a new AddFileBytesMemoryBlockCmd + * @param name the name for the new memory block. + * @param comment the comment for the block + * @param source indicates what is creating the block + * @param start the start address for the the block + * @param length the length of the new block + * @param read sets the block's read permission flag + * @param write sets the block's write permission flag + * @param execute sets the block's execute permission flag + * @param isVolatile sets the block's volatile flag + * @param fileBytes the {@link FileBytes} object that provides the byte source for this block. + * @param offset the offset into the {@link FileBytes} object for the first byte in this block. + * @param isOverlay if true, the block will be created in a new overlay address space. + */ + public AddFileBytesMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile, + FileBytes fileBytes, long offset, boolean isOverlay) { + super(name, comment, source, start, length, read, write, execute, isVolatile); + this.fileBytes = fileBytes; + this.offset = offset; + this.isOverlay = isOverlay; + } + + @Override + protected MemoryBlock createMemoryBlock(Memory memory) throws LockException, + MemoryConflictException, AddressOverflowException, DuplicateNameException { + + return memory.createInitializedBlock(name, start, fileBytes, offset, length, isOverlay); + } + +} 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 new file mode 100644 index 0000000000..050aa5bbe5 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddInitializedMemoryBlockCmd.java @@ -0,0 +1,63 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.mem.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateNameException; + +/** + * Command for adding a new memory block initialized with a specific byte. + */ +public class AddInitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd { + + private final byte initialValue; + private final boolean isOverlay; + + /** + * Create a new AddFileBytesMemoryBlockCmd + * @param name the name for the new memory block. + * @param comment the comment for the block + * @param source indicates what is creating the block + * @param start the start address for the the block + * @param length the length of the new block + * @param read sets the block's read permission flag + * @param write sets the block's write permission flag + * @param execute sets the block's execute permission flag + * @param isVolatile sets the block's volatile flag + * @param initialValue the bytes value to use throught the new block. + * @param isOverlay if true, the block will be created in a new overlay address space. + */ + public AddInitializedMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile, + byte initialValue, boolean isOverlay) { + super(name, comment, source, start, length, read, write, execute, isVolatile); + + this.initialValue = initialValue; + this.isOverlay = isOverlay; + } + + @Override + protected MemoryBlock createMemoryBlock(Memory memory) + throws LockException, MemoryConflictException, AddressOverflowException, + DuplicateNameException, CancelledException { + return memory.createInitializedBlock(name, start, length, initialValue, null, isOverlay); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java deleted file mode 100644 index 9df9c8974c..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddMemoryBlockCmd.java +++ /dev/null @@ -1,174 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.cmd.memory; - -import ghidra.framework.cmd.Command; -import ghidra.framework.model.DomainObject; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressOverflowException; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.*; -import ghidra.util.Msg; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.exception.RollbackException; - -/** - * - * Command to add a memory block. - * - * - */ -public class AddMemoryBlockCmd implements Command { - - private String name; - private String comment; - private String source; - private Address start; - private int length; - private boolean read; - private boolean write; - private boolean execute; - private boolean isVolatile; - private byte initialValue; - private MemoryBlockType blockType; - private Address baseAddr; - private Program program; - private String message; - private boolean isInitialized; - - /** - * - * Construct a new AddMemoryBlockCmd - * @param name block name - * @param comment block comments - * @param source block source - * @param start starting address of the block - * @param length block length - * @param read read permissions - * @param write write permissions - * @param execute execute permissions - * @param isVolatile volatile setting - * @param initialValue initial byte value - * @param blockType type of block to add: MemoryBlockType.DEFAULT, - * MemoryBlockType.OVERLAY, or MemoryBlockType.BIT_MAPPED or MemoryBlockType.BYTE_MAPPED - * @param baseAddr base address for the source address if the block type - * is TYPE_BIT_MAPPED or TYPE_BYTE_MAPPED; otherwise, null - */ - public AddMemoryBlockCmd(String name, String comment, String source, Address start, int length, - boolean read, boolean write, boolean execute, boolean isVolatile, byte initialValue, - MemoryBlockType blockType, Address baseAddr, boolean isInitialized) { - this.name = name; - this.comment = comment; - this.source = source; - this.start = start; - this.length = length; - this.read = read; - this.write = write; - this.execute = execute; - this.isVolatile = isVolatile; - this.initialValue = initialValue; - this.blockType = blockType; - this.baseAddr = baseAddr; - this.isInitialized = isInitialized; - - } - - /** - * @see ghidra.framework.cmd.Command#applyTo(ghidra.framework.model.DomainObject) - */ - @Override - public boolean applyTo(DomainObject obj) { - program = (Program) obj; - try { - Memory memory = program.getMemory(); - MemoryBlock block = null; - if (isInitialized) { - block = memory.createInitializedBlock(name, start, length, initialValue, null, - (blockType == MemoryBlockType.OVERLAY)); - } - else if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) { - block = memory.createUninitializedBlock(name, start, length, - (blockType == MemoryBlockType.OVERLAY)); - } - else if (blockType == MemoryBlockType.BIT_MAPPED) { - block = memory.createBitMappedBlock(name, start, baseAddr, length); - } - else { - block = memory.createByteMappedBlock(name, start, baseAddr, length); - } - block.setComment(comment); - block.setRead(read); - block.setWrite(write); - block.setExecute(execute); - block.setVolatile(isVolatile); - block.setSourceName(source); - renameFragment(block.getStart()); - return true; - } - catch (IllegalArgumentException e) { - message = e.getMessage(); - } - catch (AddressOverflowException e) { - message = e.getMessage(); - } - catch (MemoryConflictException e) { - message = e.getMessage(); - } - catch (OutOfMemoryError e) { - message = "Not enough memory to create block"; - } - catch (DuplicateNameException e) { - message = "Duplicate Name: " + e.getMessage(); - } - catch (IllegalStateException e) { - message = e.getMessage(); - } - catch (Throwable t) { - message = "Create block failed"; - Msg.showError(this, null, "Create Block Failed", t.getMessage(), t); - } - throw new RollbackException(message); - } - - /** - * @see ghidra.framework.cmd.Command#getName() - */ - @Override - public String getName() { - return "Add Memory Block"; - } - - /** - * @see ghidra.framework.cmd.Command#getStatusMsg() - */ - @Override - public String getStatusMsg() { - return message; - } - - private void renameFragment(Address blockStartAddr) { - Listing listing = program.getListing(); - String[] treeNames = listing.getTreeNames(); - for (int i = 0; i < treeNames.length; i++) { - try { - ProgramFragment frag = listing.getFragment(treeNames[i], blockStartAddr); - frag.setName(name); - } - catch (DuplicateNameException exc) { - } - } - } -} 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 new file mode 100644 index 0000000000..2e7b5df4c4 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AddUninitializedMemoryBlockCmd.java @@ -0,0 +1,58 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.cmd.memory; + +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.mem.*; +import ghidra.util.exception.DuplicateNameException; + +/** + * Command for adding uninitialized memory blocks + */ +public class AddUninitializedMemoryBlockCmd extends AbstractAddMemoryBlockCmd { + + private final boolean isOverlay; + + /** + * Create a new AddUninitializedMemoryBlockCmd + * @param name the name for the new memory block. + * @param comment the comment for the block + * @param source indicates what is creating the block + * @param start the start address for the the block + * @param length the length of the new block + * @param read sets the block's read permission flag + * @param write sets the block's write permission flag + * @param execute sets the block's execute permission flag + * @param isVolatile sets the block's volatile flag + * @param isOverlay if true, the block will be created in a new overlay address space. + */ + public AddUninitializedMemoryBlockCmd(String name, String comment, String source, Address start, + long length, boolean read, boolean write, boolean execute, boolean isVolatile, + boolean isOverlay) { + super(name, comment, source, start, length, read, write, execute, isVolatile); + + this.isOverlay = isOverlay; + } + + @Override + protected MemoryBlock createMemoryBlock(Memory memory) throws LockException, + MemoryConflictException, AddressOverflowException, DuplicateNameException { + return memory.createUninitializedBlock(name, start, length, isOverlay); + } + +} 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 00ee426751..8da90fed94 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 @@ -15,8 +15,8 @@ */ package ghidra.app.plugin.core.memory; -import java.awt.BorderLayout; -import java.awt.CardLayout; +import java.awt.*; +import java.util.List; import javax.swing.*; import javax.swing.event.*; @@ -27,14 +27,18 @@ import docking.widgets.checkbox.GCheckBox; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.label.GDLabel; import docking.widgets.label.GLabel; +import ghidra.app.plugin.core.memory.AddBlockModel.InitializedType; import ghidra.app.plugin.core.misc.RegisterField; import ghidra.app.util.*; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.mem.FileBytes; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressFactory; import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlockType; import ghidra.util.HelpLocation; +import ghidra.util.layout.HorizontalLayout; import ghidra.util.layout.PairLayout; /** @@ -50,10 +54,9 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { private JTextField commentField; private JPanel viewPanel; - private CardLayout cardLayout; - private JPanel initializedPanel; - private JPanel bottomPanel; + private CardLayout typeCardLayout; private JRadioButton initializedRB; + private GRadioButton initializedFromFileBytesRB; private JRadioButton uninitializedRB; private JCheckBox readCB; @@ -68,13 +71,25 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { private AddBlockModel model; private GhidraComboBox comboBox; private boolean updatingInitializedRB; + private CardLayout initializedTypeCardLayout; private final static String MAPPED = "Mapped"; - private final static String OTHER = "Other"; + private final static String UNMAPPED = "Unmapped"; + private static final String UNITIALIZED = "UNITIALIZED"; + private static final String INITIALIZED = "INITIALIZED"; + private static final String FILE_BYTES = "FILE_BYTES"; + private JPanel inializedTypePanel; + private RegisterField fileOffsetField; + private GhidraComboBox fileBytesComboBox; AddBlockDialog(AddBlockModel model) { super("Add Memory Block", true, true, true, false); - init(model); + this.model = model; + model.setChangeListener(this); + setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block")); + addWorkPanel(buildWorkPanel()); + addOKButton(); + addCancelButton(); } /** @@ -84,150 +99,197 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { public void stateChanged(ChangeEvent e) { setStatusText(model.getMessage()); setOkEnabled(model.isValidInfo()); - readCB.setEnabled(model.isReadEnabled()); - writeCB.setEnabled(model.isWriteEnabled()); - executeCB.setEnabled(model.isExecuteEnabled()); - volatileCB.setEnabled(model.isVolatileEnabled()); - if (initializedRB != null) { - updatingInitializedRB = true; - try { - initializedRB.setSelected(model.getInitializedState()); - } - finally { - updatingInitializedRB = false; - } - } - } - - private void init(AddBlockModel blockModel) { - this.model = blockModel; - blockModel.setChangeListener(this); - create(); - setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Add Block")); + readCB.setSelected(model.isRead()); + writeCB.setSelected(model.isWrite()); + executeCB.setSelected(model.isExecute()); + volatileCB.setSelected(model.isVolatile()); } /** * Define the Main panel for the dialog here. */ - private void create() { - cardLayout = new CardLayout(); - viewPanel = new JPanel(cardLayout); + private JComponent buildWorkPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); + panel.add(buildMainPanel(), BorderLayout.NORTH); + panel.add(buildVariablePanel(), BorderLayout.CENTER); + return panel; + } - nameField = new JTextField(); - nameField.setName("Block Name"); + private Component buildMainPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(buildBasicInfoPanel(), BorderLayout.NORTH); + panel.add(buildPermissionsPanel(), BorderLayout.CENTER); + panel.add(buildTypesPanel(), BorderLayout.SOUTH); - addrField = new AddressInput(); - addrField.setName("Start Addr"); + return panel; + } - lengthField = new RegisterField(32, null, false); - lengthField.setName("Length"); + private Component buildBasicInfoPanel() { + JPanel panel = new JPanel(new PairLayout(4, 10, 150)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5)); - commentField = new JTextField(); - commentField.setName("Comment"); + panel.add(new GLabel("Block Name:", SwingConstants.RIGHT)); + panel.add(buildNameField()); + panel.add(new GLabel("Start Addr:", SwingConstants.RIGHT)); + panel.add(buildAddressField()); + panel.add(new GLabel("Length:", SwingConstants.RIGHT)); + panel.add(buildLengthField()); + panel.add(new GLabel("Comment:", SwingConstants.RIGHT)); + panel.add(buildCommentField()); - addrFactory = model.getProgram().getAddressFactory(); - addrField.setAddressFactory(addrFactory, true); + return panel; + } - nameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - nameChanged(); - } + private Component buildPermissionsPanel() { - @Override - public void removeUpdate(DocumentEvent e) { - nameChanged(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - nameChanged(); - } - }); - - lengthField.setChangeListener(e -> lengthChanged()); - addrField.addChangeListener(ev -> addrChanged()); - - readCB = new GCheckBox(); + readCB = new GCheckBox("Read"); readCB.setName("Read"); + readCB.setSelected(model.isRead()); + readCB.addActionListener(e -> model.setRead(readCB.isSelected())); - writeCB = new GCheckBox(); + writeCB = new GCheckBox("Write"); writeCB.setName("Write"); + writeCB.setSelected(model.isWrite()); + writeCB.addActionListener(e -> model.setWrite(writeCB.isSelected())); - executeCB = new GCheckBox(); + executeCB = new GCheckBox("Execute"); executeCB.setName("Execute"); + executeCB.setSelected(model.isExecute()); + executeCB.addActionListener(e -> model.setExecute(executeCB.isSelected())); - volatileCB = new GCheckBox(); + volatileCB = new GCheckBox("Volatile"); volatileCB.setName("Volatile"); + volatileCB.setSelected(model.isVolatile()); + volatileCB.addActionListener(e -> model.setVolatile(volatileCB.isSelected())); - JPanel topPanel = new JPanel(new PairLayout(4, 10, 150)); - topPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5)); - topPanel.add(new GLabel("Block Name:", SwingConstants.RIGHT)); - topPanel.add(nameField); - topPanel.add(new GLabel("Start Addr:", SwingConstants.RIGHT)); - topPanel.add(addrField); - topPanel.add(new GLabel("Length:", SwingConstants.RIGHT)); - topPanel.add(lengthField); - topPanel.add(new GLabel("Comment:", SwingConstants.RIGHT)); - topPanel.add(commentField); + JPanel panel = new JPanel(new HorizontalLayout(10)); + panel.setBorder(BorderFactory.createEmptyBorder(10, 30, 20, 30)); + panel.add(readCB); + panel.add(writeCB); + panel.add(executeCB); + panel.add(volatileCB); - JPanel execPanel = new JPanel(); - BoxLayout bl = new BoxLayout(execPanel, BoxLayout.X_AXIS); - execPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + return panel; + } - execPanel.setLayout(bl); - execPanel.add(Box.createHorizontalStrut(10)); - execPanel.add(new GLabel("Read")); - execPanel.add(readCB); - execPanel.add(Box.createHorizontalStrut(10)); + private Component buildTypesPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createTitledBorder("Block Types")); - execPanel.add(new GLabel("Write")); - execPanel.add(writeCB); - execPanel.add(Box.createHorizontalStrut(10)); + MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT, + MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED }; - execPanel.add(new GLabel("Execute")); - execPanel.add(executeCB); - execPanel.add(Box.createHorizontalStrut(10)); + comboBox = new GhidraComboBox<>(items); + comboBox.addItemListener(e -> blockTypeSelected()); + panel.add(comboBox); + return panel; + } - execPanel.add(new GLabel("Volatile")); - execPanel.add(volatileCB); + private Component buildVariablePanel() { + typeCardLayout = new CardLayout(); + viewPanel = new JPanel(typeCardLayout); + viewPanel.setBorder(BorderFactory.createEtchedBorder()); - JPanel panel = new JPanel(); - panel.add(execPanel); + viewPanel.add(buildMappedPanel(), MAPPED); + viewPanel.add(buildUnmappedPanel(), UNMAPPED); + typeCardLayout.show(viewPanel, UNMAPPED); + return viewPanel; + } - JPanel outerTopPanel = new JPanel(new BorderLayout()); - outerTopPanel.add(topPanel, BorderLayout.NORTH); - outerTopPanel.add(panel, BorderLayout.CENTER); + private Component buildUnmappedPanel() { + JPanel panel = new JPanel(new BorderLayout()); + panel.add(buildInitializedRadioButtonPanel(), BorderLayout.NORTH); + panel.add(buildVariableInitializedPanel()); + return panel; + } - bottomPanel = new JPanel(); - BoxLayout layout = new BoxLayout(bottomPanel, BoxLayout.Y_AXIS); - bottomPanel.setLayout(layout); - bottomPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 4, 5)); - bottomPanel.add(createComboBoxPanel()); - bottomPanel.add(viewPanel); + private Component buildInitializedRadioButtonPanel() { + JPanel panel = new JPanel(new HorizontalLayout(10)); - JPanel mainPanel = new JPanel(); - layout = new BoxLayout(mainPanel, BoxLayout.Y_AXIS); - mainPanel.setLayout(layout); - mainPanel.add(outerTopPanel); - mainPanel.add(bottomPanel); - mainPanel.validate(); + ButtonGroup radioGroup = new ButtonGroup(); + initializedRB = new GRadioButton("Initialized", false); + initializedRB.setName(initializedRB.getText()); + initializedRB.addActionListener(ev -> initializeRBChanged()); - JPanel mainPanel2 = new JPanel(new BorderLayout()); - mainPanel2.add(mainPanel, BorderLayout.NORTH); - mainPanel2.add(new JPanel(), BorderLayout.CENTER); + initializedFromFileBytesRB = new GRadioButton("File Bytes", false); + initializedFromFileBytesRB.setName(initializedRB.getText()); + initializedFromFileBytesRB.addActionListener(ev -> initializeRBChanged()); - createCardPanels(); + uninitializedRB = new GRadioButton("Uninitialized", true); + uninitializedRB.setName(uninitializedRB.getText()); + uninitializedRB.addActionListener(ev -> initializeRBChanged()); - addWorkPanel(mainPanel2); - addOKButton(); - addCancelButton(); + radioGroup.add(initializedRB); + radioGroup.add(initializedFromFileBytesRB); + radioGroup.add(uninitializedRB); + + panel.add(initializedRB); + panel.add(initializedFromFileBytesRB); + panel.add(uninitializedRB); + + return panel; + } + + private Component buildVariableInitializedPanel() { + initializedTypeCardLayout = new CardLayout(); + + inializedTypePanel = new JPanel(initializedTypeCardLayout); + inializedTypePanel.add(new JPanel(), UNITIALIZED); + inializedTypePanel.add(buildInitalValuePanel(), INITIALIZED); + inializedTypePanel.add(buildFileBytesPanel(), FILE_BYTES); + return inializedTypePanel; + } + + private Component buildInitalValuePanel() { + initialValueLabel = new GDLabel("Initial Value"); + initialValueField = new RegisterField(8, null, false); + initialValueField.setName("Initial Value"); + + initialValueField.setChangeListener(e -> initialValueChanged()); + + JPanel panel = new JPanel(new PairLayout(4, 10)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5)); + panel.add(initialValueLabel); + panel.add(initialValueField); + return panel; + } + + private Component buildFileBytesPanel() { + JPanel panel = new JPanel(new PairLayout(5, 5)); + panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5)); + + panel.add(new GLabel("File Bytes:")); + panel.add(buildFileBytesCombo()); + panel.add(new GLabel("File Offset:")); + panel.add(buildFileOffsetField()); + + return panel; + } + + private Component buildFileBytesCombo() { + Memory memory = model.getProgram().getMemory(); + List allFileBytes = memory.getAllFileBytes(); + FileBytes[] fileBytes = allFileBytes.toArray(new FileBytes[allFileBytes.size()]); + + fileBytesComboBox = new GhidraComboBox<>(fileBytes) { + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + preferredSize.width = 100; + return preferredSize; + } + }; + fileBytesComboBox.addItemListener(e -> fileBytesChanged()); + if (!allFileBytes.isEmpty()) { + model.setFileBytes(allFileBytes.get(0)); + } + return fileBytesComboBox; } /** * Display the dialog filled with default values. * Used to enter a new MemoryBlock. - * @param nlines default value displayed in the text field. + * @param tool the tool that owns this dialog */ void showDialog(PluginTool tool) { @@ -237,17 +299,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { lengthField.setValue(Long.valueOf(0)); model.setLength(0); commentField.setText(""); - - readCB.setSelected(true); - writeCB.setSelected(true); - executeCB.setSelected(false); - volatileCB.setSelected(false); - initialValueField.setValue(Long.valueOf(0)); model.setBlockType(MemoryBlockType.DEFAULT); - model.setIsInitialized(initializedRB.isSelected()); + model.setInitializedType(AddBlockModel.InitializedType.UNITIALIZED); model.setInitialValue(0); + readCB.setSelected(model.isRead()); + writeCB.setSelected(model.isWrite()); + executeCB.setSelected(model.isExecute()); + volatileCB.setSelected(model.isVolatile()); + setOkEnabled(false); tool.showDialog(this, tool.getComponentProvider(PluginConstants.MEMORY_MAP)); } @@ -262,9 +323,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { */ @Override protected void okCallback() { - - if (model.execute(commentField.getText(), readCB.isSelected(), writeCB.isSelected(), - executeCB.isSelected(), volatileCB.isSelected())) { + if (model.execute()) { close(); } else { @@ -277,27 +336,17 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { if (updatingInitializedRB) { return; } - boolean selected = initializedRB.isSelected(); - model.setIsInitialized(selected); - initialValueField.setEnabled(selected); - initialValueLabel.setEnabled(selected); - if (!selected) { - initialValueField.setValue(new Long(0)); - model.setInitialValue(0); + if (initializedRB.isSelected()) { + model.setInitializedType(InitializedType.INITIALIZED_FROM_VALUE); + initializedTypeCardLayout.show(inializedTypePanel, INITIALIZED); } - } - - private void uninitializedRBChanged() { - if (updatingInitializedRB) { - return; + else if (uninitializedRB.isSelected()) { + model.setInitializedType(InitializedType.UNITIALIZED); + initializedTypeCardLayout.show(inializedTypePanel, UNITIALIZED); } - boolean selected = uninitializedRB.isSelected(); - model.setIsInitialized(!selected); - initialValueField.setEnabled(!selected); - initialValueLabel.setEnabled(!selected); - if (!selected) { - initialValueField.setValue(new Long(0)); - model.setInitialValue(0); + else if (initializedFromFileBytesRB.isSelected()) { + model.setInitializedType(InitializedType.INITIALIZED_FROM_FILE_BYTES); + initializedTypeCardLayout.show(inializedTypePanel, FILE_BYTES); } } @@ -318,21 +367,40 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { model.setBlockName(name); } + private void commentChanged() { + String comment = commentField.getText().trim(); + model.setComment(comment); + } + private void lengthChanged() { - int length = 0; + long length = 0; Long val = lengthField.getValue(); if (val != null) { - length = val.intValue(); + length = val.longValue(); } model.setLength(length); } + private void fileOffsetChanged() { + long fileOffset = -1; + Long val = fileOffsetField.getValue(); + if (val != null) { + fileOffset = val.longValue(); + } + model.setFileOffset(fileOffset); + } + + private void fileBytesChanged() { + model.setFileBytes((FileBytes) fileBytesComboBox.getSelectedItem()); + } + private void addrChanged() { Address addr = null; try { addr = addrField.getAddress(); } catch (IllegalArgumentException e) { + // just let it be null } model.setStartAddress(addr); } @@ -342,90 +410,19 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { model.setBaseAddress(baseAddress); } - private JPanel createComboBoxPanel() { - JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createTitledBorder("Block Types")); - - MemoryBlockType[] items = new MemoryBlockType[] { MemoryBlockType.DEFAULT, - MemoryBlockType.OVERLAY, MemoryBlockType.BIT_MAPPED, MemoryBlockType.BYTE_MAPPED }; - - comboBox = new GhidraComboBox<>(items); - comboBox.addItemListener(e -> blockTypeSelected()); - panel.add(comboBox); - return panel; - } - private void blockTypeSelected() { - MemoryBlockType blockType = (MemoryBlockType) comboBox.getSelectedItem(); model.setBlockType(blockType); - if (blockType == MemoryBlockType.DEFAULT) { - cardLayout.show(viewPanel, OTHER); - } - else if (blockType == MemoryBlockType.OVERLAY) { - cardLayout.show(viewPanel, OTHER); - } - else if (blockType == MemoryBlockType.BIT_MAPPED) { - cardLayout.show(viewPanel, MAPPED); + if (blockType == MemoryBlockType.DEFAULT || blockType == MemoryBlockType.OVERLAY) { + typeCardLayout.show(viewPanel, UNMAPPED); } else { - // type is Byte mapped - cardLayout.show(viewPanel, MAPPED); + typeCardLayout.show(viewPanel, MAPPED); } } - private JPanel createRadioPanel() { - JPanel panel = new JPanel(); - BoxLayout bl = new BoxLayout(panel, BoxLayout.X_AXIS); - panel.setLayout(bl); - - ButtonGroup radioGroup = new ButtonGroup(); - initializedRB = new GRadioButton("Initialized", false); - initializedRB.setName(initializedRB.getText()); - initializedRB.addActionListener(ev -> initializeRBChanged()); - - uninitializedRB = new GRadioButton("Uninitialized", true); - uninitializedRB.setName(uninitializedRB.getText()); - uninitializedRB.addActionListener(ev -> uninitializedRBChanged()); - - radioGroup.add(initializedRB); - radioGroup.add(uninitializedRB); - - panel.add(initializedRB); - panel.add(uninitializedRB); - - JPanel outerPanel = new JPanel(); - BoxLayout bl2 = new BoxLayout(outerPanel, BoxLayout.Y_AXIS); - outerPanel.setLayout(bl2); - outerPanel.add(panel); - createInitializedPanel(); - outerPanel.add(initializedPanel); - outerPanel.setBorder(BorderFactory.createEtchedBorder()); - return outerPanel; - } - - private void createCardPanels() { - viewPanel.add(createAddressPanel(), MAPPED); - viewPanel.add(createRadioPanel(), OTHER); - cardLayout.show(viewPanel, OTHER); - } - - private void createInitializedPanel() { - initialValueLabel = new GDLabel("Initial Value"); - initialValueField = new RegisterField(8, null, false); - initialValueField.setName("Initial Value"); - initialValueField.setEnabled(false); - - initialValueField.setChangeListener(e -> initialValueChanged()); - - initializedPanel = new JPanel(new PairLayout(4, 10)); - initializedPanel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5)); - initializedPanel.add(initialValueLabel); - initializedPanel.add(initialValueField); - } - - private JPanel createAddressPanel() { - JPanel addressPanel = new JPanel(new PairLayout()); + private JPanel buildMappedPanel() { + JPanel panel = new JPanel(new PairLayout()); baseAddrField = new AddressInput(); baseAddrField.setAddressFactory(addrFactory); @@ -440,10 +437,77 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { } baseAddrField.setAddress(minAddr); model.setBaseAddress(minAddr); - addressPanel.add(new GLabel("Source Addr:")); - addressPanel.add(baseAddrField); - addressPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); - return addressPanel; + panel.add(new GLabel("Source Addr:")); + panel.add(baseAddrField); + panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + return panel; + } + + private Component buildCommentField() { + commentField = new JTextField(); + commentField.setName("Comment"); + commentField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + commentChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + commentChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + commentChanged(); + } + }); + return commentField; + } + + private Component buildLengthField() { + lengthField = new RegisterField(36, null, false); + lengthField.setName("Length"); + lengthField.setChangeListener(e -> lengthChanged()); + return lengthField; + } + + private Component buildFileOffsetField() { + fileOffsetField = new RegisterField(60, null, false); + fileOffsetField.setName("File Offset"); + fileOffsetField.setChangeListener(e -> fileOffsetChanged()); + return fileOffsetField; + } + + private Component buildAddressField() { + addrField = new AddressInput(); + addrField.setName("Start Addr"); + addrFactory = model.getProgram().getAddressFactory(); + addrField.setAddressFactory(addrFactory, true); + addrField.addChangeListener(ev -> addrChanged()); + return addrField; + } + + private Component buildNameField() { + nameField = new JTextField(); + nameField.setName("Block Name"); + nameField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + nameChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + nameChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + nameChanged(); + } + }); + return nameField; } } 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 836ee087f5..017cc39ca8 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 @@ -17,13 +17,16 @@ package ghidra.app.plugin.core.memory; import javax.swing.event.ChangeListener; -import ghidra.app.cmd.memory.AddMemoryBlockCmd; +import ghidra.app.cmd.memory.*; +import ghidra.framework.cmd.Command; import ghidra.framework.plugintool.PluginTool; +import ghidra.program.database.mem.FileBytes; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.*; import ghidra.util.NamingUtilities; import ghidra.util.datastruct.StringKeyIndexer; +import ghidra.util.exception.AssertException; /** * @@ -39,23 +42,25 @@ class AddBlockModel { private String blockName; private Address startAddr; private Address baseAddr; - private int length; + private long length; private MemoryBlockType blockType; private int initialValue; private String message; private ChangeListener listener; private boolean isValid; - private boolean readEnabled; - private boolean writeEnabled; - private boolean executeEnabled; - private boolean volatileEnabled; - private boolean isInitialized; + private boolean isRead; + private boolean isWrite; + private boolean isExecute; + private boolean isVolatile; + private InitializedType initializedType; + private String comment; + private FileBytes fileBytes; + private long fileBytesOffset = -1; + + enum InitializedType { + UNITIALIZED, INITIALIZED_FROM_VALUE, INITIALIZED_FROM_FILE_BYTES; + } - /** - * Construct a new model. - * @param tool - * @param program - */ AddBlockModel(PluginTool tool, Program program) { this.tool = tool; this.program = program; @@ -64,10 +69,6 @@ class AddBlockModel { startAddr = program.getImageBase(); blockType = MemoryBlockType.DEFAULT; initialValue = 0; - readEnabled = true; - writeEnabled = true; - executeEnabled = true; - volatileEnabled = true; } void setChangeListener(ChangeListener listener) { @@ -80,18 +81,34 @@ class AddBlockModel { listener.stateChanged(null); } + public void setComment(String comment) { + this.comment = comment; + } + void setStartAddress(Address addr) { startAddr = addr; validateInfo(); listener.stateChanged(null); } - void setLength(int length) { + void setLength(long length) { this.length = length; validateInfo(); listener.stateChanged(null); } + void setFileOffset(long fileOffset) { + this.fileBytesOffset = fileOffset; + validateInfo(); + listener.stateChanged(null); + } + + void setFileBytes(FileBytes fileBytes) { + this.fileBytes = fileBytes; + validateInfo(); + listener.stateChanged(null); + } + void setInitialValue(int initialValue) { this.initialValue = initialValue; validateInfo(); @@ -100,16 +117,35 @@ class AddBlockModel { void setBlockType(MemoryBlockType blockType) { this.blockType = blockType; - readEnabled = true; - writeEnabled = true; - executeEnabled = true; - volatileEnabled = true; + isRead = true; + isWrite = true; + isExecute = false; + isVolatile = false; + initializedType = InitializedType.UNITIALIZED; validateInfo(); listener.stateChanged(null); } - void setIsInitialized(boolean isInitialized) { - this.isInitialized = isInitialized; + void setRead(boolean b) { + this.isRead = b; + } + + void setWrite(boolean b) { + this.isWrite = b; + } + + void setExecute(boolean b) { + this.isExecute = b; + } + + void setVolatile(boolean b) { + this.isVolatile = b; + } + + void setInitializedType(InitializedType type) { + this.initializedType = type; + validateInfo(); + listener.stateChanged(null); } void setBaseAddress(Address baseAddr) { @@ -142,45 +178,33 @@ class AddBlockModel { return program; } - boolean isReadEnabled() { - return readEnabled; + boolean isRead() { + return isRead; } - boolean isWriteEnabled() { - return writeEnabled; + boolean isWrite() { + return isWrite; } - boolean isExecuteEnabled() { - return executeEnabled; + boolean isExecute() { + return isExecute; } - boolean isVolatileEnabled() { - return volatileEnabled; + boolean isVolatile() { + return isVolatile; } - boolean getInitializedState() { - return isInitialized; + InitializedType getInitializedType() { + return initializedType; } - /** - * Add the block. - * @param comment block comment - * @param isRead read permissions - * @param isWrite write permissions - * @param isExecute execute permissions - * @param isVolatile volatile setting - * @return true if the block was successfully added - */ - boolean execute(String comment, boolean isRead, boolean isWrite, boolean isExecute, - boolean isVolatile) { + boolean execute() { validateInfo(); if (!isValid) { return false; } - AddMemoryBlockCmd cmd = new AddMemoryBlockCmd(blockName, comment, "- none -", startAddr, - length, isRead, isWrite, isExecute, isVolatile, (byte) initialValue, blockType, - baseAddr, isInitialized); + Command cmd = createAddBlockCommand(); if (!tool.execute(cmd, program)) { message = cmd.getStatusMsg(); return false; @@ -188,60 +212,162 @@ class AddBlockModel { return true; } + Command createAddBlockCommand() { + String source = ""; + switch (blockType) { + case BIT_MAPPED: + return new AddBitMappedMemoryBlockCmd(blockName, comment, source, startAddr, length, + isRead, isWrite, isExecute, isVolatile, baseAddr); + case BYTE_MAPPED: + return new AddByteMappedMemoryBlockCmd(blockName, comment, source, startAddr, + length, isRead, isWrite, isExecute, isVolatile, baseAddr); + case DEFAULT: + return createNonMappedMemoryBlock(source, false); + case OVERLAY: + return createNonMappedMemoryBlock(source, true); + default: + throw new AssertException("Encountered unexpected block type: " + blockType); + + } + } + + private Command createNonMappedMemoryBlock(String source, boolean isOverlay) { + switch (initializedType) { + case INITIALIZED_FROM_FILE_BYTES: + return new AddFileBytesMemoryBlockCmd(blockName, comment, source, startAddr, length, + isRead, isWrite, isExecute, isVolatile, fileBytes, fileBytesOffset, isOverlay); + case INITIALIZED_FROM_VALUE: + return new AddInitializedMemoryBlockCmd(blockName, comment, source, startAddr, + length, isRead, isWrite, isExecute, isVolatile, (byte) initialValue, isOverlay); + case UNITIALIZED: + return new AddUninitializedMemoryBlockCmd(blockName, comment, source, startAddr, + length, isRead, isWrite, isExecute, isVolatile, isOverlay); + default: + throw new AssertException( + "Encountered unexpected intialized type: " + initializedType); + + } + } + void dispose() { tool = null; program = null; } private void validateInfo() { - message = ""; - isValid = false; - if (initialValue < 0 && isInitialized) { - message = "Please enter a valid initial byte value"; - return; + isValid = hasValidName() && hasValidStartAddress() && hasValidLength() && + hasNoMemoryConflicts() && hasMappedAddressIfNeeded() && hasUniqueNameIfOverlay() && + hasInitialValueIfNeeded() && hasFileBytesInfoIfNeeded(); + } + + private boolean hasFileBytesInfoIfNeeded() { + + if (initializedType != InitializedType.INITIALIZED_FROM_FILE_BYTES) { + return true; } - if (blockName == null || blockName.length() == 0) { - message = "Please enter a name"; - return; + + if (fileBytes == null) { + message = "Please select a FileBytes entry"; + return false; } - if (nameExists(blockName)) { - message = "Block name already exists"; - return; + + if (fileBytesOffset < 0 || fileBytesOffset >= fileBytes.getSize()) { + message = + "Please enter a valid file bytes offset (0 - " + (fileBytes.getSize() - 1) + ")"; + return false; } - if (!NamingUtilities.isValidName(blockName)) { - message = "Block name is invalid"; - return; + + if (fileBytesOffset + length > fileBytes.getSize()) { + message = "File bytes offset + length exceeds file bytes size: " + fileBytes.getSize(); + return false; } - if (startAddr == null) { - message = "Please enter a valid starting address"; - return; + return true; + } + + private boolean hasInitialValueIfNeeded() { + + if (initializedType != InitializedType.INITIALIZED_FROM_VALUE) { + return true; } + + if (initialValue >= 0 && initialValue <= 255) { + return true; + } + message = "Please enter a valid initial byte value"; + return false; + } + + private boolean hasUniqueNameIfOverlay() { + if (blockType != MemoryBlockType.OVERLAY) { + return true; + } + AddressFactory factory = program.getAddressFactory(); + AddressSpace[] spaces = factory.getAddressSpaces(); + for (AddressSpace space : spaces) { + if (space.getName().equals(blockName)) { + message = "Address Space named " + blockName + " already exists"; + return false; + } + } + return true; + } + + private boolean hasMappedAddressIfNeeded() { if (blockType == MemoryBlockType.BIT_MAPPED || blockType == MemoryBlockType.BYTE_MAPPED) { - isInitialized = false; if (baseAddr == null) { String blockTypeStr = (blockType == MemoryBlockType.BIT_MAPPED) ? "bit" : "overlay"; message = "Please enter a source address for the " + blockTypeStr + " block"; - return; + return false; } } - long sizeLimit = - isInitialized ? Memory.MAX_INITIALIZED_BLOCK_SIZE : Memory.MAX_UNINITIALIZED_BLOCK_SIZE; - if (length <= 0 || length > sizeLimit) { - message = "Please enter a valid length > 0 and <= 0x" + Long.toHexString(sizeLimit); - return; - } + return true; + } + private boolean hasNoMemoryConflicts() { if (blockType == MemoryBlockType.OVERLAY) { - AddressFactory factory = program.getAddressFactory(); - AddressSpace[] spaces = factory.getAddressSpaces(); - for (int i = 0; i < spaces.length; i++) { - if (spaces[i].getName().equals(blockName)) { - message = "Address Space named " + blockName + " already exists"; - return; - } - } + return true; } - isValid = true; + Address endAddr = startAddr.add(length - 1); + AddressSet intersectRange = program.getMemory().intersectRange(startAddr, endAddr); + if (!intersectRange.isEmpty()) { + AddressRange firstRange = intersectRange.getFirstRange(); + message = "Block address conflict: " + firstRange; + return false; + } + return true; + } + + private boolean hasValidLength() { + long sizeLimit = Memory.MAX_BLOCK_SIZE; + if (length > 0 && length <= sizeLimit) { + return true; + } + message = "Please enter a valid length between 0 and 0x" + Long.toHexString(sizeLimit); + return false; + } + + private boolean hasValidStartAddress() { + if (startAddr != null) { + return true; + } + message = "Please enter a valid starting address"; + return false; + } + + private boolean hasValidName() { + if (blockName == null || blockName.length() == 0) { + message = "Please enter a name"; + return false; + } + if (nameExists(blockName)) { + message = "Block name already exists"; + return false; + } + if (!NamingUtilities.isValidName(blockName)) { + message = "Block name is invalid"; + return false; + } + return true; } /** @@ -258,8 +384,8 @@ class AddBlockModel { Memory memory = program.getMemory(); MemoryBlock[] blocks = memory.getBlocks(); - for (int i = 0; i < blocks.length; i++) { - nameIndexer.put(blocks[i].getName()); + for (MemoryBlock block : blocks) { + nameIndexer.put(block.getName()); } } 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 5745da6b37..cc0bd55c68 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 @@ -16,6 +16,7 @@ package ghidra.app.plugin.core.memory; import java.util.*; +import java.util.stream.Collectors; import javax.swing.JTable; import javax.swing.SwingUtilities; @@ -32,6 +33,7 @@ import docking.widgets.table.AbstractSortedTableModel; import ghidra.framework.model.DomainFile; import ghidra.framework.store.LockException; +import ghidra.program.database.mem.SourceInfo; import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.*; @@ -51,8 +53,9 @@ class MemoryMapModel extends AbstractSortedTableModel { final static byte VOLATILE = 7; final static byte BLOCK_TYPE = 8; final static byte INIT = 9; - final static byte SOURCE = 10; - final static byte COMMENT = 11; + final static byte BYTE_SOURCE = 10; + final static byte SOURCE = 11; + final static byte COMMENT = 12; final static String NAME_COL = "Name"; final static String START_COL = "Start"; @@ -64,6 +67,7 @@ class MemoryMapModel extends AbstractSortedTableModel { final static String VOLATILE_COL = "Volatile"; final static String BLOCK_TYPE_COL = "Type"; final static String INIT_COL = "Initialized"; + final static String BYTE_SOURCE_COL = "Byte Source"; final static String SOURCE_COL = "Source"; final static String COMMENT_COL = "Comment"; @@ -74,7 +78,7 @@ class MemoryMapModel extends AbstractSortedTableModel { private final static String COLUMN_NAMES[] = { NAME_COL, START_COL, END_COL, LENGTH_COL, READ_COL, WRITE_COL, EXECUTE_COL, VOLATILE_COL, - BLOCK_TYPE_COL, INIT_COL, SOURCE_COL, COMMENT_COL }; + BLOCK_TYPE_COL, INIT_COL, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL }; MemoryMapModel(MemoryMapProvider provider, Program program) { super(START); @@ -146,38 +150,10 @@ class MemoryMapModel extends AbstractSortedTableModel { */ @Override public int findColumn(String columnName) { - if (columnName.equals(NAME_COL)) { - return NAME; - } - if (columnName.equals(START_COL)) { - return START; - } - if (columnName.equals(END_COL)) { - return END; - } - if (columnName.equals(LENGTH_COL)) { - return LENGTH; - } - if (columnName.equals(READ_COL)) { - return READ; - } - if (columnName.equals(WRITE_COL)) { - return WRITE; - } - if (columnName.equals(EXECUTE_COL)) { - return EXECUTE; - } - if (columnName.equals(VOLATILE_COL)) { - return VOLATILE; - } - if (columnName.equals(SOURCE_COL)) { - return SOURCE; - } - if (columnName.equals(COMMENT_COL)) { - return COMMENT; - } - if (columnName.equals(BLOCK_TYPE_COL)) { - return BLOCK_TYPE; + for (int i = 0; i < COLUMN_NAMES.length; i++) { + if (COLUMN_NAMES[i].equals(columnName)) { + return i; + } } return 0; } @@ -523,10 +499,13 @@ class MemoryMapModel extends AbstractSortedTableModel { return null; } return (block.isInitialized() ? Boolean.TRUE : Boolean.FALSE); + case BYTE_SOURCE: + return getByteSourceDescription(block.getSourceInfos()); case SOURCE: if ((block.getType() == MemoryBlockType.BIT_MAPPED) || (block.getType() == MemoryBlockType.BYTE_MAPPED)) { - return ((MappedMemoryBlock) block).getOverlayedMinAddress().toString(); + SourceInfo info = block.getSourceInfos().get(0); + return info.getMappedRange().get().getMinAddress().toString(); } return block.getSourceName(); case COMMENT: @@ -543,6 +522,21 @@ class MemoryMapModel extends AbstractSortedTableModel { return null; } + private String getByteSourceDescription(List sourceInfos) { + List limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4); + + //@formatter:off + String description = limited + .stream() + .map(info -> info.getDescription()) + .collect(Collectors.joining(", ")); + //@formatter:on + if (limited != sourceInfos) { + description += "..."; + } + return description; + } + @Override public List getModelData() { return memList; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java index cd382bd70e..dd349e6be9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtil.java @@ -26,6 +26,7 @@ import ghidra.program.model.listing.*; import ghidra.program.model.mem.*; import ghidra.program.model.symbol.*; import ghidra.program.model.util.AddressLabelInfo; +import ghidra.util.Msg; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitorAdapter; @@ -187,6 +188,7 @@ public class MemoryBlockUtil { comment, source, r, w, x, monitor); } + /** * Creates an initialized memory block using the specified input stream. If the length * of the block is greater than the maximum size of a memory block (0x40000000), then @@ -241,7 +243,7 @@ public class MemoryBlockUtil { long blockLength = 0; // special case first time through loop, don't change start address while (dataLength > 0) { start = start.add(blockLength); - blockLength = Math.min(dataLength, Memory.MAX_INITIALIZED_BLOCK_SIZE); + blockLength = Math.min(dataLength, Memory.MAX_BLOCK_SIZE); String blockName = getBlockName(name, blockNum); monitor.setMessage( "Creating memory block \"" + blockName + "\" at 0x" + start + "..."); @@ -301,8 +303,8 @@ public class MemoryBlockUtil { //remove their ranges from our address set. // MemoryBlock[] existingBlocks = memory.getBlocks(); - for (int i = 0; i < existingBlocks.length; ++i) { - set.deleteRange(existingBlocks[i].getStart(), existingBlocks[i].getEnd()); + for (MemoryBlock existingBlock : existingBlocks) { + set.deleteRange(existingBlock.getStart(), existingBlock.getEnd()); } appendMessage("WARNING!!\n\tMemory block [" + name + @@ -605,16 +607,21 @@ public class MemoryBlockUtil { messages.append(msg); } - private void renameFragment(Address blockStart, String blockName) { + private void renameFragment(Address address, String name) { + adjustFragment(listing, address, name); + } + + public static void adjustFragment(Listing listing, Address address, String name) { String[] treeNames = listing.getTreeNames(); - for (int i = 0; i < treeNames.length; ++i) { + for (String treeName : treeNames) { try { - ProgramFragment frag = listing.getFragment(treeNames[i], blockStart); - frag.setName(blockName); + ProgramFragment frag = listing.getFragment(treeName, address); + frag.setName(name); } catch (DuplicateNameException e) { + Msg.warn(MemoryBlockUtil.class, + "Could not rename fragment to match newly created block because of name conflict"); } } } - } 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 new file mode 100644 index 0000000000..670bfb8816 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/MemoryBlockUtils.java @@ -0,0 +1,263 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util; + +import java.io.IOException; +import java.io.InputStream; + +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.store.LockException; +import ghidra.program.database.mem.FileBytes; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; +import ghidra.util.Msg; +import ghidra.util.exception.DuplicateNameException; + +/** + * Convenience methods for creating memory blocks. + */ +public class MemoryBlockUtils { + + /** + * Creates a new uninitialized memory block. + * @param program the program in which to create the block. + * @param isOverlay if true, the block will be created in a new overlay space for that block + * @param name the name of the new block. + * @param start the starting address of the new block. + * @param length the length of the new block + * @param comment the comment text to associate with the new block. + * @param source the source of the block (This field is not well defined - currently another comment) + * @param r the read permission for the new block. + * @param w the write permission for the new block. + * @param x the execute permission for the new block. + * @param log a {@link MessageLog} for appending error messages + * @return the newly created block or null if the operation failed. + */ + public static MemoryBlock createUninitializedBlock(Program program, boolean isOverlay, + String name, Address start, long length, String comment, String source, boolean r, + boolean w, boolean x, MessageLog log) { + + Memory memory = program.getMemory(); + try { + MemoryBlock block = memory.createUninitializedBlock(name, start, length, isOverlay); + setBlockAttributes(block, comment, source, r, w, x); + adjustFragment(program, start, name); + return block; + } + catch (LockException e) { + log.appendMsg("Failed to create memory block: exclusive lock/checkout required"); + } + catch (Exception e) { + log.appendMsg("Failed to create '" + name + "' memory block: " + e.getMessage()); + } + return null; + } + + /** + * Creates a new bit mapped memory block. (A bit mapped block is a block where each byte value + * is either 1 or 0 and the value is taken from a bit in a byte at some other address in memory) + * + * @param program the program in which to create the block. + * @param name the name of the new block. + * @param start the starting address of the new block. + * @param base the address of the region in memory to map to. + * @param length the length of the new block + * @param comment the comment text to associate with the new block. + * @param source the source of the block (This field is not well defined - currently another comment) + * @param r the read permission for the new block. + * @param w the write permission for the new block. + * @param x the execute permission for the new block. + * @param log a {@link StringBuffer} for appending error messages + * @return the new created block + */ + public static MemoryBlock createBitMappedBlock(Program program, String name, + Address start, Address base, int length, String comment, String source, boolean r, + boolean w, boolean x, MessageLog log) { + + Memory memory = program.getMemory(); + try { + + MemoryBlock block = memory.createBitMappedBlock(name, start, base, length); + + setBlockAttributes(block, comment, source, r, w, x); + adjustFragment(program, start, name); + return block; + } + catch (LockException e) { + log.appendMsg("Failed to create '" + name + + "'bit mapped memory block: exclusive lock/checkout required"); + } + catch (Exception e) { + log.appendMsg("Failed to create '" + name + "' mapped memory block: " + e.getMessage()); + } + return null; + } + + /** + * Creates a new byte mapped memory block. (A byte mapped block is a block where each byte value + * is taken from a byte at some other address in memory) + * + * @param program the program in which to create the block. + * @param name the name of the new block. + * @param start the starting address of the new block. + * @param base the address of the region in memory to map to. + * @param length the length of the new block + * @param comment the comment text to associate with the new block. + * @param source the source of the block (This field is not well defined - currently another comment) + * @param r the read permission for the new block. + * @param w the write permission for the new block. + * @param x the execute permission for the new block. + * @param log a {@link MessageLog} for appending error messages + * @return the new created block + */ + public static MemoryBlock createByteMappedBlock(Program program, String name, Address start, + Address base, int length, String comment, String source, boolean r, boolean w, + boolean x, MessageLog log) { + + Memory memory = program.getMemory(); + try { + + MemoryBlock block = memory.createByteMappedBlock(name, start, base, length); + + setBlockAttributes(block, comment, source, r, w, x); + adjustFragment(program, start, name); + return block; + } + catch (LockException e) { + log.appendMsg("Failed to create '" + name + + "' byte mapped memory block: exclusive lock/checkout required"); + } + catch (Exception e) { + log.appendMsg("Failed to create '" + name + "' mapped memory block: " + e.getMessage()); + } + return null; + } + + /** + * Creates a new initialized block in memory using the bytes from a {@link FileBytes} object. + * + * @param program the program in which to create the block. + * @param isOverlay if true, the block will be created in a new overlay space for that block + * @param name the name of the new block. + * @param start the starting address of the new block. + * @param fileBytes the {@link FileBytes} object that supplies the bytes for this block. + * @param offset the offset into the {@link FileBytes} object where the bytes for this block reside. + * @param length the length of the new block + * @param comment the comment text to associate with the new block. + * @param source the source of the block (This field is not well defined - currently another comment) + * @param r the read permission for the new block. + * @param w the write permission for the new block. + * @param x the execute permission for the new block. + * @param log a {@link MessageLog} for appending error messages + * @return the new created block + * @throws AddressOverflowException if the address + */ + + public static MemoryBlock createInitializedBlock(Program program, boolean isOverlay, + String name, Address start, FileBytes fileBytes, long offset, long length, + String comment, String source, boolean r, boolean w, boolean x, MessageLog log) + throws AddressOverflowException { + + if (!program.hasExclusiveAccess()) { + log.appendMsg("Failed to create memory block: exclusive access/checkout required"); + return null; + } + MemoryBlock block; + try { + block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset, + length, isOverlay); + } + catch (MemoryConflictException e) { + try { + block = program.getMemory().createInitializedBlock(name, start, fileBytes, offset, + length, true); + } + catch (LockException | DuplicateNameException | MemoryConflictException e1) { + throw new RuntimeException(e); + } + } + catch (LockException | DuplicateNameException e) { + throw new RuntimeException(e); + } + + setBlockAttributes(block, comment, source, r, w, x); + adjustFragment(program, start, name); + return block; + } + + /** + * Adjusts the name of the fragment at the given address to the given name. + * @param program the program whose fragment is to be renamed. + * @param address the address of the fragment to be renamed. + * @param name the new name for the fragment. + */ + public static void adjustFragment(Program program, Address address, String name) { + Listing listing = program.getListing(); + String[] treeNames = listing.getTreeNames(); + for (String treeName : treeNames) { + try { + ProgramFragment frag = listing.getFragment(treeName, address); + frag.setName(name); + } + catch (DuplicateNameException e) { + Msg.warn(MemoryBlockUtil.class, + "Could not rename fragment to match newly created block because of name conflict"); + } + } + } + + /** + * Creates a new {@link FileBytes} object using all the bytes from a {@link ByteProvider} + * @param program the program in which to create a new FileBytes object + * @param provider the ByteProvider from which to get the bytes. + * @return the newly created FileBytes object. + * @throws IOException if an IOException occurred. + */ + public static FileBytes createFileBytes(Program program, ByteProvider provider) + throws IOException { + return createFileBytes(program, provider, 0, provider.length()); + } + + /** + * Creates a new {@link FileBytes} object using a portion of the bytes from a {@link ByteProvider} + * @param program the program in which to create a new FileBytes object + * @param provider the ByteProvider from which to get the bytes. + * @param offset the offset into the ByteProvider from which to start loading bytes. + * @param length the number of bytes to load + * @return the newly created FileBytes object. + * @throws IOException if an IOException occurred. + */ + public static FileBytes createFileBytes(Program program, ByteProvider provider, long offset, + long length) throws IOException { + Memory memory = program.getMemory(); + try (InputStream fis = provider.getInputStream(offset)) { + return memory.createFileBytes(provider.getName(), offset, length, fis); + } + } + + private static void setBlockAttributes(MemoryBlock block, String comment, String source, + boolean r, + boolean w, boolean x) { + block.setComment(comment); + block.setSourceName(source); + block.setRead(r); + block.setWrite(w); + block.setExecute(x); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java index 2dd06c60d3..068a50292f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/BinaryLoader.java @@ -16,7 +16,6 @@ package ghidra.app.util.opinion; import java.io.IOException; -import java.io.InputStream; import java.util.*; import ghidra.app.util.*; @@ -25,10 +24,12 @@ import ghidra.app.util.importer.MemoryConflictHandler; import ghidra.app.util.importer.MessageLog; import ghidra.framework.model.DomainFolder; import ghidra.framework.model.DomainObject; +import ghidra.framework.store.LockException; +import ghidra.program.database.mem.FileBytes; import ghidra.program.model.address.*; import ghidra.program.model.lang.*; import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.*; import ghidra.util.Msg; import ghidra.util.NumericUtilities; import ghidra.util.exception.CancelledException; @@ -265,8 +266,7 @@ public class BinaryLoader extends AbstractProgramLoader { @Override protected List loadProgram(ByteProvider provider, String programName, DomainFolder programFolder, LoadSpec loadSpec, List