From 88bec10e604d048d90e8085d5ad3928ad5ba3b47 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Fri, 26 Jul 2024 13:12:29 -0400 Subject: [PATCH] GP-3490: Fixing GhidraDev classpath issues --- .../runtimeinfo/RuntimeInfoProvider.java | 11 ++- .../util/classfinder/ClassSearcher.java | 22 ++---- .../java/ghidra/GhidraApplicationLayout.java | 35 +++------- .../main/java/ghidra/GhidraClassLoader.java | 26 +++++-- .../src/main/java/ghidra/GhidraLauncher.java | 46 +++++++------ .../framework/ApplicationProperties.java | 12 ++-- Ghidra/application.properties | 2 +- .../GhidraDevPlugin/.launch/GhidraDev.launch | 52 +++++++------- .../GhidraDevPlugin/GhidraDev_README.html | 24 ++++++- .../GhidraDevPlugin/META-INF/MANIFEST.MF | 4 +- .../GhidraDev/GhidraDevPlugin/plugin.xml | 6 +- .../launchers/GhidraLaunchDelegate.java | 19 ++++-- .../launchers/GhidraLaunchTabGroup.java | 53 +++++++++++++-- .../utils/GhidraLaunchUtils.java | 67 ++++++++++++++++++- .../utils/GhidraModuleUtils.java | 39 +++++++---- .../utils/GhidraProjectUtils.java | 8 +-- 16 files changed, 285 insertions(+), 141 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/runtimeinfo/RuntimeInfoProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/runtimeinfo/RuntimeInfoProvider.java index 86040bb4fa..47ea398315 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/runtimeinfo/RuntimeInfoProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/runtimeinfo/RuntimeInfoProvider.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. @@ -16,7 +16,6 @@ package ghidra.app.plugin.runtimeinfo; import java.awt.*; -import java.io.File; import java.util.*; import java.util.stream.Collectors; @@ -249,11 +248,9 @@ class RuntimeInfoProvider extends ReusableDialogComponentProvider { */ private Map getClasspathMap(String propertyName) { Map map = new HashMap<>(); - StringTokenizer st = - new StringTokenizer(System.getProperty(propertyName, ""), File.pathSeparator); int i = 0; - while (st.hasMoreTokens()) { - map.put(i++, st.nextToken()); + for (String entry : GhidraClassLoader.getClasspath(propertyName)) { + map.put(i++, entry); } return map; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java index 011acf42d7..94630c1f26 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.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. @@ -30,7 +30,6 @@ import java.util.stream.Collectors; import javax.swing.event.ChangeListener; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -491,24 +490,11 @@ public class ClassSearcher { // jar files will *not* be on the standard classpath, but instead will be on CP_EXT. // List rawPaths = new ArrayList<>(); - getPropertyPaths(GhidraClassLoader.CP, rawPaths); - getPropertyPaths(GhidraClassLoader.CP_EXT, rawPaths); + rawPaths.addAll(GhidraClassLoader.getClasspath(GhidraClassLoader.CP)); + rawPaths.addAll(GhidraClassLoader.getClasspath(GhidraClassLoader.CP_EXT)); return canonicalizePaths(rawPaths); } - private static void getPropertyPaths(String property, List results) { - String paths = System.getProperty(property); - log.trace("Paths in {}: {}", property, paths); - if (StringUtils.isBlank(paths)) { - return; - } - - StringTokenizer st = new StringTokenizer(paths, File.pathSeparator); - while (st.hasMoreTokens()) { - results.add(st.nextToken()); - } - } - private static List canonicalizePaths(Collection paths) { //@formatter:off diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java index 72974f853d..007f862d44 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.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. @@ -169,28 +169,15 @@ public class GhidraApplicationLayout extends ApplicationLayout { }); } - // Examine the classpath to look for modules outside of the application root directories. - // These might exist if Ghidra was launched from an Eclipse project that resides - // external to the Ghidra installation. - for (String entry : System.getProperty("java.class.path", "").split(File.pathSeparator)) { - ResourceFile classpathEntry = new ResourceFile(entry); - - // We only care about directories (skip jars) - if (!classpathEntry.isDirectory()) { - continue; - } - - // Skip extensions in an application root directory... already found those. - if (FileUtilities.isPathContainedWithin(applicationRootDirs, classpathEntry)) { - continue; - } - - // We are going to assume that the classpath entry is in a subdirectory of the module - // directory (i.e., bin/), so only check parent directory for the module. - ResourceFile classpathEntryParent = classpathEntry.getParentFile(); - if (classpathEntryParent != null && - ModuleUtilities.isModuleDirectory(classpathEntryParent)) { - moduleRootDirectories.add(classpathEntryParent); + // Add external modules defined via a system property. This will typically be used by + // user's developing 3rd party modules from something like Eclipse. + String externalModules = System.getProperty("ghidra.external.modules", ""); + if (!externalModules.isBlank()) { + for (String path : externalModules.split(File.pathSeparator)) { + ResourceFile eclipseProjectDir = new ResourceFile(path); + if (ModuleUtilities.isModuleDirectory(eclipseProjectDir)) { + moduleRootDirectories.add(eclipseProjectDir); + } } } diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraClassLoader.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraClassLoader.java index d781613067..d6564fae8d 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraClassLoader.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraClassLoader.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. @@ -18,8 +18,7 @@ package ghidra; import java.io.File; import java.lang.instrument.Instrumentation; import java.net.*; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import ghidra.util.Msg; @@ -51,6 +50,25 @@ public class GhidraClassLoader extends URLClassLoader { */ public static final String CP_EXT = "java.class.path.ext"; + /** + * Gets a {@link List} containing the current classpath referenced by the given property name + * + * @param propertyName The property name of the classpath to get + * @return A {@link List} containing the current classpath referenced by the given property name + */ + public static List getClasspath(String propertyName) { + List result = new ArrayList<>(); + + // StringTokenizer is better than split() here because our result list will stay empty if + // the classpath is empty + StringTokenizer st = + new StringTokenizer(System.getProperty(propertyName, ""), File.pathSeparator); + while (st.hasMoreTokens()) { + result.add(st.nextToken()); + } + return result; + } + /** * Used to prevent duplicate URL's from being added to the classpath */ diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java index 8a0299d69f..d792b2c1ab 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.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. @@ -150,11 +150,15 @@ public class GhidraLauncher { List classpathList = new ArrayList<>(); Map modules = getOrderedModules(layout); + // First add any "bin" paths the module might have. These could come from external modules + // being developed and passed in via system property if we are in release mode, or they + // could be generated for each Ghidra module by Eclipse if we are in development mode. + addModuleBinPaths(classpathList, modules); + if (SystemUtilities.isInDevelopmentMode()) { - // First add Eclipse's module "bin" paths. If we didn't find any, assume Ghidra was + // If we didn't find any "bin" paths and we are in development mode, assume Ghidra was // compiled with Gradle, and add the module jars Gradle built. - addModuleBinPaths(classpathList, modules); boolean gradleDevMode = classpathList.isEmpty(); if (gradleDevMode) { // Add the module jars Gradle built. @@ -165,22 +169,16 @@ public class GhidraLauncher { else { /* Eclipse dev mode */ // Support loading pre-built, jar-based, non-repo extensions in Eclipse dev mode addExtensionJarPaths(classpathList, modules, layout); - - // Eclipse launches the Utility module, so it's already on the classpath. We don't - // want to add it a second time, so remove the one we discovered. - GModule utilityModule = modules.get("Utility"); - if (utilityModule == null) { - throw new IOException("Failed to find the 'Utility' module!"); - } - classpathList.removeIf( - e -> e.startsWith(utilityModule.getModuleRoot().getAbsolutePath())); } - // In development mode, jars do not live in module directories. Instead, each jar lives - // in an external, non-repo location, which is listed in build/libraryDependencies.txt. + // In development mode, 3rd party library jars do not live in module directories. + // Instead, each jar lives in an external, non-repo location, which is listed in + // build/libraryDependencies.txt. addExternalJarPaths(classpathList, layout.getApplicationRootDirs()); } else { + + // Release mode is simple. We expect all of Ghidra's modules to be in pre-build jars. addPatchPaths(classpathList, layout.getPatchDir()); addModuleJarPaths(classpathList, modules); } @@ -194,8 +192,16 @@ public class GhidraLauncher { // the standard classpath.) setExtensionJarPaths(modules, layout, classpathList); - classpathList = orderClasspath(classpathList, modules); - return classpathList; + // Ghidra launches from the Utility module, so it's already on the classpath. We don't + // want to add it a second time, so remove the one we discovered. + GModule utilityModule = modules.get("Utility"); + if (utilityModule == null) { + throw new IOException("Failed to find the 'Utility' module!"); + } + classpathList.removeIf( + e -> e.startsWith(utilityModule.getModuleRoot().getAbsolutePath())); + + return orderClasspath(classpathList, modules); } /** @@ -265,9 +271,9 @@ public class GhidraLauncher { } /** - * Add extension module lib jars to the given path list. (This only needed in dev mode to find - * any pre-built extensions that have been installed, since we already find extension module - * jars in production mode.) + * Add extension module lib jars to the given path list. (This is only needed in dev mode to + * find any pre-built extensions that have been installed, since we already find extension + * module jars in production mode.) * * @param pathList The list of paths to add to. * @param modules The modules to get the jars of. diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationProperties.java b/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationProperties.java index 765b5025fa..0703b3ae44 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationProperties.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationProperties.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. @@ -20,6 +20,7 @@ import java.text.SimpleDateFormat; import java.util.*; import generic.jar.ResourceFile; +import ghidra.GhidraApplicationLayout; /** * The application properties. Application properties may either be stored on disk, or created @@ -52,8 +53,11 @@ public class ApplicationProperties extends Properties { * Current application versions are: *
    *
  • 1: Layout used by Ghidra < 11.1
  • - *
  • 2: Introduced with Ghidra 11.1. Default user settings/cache/temp directories changed, - * and XDG environment variables are supported. + *
  • 2: Introduced with Ghidra 11.1. Default user settings/cache/temp directories changed, + * and XDG environment variables are supported.
  • + *
  • 3: Introduced with Ghidra 11.2. Ghidra no longer finds external modules by examining + * the initial classpath. Instead, the "ghidra.external.modules" system property is used + * (see {@link GhidraApplicationLayout}).
  • *
*/ public static final String APPLICATION_LAYOUT_VERSION_PROPERTY = "application.layout.version"; diff --git a/Ghidra/application.properties b/Ghidra/application.properties index 49cdcca99b..332f14284c 100644 --- a/Ghidra/application.properties +++ b/Ghidra/application.properties @@ -1,7 +1,7 @@ application.name=Ghidra application.version=11.2 application.release.name=DEV -application.layout.version=2 +application.layout.version=3 application.gradle.min=8.5 application.gradle.max= application.java.min=21 diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/.launch/GhidraDev.launch b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/.launch/GhidraDev.launch index 5ab9dc9fdf..219d1935e4 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/.launch/GhidraDev.launch +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/.launch/GhidraDev.launch @@ -158,6 +158,7 @@ + @@ -166,16 +167,14 @@ - - - + - - + + @@ -204,7 +203,7 @@ - + @@ -219,7 +218,7 @@ - + @@ -248,7 +247,7 @@ - + @@ -266,6 +265,7 @@ + @@ -285,7 +285,7 @@ - + @@ -293,7 +293,7 @@ - + @@ -309,6 +309,7 @@ + @@ -336,21 +337,20 @@ - - + - + - + @@ -381,7 +381,7 @@ - + @@ -395,6 +395,7 @@ + @@ -407,19 +408,14 @@ - - - - - - - - - - - - - + + + + + + + + diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/GhidraDev_README.html b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/GhidraDev_README.html index 80f1d798a6..50065d62a6 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/GhidraDev_README.html +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/GhidraDev_README.html @@ -19,7 +19,7 @@

GhidraDev README

GhidraDev provides support for developing and debugging Ghidra scripts and modules in Eclipse.

-

The information provided in this document is effective as of GhidraDev 3.1.0 and is subject to +

The information provided in this document is effective as of GhidraDev 4.0.0 and is subject to change with future releases.

    @@ -54,6 +54,24 @@ change with future releases.

Change History

+

4.0.0: +

    +
  • + GhidraDev has been upgraded to be compatible with Ghidra 11.2 and later. Older versions of + GhidraDev will report an error when trying to link against Ghidra 11.2 or later. +
  • +
  • + GhidraDev now requires Eclipse 2023-12 4.30 or later. +
  • +
  • + GhidraDev now requires JDK 21. +
  • +
  • + Fixed an issue that could result in a GhidraHelpService exception when launching + Ghidra. GhidraDev now properly enforces that Ghidra is only launched with Utility.jar on + the initial classpath. +
  • +

3.1.0:

  • @@ -176,8 +194,8 @@ that specify other projects on their build paths.

    Minimum Requirements

      -
    • Eclipse 2021-12 4.22 or later
    • -
    • Ghidra 9.1 or later
    • +
    • Eclipse 2023-12 4.30 or later
    • +
    • Ghidra 11.2 or later

    (Back to Top)

    diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/META-INF/MANIFEST.MF b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/META-INF/MANIFEST.MF index 591c26d03e..f043de2900 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/META-INF/MANIFEST.MF +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: GhidraDev Bundle-SymbolicName: ghidra.ghidradev;singleton:=true -Bundle-Version: 3.1.0.qualifier +Bundle-Version: 4.0.0.qualifier Bundle-Activator: ghidradev.Activator Require-Bundle: org.eclipse.ant.core;bundle-version="3.6.200", org.eclipse.buildship.core;bundle-version="3.1.5", @@ -27,7 +27,7 @@ Require-Bundle: org.eclipse.ant.core;bundle-version="3.6.200", org.python.pydev.ast;bundle-version="[6.3.1,10.0.0)";resolution:=optional, org.eclipse.cdt.core;bundle-version="5.9.1";resolution:=optional, org.eclipse.cdt.ui;bundle-version="5.9.0";resolution:=optional -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: Ghidra Bundle-ActivationPolicy: lazy Bundle-ClassPath: ., diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/plugin.xml b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/plugin.xml index caaaaf2af3..d377ab0ca7 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/plugin.xml +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/plugin.xml @@ -365,9 +365,13 @@ + migrationDelegate="org.eclipse.jdt.internal.launching.JavaMigrationDelegate" + name="Ghidra" + sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector" + sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"> tabs = new ArrayList<>(); tabs.add(getJavaMainTab()); tabs.add(getUserDefinedArgumentsTab()); - tabs.add(new JavaClasspathTab()); + tabs.add(getJavaDependenciesTab()); + tabs.add(getSourceLookupTab()); tabs.add(new EnvironmentTab()); tabs.add(getCommonTab()); @@ -169,6 +171,49 @@ public class GhidraLaunchTabGroup extends AbstractLaunchConfigurationTabGroup { }; } + /** + * Gets the {@link JavaDependenciesTab} to use, with all Ghidra jars removed except Utility.jar. + * + * @return The {@link JavaDependenciesTab} to use, with all Ghidra jars removed except + * Utility.jar. + */ + private JavaDependenciesTab getJavaDependenciesTab() { + return new JavaDependenciesTab() { + @Override + public void initializeFrom(ILaunchConfiguration config) { + try { + ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy(); + GhidraLaunchUtils.setClasspath(wc); + super.initializeFrom(wc.doSave()); + } + catch (CoreException e) { + EclipseMessageUtils.error("Failed to initialize the java dependencies tab.", e); + } + } + }; + } + + /** + * Gets the {@link SourceLookupTab} to use, with all Ghidra jars added. + * + * @return The {@link SourceLookupTab} to use, with all Ghidra jars added. + */ + private SourceLookupTab getSourceLookupTab() { + return new SourceLookupTab() { + @Override + public void initializeFrom(ILaunchConfiguration config) { + try { + ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy(); + GhidraLaunchUtils.setSource(wc); + super.initializeFrom(wc.doSave()); + } + catch (CoreException e) { + EclipseMessageUtils.error("Failed to initialize the source lookup tab.", e); + } + } + }; + } + /** * Gets the {@link CommonTab} to use, with the new launch configuration added to the favorites. * diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraLaunchUtils.java b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraLaunchUtils.java index c4e82f5d6b..e70f7c7a60 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraLaunchUtils.java +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraLaunchUtils.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. @@ -19,13 +19,15 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.*; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationManager; import org.eclipse.debug.internal.ui.launchConfigurations.LaunchHistory; import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; +import org.eclipse.jdt.launching.*; import ghidra.GhidraLauncher; @@ -83,6 +85,8 @@ public class GhidraLaunchUtils { javaProject.getProject().getName()); setMainTypeName(wc); setMemory(wc, runConfigMemory); + setClasspath(wc); + setSource(wc); setFavorites(wc); return wc; } @@ -168,6 +172,63 @@ public class GhidraLaunchUtils { return wc; } + /** + * Removes all project jars from the classpath except Utility.jar. + * + * @param wc The launch configuration working copy to modify. + * @return The modified working copy. + * @throws CoreException if there was an Eclipse-related issue modifying the classpath. + */ + public static ILaunchConfigurationWorkingCopy setClasspath(ILaunchConfigurationWorkingCopy wc) + throws CoreException { + List newList = new ArrayList<>(); + for (IRuntimeClasspathEntry entry : JavaRuntime.computeUnresolvedRuntimeClasspath(wc)) { + switch (entry.getClasspathEntry().getEntryKind()) { + case IClasspathEntry.CPE_LIBRARY: + if (entry.getPath().toOSString().endsWith("Utility.jar")) { + newList.add(entry.getMemento()); + } + break; + case IClasspathEntry.CPE_CONTAINER: + newList.add(entry.getMemento()); + break; + case IClasspathEntry.CPE_PROJECT: + case IClasspathEntry.CPE_SOURCE: + case IClasspathEntry.CPE_VARIABLE: + default: + break; + } + } + wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, newList); + wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false); + return wc; + } + + /** + * Adds all project jars that have associated source to the source path + * + * @param wc The launch configuration working copy to modify. + * @return The modified working copy. + * @throws CoreException if there was an Eclipse-related issue modifying the source path. + */ + public static ILaunchConfigurationWorkingCopy setSource(ILaunchConfigurationWorkingCopy wc) + throws CoreException { + List newList = new ArrayList<>(); + IJavaProject javaProject = JavaRuntime.getJavaProject(wc); + if (javaProject != null) { + for (IClasspathEntry entry : javaProject.getRawClasspath()) { + IPath sourcePath = entry.getSourceAttachmentPath(); + if (sourcePath != null) { + newList.add( + JavaRuntime.newArchiveRuntimeClasspathEntry(sourcePath).getMemento()); + } + } + } + wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH, newList); + wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_SOURCE_PATH, false); + return wc; + } + /** * Sets the favorites attribute in the provided working copy to include the launcher in both * the run and debug launch groups. diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraModuleUtils.java b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraModuleUtils.java index 98e65deab5..5a83376d9c 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraModuleUtils.java +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraModuleUtils.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. @@ -62,6 +62,14 @@ public class GhidraModuleUtils { } } + /** + * Stores a source folder and its corresponding output folder + * + * @param sourceFolder The source folder + * @param outputFolder The output folder + */ + private record SourceFolderInfo(IFolder sourceFolder, IFolder outputFolder) {} + /** * Creates a new Ghidra module project with the given name. * @@ -90,20 +98,27 @@ public class GhidraModuleUtils { IProject project = javaProject.getProject(); // Create source directories - List sourceFolders = new ArrayList<>(); - sourceFolders.add(project.getFolder("src/main/java")); - sourceFolders.add(project.getFolder("src/main/help")); - sourceFolders.add(project.getFolder("src/main/resources")); - sourceFolders.add(project.getFolder("src/test/java")); - sourceFolders.add(project.getFolder("ghidra_scripts")); - for (IFolder sourceFolder : sourceFolders) { - GhidraProjectUtils.createFolder(sourceFolder, monitor); + List sourceFolderInfos = new ArrayList<>(); + sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/java"), + project.getFolder("bin/main"))); + sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/help"), + project.getFolder("bin/main"))); + sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/main/resources"), + project.getFolder("bin/main"))); + sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("src/test/java"), + project.getFolder("bin/test"))); + sourceFolderInfos.add(new SourceFolderInfo(project.getFolder("ghidra_scripts"), + project.getFolder("bin/scripts"))); + for (SourceFolderInfo sourceFolderInfo : sourceFolderInfos) { + GhidraProjectUtils.createFolder(sourceFolderInfo.outputFolder(), monitor); } // Put the source directories in the project's classpath List classpathEntries = new LinkedList<>(); - for (IFolder sourceFolder : sourceFolders) { - classpathEntries.add(JavaCore.newSourceEntry(sourceFolder.getFullPath())); + for (SourceFolderInfo sourceFolderInfo : sourceFolderInfos) { + classpathEntries + .add(JavaCore.newSourceEntry(sourceFolderInfo.sourceFolder().getFullPath(), + new IPath[0], sourceFolderInfo.outputFolder().getFullPath())); } GhidraProjectUtils.addToClasspath(javaProject, classpathEntries, monitor); diff --git a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraProjectUtils.java b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraProjectUtils.java index f01a21fcee..059140616f 100644 --- a/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraProjectUtils.java +++ b/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/src/main/java/ghidradev/ghidraprojectcreator/utils/GhidraProjectUtils.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. @@ -301,8 +301,8 @@ public class GhidraProjectUtils { // Configure Java compiler for the project configureJavaCompiler(javaProject, javaConfig); - // Setup bin folder - IFolder binFolder = project.getFolder("bin"); + // Setup default bin folder + IFolder binFolder = project.getFolder("bin/default"); javaProject.setOutputLocation(binFolder.getFullPath(), monitor); // Add Eclipse's built-in JUnit to classpath