Ghidra: Linux/ghidraRun: results in java.lang.ClassNotFoundException error

Created on 4 Apr 2019  路  16Comments  路  Source: NationalSecurityAgency/ghidra

Describe the bug

Running the ./Ghidra/RuntimeScripts/Linux/ghidraRun script to launch Ghidra results in a java.lang.ClassNotFoundException error after a fresh build on Linux, following the instructions outlined in DevGuide.

To Reproduce
Steps to reproduce the behavior:

  1. Build Ghidra by following the steps in DevGuide.
  2. Run the ./Ghidra/RuntimeScripts/Linux/ghidraRun script.
  3. See error java.lang.ClassNotFoundException: LaunchSupport.
u@x1 ~/D/g/ghidra> ./Ghidra/RuntimeScripts/Linux/ghidraRun
Error: Could not find or load main class LaunchSupport
Caused by: java.lang.ClassNotFoundException: LaunchSupport
Error: Could not find or load main class LaunchSupport
Caused by: java.lang.ClassNotFoundException: LaunchSupport
Error: Could not find or load main class LaunchSupport
Caused by: java.lang.ClassNotFoundException: LaunchSupport

Failed to find a supported JDK.  Please refer to the Ghidra Installation Guide's Troubleshooting section.

Expected behavior

Running the Ghidra program without any issues.

Environment (please complete the following information):

  • OS: Arch Linux
$ uname -a
Linux x1 5.0.5-arch1-1-ARCH #1 SMP PREEMPT Wed Mar 27 17:53:10 UTC 2019 x86_64 GNU/Linux
  • Java Version: jdk-openjdk 11.0.3.u4-1

  • Ghidra Version: 49c2010b63b56c8f20845f3970fedd95d003b1e9 (Ghidra_9.0.2_build)

Additional context

The issue seems to be either in the Ghidra/RuntimeScripts/Linux/support/launch.sh script or the output location of build artefacts. In particular, CPATH and LS_CPATH in the launch.sh script both point to bin/main subdirectories (e.g. ${INSTALL_DIR}/Ghidra/Framework/Utility/bin/main) that are not present after a fresh build of Ghidra.

Note, this is in the Development environment, so the INSTALL_DIR is the repository root of ghidra.

The bin directory is not present after a fresh build of Ghidra. However, the build directory is.

u@x1 ~/D/g/ghidra> ls Ghidra/Framework/Utility/bin/main
ls: cannot access 'Ghidra/Framework/Utility/bin/main': No such file or directory
u@x1 ~/D/g/ghidra> ls Ghidra/Framework/Utility
Module.manifest  build/  build.gradle  certification.manifest  src/

Replacing e.g. LS_CPATH as follows, makes the script go further, before receiving another java.lang.ClassNotFoundException error.

-       LS_CPATH="${INSTALL_DIR}/GhidraBuild/LaunchSupport/bin/main"
+       LS_CPATH="${INSTALL_DIR}/GhidraBuild/LaunchSupport/build/classes/java/main"

What is the proper solution to finding specifying the Java class after building Ghidra?

Cheers,
Robin

wont fix

Most helpful comment

While working on trying to get around this exact problem, I went ahead and tried to mess with getting the bin folder to behave as though it were built with eclipse by symlinking. I only tested it for the Features/Utility sub-project, but I was able to rm -r bin/main and then ln -s build/classes/java/main bin/main, and I can load ghidra in dev mode using ghidraRun, and it launches just fine.

I need to do further testing to see if I can remove eclipse fully from the loop, but it seems like writing a script to traverse all the sub-project directories, and generating a symlink, should "just work :tm:".

It may be possible to even add a gradle method like gradle cmdLineDev which would do this as part of the setup process, and gets around the issue of needing to change all of the source files. If this is something worth adding to the project, I'll look at figuring out gradle well enough to do this, and submit a pull request at some point.

All 16 comments

The problem here is that when you run the launch scripts out of the development environment, it is assumed you have set things up with Eclipse. Eclipse builds to the bin directory. Were you planning on setting up Eclipse or did you want to try to make changes to the source a different way?

Hey Ryan,

