GP-5836: Fixed GhidraDev bug that prevented Ghidra from discovering the

Ghidra module project when launched with the PyGhidra run config
This commit is contained in:
Ryan Kurtz 2025-07-17 08:41:17 -04:00
parent 0bd8870da3
commit 396dd6e1de
7 changed files with 49 additions and 43 deletions

View file

@ -2,7 +2,7 @@
<feature <feature
id="ghidra.ghidradev" id="ghidra.ghidradev"
label="GhidraDev" label="GhidraDev"
version="5.0.0.qualifier" version="5.0.1.qualifier"
provider-name="Ghidra"> provider-name="Ghidra">
<description> <description>

View file

@ -156,6 +156,7 @@
<setEntry value="org.python.pydev.feature:default"/> <setEntry value="org.python.pydev.feature:default"/>
</setAttribute> </setAttribute>
<setAttribute key="selected_target_bundles"> <setAttribute key="selected_target_bundles">
<setEntry value="aQute.libg@default:default"/>
<setEntry value="bcpg@default:default"/> <setEntry value="bcpg@default:default"/>
<setEntry value="bcprov@default:default"/> <setEntry value="bcprov@default:default"/>
<setEntry value="bcutil@default:default"/> <setEntry value="bcutil@default:default"/>
@ -163,6 +164,7 @@
<setEntry value="biz.aQute.bndlib@default:default"/> <setEntry value="biz.aQute.bndlib@default:default"/>
<setEntry value="ch.qos.logback.classic@default:default"/> <setEntry value="ch.qos.logback.classic@default:default"/>
<setEntry value="ch.qos.logback.core@default:default"/> <setEntry value="ch.qos.logback.core@default:default"/>
<setEntry value="com.github.weisj.jsvg@default:default"/>
<setEntry value="com.google.gson@default:default"/> <setEntry value="com.google.gson@default:default"/>
<setEntry value="com.google.guava.failureaccess@default:default"/> <setEntry value="com.google.guava.failureaccess@default:default"/>
<setEntry value="com.google.guava@default:default"/> <setEntry value="com.google.guava@default:default"/>
@ -173,11 +175,11 @@
<setEntry value="com.sun.el.javax.el@default:default"/> <setEntry value="com.sun.el.javax.el@default:default"/>
<setEntry value="com.sun.jna.platform@default:default"/> <setEntry value="com.sun.jna.platform@default:default"/>
<setEntry value="com.sun.jna@default:default"/> <setEntry value="com.sun.jna@default:default"/>
<setEntry value="jakarta.annotation-api*1.3.5@default:default"/> <setEntry value="jakarta.annotation-api@default:default"/>
<setEntry value="jakarta.annotation-api*2.1.1@default:default"/>
<setEntry value="jakarta.inject.jakarta.inject-api*1.0.5@default:default"/> <setEntry value="jakarta.inject.jakarta.inject-api*1.0.5@default:default"/>
<setEntry value="jakarta.inject.jakarta.inject-api*2.0.1@default:default"/> <setEntry value="jakarta.inject.jakarta.inject-api*2.0.1@default:default"/>
<setEntry value="jakarta.servlet-api@default:default"/> <setEntry value="jakarta.servlet-api@default:default"/>
<setEntry value="javax.annotation@default:default"/>
<setEntry value="jaxen@default:default"/> <setEntry value="jaxen@default:default"/>
<setEntry value="org.apache.aries.spifly.dynamic.bundle@default:default"/> <setEntry value="org.apache.aries.spifly.dynamic.bundle@default:default"/>
<setEntry value="org.apache.batik.constants@default:default"/> <setEntry value="org.apache.batik.constants@default:default"/>
@ -185,11 +187,8 @@
<setEntry value="org.apache.batik.i18n@default:default"/> <setEntry value="org.apache.batik.i18n@default:default"/>
<setEntry value="org.apache.batik.util@default:default"/> <setEntry value="org.apache.batik.util@default:default"/>
<setEntry value="org.apache.commons.cli@default:default"/> <setEntry value="org.apache.commons.cli@default:default"/>
<setEntry value="org.apache.commons.collections@default:default"/>
<setEntry value="org.apache.commons.commons-beanutils@default:default"/>
<setEntry value="org.apache.commons.commons-codec@default:default"/> <setEntry value="org.apache.commons.commons-codec@default:default"/>
<setEntry value="org.apache.commons.commons-io@default:default"/> <setEntry value="org.apache.commons.commons-io@default:default"/>
<setEntry value="org.apache.commons.jxpath@default:default"/>
<setEntry value="org.apache.commons.lang3@default:default"/> <setEntry value="org.apache.commons.lang3@default:default"/>
<setEntry value="org.apache.commons.logging@default:default"/> <setEntry value="org.apache.commons.logging@default:default"/>
<setEntry value="org.apache.felix.gogo.command@default:default"/> <setEntry value="org.apache.felix.gogo.command@default:default"/>
@ -351,6 +350,7 @@
<setEntry value="org.eclipse.search.core@default:default"/> <setEntry value="org.eclipse.search.core@default:default"/>
<setEntry value="org.eclipse.search@default:default"/> <setEntry value="org.eclipse.search@default:default"/>
<setEntry value="org.eclipse.swt.cocoa.macosx.aarch64@default:false"/> <setEntry value="org.eclipse.swt.cocoa.macosx.aarch64@default:false"/>
<setEntry value="org.eclipse.swt.svg@default:default"/>
<setEntry value="org.eclipse.swt@default:default"/> <setEntry value="org.eclipse.swt@default:default"/>
<setEntry value="org.eclipse.team.core@default:default"/> <setEntry value="org.eclipse.team.core@default:default"/>
<setEntry value="org.eclipse.team.ui@default:default"/> <setEntry value="org.eclipse.team.ui@default:default"/>
@ -391,7 +391,6 @@
<setEntry value="org.hamcrest.core@default:default"/> <setEntry value="org.hamcrest.core@default:default"/>
<setEntry value="org.hamcrest@default:default"/> <setEntry value="org.hamcrest@default:default"/>
<setEntry value="org.jdom2@default:default"/> <setEntry value="org.jdom2@default:default"/>
<setEntry value="org.jdom@default:default"/>
<setEntry value="org.junit@default:default"/> <setEntry value="org.junit@default:default"/>
<setEntry value="org.mortbay.jasper.apache-el@default:default"/> <setEntry value="org.mortbay.jasper.apache-el@default:default"/>
<setEntry value="org.mortbay.jasper.apache-jsp@default:default"/> <setEntry value="org.mortbay.jasper.apache-jsp@default:default"/>
@ -439,7 +438,7 @@
<setAttribute key="selected_workspace_bundles"> <setAttribute key="selected_workspace_bundles">
<setEntry value="ghidra.ghidradev@default:default"/> <setEntry value="ghidra.ghidradev@default:default"/>
</setAttribute> </setAttribute>
<booleanAttribute key="show_selected_only" value="false"/> <booleanAttribute key="show_selected_only" value="true"/>
<booleanAttribute key="tracing" value="false"/> <booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/> <booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfig" value="true"/> <booleanAttribute key="useDefaultConfig" value="true"/>

