diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest index 8afcc61691..98ad1f984a 100644 --- a/Ghidra/Features/Base/certification.manifest +++ b/Ghidra/Features/Base/certification.manifest @@ -437,6 +437,7 @@ src/main/help/help/topics/GhidraScriptMgrPlugin/images/text_list_bullets.png||FA src/main/help/help/topics/GhidraScriptMgrPlugin/images/text_lowercase.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/GhidraScriptMgrPlugin/images/textfield_rename.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END| src/main/help/help/topics/GhidraScriptMgrPlugin/images/undo.png||GHIDRA||||END| +src/main/help/help/topics/BundleManager/BundleManager.htm||GHIDRA||||END| src/main/help/help/topics/GhidraServer/GhidraServer.htm||GHIDRA||||END| src/main/help/help/topics/Glossary/glossary.htm||GHIDRA||||END| src/main/help/help/topics/Glossary/images/BigEndian.png||GHIDRA||reviewed||END| diff --git a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml index d154fd594e..9a27608027 100644 --- a/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/Base/src/main/help/help/TOC_Source.xml @@ -354,9 +354,10 @@ - + + + + + Ghidra Bundles + + + + +

Ghidra Bundles

+ +

Dynamic modules

+

Scripting brings a poweful form of dynamic extensibilty to Ghidra, + where Java source code is (re)compiled, loaded, and run without exiting + Ghidra. When a script grows large or requires external dependencies, it + might be worth the effort think of how to split up code into sensible + pieces, or modules.

+ +

To support modularity while preserving the dynamic nature of scripts, + Ghidra uses OSGi. Without delving too + much into terminology, the key things to know are

+
    +
  1. The unit of modularity in OSGi is the bundle. Bundles are + mostly independent components with well-defined, + collaborations. +
  2. + +
  3. Concretely, a bundle is a Jar file with extra metadata in its + manifest file that tells the framework what it uses and what it + provides.
  4. + +
  5. Bundles can export packages for use by other bundles. + Exported packages can have versions.
  6. + +
  7. Bundles can import packages from other bundles. Imports can + be constrained by version.
  8. + +
  9. Ghidra can compile source directories to bundles. We refer to these + as source bundles. + +
  10. The entire Ghidra API is part of the "system bundle", so hosted + bundles have complete access.
  11. + +
+ + +

Source bundles

+

When a directory is added to the Bundle Manager, it is treated as a + source bundle and of its Java contents will be compiled to
+     <user home>/.ghidra/.ghidra-<version>/osgi/compiled-bundles/<hash>,
+ where <hash> is a hash of the source bundle location.

+

exploded bundles

+

+ Each subdirectory of compiled-bundles/ is an exploded jar -- by re-compressing it, + we get a standard Jar bundle: +

+ +
+            jar cMf mybundle.jar -C $HOME/.ghidra/.ghidra_<version>/osgi/compiled-bundles/<hash>  .
+      
+
+

+ mybundle.jar could then be shared in binary form. +

+ +

generated files

+

If there is no manifest in the source directory, Ghidra generates one + using bndlib so that: +