This may or may not be related, but I think the DevGuide.md is a little vague for folks new to Eclipse or even folks like me who can't remember anything I did a week ago. Specifically, there's the "Install Development and Build Tools" section, which describes setting up Eclipse-related stuff, and the "Run Ghidra from Eclipse" section, which describes buttons to click in the Eclipse GUI, but there are at least two things missing. One is the general context, i.e. you now have a zip file in build that can be used in much the same way as the distro and an environment set up for development in Eclipse. The second piece that may be obvious to some users but not others is how do I actually setup the Eclipse project, i.e. File->Import...->General->Existing Projects into Workspace->Browse to 'ghidra'->Select 'Search for nested projects'

D

NOTE: The following scripts are broken in 9.0.2 for development mode use:
svrAdmin, svrAdmin.bat and createPdbXmlFiles.bat

The problem here is that when you run the launch scripts out of the development environment, it is assumed you have set things up with Eclipse. Eclipse builds to the bin directory. Were you planning on setting up Eclipse or did you want to try to make changes to the source a different way?

That explains it. I was planning on making changes to the source using other editors. What is the recommended way to configure these Java class paths when compiling from terminal?

We'll have to play with it to see what would be involved to make it work. We will also have to discuss if its something we want to support.

At the very least we need to look in the build directory rather than the bin directory throughout the code. We do this in the launcher scripts, as well as within the Java code (ModuleUtilities.java:197).

This has been fixed for 9.0.3

Just to clarify, 9.0.3 is yet to be released? I don't see any new commits in the master branch.

My bad - I did not properly consider your use-case which is not supported. My focus was on some of the scripts improperly referencing the bin repo, not the bin directories which are produced by Eclipse during development. Ghidra's supported development mode assumes/requires that you are using Eclipse. Ghidra's development use case supported by our scripts is not intended to support the one you have attempted. If you do not wish to develop with Eclipse, you could simply perform a full build (e.g., gradle buildGhidra) and run from the unpacked installation.

The changes I have made are still in a branch and not yet merged, although as indicated above they will not address your issue. I apologize for the confusion.

Sorry for the confusion on this @mewmew. We discussed it and decided that we are going to continue to rely on Eclipse compiling to the bin/ directory for launching from development mode. Running these scripts from development mode is mainly intended for debugging/developing the scripts themselves. It would get too messy to add a 3rd place to look for class files.

Sorry for the confusion on this @mewmew. We discussed it and decided that we are going to continue to rely on Eclipse compiling to the bin/ directory for launching from development mode. Running these scripts from development mode is mainly intended for debugging/developing the scripts themselves. It would get too messy to add a 3rd place to look for class files.

Thank you for the follow-up. While not the decision I was hoping for, I understand the rationale.

Kindly,
Robin

While working on trying to get around this exact problem, I went ahead and tried to mess with getting the bin folder to behave as though it were built with eclipse by symlinking. I only tested it for the Features/Utility sub-project, but I was able to rm -r bin/main and then ln -s build/classes/java/main bin/main, and I can load ghidra in dev mode using ghidraRun, and it launches just fine.

I need to do further testing to see if I can remove eclipse fully from the loop, but it seems like writing a script to traverse all the sub-project directories, and generating a symlink, should "just work :tm:".

It may be possible to even add a gradle method like gradle cmdLineDev which would do this as part of the setup process, and gets around the issue of needing to change all of the source files. If this is something worth adding to the project, I'll look at figuring out gradle well enough to do this, and submit a pull request at some point.