View file

@ -3,7 +3,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: GhidraDev Bundle-Name: GhidraDev
Bundle-SymbolicName: ghidra.ghidradev;singleton:=true Bundle-SymbolicName: ghidra.ghidradev;singleton:=true
Bundle-Version: 5.0.0.qualifier Bundle-Version: 5.0.1.qualifier
Bundle-Activator: ghidradev.Activator Bundle-Activator: ghidradev.Activator
Require-Bundle: org.eclipse.ant.core;bundle-version="3.7.200", Require-Bundle: org.eclipse.ant.core;bundle-version="3.7.200",
org.eclipse.buildship.core;bundle-version="3.1.8", org.eclipse.buildship.core;bundle-version="3.1.8",

View file

@ -1,7 +1,7 @@
# GhidraDev Eclipse Plugin # GhidraDev Eclipse Plugin
GhidraDev provides support for developing and debugging Ghidra scripts and modules in Eclipse. GhidraDev provides support for developing and debugging Ghidra scripts and modules in Eclipse.
The information provided in this document is effective as of GhidraDev 5.0.0 and is subject to The information provided in this document is effective as of GhidraDev 5.0.1 and is subject to
change with future releases. change with future releases.
## Table of Contents ## Table of Contents
@ -31,6 +31,10 @@ change with future releases.
12. [Building](#building) 12. [Building](#building)
## Change History ## Change History
__5.0.1:__
* Fixed a bug that prevented Ghidra from discovering the Ghidra module project when launched with
the PyGhidra run configuration.
__5.0.0:__ __5.0.0:__
* Added support for PyGhidra. * Added support for PyGhidra.

View file

@ -21,11 +21,11 @@ import java.text.ParseException;
import javax.naming.OperationNotSupportedException; import javax.naming.OperationNotSupportedException;
import org.eclipse.core.resources.*; import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.*;
import org.eclipse.debug.core.*; import org.eclipse.debug.core.*;
import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.JavaLaunchDelegate; import org.eclipse.jdt.launching.JavaLaunchDelegate;
@ -102,7 +102,7 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
vmArgs += " " + configuration.getAttribute(GhidraLaunchUtils.ATTR_VM_ARGUMENTS, "").trim(); vmArgs += " " + configuration.getAttribute(GhidraLaunchUtils.ATTR_VM_ARGUMENTS, "").trim();
vmArgs += " -Dghidra.external.modules=\"%s%s%s\"".formatted( vmArgs += " -Dghidra.external.modules=\"%s%s%s\"".formatted(
javaProject.getProject().getLocation(), File.pathSeparator, javaProject.getProject().getLocation(), File.pathSeparator,
getProjectDependencyDirs(javaProject)); GhidraProjectUtils.getProjectDependencyDirs(javaProject));
File pyDevSrcDir = PyDevUtils.getPyDevSrcDir(); File pyDevSrcDir = PyDevUtils.getPyDevSrcDir();
if (pyDevSrcDir != null) { if (pyDevSrcDir != null) {
vmArgs += " " + "-Declipse.pysrc.dir=\"" + pyDevSrcDir + "\""; vmArgs += " " + "-Declipse.pysrc.dir=\"" + pyDevSrcDir + "\"";
@ -115,7 +115,7 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
ResourcesPlugin.getWorkspace().getRoot().getLocation() + "\""; ResourcesPlugin.getWorkspace().getRoot().getLocation() + "\"";
vmArgs += " " + "-Declipse.project.dir=\"" + javaProject.getProject().getLocation() + "\""; vmArgs += " " + "-Declipse.project.dir=\"" + javaProject.getProject().getLocation() + "\"";
vmArgs += " " + "-Declipse.project.dependencies=\"" + vmArgs += " " + "-Declipse.project.dependencies=\"" +
getProjectDependencyDirs(javaProject) + "\""; GhidraProjectUtils.getProjectDependencyDirs(javaProject) + "\"";
//---------------------------------------- //----------------------------------------
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, vmArgs); wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, vmArgs);
@ -128,34 +128,6 @@ public class GhidraLaunchDelegate extends JavaLaunchDelegate {
super.launch(wc.doSave(), mode, launch, monitor); super.launch(wc.doSave(), mode, launch, monitor);
} }
/**
* For the given Java project, gets all of its classpath dependencies that are themselves
* projects. The result is formatted as a string of paths separated by
* {@link File#pathSeparator}.
*
* @param javaProject The Java project whose project dependencies we are getting.
* @return A string of paths separated by {@link File#pathSeparator} that represents the given
* Java project's dependencies that are projects. Could be empty if there are no
* dependencies.
* @throws CoreException if there was an Eclipse-related problem with getting the dependencies.
*/
private static String getProjectDependencyDirs(IJavaProject javaProject) throws CoreException {
String paths = "";
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
if (!paths.isEmpty()) {
paths += File.pathSeparator;
}
IResource resource =
ResourcesPlugin.getWorkspace().getRoot().findMember(entry.getPath());
if (resource != null) {
paths += resource.getLocation();
}
}
}
return paths;
}
/** /**
* Handles extra things that should happen when we are launching in debug mode. * Handles extra things that should happen when we are launching in debug mode.
*/ */