+
    +
  • Every package is exported unless it contains private or internal in its name.
  • +
  • Currently active bundles satisfying @importpackage requirments made available for import.
  • +
  • Every package from Ghidra core is available for import. (Ghidra's core API is part of the system bundle)
  • +
+

+ If no bundle activator is present, a stub is created and referenced in the generated manifest. +

+ +

Dependency

+

Two basic types of code dependency are available when developing with + OSGi, intra-bundle and inter-bundle.

+ +

intra-bundle (compile time) dependency

+

Classes within a bundle, e.g. source files in a source bundle, can rely + one another with Java's usual package import.

+

This kind of dependency is resolved at compile time -- if a class isn't + imported or present, compilation will fail!

+ +

inter-bundle (run time) dependency

+

To make use of code from other bundles, a bundle must declare its + requirements. When a bundle is activated, the framework attempts to + resolve its declared dependencies against active bundles. The + first match, in the order those bundles were activated, will be "wired" to + the dependent.

+ +

Note: OSGi bundle dependency is very similar to Java 9 modules, except + that Java 9 modules provide load time resolution.

+ +

Via the Jar manifest, there are multiple ways to configure the + requirements of a bundle. We'll describe only one, the + Import-Package attribute, because Ghidra provides a shortcut + which we'll describe afterwards.

+ +

Import-Package in the manifest

+

You can find more detail at https://osgi.org for more detail.

+

These examples should demonstrated the most common usage. Note: the + file META-INF/MANIFEST.MF should have only one + Import-Package entry:

+ +
+          Import-Package:com.example.stuff
+          Import-Package:com.example.stuff;version=1.2.3
+          Import-Package:com.example.stuff;version="[1.2.0,1.3)"
+          Import-Package:com.example.stuff,org.example.otherstuff
+          Import-Package:com.example.stuff;version="[1.2.0,1.3)",org.example.otherstuff
+          Import-Package:com.example.stuff;version="[1.2.0,1.3)",org.example.otherstuff;version=2.3.4
+    
+
+ +

@importpackage in source

+

If a source bundle includes a file META-INF/MANIFEST.MF, it + will be used as the bundle's manifest. If not, a manifest will be + generated, see above.

+ +

Ghidra provides a shortcut to populate Import-Package, the + @importpackage metadata tag. The @importpackage + values from all source files in a source bundle are appended to the + generated manifest Import-Package attribute.

+ + +

Adding and removing bundles from the manager

+

Adding a directory to the bundle manager and then enabling it makes any + contained scripts visible in the script manager.

+

Removing a bundle deactives, disables, and removes it from the list.

+ +

Activating and deactivating bundles

+

In order to make a bundle's contents available to other bundles, it must + be activated. This corresponds the OSGi bundle ACTIVE state. + Ghidra will compile source bundles prior to installing and activating.

+ +

On deactivation, any dependents of a bundle are stopped first, then the + bundle itself is stopped

+ +

Cleaning bundles

+

When Ghidra builds a source bundle, the result is cached to a + subdirectory of
+     <user home>/.ghidra/.ghidra-<version>/osgi/compiled-bundles/<hash>.
+ These cached files speed up subsequent builds. A clean + deactivates then wipes the subdirectory of the selected bundle.

+ + + diff --git a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm index cca4bb5094..fa5099cd8b 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/GhidraScriptMgrPlugin.htm @@ -271,16 +271,20 @@

-

Script Directories Script Directories / Bundle Manager 

-

Allows you to add and remove directories to search for scripts. The default - directories are your home directory and the various system directories (e.g., - $GHIDRA_HOME/Features/Base/ghidra_scripts). You - can save directories, but ignore them in the Script Manager dialog by selecting/deselecting - the "Use" column checkbox.
-

+

Allows you to add and remove directories to search for + scripts and bundle dependencies for use by scripts. The default + directories are your home directory and the various system directories + (e.g., $GHIDRA_HOME/Features/Base/ghidra_scripts). You can + save directories, but ignore them in the Script Manager dialog by + selecting/deselecting the "Enable" column checkbox.

+ +

For more information on Ghidra's dynamic module support, see + Ghidra Bundles.

+
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm index a4aa8f82ee..0a3e5f3afc 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/GhidraScriptMgrPlugin/ScriptDevelopment.htm @@ -48,7 +48,7 @@ This is highly recommended if you plan to do more than simple edits to scripts.

-

Meta-data

+

Meta-data

The scripting framework supports special meta-data comments. This comment is treated @@ -84,6 +84,28 @@

+

@importpackage

+ +
+

This tag is a convenience for declaring bundle dependencies. The tag's + contents syntax is that of the Import-Package attribute + in an OSGi bundle manifest. +

+ +

For example:

+ +
+

+ @importpackage com.example.library
+ @importpackage org.apache.commons.collections.properties;version=4.4
+ @importpackage org.ghidra.analysis;version="[1.1,2)"
+ @importpackage com.example.library1,com.example.library2
+

+
+ +
+ +

@keybinding

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java index 89ee83dd0d..4c506ac1db 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java @@ -37,6 +37,7 @@ import ghidra.app.services.ConsoleService; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.preferences.Preferences; +import ghidra.util.HelpLocation; import ghidra.util.exception.CancelledException; import ghidra.util.filechooser.GhidraFileChooserModel; import ghidra.util.filechooser.GhidraFileFilter; @@ -62,7 +63,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter { static final String BUNDLE_LIST_GROUP = "1bundle list group"; public BundleStatusComponentProvider(PluginTool tool, String owner, BundleHost bundleHost) { - super(tool, "Bundle Status Component", owner); + super(tool, "BundleManager", owner); + setHelpLocation(new HelpLocation("BundleManager", "BundleManager")); + setTitle("Bundle Manager"); + this.bundleHost = bundleHost; this.bundleStatusTableModel = new BundleStatusTableModel(this, bundleHost); @@ -119,10 +123,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter { // to allow custom cell renderers bundleStatusTable.setAutoCreateColumnsFromModel(false); - int skinnyWidth = 50; - TableColumn column; + int skinnyWidth = 60; // column = bundleStatusTable.getColumnModel().getColumn( bundleStatusTableModel.enabledColumn.index); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java index 85a6534365..e612fac2e7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java @@ -142,7 +142,7 @@ public class GhidraSourceBundle extends GhidraBundle { private String parseImps(ResourceFile javaSource) { // XXX don't use @imports, use an annotation - return GhidraScriptUtil.newScriptInfo(javaSource).getImports(); + return GhidraScriptUtil.newScriptInfo(javaSource).getImportPackage(); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java index ca84b4b4d3..5db19973c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java @@ -353,7 +353,7 @@ class GhidraScriptActionManager { refreshAction.setEnabled(true); plugin.getTool().addLocalAction(provider, refreshAction); - bundleStatusAction = new DockingAction("Bundle Status", plugin.getName()) { + bundleStatusAction = new DockingAction("Script Directories", plugin.getName()) { @Override public void actionPerformed(ActionContext context) { provider.showBundleStatusComponent(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java index 2bb59ec05c..2c18083f5e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ScriptInfo.java @@ -45,14 +45,14 @@ public class ScriptInfo { */ public static final String DELIMITTER = "."; - public static final String AT_AUTHOR = "@author"; - public static final String AT_CATEGORY = "@category"; - public static final String AT_KEYBINDING = "@keybinding"; - public static final String AT_MENUPATH = "@menupath"; - public static final String AT_TOOLBAR = "@toolbar"; + static final String AT_AUTHOR = "@author"; + static final String AT_CATEGORY = "@category"; + static final String AT_KEYBINDING = "@keybinding"; + static final String AT_MENUPATH = "@menupath"; + static final String AT_TOOLBAR = "@toolbar"; // omit from METADATA to avoid pre-populating in new scripts - public static final String AT_IMPORTS = "@imports"; + private static final String AT_IMPORTPACKAGE = "@importpackage"; public static final String[] METADATA = { AT_AUTHOR, AT_CATEGORY, AT_KEYBINDING, AT_MENUPATH, AT_TOOLBAR, }; @@ -71,7 +71,7 @@ public class ScriptInfo { private String[] menupath = new String[0]; private String toolbar; private ImageIcon toolbarImage; - private String imports; + private String importpackage; /** * Constructs a new script. @@ -96,7 +96,7 @@ public class ScriptInfo { menupath = new String[0]; toolbar = null; toolbarImage = null; - imports = null; + importpackage = null; keybindingErrorMessage = null; } @@ -277,8 +277,8 @@ public class ScriptInfo { else if (line.startsWith(AT_TOOLBAR)) { toolbar = getTagValue(AT_TOOLBAR, line); } - else if (line.startsWith(AT_IMPORTS)) { - imports = getTagValue(AT_IMPORTS, line); + else if (line.startsWith(AT_IMPORTPACKAGE)) { + importpackage = getTagValue(AT_IMPORTPACKAGE, line); } } catch (Exception e) { @@ -440,9 +440,9 @@ public class ScriptInfo { * Returns the script imports * @return the script imports */ - public String getImports() { + public String getImportPackage() { parseHeader(); - return imports; + return importpackage; } /** diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java index cc453e31eb..e091ea3bdf 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java @@ -301,7 +301,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest { pushNewBundle(); // @imports tag is only parsed from classes in default package addClass( - "//@imports lib\n" + "//@importpackage lib\n" , "import lib.Library;\n" , diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java index 3bbc990083..5142c63347 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java @@ -276,7 +276,7 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes // Tests that the user can add an additional script path directory and choose that one // to use // - DockingActionIf bundleStatusAction = getAction(plugin, "Bundle Status"); + DockingActionIf bundleStatusAction = getAction(plugin, "Script Directories"); performAction(bundleStatusAction, false); waitForSwing(); diff --git a/Ghidra/Features/Python/src/main/help/help/TOC_Source.xml b/Ghidra/Features/Python/src/main/help/help/TOC_Source.xml index 5069c64d9c..acdd831d0b 100644 --- a/Ghidra/Features/Python/src/main/help/help/TOC_Source.xml +++ b/Ghidra/Features/Python/src/main/help/help/TOC_Source.xml @@ -3,7 +3,7 @@ - + - \ No newline at end of file +