diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html index 5c21d95efa..cacfe49fff 100644 --- a/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html +++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/Debugger/GettingStarted.html @@ -53,7 +53,7 @@
The default tool is pre-configured with a collection of plugins relevant for the Listing and for Debugger-related operations. As always, there is some chance that the tool will launch with some portion of the plugins not displayed or with a less-than-optimal layout. To verify which - plugins you have, you can select
- . "Debugger" + plugins you have, you can select . "Debugger" should already be selected. Choosing "Configure All Plugins" (the plug icon near the top right), should show the full list of pre-selected plugins. Debugger-related plugins all begin with "Debugger". At a bare minimum, you will need the "DebuggerTargetsPlugin" and the diff --git a/Ghidra/Extensions/MachineLearning/lib/README.txt b/Ghidra/Extensions/MachineLearning/lib/README.txt deleted file mode 100644 index 1f96701da7..0000000000 --- a/Ghidra/Extensions/MachineLearning/lib/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -The "lib" directory is intended to hold Jar files which this contrib -is dependent upon. This directory may be eliminated from a specific -contrib if no other Jar files are needed. diff --git a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Extensions.htm b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Extensions.htm index 72b5cfd911..43863e93b7 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Extensions.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/FrontEndPlugin/Extensions.htm @@ -1,71 +1,156 @@ - - -Ghidra Extensions (formerly 'contribs') are Ghidra software modules that are included with a Ghidra release but not -installed by default. Ghidra Extensions can be installed and uninstalled by Ghidra at runtime, with the changes taking -effect when Ghidra is restarted. This dialog can be opened by selecting the Extensions -option on the project file menu.
+ +-
![]() |
-
Ghidra Extensions are Ghidra software modules that can be installed + into a Ghidra distribution. This allows users to create and share new plugins and scripts. + Ghidra ships with some pre-built extensions that not installed by default. +
+Ghidra Extensions can be installed and uninstalled at runtime, with the changes taking effect + when Ghidra is restarted. The extension installation dialog can + be opened by selecting the Install Extensions option on the project File menu.
--The list of extensions is populated when the dialog is launched. To build the list, Ghidra looks in several locations: -+-
-- Extension Installation Directories: Contains any extensions that have been installed. The directories are located at:
--
-- [user dir]/.ghidra/.ghidra_[version]/Extensions - Installed/uninstalled from this dialog
-- [installation dir]/Ghidra/Extensions/ - Installed/uninstalled from filesystem manually
-- Extensions Archive Directory: This is where all archive files (zips) are stored. It is located at [installation dir]/Extensions/Ghidra/
-Note: Extensions that have been installed directly into the Ghidra installation directory cannot be uninstalled -from this dialog. They must be manually removed from the filesystem.
-
++ -+ ++
++ ++
+
+
-Displays metadata about the extension selected in the Extensions List. The information displayed is extracted from the extensions.properties file associated -with the extension. ++Dialog Components
--
extension.properties
The existence of this file is what tells Ghidra that the folder or zip file is a Ghidra Extension. It is a simple property file that can contain the following 4 attributes:
--
-- name: Human-readable name of the extension. This is what will be displayed in the dialog.
-- desc: Brief description of the extension.
-- author: Creator of the extension.
-- createdOn: Date of extension creation, in the format mm/dd/yyyy
-
-+-
-Allows the user to install a new extension. An extension can be any folder or zip file that contains an extensions.properties file. -When one of these is selected, it will be copied to the extension installation folder and extracted (if it is a zip). -
Reloads the Extensions List -
++ +The list of extensions is populated when the dialog is launched. To build the list, Ghidra + looks in several locations:
- - - ++
+ +- Extension Installation Directories: Contains any extensions that have been installed. + The directories are located at:
+ +- +
+ ++
+- [user dir]/.ghidra/.ghidra_[version]/Extensions - Installed/uninstalled from + this dialog
+ +- [installation dir]/Ghidra/Extensions/ - Installed/uninstalled from + filesystem manually
+- Extensions Archive Directory: This is where archive files (zips) that are bundled with + the distribution are stored. It is + located at [installation dir]/Extensions/Ghidra/. This directory is not intended for + end-user extensions. +
+++ + ++
The color red is used in the table + to indicate that the extension version does not match the Ghidra version.
Note: Extensions that have been installed directly into the Ghidra installation + directory cannot be uninstalled from this dialog. They must be manually removed from the + filesystem.
+
++ +Displays metadata about the extension selected in the Extensions List. The information + displayed is extracted from the
+ +extensions.properties
file associated with the + extension.The existence of this file is what tells Ghidra that the folder or zip file is a Ghidra + Extension. It is a simple property file that can contain the following attributes:
+ ++
+- name: Human-readable name of the extension. This is what will be displayed in + the dialog.
+ +- description: Brief description of the extension.
+ +- author: Creator of the extension.
+ +- createdOn: Date of extension creation, in the format mm/dd/yyyy.
+ +- version: The version of Ghidra for which this extension was built.
+
++ + ++
+- + +
Allows the user to install a + new extension. An extension can be any folder or zip file that contains an + extensions.properties file. When one of these is selected, it will be copied to the + extension installation folder and extracted (if it is a zip).
- +
Reloads the Extensions + List
+++ An extension is simply a Ghidra module that contains an
+extension.properties
file. + Building an extension is very similar to building a ghidra module, which is done by using +gradle
. ++ Ghidra includes a
+Skeleton
module in the distribution that is meant to be used as + a template when creating extensions. This module can be found at ++++
+<GHIDRA_INSTALL_DIR>/Extensions/Ghidra
++ Copy and rename this directory to get started writing your own module. You can then use +
+gradle
to build the extension by running this command from within your extension + directory: ++++
+gradle -PGHIDRA_INSTALL_DIR=/path/to/ghidra/ghidra_<version>/ buildExtension
+
This plugin doesn't perform any natural language translation by itself. The
user must install string translation services that do the actual translation.
Extensions to Ghidra are installed via the File
- Install Extensions...
+
Install Extensions
menu.
When a string has been translated, the translated value will be shown in place of
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/GhidraRun.java b/Ghidra/Features/Base/src/main/java/ghidra/GhidraRun.java
index 762b6ce7d0..33d25f5194 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/GhidraRun.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/GhidraRun.java
@@ -30,8 +30,8 @@ import ghidra.framework.client.RepositoryAdapter;
import ghidra.framework.data.DomainObjectAdapter;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.*;
-import ghidra.framework.plugintool.dialog.ExtensionUtils;
import ghidra.framework.project.DefaultProjectManager;
+import ghidra.framework.project.extensions.ExtensionUtils;
import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramDB;
import ghidra.util.*;
@@ -81,7 +81,7 @@ public class GhidraRun implements GhidraLaunchable {
updateSplashScreenStatusMessage("Populating Ghidra help...");
GhidraHelpService.install();
- ExtensionUtils.cleanupUninstalledExtensions();
+ ExtensionUtils.initializeExtensions();
// Allows handling of old content which did not have a content type property
DomainObjectAdapter.setDefaultContentClass(ProgramDB.class);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
index 59d04bca61..197e098a5f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/util/GhidraJarBuilder.java
@@ -26,7 +26,7 @@ import generic.jar.*;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable;
import ghidra.framework.*;
-import ghidra.framework.plugintool.dialog.ExtensionUtils;
+import ghidra.framework.project.extensions.ExtensionUtils;
import ghidra.util.classfinder.ClassFinder;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.AssertException;
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTaskTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTaskTest.java
index 0b7ea98512..343968bacc 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTaskTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTaskTest.java
@@ -30,7 +30,7 @@ import ghidra.framework.model.Project;
import ghidra.framework.options.Options;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
-import ghidra.framework.plugintool.util.PluginsConfiguration;
+import ghidra.framework.plugintool.PluginsConfiguration;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.listing.Program;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
index 46893f5f64..e64e5855c0 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
@@ -182,9 +182,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
}
protected void deleteUserScripts() throws IOException {
-
Path userScriptDir = Paths.get(GhidraScriptUtil.USER_SCRIPTS_DIR);
- FileUtilities.forEachFile(userScriptDir, paths -> paths.forEach(p -> delete(p)));
+ FileUtilities.forEachFile(userScriptDir, script -> delete(script));
}
//==================================================================================================
@@ -988,10 +987,10 @@ public abstract class AbstractGhidraScriptMgrPluginTest
// destroy any NewScriptxxx files...and Temp ones too
List
- * Note: The presence of an extensions.properties file is the difference.
- */
- @Test
- public void testIsExtension_Zip() throws IOException, ExtensionException {
- File zipFile1 = createExtensionZip(DEFAULT_EXT_NAME);
- assertTrue(ExtensionUtils.isExtension(new ResourceFile(zipFile1)));
-
- File zipFile2 = createNonExtensionZip(DEFAULT_EXT_NAME);
- assertTrue(!ExtensionUtils.isExtension(new ResourceFile(zipFile2)));
- }
-
- /*
- * Verifies that we can recognize when a directory represents an extension.
- *
- * Note: The presence of an extensions.properties file is the difference.
- */
- @Test
- public void testIsExtension_Folder() throws IOException, ExtensionException {
- File extDir = createTempDirectory("TestExtFolder");
- new File(extDir, "extension.properties").createNewFile();
- assertTrue(ExtensionUtils.isExtension(new ResourceFile(extDir)));
-
- File nonExtDir = createTempDirectory("TestNonExtFolder");
- assertTrue(!ExtensionUtils.isExtension(new ResourceFile(nonExtDir)));
- }
-
- /*
- * Verifies that the we can retrieve all unique extensions in the archive and
- * install folders.
- *
- * Note: This test eliminates the need to test the methods for retrieving archived vs. installed
- * extensions individually.
- */
- @Test
- public void testGetExtensions() throws ExtensionException, IOException {
-
- // First create an extension and install it, so we have 2 extensions: one in
- // the archive folder, and one in the install folder.
- File zipFile = createExtensionZip(DEFAULT_EXT_NAME);
- ExtensionUtils.install(new ResourceFile(zipFile));
-
- // Now getExtensions should give us exactly 1 extension in the return.
- Set
menu option, then clicking on the Configure link under the
diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java
index a1ee32be54..ae0b8c6eda 100644
--- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java
+++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/VTMatchApplyFunctionSignatureTest.java
@@ -15,85 +15,30 @@
*/
package ghidra.feature.vt.api;
-import static ghidra.feature.vt.db.VTTestUtils.addr;
-import static ghidra.feature.vt.db.VTTestUtils.createMatchSetWithOneMatch;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.CALLING_CONVENTION;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_RETURN_TYPE;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.FUNCTION_SIGNATURE;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.INLINE;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.NO_RETURN;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_COMMENTS;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_DATA_TYPES;
-import static ghidra.feature.vt.gui.util.VTOptionDefines.PARAMETER_NAMES;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static ghidra.feature.vt.db.VTTestUtils.*;
+import static ghidra.feature.vt.gui.util.VTOptionDefines.*;
+import static org.junit.Assert.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.*;
import ghidra.feature.vt.api.db.VTSessionDB;
-import ghidra.feature.vt.api.main.VTAssociationStatus;
-import ghidra.feature.vt.api.main.VTMarkupItem;
-import ghidra.feature.vt.api.main.VTMarkupItemStatus;
-import ghidra.feature.vt.api.main.VTMatch;
-import ghidra.feature.vt.api.main.VTSession;
+import ghidra.feature.vt.api.main.*;
import ghidra.feature.vt.api.markuptype.FunctionSignatureMarkupType;
-import ghidra.feature.vt.gui.plugin.VTController;
-import ghidra.feature.vt.gui.plugin.VTControllerImpl;
-import ghidra.feature.vt.gui.plugin.VTPlugin;
-import ghidra.feature.vt.gui.task.ApplyMatchTask;
-import ghidra.feature.vt.gui.task.ClearMatchTask;
-import ghidra.feature.vt.gui.task.VtTask;
+import ghidra.feature.vt.gui.plugin.*;
+import ghidra.feature.vt.gui.task.*;
import ghidra.feature.vt.gui.util.MatchInfo;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.CallingConventionChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.CommentChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionNameChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.FunctionSignatureChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.HighestSourcePriorityChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.LabelChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.ParameterDataTypeChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.ReplaceChoices;
-import ghidra.feature.vt.gui.util.VTMatchApplyChoices.SourcePriorityChoices;
+import ghidra.feature.vt.gui.util.VTMatchApplyChoices.*;
import ghidra.feature.vt.gui.util.VTOptionDefines;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.model.address.Address;
-import ghidra.program.model.data.ArrayDataType;
-import ghidra.program.model.data.BooleanDataType;
-import ghidra.program.model.data.CategoryPath;
-import ghidra.program.model.data.CharDataType;
-import ghidra.program.model.data.DataType;
-import ghidra.program.model.data.FloatDataType;
-import ghidra.program.model.data.IntegerDataType;
-import ghidra.program.model.data.Pointer;
-import ghidra.program.model.data.PointerDataType;
-import ghidra.program.model.data.StructureDataType;
-import ghidra.program.model.data.TypeDef;
-import ghidra.program.model.data.TypedefDataType;
-import ghidra.program.model.data.Undefined4DataType;
-import ghidra.program.model.data.VoidDataType;
-import ghidra.program.model.data.WordDataType;
-import ghidra.program.model.lang.CompilerSpec;
-import ghidra.program.model.lang.CompilerSpecDescription;
-import ghidra.program.model.lang.CompilerSpecID;
-import ghidra.program.model.lang.Language;
-import ghidra.program.model.lang.LanguageID;
-import ghidra.program.model.lang.LanguageNotFoundException;
-import ghidra.program.model.lang.LanguageService;
-import ghidra.program.model.listing.Function;
-import ghidra.program.model.listing.IncompatibleLanguageException;
-import ghidra.program.model.listing.Parameter;
-import ghidra.program.model.listing.ParameterImpl;
-import ghidra.program.model.listing.Program;
+import ghidra.program.model.data.*;
+import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.DefaultLanguageService;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@@ -104,8 +49,6 @@ import ghidra.util.task.TaskMonitor;
public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedIntegrationTest {
-// private static final String TEST_SOURCE_PROGRAM_NAME = "VersionTracking/WallaceSrc";
-// private static final String TEST_DESTINATION_PROGRAM_NAME = "VersionTracking/WallaceVersion2";
private TestEnv env;
private PluginTool tool;
private VTController controller;
@@ -130,8 +73,8 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
public void setUp() throws Exception {
env = new TestEnv();
- sourceProgram = createSourceProgram();// env.getProgram(TEST_SOURCE_PROGRAM_NAME);
- destinationProgram = createDestinationProgram();// env.getProgram(TEST_DESTINATION_PROGRAM_NAME);
+ sourceProgram = createSourceProgram();
+ destinationProgram = createDestinationProgram();
tool = env.getTool();
tool.addPlugin(VTPlugin.class.getName());
@@ -142,37 +85,15 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
sourceProgram, destinationProgram, this);
- runSwing(new Runnable() {
- @Override
- public void run() {
- controller.openVersionTrackingSession(session);
- }
- });
+ runSwing(() -> controller.openVersionTrackingSession(session));
setAllOptionsToDoNothing();
-//
-// env = new VTTestEnv();
-// session = env.createSession(TEST_SOURCE_PROGRAM_NAME, TEST_DESTINATION_PROGRAM_NAME);
-// try {
-// correlator =
-// vtTestEnv.correlate(new ExactMatchInstructionsProgramCorrelatorFactory(), null,
-// TaskMonitor.DUMMY);
-// }
-// catch (Exception e) {
-// Assert.fail(e.getMessage());
-// e.printStackTrace();
-// }
-// sourceProgram = env.getSourceProgram();
-// destinationProgram = env.getDestinationProgram();
-// controller = env.getVTController();
-// env.showTool();
-//
// Logger functionLogger = Logger.getLogger(FunctionDB.class);
-// functionLogger.setLevel(Level.TRACE);
-//
+// Configurator.setLevel(functionLogger.getName(), org.apache.logging.log4j.Level.TRACE);
+//
// Logger variableLogger = Logger.getLogger(VariableSymbolDB.class);
-// variableLogger.setLevel(Level.TRACE);
+// Configurator.setLevel(variableLogger.getName(), org.apache.logging.log4j.Level.TRACE);
}
@@ -478,7 +399,6 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
checkSignatures("undefined use(Gadget * this, Person * person)",
"undefined FUN_00401040(void * this, undefined4 param_1)");
-
tx(sourceProgram, () -> {
sourceFunction.setCustomVariableStorage(true);
@@ -487,7 +407,6 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
SourceType.USER_DEFINED);
});
-
DataType personType = sourceProgram.getDataTypeManager().getDataType("/Person");
assertNotNull(personType);
@@ -495,7 +414,6 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
destinationFunction.setCustomVariableStorage(true);
});
-
// Set the function signature options for this test
ToolOptions applyOptions = controller.getOptions();
applyOptions.setEnum(FUNCTION_SIGNATURE, FunctionSignatureChoices.REPLACE);
@@ -744,24 +662,14 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
public void testApplyMatch_ReplaceSignatureAndCallingConventionDifferentLanguageFailUsingNameMatch()
throws Exception {
- runSwing(new Runnable() {
- @Override
- public void run() {
- controller.closeCurrentSessionIgnoringChanges();
- }
- });
+ runSwing(() -> controller.closeCurrentSessionIgnoringChanges());
env.release(destinationProgram);
destinationProgram = createToyDestinationProgram();// env.getProgram("helloProgram"); // get a program without cdecl
session =
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
sourceProgram, destinationProgram, this);
- runSwing(new Runnable() {
- @Override
- public void run() {
- controller.openVersionTrackingSession(session);
- }
- });
+ runSwing(() -> controller.openVersionTrackingSession(session));
useMatch("0x00401040", "0x00010938");
@@ -1699,12 +1607,9 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
final String[] sourceStringBox = new String[1];
final String[] destinationStringBox = new String[1];
- runSwing(new Runnable() {
- @Override
- public void run() {
- sourceStringBox[0] = sourceFunction.getPrototypeString(false, false);
- destinationStringBox[0] = destinationFunction.getPrototypeString(false, false);
- }
+ runSwing(() -> {
+ sourceStringBox[0] = sourceFunction.getPrototypeString(false, false);
+ destinationStringBox[0] = destinationFunction.getPrototypeString(false, false);
});
assertEquals(expectedSourceSignature, sourceStringBox[0]);
diff --git a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/ThemingUserDocs.html b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/ThemingUserDocs.html
index f869df2f49..87ddf04dc6 100644
--- a/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/ThemingUserDocs.html
+++ b/Ghidra/Framework/Docking/src/main/help/help/topics/Theming/ThemingUserDocs.html
@@ -153,7 +153,7 @@
modified using the Theme Editor Dialog. The Theme Editor Dialog
can be invoked from the main application menu using the
EditTheme
Configure..." menu. Choose the
+ src="help/shared/arrow.gif" border="0">Configure" menu. Choose the
tab for the appropriate type and double-click on the ID column or Current Value column of the
item you want to change. An editor for that type will appear.