View file

@ -88,7 +88,10 @@ public class PyGhidraLaunchDelegate extends RegularLaunchConfigurationDelegate {
.formatted(project.getName())); .formatted(project.getName()));
// Set program arguments // Set program arguments
wc.setAttribute(PyDevUtils.getAttrProgramArguments(), "-v -g"); wc.setAttribute(PyDevUtils.getAttrProgramArguments(),
"-v -g -D ghidra.external.modules=\"%s%s%s\"".formatted(
javaProject.getProject().getLocation(), File.pathSeparator,
GhidraProjectUtils.getProjectDependencyDirs(javaProject)));
// Set Python interpreter // Set Python interpreter
String interpreterName = PyDevUtils.getInterpreterName(project); String interpreterName = PyDevUtils.getInterpreterName(project);

View file

@ -205,6 +205,34 @@ public class GhidraProjectUtils {
return project; return project;
} }
/**
* For the given Java project, gets all of its classpath dependencies that are themselves
* projects. The result is formatted as a string of paths separated by
* {@link File#pathSeparator}.
*
* @param javaProject The Java project whose project dependencies we are getting.
* @return A string of paths separated by {@link File#pathSeparator} that represents the given
* Java project's dependencies that are projects. Could be empty if there are no
* dependencies.
* @throws CoreException if there was an Eclipse-related problem with getting the dependencies.
*/
public static String getProjectDependencyDirs(IJavaProject javaProject) throws CoreException {
String paths = "";
for (IClasspathEntry entry : javaProject.getRawClasspath()) {
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
if (!paths.isEmpty()) {
paths += File.pathSeparator;
}
IResource resource =
ResourcesPlugin.getWorkspace().getRoot().findMember(entry.getPath());
if (resource != null) {
paths += resource.getLocation();
}
}
}
return paths;
}
/** /**
* Creates the given folder, including any necessary but nonexistent parent directories. * Creates the given folder, including any necessary but nonexistent parent directories.
* *