@hedgeberg I am currently trying to also setup a dev environment with IntelliJ and running into similar issues. Specifying -DbinaryPath=build/classes/java/main as a JVM argument works and allows Ghidra to find all the classes. It then crashes/fails because various files like the UserAgreement or images like ./Ghidra/Framework/Generic/bin/main/images/core.png are also expected in this location. Adding more paths to the binaryPath seems to work, i.e. -DbinaryPath=build/classes/java/main:build/resources/main/ allows the images to be found, but the tips are now missing. So I think if all the paths are added this would be enough and would in total just be one more VM argument in a launch configuration. This also seems cleaner then symlinking (which isn't really supported on windows I think?)

I ran into this exact issue while testing the other day, and didn't find that particular solution, so that's really helpful! Thanks!

It should be possible to add all the needed paths to the list of binary paths in the ClassLoader object, for what it's worth. Experimented with that and had some limited success, so that may be even cleaner since it would be truly cross-platform by default. Let me check what all I did, I'll post again in a moment.

So, the place to make this change would be to target the getModuleBinDirectories method in Ghidra/Framework/Utility/src/main/java/utility/module/ModuleUtilities.java

We'd need to also add logic to GhidraLauncher and/or SystemUtilties to add a launch mode which would allow us to do something like:

public static Collection<ResourceFile> getModuleBinDirectories(Map<String, GModule> modules) {
    String[] binaryPathTokens;
        //to be clear, isInNoEclipseBuildMode() does not exist yet
        //I've been messing with changing the logic here based on
        //SystemUtilties.isInDevelopmentMode(), which does exist and should be 
        //a good example of how to add a launch mode.
    if(SystemUtilities.isInNoEclipseBuildMode()){ 
        binaryPathTokens = ["build/classes/java/main", "build/resources/main/"];
                //can add as many paths as seems appropriate here
    } else {
        binaryPathTokens = BINARY_PATH.split(":");
    }
    for (String tmp : binaryPathTokens) {
            System.err.println(tmp);
        }
        List<ResourceFile> binDirectories = new ArrayList<>();
        for (GModule module : modules.values()) {
            Arrays.stream(binaryPathTokens).forEach(
                token -> module.collectExistingModuleDirs(binDirectories, token));
        }
        return binDirectories;
    }

but once that's done, it should be possible to get an Eclipse-less build environment working without muddying the current default way that development mode is invoked.

I'm unsure if the ghidra devs (ping @ryanmkurtz @ghidra1) would accept a pull request like this, so it might be necessary for a user of your IntelliJ layout to set this up themselves by importing a patch file, but I think this should work for getting non-eclipse dev-mode launchers working. If you do go forward with this @fmagin please drop a link to how you set it up somewhere, I'd really love to shift away from eclipse if possible for some of the more-basic dev work that doesn't rely on the GhidraDev eclipse/ghidra cross-communication.

I am currently writing up a blogpost (series?) on setting up the full IDE experience in IntelliJ, and I got nearly everything working. I can give you access to the repo with the drafts.

Are you sure the changes are needed? Resource loading seems to work via the class loader as far as I understood and that searches the binary path. I tested -DbinaryPath=build/classes/java/main:build/resources/main/ this morning and I think it worked and loaded the images.

    if(SystemUtilities.isInNoEclipseBuildMode()){ 
        binaryPathTokens = ["build/classes/java/main", "build/resources/main/"];
                //can add as many paths as seems appropriate here
    } else {
        binaryPathTokens = BINARY_PATH.split(":");
    }

This seems completely redundant because BINARY_PATH is what is supplied via -DbinaryPath or a default of bin/main. So you could just pass -DbinaryPath=build/classes/java/main:build/resources/main/ and get the same behavior without a potential check to SystemUtilities.isInNoEclipseBuildMode(). My setup is recognized as a development install by isInDevelopmentMode so the logic is sufficient. The trick is specifying a folder and not a jar as a classpath. I think we can get it to work so the only thing that is needed is a custom launch config (which can't be imported from Eclipse anyway) that specifies the binaryPath (that depends on the IDE default build locations) and the initial classpath.

There are some minor issues like the default build action of IntelliJ building in a way that doesn't succeed, but just invoking the appropriate (unsure which) gradle task instead of a build action when launching is part of the launch config.

I just confirmed this, clean clone and prepDev and generating the eclipse files, then importing into IntelliJ and setting up a launch configuration with binary path of -DbinaryPath=build/classes/java/main:build/resources/main/:bin/default/ mostly works despite the build process of IntelliJ being unable to compile some classes. There are still some files not being found (like the standard project layout) but I think those are just a matter of adding them to the binaryPath. Some classes are missing but I blame the IntelliJ build process failing for that. Most importantly I can properly debug this setup with IntelliJ and thus compare it to the frankenstein version where I built the files with Eclipse to see how the files are searched and where they are expected to be.

After actually running a gradle classes I can start Ghidra from IntelliJ without issues, so I guess some of the missing classes where compiled by that and Ghidra generated some defaults in the expected locations for stuff like the layout.

Edit:
So in total I can reliable get a development setup with IntelliJ that works without any modifications of the upstream source code and ever starting Eclipse. The only thing needed and non trivial is the correct launch configuration which I am going to fully document and publish soon. The core points are just adding -DbinaryPath=build/classes/java/main:build/resources/main/ as VM argument and Framework Utility as Classpath of Module though

The setup is now documented at https://reversing.technology/

Was this page helpful?
0 / 5 - 0 ratings