diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Instr_From.png b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Instr_From.png index 9e73129595..1d70a7dc7d 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Instr_From.png and b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Instr_From.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Refs_To.png b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Refs_To.png index c0b6fcf1d1..6400bb17ec 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Refs_To.png and b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Refs_To.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Symbol_Table.png b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Symbol_Table.png index 5d9ca77180..5746ce218e 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Symbol_Table.png and b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/images/Symbol_Table.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_references.htm b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_references.htm index b76c81f2d3..0b16dd86d0 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_references.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_references.htm @@ -165,7 +165,7 @@ -

Delete Reference

+

Delete Reference

This action will delete all selected references from the database. diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm index cd524e2967..af5b752873 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTablePlugin/symbol_table.htm @@ -145,7 +145,7 @@

  • Select the symbols in the Symbol Table (hold the <Ctrl> key down to add to the selection) to be deleted.
  • -
  • Right-mouse-click and select "Delete" from the popup menu, or click the Right-mouse-click and select Delete from the popup menu, or click the button in the Symbol Table toolbar.
  • @@ -153,7 +153,10 @@

    Notes on deleting a symbol:

      -
    1. You can only delete a default symbol when it has zero (0) references.
    2. +
    3. You can only delete a default symbol when it has zero (0) references. Use the + Delete All References action (described below) to remove all references. Once + all references to a default symbol are removed, the symbol will be removed. +
    4. If you delete a user-defined symbol with references, then a default symbol will automatically be created and assigned those references.
    5. @@ -163,6 +166,22 @@
    + + +

    Deleting Symbol References

    + +
    +

    You can use the Symbol Table to delete all referencs for a given symbol(s). + +

    To delete symbol references:

    + +
      +
    1. Select the symbols in the Symbol Table (hold the <Ctrl> key down to add to the + selection) to be deleted.
    2. + +
    3. Right-mouse-click and select References->Delete All from the popup menu.
    4. +
    +

    Making a Selection

    @@ -190,7 +209,7 @@

    - Making a Selection

    + Navigate on Incoming Location Changes

    When selected, the Symbol Table will select the row in the table that corresponds to the @@ -198,6 +217,15 @@

    +

    + Navigate on Table Row Changes

    + +
    +

    When toggle on, table row selections in the Symbol Table cause the + Listing to go to the + selected address.

    +
    +

    Renaming a Symbol

    @@ -619,8 +647,8 @@

    Related Topics

    diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/refs/RemoveReferenceCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/refs/RemoveReferenceCmd.java index a9a0aa983c..09777cb036 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/refs/RemoveReferenceCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/refs/RemoveReferenceCmd.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. @@ -63,7 +63,7 @@ public class RemoveReferenceCmd implements Command { return true; } - status = "Reference not found"; + status = "Reference not found - from: %s; to: %s ".formatted(fromAddr, toAddr); return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferencePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferencePanel.java index 0b7263dba9..9844e26e60 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferencePanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/ReferencePanel.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. @@ -47,8 +47,13 @@ class ReferencePanel extends JPanel { refTable = threadedTablePanel.getTable(); refTable.setAutoLookupColumn(SymbolReferenceModel.LABEL_COL); refTable.setPreferredScrollableViewportSize(new Dimension(250, 200)); - refTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + refTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); refTable.installNavigation(provider.getTool()); + refTable.getSelectionModel().addListSelectionListener(e -> { + if (!e.getValueIsAdjusting()) { + provider.contextChanged(); + } + }); this.listener = e -> referenceProvider.updateTitle(); refTable.getModel().addTableModelListener(listener); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java index cf9391c60f..a720ac7b9f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symtable/SymbolTablePlugin.java @@ -18,8 +18,10 @@ package ghidra.app.plugin.core.symtable; import static ghidra.framework.model.DomainObjectEvent.*; import static ghidra.program.util.ProgramEvent.*; +import java.awt.Component; import java.awt.Cursor; import java.awt.event.KeyEvent; +import java.util.ArrayList; import java.util.List; import javax.swing.Icon; @@ -27,8 +29,12 @@ import javax.swing.Icon; import docking.ActionContext; import docking.action.*; import docking.action.builder.ActionBuilder; +import docking.widgets.OptionDialog; +import docking.widgets.OptionDialogBuilder; import generic.theme.GIcon; import ghidra.app.CorePluginPackage; +import ghidra.app.cmd.refs.RemoveReferenceCmd; +import ghidra.app.context.ProgramSymbolActionContext; import ghidra.app.events.ProgramActivatedPluginEvent; import ghidra.app.events.ProgramLocationPluginEvent; import ghidra.app.plugin.PluginCategoryNames; @@ -47,11 +53,13 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; import ghidra.program.util.ProgramChangeRecord; import ghidra.program.util.ProgramLocation; +import ghidra.util.HelpLocation; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; import ghidra.util.table.GhidraTable; import ghidra.util.table.SelectionNavigationAction; import ghidra.util.table.actions.MakeProgramSelectionAction; -import ghidra.util.task.SwingUpdateManager; -import ghidra.util.task.TaskMonitor; +import ghidra.util.task.*; import ghidra.util.worker.Job; import ghidra.util.worker.Worker; import resources.Icons; @@ -84,10 +92,6 @@ public class SymbolTablePlugin extends Plugin { final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR); final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); - private DockingAction openRefsAction; - private DockingAction deleteAction; - private DockingAction makeSelectionAction; - private DockingAction setFilterAction; private ToggleDockingAction referencesToAction; private ToggleDockingAction instructionsFromAction; private ToggleDockingAction dataFromAction; @@ -144,9 +148,6 @@ public class SymbolTablePlugin extends Plugin { super.dispose(); swingMgr.dispose(); - deleteAction.dispose(); - makeSelectionAction.dispose(); - domainObjectWorker.dispose(); if (symProvider != null) { symProvider.dispose(); @@ -345,13 +346,14 @@ public class SymbolTablePlugin extends Plugin { private void createSymActions() { String popupGroup = "1"; - openRefsAction = new DockingAction("Symbol References", getName(), KeyBindingType.SHARED) { - @Override - public void actionPerformed(ActionContext context) { - refProvider.open(); - refProvider.setCurrentSymbol(symProvider.getCurrentSymbol()); - } - }; + DockingAction openRefsAction = + new DockingAction("Symbol References", getName(), KeyBindingType.SHARED) { + @Override + public void actionPerformed(ActionContext context) { + refProvider.open(); + refProvider.setCurrentSymbol(symProvider.getCurrentSymbol()); + } + }; Icon icon = new GIcon("icon.plugin.symboltable.referencetable.provider"); openRefsAction.setPopupMenuData( new MenuData(new String[] { "Symbol References" }, icon, popupGroup)); @@ -360,7 +362,7 @@ public class SymbolTablePlugin extends Plugin { openRefsAction.setDescription("Display Symbol References"); tool.addLocalAction(symProvider, openRefsAction); - deleteAction = new DockingAction("Delete Symbols", getName()) { + DockingAction deleteAction = new DockingAction("Delete Symbols", getName()) { @Override public void actionPerformed(ActionContext context) { symProvider.deleteSymbols(); @@ -385,18 +387,23 @@ public class SymbolTablePlugin extends Plugin { deleteAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_DELETE, 0)); deleteAction.setDescription("Delete Selected Symbols"); - deleteAction.setEnabled(false); tool.addLocalAction(symProvider, deleteAction); DockingAction editExternalLocationAction = new EditExternalLocationAction(this); tool.addLocalAction(symProvider, editExternalLocationAction); - makeSelectionAction = new MakeProgramSelectionAction(this, symProvider.getTable()); - makeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup); + MakeProgramSelectionAction symbolMakeSelectionAction = + new MakeProgramSelectionAction(this, symProvider.getTable()); + symbolMakeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup); - tool.addLocalAction(symProvider, makeSelectionAction); + MakeProgramSelectionAction refsMakeSelectionAction = + new MakeProgramSelectionAction(this, refProvider.getTable()); + refsMakeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup); - setFilterAction = new DockingAction("Set Filter", getName()) { + tool.addLocalAction(symProvider, symbolMakeSelectionAction); + tool.addLocalAction(refProvider, refsMakeSelectionAction); + + DockingAction setFilterAction = new DockingAction("Set Filter", getName()) { @Override public void actionPerformed(ActionContext context) { symProvider.setFilter(); @@ -438,6 +445,8 @@ public class SymbolTablePlugin extends Plugin { } }; selectionNavigationAction.getToolBarData().setToolBarGroup(navGroup); + selectionNavigationAction.setHelpLocation(new HelpLocation(getName(), + "Navigate_on_Row_Changes")); tool.addLocalAction(symProvider, selectionNavigationAction); String pinnedPopupGroup = "2"; // second group @@ -450,6 +459,81 @@ public class SymbolTablePlugin extends Plugin { CreateSymbolTableAction tableAction = new CreateSymbolTableAction(this); tableAction.getPopupMenuData().setMenuGroup(popupGroup); tool.addLocalAction(symProvider, tableAction); + + //@formatter:off + String bottomGroup = "ShowReferencesTo" + 1; + new ActionBuilder("Delete All References", getName()) + .sharedKeyBinding() + .popupMenuIcon(Icons.DELETE_ICON) + .popupMenuPath("References", "Delete All") + .popupMenuGroup(bottomGroup) + .withContext(ProgramSymbolActionContext.class) + .enabledWhen(this::hasSymbolsSelected) + .onAction(this::deleteRefs) + .buildAndInstallLocal(symProvider); + //@formatter:on + } + + private boolean hasSymbolsSelected(ProgramSymbolActionContext c) { + if (symProvider.isBusy()) { + return false; + } + + Component source = c.getSourceComponent(); + if (source != symProvider.getTable()) { + return false; + } + + return c.getSymbolCount() != 0; + } + + private void deleteRefs(ProgramSymbolActionContext c) { + + int result = new OptionDialogBuilder("Delete References?", + "Delete all references to the selected symbol(s)?") + .addOption("Delete") + .addCancel() + .show(symProvider.getComponent()); + if (result != OptionDialog.OPTION_ONE) { + return; + } + + int n = c.getSymbolCount(); + Iterable it = c.getSymbols(); + TaskLauncher.launchModal("Deleting References", monitor -> { + try { + doDeleteRefs(it, n, monitor); + } + catch (CancelledException e) { + // don't care + } + }); + } + + private void doDeleteRefs(Iterable it, int n, TaskMonitor monitor) + throws CancelledException { + + monitor.initialize(n, "Gathering references..."); + List commands = new ArrayList<>(); + for (Symbol s : it) { + monitor.increment(); + Reference[] refs = s.getReferences(); + for (Reference ref : refs) { + RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref); + commands.add(cmd); + } + } + + monitor.initialize(n, "Deleting references..."); + + currentProgram.withTransaction("Deleting References", () -> { + for (RemoveReferenceCmd cmd : commands) { + monitor.increment(); + if (!cmd.applyTo(currentProgram)) { + Msg.error(this, "Failed to delete reference: " + cmd.getStatusMsg()); + } + } + }); } private void createRefActions() { @@ -525,26 +609,21 @@ public class SymbolTablePlugin extends Plugin { //@formatter:off toolbarGroup = "2"; - String actionName = "Delete Reference"; - new ActionBuilder(actionName, getName()) + new ActionBuilder("Delete References", getName()) .sharedKeyBinding() .toolBarIcon(Icons.DELETE_ICON) .toolBarGroup(toolbarGroup) + .withContext(ReferenceTableContext.class) .enabledWhen(c -> { - if (!(c instanceof ReferenceTableContext context)) { - return false; - } - if (refProvider.isBusy()) { return false; } - List refs = context.getSelectedReferences(); + List refs = c.getSelectedReferences(); return !refs.isEmpty(); }) .onAction(c -> { - ReferenceTableContext context = (ReferenceTableContext) c; - List refs = context.getSelectedReferences(); + List refs = c.getSelectedReferences(); refProvider.deleteRows(refs); }) .buildAndInstallLocal(refProvider); diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java index e791f29295..9bb943ba02 100644 --- a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java +++ b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java @@ -63,8 +63,8 @@ import ghidra.app.util.viewer.field.AddressFieldFactory; import ghidra.app.util.viewer.field.FieldFactory; import ghidra.app.util.viewer.format.FieldFormatModel; import ghidra.app.util.viewer.format.FormatManager; -import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.app.util.viewer.listingpanel.ListingMarginProvider; +import ghidra.app.util.viewer.listingpanel.ListingPanel; import ghidra.docking.settings.SettingsDefinition; import ghidra.framework.ToolUtils; import ghidra.framework.plugintool.Plugin; @@ -692,6 +692,7 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn Assert.assertNotNull("Did not find a dialog to capture for class: " + clazz, dialogProvider); JDialog dialog = (JDialog) getInstanceField("dialog", dialogProvider); + dialog.toFront(); waitForSwing(); paintFix(dialog); runSwing(() -> generateImage(dialog)); @@ -702,7 +703,7 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn Assert.assertNotNull("Dialog cannot be null", provider); JDialog dialog = (JDialog) getInstanceField("dialog", provider); - + dialog.toFront(); paintFix(dialog); runSwing(() -> generateImage(dialog)); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java index 65040654b1..0aa982ce06 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symtable/SymbolTablePluginTest.java @@ -105,7 +105,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest { viewRefAction = CollectionUtils.any(symbolReferencesActions); deleteAction = getAction(plugin, "Delete Symbols"); - makeSelectionAction = getAction(plugin, "Make Selection"); + makeSelectionAction = getLocalAction(provider, "Make Selection"); setFilterAction = getAction(plugin, "Set Filter"); setPinnedAction = getAction(plugin, "Pin Symbol"); clearPinnedAction = getAction(plugin, "Clear Pinned Symbol"); @@ -481,6 +481,46 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest { plugin.getSymbolProvider().getName() + " " + plugin.getSymbolProvider().getSubTitle()); } + private void assertEnabled(DockingActionIf action, boolean expected) { + + ActionContext context = runSwing(() -> provider.getActionContext(null)); + boolean actual = runSwing(() -> action.isEnabledForContext(context)); + if (expected) { + assertTrue("Action should have been enabled: " + action.getName(), actual); + } + else { + assertFalse("Action should not have been enabled: " + action.getName(), actual); + } + } + + @Test + public void testDeleteAll() throws Exception { + + openProgram("sample"); + + // + // Test that we can delete all references to a symbol from the symbol table. + // + DockingActionIf deleteAllAction = getAction(plugin, "Delete All References"); + int selectedRows = symbolTable.getSelectedRowCount(); + assertEquals(0, selectedRows); + assertEnabled(deleteAllAction, false); + + // Select a symbol with references + int row = findRow("ghidra"); + Rectangle rect = symbolTable.getCellRect(row, 0, true); + symbolTable.scrollRectToVisible(rect); + singleClick(symbolTable, row, 0); + assertEnabled(deleteAllAction, true); + + Symbol symbol = getSymbol(row); + int startRefCount = symbol.getReferenceCount(); + assertTrue(startRefCount > 0); + + performAction(deleteAllAction, provider, true); + assertEquals(0, symbol.getReferenceCount()); + } + @Test public void testDeleteParamter_ForSCR_7892() throws Exception { openProgram("login"); @@ -1731,11 +1771,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest { return (GhidraTableFilterPanel) getInstanceField("tableFilterPanel", panel); } - private void singleClick(final JTable table, final int row, final int col) throws Exception { + private void singleClick(final JTable table, final int row, final int col) { clickTableCell(table, row, col, 1); } - private void doubleClick(final JTable table, final int row, final int col) throws Exception { + private void doubleClick(final JTable table, final int row, final int col) { clickTableCell(table, row, col, 2); } diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/SymbolTablePluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/SymbolTablePluginScreenShots.java index 74be7eac98..0ad6bf226a 100644 --- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/SymbolTablePluginScreenShots.java +++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/SymbolTablePluginScreenShots.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. @@ -49,7 +49,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator { GTable table = getTable(symbolProvider); setColumnSizes(table); selectRow(table, "WideCharToMultiByte"); - triggerText(table, "\n"); // hack to kick the references table Window window = windowForComponent(symbolProvider.getComponent()); captureWindow(window); @@ -67,7 +66,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator { GTable table = getTable(symbolProvider); setColumnSizes(table); selectRow(table, "_malloc00403762"); - triggerText(table, "\n"); // hack to kick the references table Window window = windowForComponent(symbolProvider.getComponent()); captureWindow(window); @@ -85,7 +83,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator { GTable table = getTable(symbolProvider); setColumnSizes(table); selectRow(table, "FUN_004010e0"); - triggerText(table, "\n"); // hack to kick the references table Window window = windowForComponent(symbolProvider.getComponent()); captureWindow(window); @@ -124,8 +121,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator { performAction("Set Filter", "SymbolTablePlugin", false); - FilterDialog dialog = - waitForDialogComponent(null, FilterDialog.class, DEFAULT_WINDOW_TIMEOUT); + FilterDialog dialog = waitForDialogComponent(FilterDialog.class); final JCheckBox advancedCheckBox = (JCheckBox) getInstanceField("advancedFilterCheckbox", dialog); @@ -160,7 +156,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator { private GTable getTable(ComponentProvider provider) { Object symbolPanel = getInstanceField("symbolPanel", provider); - return (GTable) getInstanceField("symTable", symbolPanel); + return (GTable) getInstanceField("gTable", symbolPanel); } private void setColumnSizes(final GTable table) {