From 6cc519f2aa5b7635e69f25d4188e304adcd143eb Mon Sep 17 00:00:00 2001 From: Dan <46821332+nsadeveloper789@users.noreply.github.com> Date: Mon, 20 Sep 2021 10:28:11 -0400 Subject: [PATCH] GP-1309: Making GDB manager's line separator configurable . --- .../gdb/GdbInJvmDebuggerModelFactory.java | 2 ++ .../gdb/GdbOverSshDebuggerModelFactory.java | 20 ++++++++++++ .../gadp/GdbLocalDebuggerModelFactory.java | 2 ++ .../java/agent/gdb/manager/GdbManager.java | 31 +++++++++++++++++++ .../gdb/manager/impl/GdbManagerImpl.java | 22 ++++++++++--- .../agent/gdb/model/impl/GdbModelImpl.java | 8 +++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java index f7c949b86c..45bad556f4 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbInJvmDebuggerModelFactory.java @@ -44,6 +44,8 @@ public class GdbInJvmDebuggerModelFactory implements DebuggerModelFactory { public final Property useExistingOption = Property.fromAccessors(boolean.class, this::isUseExisting, this::setUseExisting); + // TODO: newLine option? + @Override public CompletableFuture build() { // TODO: Choose Linux or Windows pty based on host OS diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbOverSshDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbOverSshDebuggerModelFactory.java index fe47af9cf3..a2c1a31d0b 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbOverSshDebuggerModelFactory.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/GdbOverSshDebuggerModelFactory.java @@ -58,6 +58,12 @@ public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory { public final Property keyFileOption = Property.fromAccessors(String.class, this::getKeyFile, this::setKeyFile); + // Always default to false, despite local system, because remote is likely Linux. + private boolean useCrlf = false; + @FactoryOption("Use DOS line endings (unchecked for UNIX)") + public final Property crlfNewLineOption = + Property.fromAccessors(Boolean.class, this::isUseCrlf, this::setUseCrlf); + @Override public CompletableFuture build() { return CompletableFuture.supplyAsync(() -> { @@ -68,6 +74,12 @@ public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory { factory.setUsername(username); return new GdbModelImpl(factory); }).thenCompose(model -> { + if (useCrlf) { + model.setDosNewLine(); + } + else { + model.setUnixNewLine(); + } return model.startGDB(existing ? null : gdbCmd, new String[] {}).thenApply(__ -> model); }); } @@ -125,4 +137,12 @@ public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory { public void setKeyFile(String keyFile) { this.keyFile = keyFile; } + + public boolean isUseCrlf() { + return useCrlf; + } + + public void setUseCrlf(boolean useCrlf) { + this.useCrlf = useCrlf; + } } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java index 5aec358bb6..d44496fb30 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/gadp/GdbLocalDebuggerModelFactory.java @@ -41,6 +41,8 @@ public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModel public final Property useExistingOption = Property.fromAccessors(boolean.class, this::isUseExisting, this::setUseExisting); + // TODO: newLine option? + @Override public boolean isCompatible() { // TODO: Could potentially support GDB on Windows, but the pty thing would need porting. diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java index 070114bf03..57973b2cfc 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/GdbManager.java @@ -107,6 +107,37 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions { return new GdbManagerImpl(ptyFactory); } + /** + * Set the line terminator (separator) used to serialize commands to GDB + * + *

+ * Because the manager may be communicating to GDB running remotely, possibly on another + * platform, it may be necessary to customize the line terminator. The manager will default to + * the line terminator used by the local system, i.e., {@link System#lineSeparator()}. + * + *

+ * While permitted, it is not advisable to modify this parameter while the manager is running. + * Chances are, if this was mis-configured, the manager and session are hopelessly out of sync. + * Start a new properly configured session instead. + * + * @param newLine the line separator to use + */ + public void setNewLine(String newLine); + + /** + * Set to UNIX-style (CR) line terminator + */ + default void setUnixNewLine() { + setNewLine("\n"); + } + + /** + * Set to DOS-style (CRLF) line terminator + */ + default void setDosNewLine() { + setNewLine("\r\n"); + } + /** * Launch GDB * diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java index f4e261450b..9e8a361d1c 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/manager/impl/GdbManagerImpl.java @@ -45,6 +45,7 @@ import ghidra.dbg.util.PrefixMap; import ghidra.framework.Application; import ghidra.lifecycle.Internal; import ghidra.util.Msg; +import ghidra.util.SystemUtilities; import ghidra.util.datastruct.ListenerSet; import sun.misc.Signal; import sun.misc.SignalHandler; @@ -75,7 +76,7 @@ public class GdbManagerImpl implements GdbManager { } private static final boolean LOG_IO = - Boolean.getBoolean("agent.gdb.manager.log"); + Boolean.getBoolean("agent.gdb.manager.log") || SystemUtilities.isInDevelopmentMode(); private static PrintWriter DBG_LOG = null; private static final String PROMPT_GDB = "(gdb)"; public static final int INTERRUPT_MAX_RETRIES = 3; @@ -158,6 +159,8 @@ public class GdbManagerImpl implements GdbManager { private PtyThread cliThread; private PtyThread mi2Thread; + private String newLine = System.lineSeparator(); + private final AsyncLock cmdLock = new AsyncLock(); private final AtomicReference cmdLockHold = new AtomicReference<>(null); private ExecutorService executor; @@ -205,8 +208,11 @@ public class GdbManagerImpl implements GdbManager { try { File userSettings = Application.getUserSettingsDirectory(); File logFile = new File(userSettings, "GDB.log"); - if (!logFile.canWrite()) { - throw new AssertionError(logFile.getPath() + " appears to be unwritable"); + try { + logFile.createNewFile(); + } + catch (Exception e) { + throw new AssertionError(logFile.getPath() + " appears to be unwritable", e); } DBG_LOG = new PrintWriter(new FileOutputStream(logFile)); } @@ -559,6 +565,11 @@ public class GdbManagerImpl implements GdbManager { }); } + @Override + public void setNewLine(String newLine) { + this.newLine = newLine; + } + @Override public void start(String gdbCmd, String... args) throws IOException { List fullargs = new ArrayList<>(); @@ -592,7 +603,8 @@ public class GdbManagerImpl implements GdbManager { cliThread = iniThread; cliThread.setName("GDB Read CLI"); - cliThread.writer.println("new-ui mi2 " + mi2Pty.getChild().nullSession()); + cliThread.writer + .print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine); cliThread.writer.flush(); mi2Thread = new PtyThread(mi2Pty, Channel.STDOUT, Interpreter.MI2); @@ -743,7 +755,7 @@ public class GdbManagerImpl implements GdbManager { Interpreter interpreter = cmd.getInterpreter(); PrintWriter wr = getWriter(interpreter); //Msg.debug(this, "STDIN: " + text); - wr.println(text); + wr.print(text + newLine); wr.flush(); if (LOG_IO) { DBG_LOG.println(">" + interpreter + ": " + text); diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java index 81cd9e5325..6304afa018 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImpl.java @@ -131,6 +131,14 @@ public class GdbModelImpl extends AbstractDebuggerObjectModel { } } + public void setUnixNewLine() { + gdb.setUnixNewLine(); + } + + public void setDosNewLine() { + gdb.setDosNewLine(); + } + public CompletableFuture startGDB(String gdbCmd, String[] args) { try { gdb.start(gdbCmd, args);