From 1fc22ba44f88bba26d728dd4d609d28c18633ba3 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Tue, 5 May 2026 15:14:26 -0400 Subject: [PATCH] GP-2393 - Removed size restriction in Select Bytes dialog --- .../plugin/core/select/SelectBlockDialog.java | 210 +++---- .../core/select/SelectBlockPluginTest.java | 565 +++++++++++------- .../ghidra/program/model/address/Address.java | 34 +- .../program/model/address/AddressSpace.java | 92 +-- 4 files changed, 522 insertions(+), 379 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java index c42d876112..3862a774f5 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/select/SelectBlockDialog.java @@ -31,30 +31,28 @@ import ghidra.framework.plugintool.PluginTool; import ghidra.program.model.address.*; import ghidra.program.util.ProgramSelection; import ghidra.util.HelpLocation; +import ghidra.util.Msg; import ghidra.util.layout.PairLayout; /** - * Class to set up dialog box that will enable the user - * to set the available options for block selection + * Dialog for making program selections */ class SelectBlockDialog extends ReusableDialogComponentProvider { - private static final String OVERFLOW_SELECTION_WARNING = - "Selection is larger than available " + "bytes, using the end of the address space"; + + private PluginTool tool; + private Navigatable navigatable; private JTextField toAddressField; - private IntegerTextField numberInputField; // AddressInput allows decimal and hex input + private IntegerTextField lengthField; private JRadioButton forwardButton; private JRadioButton backwardButton; private JRadioButton allButton; private JRadioButton toButton; - private Navigatable navigatable; - private PluginTool tool; SelectBlockDialog(PluginTool tool, Navigatable navigatable) { super("Select Bytes", false, true, true, false); this.tool = tool; this.navigatable = navigatable; -// navigatable.addNavigatableListener(this); addWorkPanel(buildPanel()); addOKButton(); @@ -62,7 +60,6 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { addDismissButton(); setHelpLocation(new HelpLocation("SelectBlockPlugin", "Select_Block_Help")); - // make sure the state of the widgets is correct setItemsEnabled(false); forwardButton.doClick(); } @@ -94,11 +91,10 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { main.add(toAddressField); main.add(new GLabel("Length: ")); - numberInputField = new IntegerTextField(10); - numberInputField.getComponent().getAccessibleContext().setAccessibleName("Number Input"); - numberInputField.setMaxValue(BigInteger.valueOf(Integer.MAX_VALUE)); - numberInputField.setMinValue(BigInteger.ZERO); - main.add(numberInputField.getComponent()); + lengthField = new IntegerTextField(10); + lengthField.getComponent().getAccessibleContext().setAccessibleName("Number Input"); + lengthField.setMinValue(BigInteger.ZERO); + main.add(lengthField.getComponent()); main.getAccessibleContext().setAccessibleName("Block"); return main; } @@ -169,6 +165,11 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { navigatable = null; } + void setNavigatable(Navigatable navigatable) { + this.navigatable = navigatable; + setOkEnabled(navigatable != null); + } + void show(ComponentProvider provider) { tool.showDialog(this, provider); repack(); @@ -187,188 +188,147 @@ class SelectBlockDialog extends ReusableDialogComponentProvider { private void setLengthInputEnabled(boolean enabled) { if (!enabled) { - numberInputField.setValue(null); + lengthField.setValue(null); } - numberInputField.setEnabled(enabled); + lengthField.setEnabled(enabled); } @Override protected void okCallback() { if (toButton.isSelected()) { - handleToAddressSelection(); + selectToAddress(); } else if (allButton.isSelected()) { - handleAllSelection(); + selectAll(); } else if (forwardButton.isSelected()) { - handleForwardSelection(); + createSelection(true); } else if (backwardButton.isSelected()) { - handleBackwardSelection(); + createSelection(false); } else { setStatusText("You must choose the type of selection to make"); } } - private void handleAllSelection() { + private void selectAll() { AddressSetView addressSet = navigatable.getProgram().getMemory(); ProgramSelection selection = new ProgramSelection(addressSet); NavigationUtils.setSelection(tool, navigatable, selection); clearStatusText(); } - private void handleToAddressSelection() { - Address toAddress = null; + private void selectToAddress() { + String addressValue = toAddressField.getText(); clearStatusText(); // make sure the order of the addresses is correct Address currentAddress = navigatable.getLocation().getAddress(); + Address to = null; try { - toAddress = currentAddress.getAddress(addressValue); + to = currentAddress.getAddress(addressValue); } catch (AddressFormatException e) { // use the fact that toAddress remains null } - if (toAddress == null) { + + if (to == null) { setStatusText("Invalid address value, enter another address"); return; } - if (toAddress.compareTo(currentAddress) < 0) { - Address tmp = toAddress; - toAddress = currentAddress; + + if (to.compareTo(currentAddress) < 0) { + Address tmp = to; + to = currentAddress; currentAddress = tmp; } - AddressSet addressSet = new AddressSet(currentAddress, toAddress); + AddressSet addressSet = new AddressSet(currentAddress, to); ProgramSelection selection = new ProgramSelection(addressSet); NavigationUtils.setSelection(tool, navigatable, selection); } - private void handleForwardSelection() { - // value is a length - int length = numberInputField.getIntValue(); // throws NFE - if (length == 0) { + private void createSelection(boolean forward) { + BigInteger length = lengthField.getValue(); + if (length == null || length == BigInteger.ZERO) { setStatusText("length must be > 0"); return; } clearStatusText(); - Address currentAddress = navigatable.getLocation().getAddress(); - - AddressSet addressSet = new AddressSet(navigatable.getSelection()); - - if (addressSet.isEmpty()) { - addressSet.addRange(currentAddress, currentAddress); + AddressSet startSet; + ProgramSelection currentSelection = navigatable.getSelection(); + if (!currentSelection.isEmpty()) { + startSet = new AddressSet(currentSelection); + } + else { + Address currentAddress = navigatable.getLocation().getAddress(); + startSet = new AddressSet(currentAddress); } - AddressRangeIterator aiter = addressSet.getAddressRanges(); + AddressRangeIterator it = startSet.getAddressRanges(); AddressSet newSet = new AddressSet(); - while (aiter.hasNext()) { - AddressRange range = aiter.next(); - Address toAddress = createForwardToAddress(range.getMinAddress(), length - 1); - if (toAddress != null) { - newSet.addRange(range.getMinAddress(), toAddress); + while (it.hasNext()) { + AddressRange range = it.next(); + + if (forward) { + Address from = range.getMinAddress(); + createForwardRange(newSet, from, length); + } + else { + Address to = range.getMaxAddress(); + createBackwardRange(newSet, to, length); } } - ProgramSelection selection = new ProgramSelection(newSet); - NavigationUtils.setSelection(tool, navigatable, selection); + + ProgramSelection newSelection = new ProgramSelection(newSet); + NavigationUtils.setSelection(tool, navigatable, newSelection); } - private void handleBackwardSelection() { - // value is a length - int length = numberInputField.getIntValue(); - if (length == 0) { - setStatusText("length must be > 0"); - return; - } - clearStatusText(); - - Address currentAddress = navigatable.getLocation().getAddress(); - AddressSet addressSet = new AddressSet(navigatable.getSelection()); - if (addressSet.isEmpty()) { - addressSet.addRange(currentAddress, currentAddress); - } - - AddressRangeIterator aiter = addressSet.getAddressRanges(); - AddressSet newSet = new AddressSet(); - while (aiter.hasNext()) { - AddressRange range = aiter.next(); - - Address fromAddress = createBackwardToAddress(range.getMaxAddress(), length - 1); - if (fromAddress != null) { - newSet.addRange(fromAddress, range.getMaxAddress()); - } - } - ProgramSelection selection = new ProgramSelection(newSet); - NavigationUtils.setSelection(tool, navigatable, selection); + private void createForwardRange(AddressSet set, Address from, BigInteger length) { + Address to = getToAddress(from, length); + set.addRange(from, to); } - private Address createBackwardToAddress(Address toAddress, long length) { - AddressSpace addressSpace = toAddress.getAddressSpace(); - if (addressSpace.isOverlaySpace()) { - OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - AddressRange range = oas.getOverlayAddressSet().getRangeContaining(toAddress); - if (range == null) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return toAddress; - } - long avail = toAddress.subtract(range.getMinAddress()); - if (avail < (length - 1)) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return range.getMinAddress(); - } - } + private void createBackwardRange(AddressSet set, Address to, BigInteger length) { + Address from = getFromAddress(to, length); + set.addRange(from, to); + } - Address addr = null; + private Address getFromAddress(Address to, BigInteger length) { + + // subtract one to be inclusive; address ranges are inclusive + BigInteger inclusiveLength = length.subtract(BigInteger.ONE); try { - addr = toAddress.subtractNoWrap(length); + return to.subtractNoWrap(inclusiveLength); } - catch (AddressOverflowException aoobe) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - addr = addressSpace.getMinAddress(); + catch (AddressOverflowException e) { + showWarningDialog(); + AddressSpace space = to.getAddressSpace(); + return space.getMinAddress(); } - - return addr; } - private Address createForwardToAddress(Address fromAddress, long length) { + private Address getToAddress(Address from, BigInteger length) { - AddressSpace addressSpace = fromAddress.getAddressSpace(); - if (addressSpace.isOverlaySpace()) { - OverlayAddressSpace oas = (OverlayAddressSpace) addressSpace; - AddressRange range = oas.getOverlayAddressSet().getRangeContaining(fromAddress); - if (range == null) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return fromAddress; - } - long avail = range.getMaxAddress().subtract(fromAddress); - if (avail < (length - 1)) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - return range.getMaxAddress(); - } - } - - Address addr = null; + // subtract one to be inclusive; address ranges are inclusive + BigInteger inclusiveLength = length.subtract(BigInteger.ONE); try { - addr = fromAddress.addNoWrap(length); + return from.addNoWrap(inclusiveLength); } - catch (AddressOverflowException aoobe) { - showWarningDialog(OVERFLOW_SELECTION_WARNING); - addr = addressSpace.getMaxAddress(); + catch (AddressOverflowException e) { + showWarningDialog(); + AddressSpace space = from.getAddressSpace(); + return space.getMaxAddress(); } - - return addr; } - private void showWarningDialog(final String text) { - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(getComponent(), text)); + private void showWarningDialog() { + Msg.showWarn(this, getComponent(), "Selection Overflow", + "Selection is larger than available bytes. Using the boundary of the address space."); } - public void setNavigatable(Navigatable navigatable) { - this.navigatable = navigatable; - setOkEnabled(navigatable != null); - } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java index 42c940d0ea..662d075616 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/select/SelectBlockPluginTest.java @@ -4,9 +4,9 @@ * 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. @@ -35,23 +35,24 @@ import ghidra.app.plugin.core.navigation.NextPrevAddressPlugin; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; +import ghidra.program.database.mem.MemoryMapDB; import ghidra.program.model.address.*; -import ghidra.program.util.ProgramLocation; +import ghidra.program.model.mem.MemoryBlock; import ghidra.program.util.ProgramSelection; import ghidra.test.*; +import ghidra.util.task.TaskMonitor; /** - * Test class to test SelectBlock + * Test class to test the Select Bytes dialog. */ - public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { + private static final String SELECT_BYTES_BUTTON_NAME = "Select Bytes"; private TestEnv env; private PluginTool tool; private CodeBrowserPlugin browser; - private SelectBlockPlugin plugin; - private DockingActionIf action; + private DockingActionIf showDialogAction; private ProgramDB program; @Before @@ -63,9 +64,9 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { configureTool(tool); browser = env.getPlugin(CodeBrowserPlugin.class); - plugin = env.getPlugin(SelectBlockPlugin.class); + SelectBlockPlugin plugin = env.getPlugin(SelectBlockPlugin.class); - action = (DockingActionIf) getInstanceField("toolBarAction", plugin); + showDialogAction = (DockingActionIf) getInstanceField("toolBarAction", plugin); } @After @@ -82,7 +83,7 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - public void open8051Program() throws Exception { + private void open8051Program() throws Exception { ProgramBuilder builder = new ProgramBuilder("program2", ProgramBuilder._8051); builder.createMemory("CODE", "CODE:0000", 0x6fff); program = builder.getProgram(); @@ -91,18 +92,6 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { waitForSwing(); } - private void closeProgram() throws Exception { - if (program != null) { - env.close(program); - program = null; - } - } - - private Address addr(long addr) { - AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); - return space.getAddress(addr); - } - private void configureTool(PluginTool toolToConfigure) throws Exception { toolToConfigure.addPlugin(BlockModelServicePlugin.class.getName()); toolToConfigure.addPlugin(NextPrevAddressPlugin.class.getName()); @@ -111,6 +100,311 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { toolToConfigure.addPlugin(SelectBlockPlugin.class.getName()); } + @Test + public void testActionEnablement() throws Exception { + + assertFalse(showDialogAction.isEnabledForContext(getContext())); + openProgram(); + assertTrue(showDialogAction.isEnabledForContext(getContext())); + + SelectBlockDialog dialog = showDialog(); + + closeProgram(); + assertTrue(!showDialogAction.isEnabledForContext(getContext())); + close(dialog); + } + + @Test + public void testSelectAll() throws Exception { + openProgram(); + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(program.getMemory()), new AddressSet(selection)); + close(dialog); + } + + @Test + public void testSelectForward() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + assertAddressFieldDisabled(dialog); + assertLengthFieldDisabled(dialog); + + pressSelectForward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006420), addr(0x100651f)), selection); + close(dialog); + } + + @Test + public void testBadInput() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setAddress(dialog, "foo"); + + pressSelectBytes(dialog); + assertEquals("length must be > 0", dialog.getStatusText()); + close(dialog); + } + + @Test + public void testSelectBackward() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006321), addr(0x1006420)), selection); + close(dialog); + + } + + @Test + public void testSelectToAddr() throws Exception { + openProgram(); + goTo(addr(0x1006420)); + + SelectBlockDialog dialog = showDialog(); + + pressSelectToAddress(dialog); + + setAddress(dialog, "0x1007000"); + + pressSelectBytes(dialog); + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x1006420), addr(0x1006fff)), selection); + close(dialog); + } + + @Test + public void testSelectForward_OverlayBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayStart); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectForward_OverlayBlock_LengthLargerThanBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayStart); + + SelectBlockDialog dialog = showDialog(); + + pressSelectForward(dialog); + + setLength(dialog, 0x101); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectBackward_OverlayBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayEnd); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x100); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSelectBackward_OverlayBlock_LengthLargerThanBlock() throws Exception { + + openProgram(); + + Address start = addr(0x1006420); + + MemoryBlock block = createOverlayBlock(start, 0x100); + Address overlayStart = block.getStart(); + Address overlayEnd = block.getEnd(); + goTo(overlayEnd); + + SelectBlockDialog dialog = showDialog(); + + pressSelectBackward(dialog); + + setLength(dialog, 0x101); + + pressSelectBytes(dialog); + + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(overlayStart, overlayEnd), selection); + close(dialog); + } + + @Test + public void testSegmentedProgram() throws Exception { + + open8051Program(); + Address startAddress = addr(0x6420); + goTo(startAddress); + + SelectBlockDialog dialog = showDialog(); + + pressSelectAll(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldDisabled(dialog); + + pressSelectBytes(dialog); + ProgramSelection selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(program.getMemory()), new AddressSet(selection)); + + //--------------------------- + + clearSelection(); + + pressSelectToAddress(dialog); + + assertAddressFieldEnabled(dialog); + assertLengthFieldDisabled(dialog); + + setAddress(dialog, "0x6520"); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(startAddress, addr(0x6520)), selection); + + //--------------------------- + + clearSelection(); + + pressSelectForward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 100); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(startAddress, addr(0x6483)), selection); + + //--------------------------- + + clearSelection(); + + pressSelectBackward(dialog); + + assertAddressFieldDisabled(dialog); + assertLengthFieldEnabled(dialog); + + setLength(dialog, 100); + + pressSelectBytes(dialog); + selection = browser.getCurrentSelection(); + assertEquals(new AddressSet(addr(0x63bd), startAddress), selection); + close(dialog); + } + + @Test + public void testCloseProgram() throws Exception { + openProgram(); + closeProgram(); + + assertFalse(showDialogAction.isEnabledForContext(getContext())); + } + +//================================================================================================= +// Private Methods +//================================================================================================= + + private void pressSelectToAddress(SelectBlockDialog dialog) { + JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); + pressButton(toButton, true); + } + + private SelectBlockDialog showDialog() { + performAction(showDialogAction, getContext(), true); + SelectBlockDialog dialog = waitForDialogComponent(SelectBlockDialog.class); + return dialog; + } + + private MemoryBlock createOverlayBlock(Address start, int length) { + return tx(program, () -> { + MemoryMapDB memory = program.getMemory(); + return memory.createInitializedBlock("Name", start, length, (byte) 0, TaskMonitor.DUMMY, + true); + }); + } + + private Address addr(long addr) { + AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); + return space.getAddress(addr); + } + private ActionContext getContext() { ActionContext context = browser.getProvider().getActionContext(null); if (context == null) { @@ -119,205 +413,64 @@ public class SelectBlockPluginTest extends AbstractGhidraHeadedIntegrationTest { return context; } - @Test - public void testActionEnablement() throws Exception { - assertTrue(!action.isEnabledForContext(getContext())); - openProgram(); - assertTrue(action.isEnabledForContext(getContext())); - performAction(action, getContext(), true); - assertTrue(action.isEnabledForContext(getContext())); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - assertNotNull(dialog); - assertTrue(dialog.isVisible()); - closeProgram(); - assertTrue(!action.isEnabledForContext(getContext())); - runSwing(() -> dialog.close()); + private void closeProgram() throws Exception { + if (program != null) { + env.close(program); + program = null; + } } - @Test - public void testSelectAll() throws Exception { - openProgram(); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(program.getMemory()), new AddressSet(currSelection)); - runSwing(() -> dialog.close()); - } - - @Test - public void testSelectForward() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - - JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); - assertTrue(!addressInputField.isEnabled()); - - final IntegerTextField inputField = - (IntegerTextField) getInstanceField("numberInputField", dialog); - assertTrue(!inputField.getComponent().isEnabled()); - - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - runSwing(() -> inputField.setValue(0x100)); - - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006420), addr(0x100651f)), currSelection); - runSwing(() -> dialog.close()); - } - - @Test - public void testBadInput() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - final JTextField addressInputField = - (JTextField) getInstanceField("toAddressField", dialog); - - runSwing(() -> addressInputField.setText("foo")); - pressSelectBytes(dialog); - assertEquals("length must be > 0", dialog.getStatusText()); - runSwing(() -> dialog.close()); - } - - @Test - public void testSelectBackward() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); + private void pressSelectBackward(SelectBlockDialog dialog) { JRadioButton backwardButton = (JRadioButton) findComponentByName(dialog, "backwardButton"); pressButton(backwardButton, true); - - final IntegerTextField inputField = - (IntegerTextField) getInstanceField("numberInputField", dialog); - runSwing(() -> inputField.setValue(256)); - - pressButtonByText(dialog, SELECT_BYTES_BUTTON_NAME, true); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006321), addr(0x1006420)), currSelection); - runSwing(() -> dialog.close()); - } - @Test - public void testSelectToAddr() throws Exception { - openProgram(); - browser.goTo(new ProgramLocation(program, addr(0x1006420))); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); - pressButton(toButton, true); - + private void setAddress(SelectBlockDialog dialog, String text) { final JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); - runSwing(() -> addressInputField.setText("0x1007000")); - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x1006420), addr(0x1006fff)), currSelection); - runSwing(() -> dialog.close()); - + runSwing(() -> addressInputField.setText(text)); } - @Test - public void testSegmentedProgram() throws Exception { - // select all - open8051Program(); - Address startAddress = addr(0x6420); - browser.goTo(new ProgramLocation(program, startAddress)); - performAction(action, getContext(), true); - SelectBlockDialog dialog = - waitForDialogComponent(tool.getToolFrame(), SelectBlockDialog.class, 1000); - JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); - pressButton(allButton, true); - - // all input fields disabled - final JTextField addressInputField = - (JTextField) getInstanceField("toAddressField", dialog); - assertTrue(!addressInputField.isEnabled()); - + private void setLength(SelectBlockDialog dialog, int length) { IntegerTextField inputField = (IntegerTextField) getInstanceField("numberInputField", dialog); - assertTrue(!inputField.getComponent().isEnabled()); - - pressSelectBytes(dialog); - ProgramSelection currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(program.getMemory()), new AddressSet(currSelection)); - - clearSelection(); - - // to address - JRadioButton toButton = (JRadioButton) findComponentByName(dialog, "toButton"); - pressButton(toButton, true); - - // only address input field is enabled - assertTrue(addressInputField.isEnabled()); - assertTrue(!inputField.getComponent().isEnabled()); - - runSwing(() -> addressInputField.setText("0x6520")); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(startAddress, addr(0x6520)), currSelection); - - clearSelection(); - - // select forward - JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); - pressButton(forwardButton, true); - - // only address input field is enabled - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - inputField.setValue(100); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(startAddress, addr(0x6483)), currSelection); - - clearSelection(); - - // select backward - JRadioButton backwardButton = (JRadioButton) findComponentByName(dialog, "backwardButton"); - pressButton(backwardButton, true); - - // only address input field is enabled - assertTrue(!addressInputField.isEnabled()); - assertTrue(inputField.getComponent().isEnabled()); - - inputField.setValue(100); - pressSelectBytes(dialog); - currSelection = browser.getCurrentSelection(); - assertEquals(new AddressSet(addr(0x63bd), startAddress), currSelection); - runSwing(() -> dialog.close()); + runSwing(() -> inputField.setValue(length)); } - @Test - public void testCloseProgram() throws Exception { - openProgram(); - closeProgram(); + private void pressSelectForward(SelectBlockDialog dialog) { + JRadioButton forwardButton = (JRadioButton) findComponentByName(dialog, "forwardButton"); + pressButton(forwardButton, true); + } - assertTrue(!action.isEnabledForContext(getContext())); + private void assertLengthFieldDisabled(SelectBlockDialog dialog) { + IntegerTextField inputField = + (IntegerTextField) getInstanceField("numberInputField", dialog); + assertFalse(inputField.getComponent().isEnabled()); + } + + private void assertLengthFieldEnabled(SelectBlockDialog dialog) { + IntegerTextField inputField = + (IntegerTextField) getInstanceField("numberInputField", dialog); + assertTrue(inputField.getComponent().isEnabled()); + } + + private void assertAddressFieldDisabled(SelectBlockDialog dialog) { + JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); + assertFalse(addressInputField.isEnabled()); + } + + private void assertAddressFieldEnabled(SelectBlockDialog dialog) { + JTextField addressInputField = (JTextField) getInstanceField("toAddressField", dialog); + assertTrue(addressInputField.isEnabled()); + } + + private void pressSelectAll(SelectBlockDialog dialog) { + JRadioButton allButton = (JRadioButton) findComponentByName(dialog, "allButton"); + pressButton(allButton, true); + } + + private void goTo(Address a) { + goTo(tool, program, a); } private void clearSelection() { 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 5fe08eca94..ae1188b31f 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 @@ -68,6 +68,7 @@ public interface Address extends Comparable
{ /** * Returns a new address in this address's space with the given offset. + * *

NOTE: for those spaces with an addressable unit size other than 1, the address returned * may not correspond to an addressable unit/word boundary if a byte-offset is specified. * @@ -81,14 +82,16 @@ public interface Address extends Comparable

{ * @throws AddressOutOfBoundsException if the offset is less than 0 or greater than the max * offset allowed for this space. */ - Address getNewAddress(long offset, boolean isAddressableWordOffset) + public Address getNewAddress(long offset, boolean isAddressableWordOffset) throws AddressOutOfBoundsException; /** * Returns a new address in this address's space with the given offset. The specified offset * will be truncated within the space and will not throw an exception. + * *

NOTE: for those spaces with an addressable unit size other than 1, the address returned * may not correspond to a word boundary (addressable unit) if a byte-offset is specified. + * * @param offset the offset for the new address. * @param isAddressableWordOffset if true the specified offset is an addressable unit/word * offset, otherwise offset is a byte offset. See @@ -97,7 +100,7 @@ public interface Address extends Comparable

{ * (i.e., wordOffset = byteOffset * addressableUnitSize). * @return address with given byte offset truncated to the physical space size */ - Address getNewTruncatedAddress(long offset, boolean isAddressableWordOffset); + public Address getNewTruncatedAddress(long offset, boolean isAddressableWordOffset); /** * Returns the number of bytes needed to form a pointer to this address. The result will be @@ -193,7 +196,7 @@ public interface Address extends Comparable
{ * Creates a new address by subtracting the displacement from the current address. If the * offset is greater than the max offset of the address space, the high order bits are masked * off, making the address wrap. For non-segmented addresses this will be the same as - * subtractWrap(). For segmented addresses, the address will wrap when the 20 bit (oxfffff) + * subtractWrap(). For segmented addresses, the address will wrap when the 20 bit (0xfffff) * offset is exceeded, as opposed to when the segment offset is exceeded. * @param displacement the displacement to add. * @return The new Address formed by subtracting the displacement from this address's offset. @@ -212,6 +215,18 @@ public interface Address extends Comparable
{ */ public Address subtractNoWrap(long displacement) throws AddressOverflowException; + /** + * Creates a new Address by subtracting displacement from the Address. The Address will not + * wrap within the space and in fact will throw an exception if the result is less than the min + * address in this space or greater than the max address in this space. + * + * @param displacement the displacement to subtract. + * @return The new Address + * @throws AddressOverflowException if the offset in this Address would overflow due to this + * operation. + */ + public Address subtractNoWrap(BigInteger displacement) throws AddressOverflowException; + /** * Creates a new address (possibly in a new space) by subtracting the displacement to this * address. @@ -235,7 +250,7 @@ public interface Address extends Comparable
{ * Creates a new address by adding the displacement to the current address. If the offset is * greater than the max offset of the address space, the high order bits are masked off, making * the address wrap. For non-segmented addresses this will be the same as addWrap(). For - * segmented addresses, the address will wrap when the 20 bit (oxfffff) offset is exceeded, as + * segmented addresses, the address will wrap when the 20 bit (0xfffff) offset is exceeded, as * opposed to when the segment offset is exceeded. * @param displacement the displacement to add. * @return The new Address formed by adding the displacement to this address's offset. @@ -253,8 +268,16 @@ public interface Address extends Comparable
{ */ public Address addNoWrap(long displacement) throws AddressOverflowException; + /** + * Creates a new Address with a displacement relative to this Address. The Address will not + * wrap around! An exception will be throw if the result is not within this address space. + * + * @param displacement the displacement to add. + * @return the new address. + * @throws AddressOverflowException if the offset in this Address would overflow (wrap around) + * due to this operation. + */ public Address addNoWrap(BigInteger displacement) throws AddressOverflowException; - public Address subtractNoWrap(BigInteger displacement) throws AddressOverflowException; /** * Creates a new address (possibly in a new space) by adding the displacement to this address. @@ -394,6 +417,7 @@ public interface Address extends Comparable
{ /** * Returns true if this address represents a location in the register space. + * *

NOTE: It is important to note that a {@link Register} could reside within a memory space * and not the register space in which case this method would return false for its address. * @return true if a register address diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java index bef7522f99..d34aef083b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSpace.java @@ -105,20 +105,20 @@ public interface AddressSpace extends Comparable { * {@return the name of this address space}. * With the exception of {@link OverlayAddressSpace}, the name of an address space may not change. */ - String getName(); + public String getName(); /** * Get the ID for this space * * @return space ID */ - int getSpaceID(); + public int getSpaceID(); /** * {@return the number of bits that are used to form the address.} Thus * the maximum offset for this address space will be 2^size-1. */ - int getSize(); + public int getSize(); /** * {@return the number of data bytes which correspond to each addressable @@ -134,7 +134,7 @@ public interface AddressSpace extends Comparable { * wordOffset = getAddressableWordOffset(byteOffset) * */ - int getAddressableUnitSize(); + public int getAddressableUnitSize(); /** * Get the addressable memory word offset which corresponds to the specified @@ -151,17 +151,17 @@ public interface AddressSpace extends Comparable { * @see Program#getDefaultPointerSize() for a user adjustable pointer size which is derived from the * CompilerSpec store pointer size. */ - int getPointerSize(); + public int getPointerSize(); /** * {@return the type of this address space} */ - int getType(); + public int getType(); /** * {@return the unique index for this address space} */ - int getUnique(); + public int getUnique(); /** * Parses the String into an address within this address space. @@ -171,7 +171,7 @@ public interface AddressSpace extends Comparable { * @throws AddressFormatException if the string cannot be parsed or the * parsed offset is larger than the size for this space. */ - Address getAddress(String addrString) throws AddressFormatException; + public Address getAddress(String addrString) throws AddressFormatException; /** * Parses the String into an address within this address space. @@ -182,47 +182,54 @@ public interface AddressSpace extends Comparable { * @throws AddressFormatException if the string cannot be parsed or the * parsed offset is larger than the size for this space. */ - Address getAddress(String addrString, boolean caseSensitive) throws AddressFormatException; + public Address getAddress(String addrString, boolean caseSensitive) + throws AddressFormatException; /** * Returns a new address in this space with the given byte offset. + *

* NOTE: This method is the same as invoking getAddress(long byteOffset, false). + * * @param byteOffset the byte offset for the new address. * @return address with given byte offset * @throws AddressOutOfBoundsException if the offset is less than 0 or greater * than the max offset allowed for this space. */ - Address getAddress(long byteOffset) throws AddressOutOfBoundsException; + public Address getAddress(long byteOffset) throws AddressOutOfBoundsException; /** - * Returns a new address in this space with the given offset. + * Returns a new address in this space with the given offset. + *

* NOTE: for those spaces with an addressable unit size other than 1, the address * returned may not correspond to an addressable unit/word boundary if a byte-offset * is specified. + * * @param offset the offset for the new address. - * @param isAddressableWordOffset if true the specified offset is an addressable unit/word offset, - * otherwise offset is a byte offset. See {@link #getAddressableUnitSize()} + * @param isAddressableWordOffset if true the specified offset is an addressable unit/word + * offset, otherwise offset is a byte offset. See {@link #getAddressableUnitSize()} * to understand the distinction (i.e., wordOffset = byteOffset * addressableUnitSize). * @return address with given offset * @throws AddressOutOfBoundsException if the offset is less than 0 or greater * than the max offset allowed for this space. */ - Address getAddress(long offset, boolean isAddressableWordOffset) + public Address getAddress(long offset, boolean isAddressableWordOffset) throws AddressOutOfBoundsException; /** * Returns a new address in this space with the given offset. The specified * offset will be truncated within the space and will not throw an exception. + *

* NOTE: for those spaces with an addressable unit size other than 1, the address * returned may not correspond to a word boundary (addressable unit) if a byte-offset * is specified. + * * @param offset the offset for the new address. * @param isAddressableWordOffset if true the specified offset is an addressable unit/word offset, * otherwise offset is a byte offset. See {@link #getAddressableUnitSize()} * to understand the distinction (i.e., wordOffset = byteOffset * addressableUnitSize). * @return address with given byte offset truncated to the physical space size */ - Address getTruncatedAddress(long offset, boolean isAddressableWordOffset); + public Address getTruncatedAddress(long offset, boolean isAddressableWordOffset); /** * Get a byte address from this address space. Don't allow overlay spaces @@ -231,18 +238,17 @@ public interface AddressSpace extends Comparable { * * @param byteOffset the byte offset for the new address. * @return an address if the offset is valid. - * * @throws AddressOutOfBoundsException if the offset is less than 0 or greater * than the max offset allowed for this space. */ - Address getAddressInThisSpaceOnly(long byteOffset) throws AddressOutOfBoundsException; + public Address getAddressInThisSpaceOnly(long byteOffset) throws AddressOutOfBoundsException; /** * Truncate the specified byte offset within this space to produce a valid offset. * @param byteOffset any byte offset * @return truncated byte offset */ - long truncateOffset(long byteOffset); + public long truncateOffset(long byteOffset); /** * Truncate the specified addressable unit/word offset within this space to produce a @@ -250,7 +256,7 @@ public interface AddressSpace extends Comparable { * @param wordOffset any addressable unit/word offset * @return truncated word offset */ - long truncateAddressableWordOffset(long wordOffset); + public long truncateAddressableWordOffset(long wordOffset); /** * Get an address that is relative to this address space. @@ -258,10 +264,9 @@ public interface AddressSpace extends Comparable { * this space, return an address based in this space. * * @param addr address possibly falling within this overlay space. - * * @return an address relative to this overlay */ - Address getOverlayAddress(Address addr); + public Address getOverlayAddress(Address addr); /** * Calculates the displacement between addr1 and addr2 (addr1 - addr2) @@ -277,6 +282,7 @@ public interface AddressSpace extends Comparable { /** * Creates a new address by subtracting displacement from addr's offset. + * * @param addr the original address. The new address will wrap in a manner * that depends on the address space. For a generic address space this will * wrap at the extents of the address space. For a segmented address space @@ -291,12 +297,12 @@ public interface AddressSpace extends Comparable { * address. If the offset is greater than the max offset of the address space, the high * order bits are masked off, making the address wrap. For non-segmented addresses this * will be the same as subtractWrap(). For segmented addresses, the address will wrap when - * the 20 bit (oxfffff) offset is exceeded, as opposed to when the segment offset is exceeded. + * the 20 bit (0xfffff) offset is exceeded, as opposed to when the segment offset is exceeded. * @param addr the address to subtract the displacement from. * @param displacement the displacement to subtract. * @return The new Address formed by subtracting the displacement from the specified address. */ - Address subtractWrapSpace(Address addr, long displacement); + public Address subtractWrapSpace(Address addr, long displacement); /** * Creates a new address by subtracting displacement from addr's offset. @@ -308,6 +314,17 @@ public interface AddressSpace extends Comparable { */ public Address subtractNoWrap(Address addr, long displacement) throws AddressOverflowException; + /** + * Creates a new address by subtracting the displacement to the given address. The + * new address will NOT wrap! + * @param addr the original address. + * @param displacement the displacement to subtract. + * @return The new address created by subtracting the displacement to addr.offset. + * @throws AddressOverflowException if the addition would cause a wrap, + */ + public Address subtractNoWrap(GenericAddress addr, BigInteger displacement) + throws AddressOverflowException; + /** * Creates a new address (possibly in a new space) by subtracting the given * displacement from the given address. @@ -335,12 +352,12 @@ public interface AddressSpace extends Comparable { * address. If the offset is greater than the max offset of the address space, the high * order bits are masked off, making the address wrap. For non-segmented addresses this * will be the same as addWrap(). For segmented addresses, the address will wrap when - * the 20 bit (oxfffff) offset is exceeded, as opposed to when the segment offset is exceeded. + * the 20 bit (0xfffff) offset is exceeded, as opposed to when the segment offset is exceeded. * @param addr the address to add the displacement to. * @param displacement the displacement to add. - * @return The new Address formed by adding the displacement to the specified addresst. + * @return The new Address formed by adding the displacement to the specified address. */ - Address addWrapSpace(Address addr, long displacement); + public Address addWrapSpace(Address addr, long displacement); /** * Creates a new address by adding displacement to the given address. The @@ -363,17 +380,6 @@ public interface AddressSpace extends Comparable { public Address addNoWrap(GenericAddress addr, BigInteger displacement) throws AddressOverflowException; - /** - * Creates a new address by subtracting the displacement to the given address. The - * new address will NOT wrap! - * @param addr the original address. - * @param displacement the displacement to subtract. - * @return The new address created by subtracting the displacement to addr.offset. - * @throws AddressOverflowException if the addition would cause a wrap, - */ - public Address subtractNoWrap(GenericAddress addr, BigInteger displacement) - throws AddressOverflowException; - /** * Creates a new address (possibly in a new space) by adding the given * displacement from the given address. @@ -402,7 +408,7 @@ public interface AddressSpace extends Comparable { /** * Get the maximum address allowed for this AddressSpace. - * + *

* NOTE: Use of this method to identify the region associated with an overlay memory block * within its overlay address space is no longer supported. Defined regions of an overlay space * may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}. @@ -414,7 +420,7 @@ public interface AddressSpace extends Comparable { /** * Get the minimum address allowed for this AddressSpace. * For a memory space the returned address will have an offset of 0 within this address space. - * + *

* NOTE: Use of this method to identify the region associated with an overlay memory block * within its overlay address space is no longer supported. Defined regions of an overlay space * may now be determined using {@link OverlayAddressSpace#getOverlayAddressSet()}. @@ -506,22 +512,22 @@ public interface AddressSpace extends Comparable { * * @return true if this space has any registers mapped in it. */ - boolean hasMappedRegisters(); + public boolean hasMappedRegisters(); /** * {@return true if the address should display its addressSpace name} */ - boolean showSpaceName(); + public boolean showSpaceName(); /** * {@return true if this addressSpace is an overlay address space} */ - boolean isOverlaySpace(); + public boolean isOverlaySpace(); /** * {@return true if space uses signed offset} */ - boolean hasSignedOffset(); + public boolean hasSignedOffset(); /** * Determine if the specific name is a valid address space name (e.g., allowed