GP-0 Improved process error handling for Decompiler and GNU Demangler.

Corrected minor GNU Demangler demangler command and script issues.
This commit is contained in:
ghidra1 2024-06-06 15:11:14 -04:00
parent 7079179b59
commit 3ebc46a2ca
6 changed files with 128 additions and 20 deletions

View file

@ -104,7 +104,12 @@ public class DemangleSymbolScript extends GhidraScript {
println("Successfully demangled\n" + name + '\n' + cmd.getResult());
}
else {
println("Failed to demangle\n" + name + '\n' + cmd.getStatusMsg());
String statusMsg = cmd.getStatusMsg();
String msg = "Failed to demangle\n" + name;
if (statusMsg != null) {
msg += "\n" + statusMsg;
}
println(msg);
}
}
}

View file

@ -59,7 +59,12 @@ public class DecompilerParallelConventionAnalysisCmd extends BackgroundCommand<P
newInterface.setOptions(opts);
if (!newInterface.openProgram(program)) {
throw new IOException("Unable to create decompiler for program: " + program);
String msg = "Unable to create decompiler for program: " + program;
String lastMessage = newInterface.getLastMessage();
if (lastMessage != null) {
msg += "\n" + lastMessage;
}
throw new IOException(msg);
}
return newInterface;

View file

@ -19,6 +19,7 @@ import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.io.*;
import java.util.concurrent.TimeUnit;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.InjectPayload;
@ -46,6 +47,8 @@ import ghidra.util.timer.GTimerMonitor;
*/
public class DecompileProcess {
private static boolean errorDisplayed = false; // launch failure previously displayed
// public static DecompileProcess decompProcess = null;
private final static byte[] command_start = { 0, 0, 1, 2 };
@ -103,6 +106,14 @@ public class DecompileProcess {
};
stringDecoder = new StringIngest();
}
private static synchronized boolean getAndSetErrorDisplayed() {
boolean b = errorDisplayed;
if (!b) {
errorDisplayed = true;
}
return b;
}
public void dispose() {
if (disposestate != DisposeState.NOT_DISPOSED) {
@ -133,19 +144,46 @@ public class DecompileProcess {
if (exepath == null || exepath.length == 0 || exepath[0] == null) {
throw new IOException("Could not find decompiler executable");
}
IOException exc = null;
String err = "";
statusGood = false;
try {
nativeProcess = runtime.exec(exepath);
// Give process time to load and report possible error
nativeProcess.waitFor(200, TimeUnit.MILLISECONDS);
nativeIn = nativeProcess.getInputStream();
nativeOut = nativeProcess.getOutputStream();
statusGood = true;
statusGood = nativeProcess.isAlive();
if (!statusGood) {
err = new String(nativeProcess.getErrorStream().readAllBytes());
nativeProcess.destroy();
nativeProcess = null;
}
}
catch (IOException e) {
disposestate = DisposeState.DISPOSED_ON_STARTUP_FAILURE;
statusGood = false;
Msg.showError(this, null, "Problem launching decompiler",
"Please report this stack trace to the Ghidra Team", e);
throw e;
exc = e;
}
catch (InterruptedException e) {
// ignore
}
finally {
if (!statusGood) {
disposestate = DisposeState.DISPOSED_ON_STARTUP_FAILURE;
if (!getAndSetErrorDisplayed()) {
String errorDetail = err;
if (exc != null) {
errorDetail = exc.getMessage() + "\n" + errorDetail;
}
errorDetail = "Decompiler executable may not be compatible with your system and may need to be rebuilt.\n" +
"(see InstallationGuide.html, 'Building Native Components').\n\n" +
errorDetail;
Msg.showError(this, null, "Failed to launch Decompiler process", errorDetail);
}
if (exc == null) {
throw new IOException("Decompiler process failed to launch (see log for details)");
}
throw exc;
}
}
}

View file

@ -30,6 +30,14 @@ public class DecompileProcessFactory {
private static final String WIN32_EXECNAME = "decompile.exe";
private static boolean errorDisplayed = false;
private static synchronized boolean getAndSetErrorDisplayed() {
boolean b = errorDisplayed;
if (!b) {
errorDisplayed = true;
}
return b;
}
public synchronized static DecompileProcess get() {
getExePath();
@ -59,9 +67,8 @@ public class DecompileProcessFactory {
exepath = file.getAbsolutePath();
}
catch (OSFileNotFoundException e) {
if (!errorDisplayed) {
errorDisplayed = true;
Msg.showError(DecompileProcessFactory.class, null, "Decompiler missing",
if (!getAndSetErrorDisplayed()) {
Msg.showError(DecompileProcessFactory.class, null, "Decompiler Not Found",
e.getMessage());
}
}

View file

@ -196,6 +196,12 @@ public class GnuDemangler implements Demangler {
// This is the current list of known demangler start patterns. Add to this list if we
// find any other known GNU start patterns.
if (mangled.startsWith(GLOBAL_PREFIX)) {
int index = mangled.indexOf("_Z");
if (index > 0) {
return false;
}
}
if (mangled.startsWith("_Z")) {
return false;
}

View file

@ -18,6 +18,7 @@ package ghidra.app.util.demangler.gnu;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
@ -25,6 +26,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.*;
import ghidra.util.Msg;
/**
* A class that allows for the reuse of native demangler executable processes. This class will
@ -37,6 +39,16 @@ public class GnuDemanglerNativeProcess {
private static final String DEFAULT_NATIVE_OPTIONS = "";
private static final Map<String, GnuDemanglerNativeProcess> processesByName =
new HashMap<>();
private static boolean errorDisplayed = false;
private static synchronized boolean getAndSetErrorDisplayed() {
boolean b = errorDisplayed;
if (!b) {
errorDisplayed = true;
}
return b;
}
private String applicationName;
private String options;
@ -153,16 +165,51 @@ public class GnuDemanglerNativeProcess {
private void createProcess() throws IOException {
String[] command = buildCommand();
process = Runtime.getRuntime().exec(command);
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
reader = new BufferedReader(new InputStreamReader(in));
writer = new PrintWriter(out);
IOException exc = null;
String err = "";
isDisposed = true;
try {
process = Runtime.getRuntime().exec(command);
// Give process time to load and report possible error
process.waitFor(200, TimeUnit.MILLISECONDS);
InputStream in = process.getInputStream();
OutputStream out = process.getOutputStream();
reader = new BufferedReader(new InputStreamReader(in));
writer = new PrintWriter(out);
isDisposed = !process.isAlive();
if (isDisposed) {
err = new String(process.getErrorStream().readAllBytes());
process.destroy();
process = null;
}
}
catch (IOException e) {
exc = e;
}
catch (InterruptedException e) {
// ignore
}
finally {
if (isDisposed) {
if (!getAndSetErrorDisplayed()) {
String errorDetail = err;
if (exc != null) {
errorDetail = exc.getMessage() + "\n" + errorDetail;
}
errorDetail = "GNU Demangler executable may not be compatible with your system and may need to be rebuilt.\n" +
"(see InstallationGuide.html, 'Building Native Components').\n\n" +
errorDetail;
Msg.showError(this, null, "Failed to launch GNU Demangler process", errorDetail);
}
if (exc == null) {
throw new IOException("GNU Demangler process failed to launch (see log for details)");
}
throw exc;
}
}
checkForError(command);
isDisposed = false;
String key = getKey(applicationName, options);
processesByName.put(key, this);
}