From 0a3574d0c07887f105854747d6153614dcfe4be4 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Thu, 12 Sep 2019 09:40:36 -0400 Subject: [PATCH] GT-3146: Preventing Ghidra from launching with 32-bit Java (fixes #882). --- DevGuide.md | 2 +- .../src/main/java/LaunchSupport.java | 9 +-- .../main/java/ghidra/launch/JavaConfig.java | 47 ++++++++++---- .../main/java/ghidra/launch/JavaVersion.java | 65 ++++++++++++++----- .../java/ghidra/launch/WindowsJavaFinder.java | 1 + GhidraDocs/InstallationGuide.html | 6 +- 6 files changed, 93 insertions(+), 37 deletions(-) diff --git a/DevGuide.md b/DevGuide.md index a1e95bbd9b..4242a01da2 100644 --- a/DevGuide.md +++ b/DevGuide.md @@ -6,7 +6,7 @@ The following is a list of dependencies, in no particular order. This guide includes instructions for obtaining many of these at the relevant step(s). You may not need all of these, depending on which portions you are building or developing. -* Java JDK 11 - Free long term support (LTS) versions of JDK 11 are provided by: +* Java JDK 11 (64-bit) - Free long term support (LTS) versions of JDK 11 are provided by: - AdoptOpenJDK - https://adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot - Amazon Corretto diff --git a/GhidraBuild/LaunchSupport/src/main/java/LaunchSupport.java b/GhidraBuild/LaunchSupport/src/main/java/LaunchSupport.java index 9748fae528..f4ad7e4252 100644 --- a/GhidraBuild/LaunchSupport/src/main/java/LaunchSupport.java +++ b/GhidraBuild/LaunchSupport/src/main/java/LaunchSupport.java @@ -224,10 +224,11 @@ public class LaunchSupport { javaRange = min + "-" + max; } - System.out.println("*******************************************************"); + System.out.println("******************************************************************"); System.out.println( - javaName + " " + javaRange + " could not be found and must be manually chosen!"); - System.out.println("*******************************************************"); + javaName + " " + javaRange + " (" + javaConfig.getSupportedArchitecture() + + "-bit) could not be found and must be manually chosen!"); + System.out.println("******************************************************************"); File javaHomeDir = null; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); @@ -259,7 +260,7 @@ public class LaunchSupport { } System.out.println( "Java version " + javaVersion + " is outside of supported range: [" + - javaRange + "]"); + javaRange + " " + javaConfig.getSupportedArchitecture() + "-bit]"); } catch (FileNotFoundException e) { System.out.println( diff --git a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaConfig.java b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaConfig.java index 1919075a43..c6ebc5791c 100644 --- a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaConfig.java +++ b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaConfig.java @@ -84,6 +84,16 @@ public class JavaConfig { return maxSupportedJava; } + /** + * Gets the Java configuration's supported Java architecture. All supported Java + * configurations must have an architecture of 64. + * + * @return The Java configuration's supported Java architecture (64). + */ + public int getSupportedArchitecture() { + return 64; + } + /** * Gets the Java configuration's compiler compliance level that was used to build the * associated installation. @@ -161,6 +171,10 @@ public class JavaConfig { * @return True if the given Java version is supported by this Java launch configuration. */ public boolean isJavaVersionSupported(JavaVersion javaVersion) { + if (javaVersion.getArchitecture() != getSupportedArchitecture()) { + return false; + } + int major = javaVersion.getMajor(); return major >= minSupportedJava && (maxSupportedJava == 0 || major <= maxSupportedJava); @@ -229,29 +243,34 @@ public class JavaConfig { */ private JavaVersion runAndGetJavaVersion(File javaExecutable) throws ParseException, IOException { - Runtime rt = Runtime.getRuntime(); - Process proc = rt.exec(new String[] { javaExecutable.getAbsolutePath(), "-version" }); + String version = ""; + String arch = ""; + Process proc = Runtime.getRuntime().exec(new String[] { javaExecutable.getAbsolutePath(), + "-XshowSettings:properties", "-version" }); try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) { String line; - while ((line = reader.readLine()) != null) { + while ((version.isEmpty() || arch.isEmpty()) && (line = reader.readLine()) != null) { line = line.trim(); - - // If the _JAVA_OPTIONS or JAVA_TOOL_OPTIONS environment variables are set, STDERR - // will start with "Picked up..." lines that need to be ignored so we can get to the - // java version line. - if (line.startsWith("Picked up")) { - continue; - } - String[] parts = line.split("\\s"); - if (parts.length < 3) { - throw new ParseException("Failed to parse version: " + line, 0); + String searchString = "java.version = "; + if (line.startsWith(searchString)) { + version = line.substring(searchString.length()); + } + + searchString = "sun.arch.data.model = "; + if (line.startsWith(searchString)) { + arch = line.substring(searchString.length()); } - return new JavaVersion(parts[2]); } + } + if (version.isEmpty()) { throw new ParseException("Failed to find Java version", 0); } + if (arch.isEmpty()) { + throw new ParseException("Failed to find Java architecture", 0); + } + return new JavaVersion(version, arch); } /** diff --git a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaVersion.java b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaVersion.java index 69149ef57e..98efaa4f9f 100644 --- a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaVersion.java +++ b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/JavaVersion.java @@ -19,22 +19,29 @@ import java.text.ParseException; /** * Class to more conveniently represent a Java version string. + *

+ * Note: this class has a natural ordering that is inconsistent with equals + * (the architecture part of the version is disregarded in the + * {@link #compareTo(JavaVersion)} method). */ public class JavaVersion implements Comparable { private int major; private int minor; private int patch; + private int arch; /** * Creates a new {@link JavaVersion} object from the given version string. * * @param version A version string. - * @throws ParseException if the version string failed to parse. The exception's - * message has more detailed information about why it failed. + * @param architecture An architecture string (32 or 64). + * @throws ParseException if the version or architecture string failed to parse. + * The exception's message has more detailed information about why it failed. */ - public JavaVersion(String version) throws ParseException { - parse(version); + public JavaVersion(String version, String architecture) throws ParseException { + parseVersion(version); + parseArchitecture(architecture); } /** @@ -64,12 +71,21 @@ public class JavaVersion implements Comparable { return patch; } + /** + * Gets the architecture. + * + * @return The architecture. + */ + public int getArchitecture() { + return arch; + } + @Override public String toString() { if (major < 9) { - return String.format("1.%d.%d_%d", major, minor, patch); + return String.format("1.%d.%d_%d (%d-bit)", major, minor, patch, arch); } - return String.format("%d.%d.%d", major, minor, patch); + return String.format("%d.%d.%d (%d-bit)", major, minor, patch, arch); } @Override @@ -102,6 +118,7 @@ public class JavaVersion implements Comparable { result = prime * result + major; result = prime * result + minor; result = prime * result + patch; + result = prime * result + arch; return result; } @@ -126,6 +143,9 @@ public class JavaVersion implements Comparable { if (patch != other.patch) { return false; } + if (arch != other.arch) { + return false; + } return true; } @@ -136,7 +156,7 @@ public class JavaVersion implements Comparable { * @throws ParseException if the version string failed to parse. The exception's message * has more detailed information about why it failed. */ - private void parse(String version) throws ParseException { + private void parseVersion(String version) throws ParseException { if (version == null) { throw new ParseException("Version is null", 0); } @@ -155,26 +175,26 @@ public class JavaVersion implements Comparable { } String[] versionParts = version.split("[._]"); - int firstValue = parse(versionParts[0], "first value"); + int firstValue = parseVersionPart(versionParts[0], "first value"); if (firstValue == 1) { // Follows the Java 8 and earlier format of 1.major.minor_patch if (versionParts.length > 1) { - major = parse(versionParts[1], "major"); + major = parseVersionPart(versionParts[1], "major"); if (versionParts.length > 2) { - minor = parse(versionParts[2], "minor"); + minor = parseVersionPart(versionParts[2], "minor"); if (versionParts.length > 3) { - patch = parse(versionParts[3], "patch"); + patch = parseVersionPart(versionParts[3], "patch"); } } } } else if (firstValue >= 9) { // Follows the Java 9 and later format of major.minor.patch - major = parse(versionParts[0], "major"); + major = parseVersionPart(versionParts[0], "major"); if (versionParts.length > 1) { - minor = parse(versionParts[1], "minor"); + minor = parseVersionPart(versionParts[1], "minor"); if (versionParts.length > 2) { - patch = parse(versionParts[2], "patch"); + patch = parseVersionPart(versionParts[2], "patch"); } } } @@ -192,7 +212,7 @@ public class JavaVersion implements Comparable { * @throws ParseException if the version part string failed to parse to a valid version part * integer. */ - private int parse(String versionPart, String versionPartName) throws ParseException { + private int parseVersionPart(String versionPart, String versionPartName) throws ParseException { try { int i = Integer.parseInt(versionPart); if (i < 0) { @@ -205,4 +225,19 @@ public class JavaVersion implements Comparable { 0); } } + + /** + * Parses the architecture integer out of the given architecture string. + * + * @param architecture An architecture string. + * @throws ParseException if the architecture string failed to parse. + */ + private void parseArchitecture(String architecture) throws ParseException { + try { + arch = Integer.parseInt(architecture); + } + catch (NumberFormatException e) { + throw new ParseException("Failed to parse architecture: " + architecture, 0); + } + } } diff --git a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/WindowsJavaFinder.java b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/WindowsJavaFinder.java index dad592cd2f..c900f24558 100644 --- a/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/WindowsJavaFinder.java +++ b/GhidraBuild/LaunchSupport/src/main/java/ghidra/launch/WindowsJavaFinder.java @@ -28,6 +28,7 @@ public class WindowsJavaFinder extends JavaFinder { protected List getJavaRootInstallDirs() { List javaRootInstallDirs = new ArrayList<>(); javaRootInstallDirs.add(new File("C:\\Program Files\\Java")); + javaRootInstallDirs.add(new File("C:\\Program Files\\Amazon Corretto")); return javaRootInstallDirs; } diff --git a/GhidraDocs/InstallationGuide.html b/GhidraDocs/InstallationGuide.html index e4a396ac0e..bbdd85e9a2 100644 --- a/GhidraDocs/InstallationGuide.html +++ b/GhidraDocs/InstallationGuide.html @@ -81,7 +81,7 @@ Ghidra team if you have a specific need.

Software