mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-26 06:02:29 +00:00
GP-445: Relieving GDB agent of its need for new-ui.
This commit is contained in:
parent
18ef51669a
commit
77894a0268
@ -22,6 +22,7 @@ import java.util.List;
|
||||
import agent.gdb.manager.GdbManager;
|
||||
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||
import ghidra.dbg.util.ShellUtils;
|
||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
||||
|
||||
@FactoryDescription( //
|
||||
@ -31,8 +32,12 @@ import ghidra.util.classfinder.ExtensionPointProperties;
|
||||
@ExtensionPointProperties(priority = 100)
|
||||
public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
||||
public static boolean checkGdbPresent(String gdbCmd) {
|
||||
List<String> args = ShellUtils.parseArgs(gdbCmd);
|
||||
if (args.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(gdbCmd, "--version");
|
||||
ProcessBuilder builder = new ProcessBuilder(args.get(0), "--version");
|
||||
builder.redirectError(Redirect.INHERIT);
|
||||
builder.redirectOutput(Redirect.INHERIT);
|
||||
@SuppressWarnings("unused")
|
||||
@ -92,13 +97,17 @@ public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModel
|
||||
|
||||
@Override
|
||||
protected void completeCommandLine(List<String> cmd) {
|
||||
List<String> gdbCmdLine = ShellUtils.parseArgs(gdbCmd);
|
||||
cmd.add(GdbGadpServer.class.getCanonicalName());
|
||||
// TODO: Option for additional GDB command-line parameters
|
||||
if (!existing && gdbCmdLine.size() >= 2) {
|
||||
cmd.addAll(gdbCmdLine.subList(1, gdbCmdLine.size()));
|
||||
}
|
||||
cmd.add("--gadp-args");
|
||||
cmd.addAll(List.of("-H", host));
|
||||
cmd.addAll(List.of("-p", Integer.toString(port))); // Available ephemeral port
|
||||
if (!existing) {
|
||||
cmd.addAll(List.of("-g", gdbCmd));
|
||||
if (!existing && gdbCmdLine.size() >= 1) {
|
||||
cmd.add("-g");
|
||||
cmd.add(gdbCmdLine.get(0));
|
||||
}
|
||||
else {
|
||||
cmd.add("-x");
|
||||
|
@ -72,6 +72,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Just a vanilla demo of the manager
|
||||
*
|
||||
* <p>
|
||||
* This presents the usual GDB CLI, using GDB/MI on the back end. The manager is keeps track of
|
||||
* events; however, in this vanilla front end, nothing consumes them. This also provides a quick
|
||||
* test to ensure the console loop operates correctly, or at least closely enough to actual GDB.
|
||||
@ -100,8 +101,6 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
* @return the manager
|
||||
*/
|
||||
public static GdbManager newInstance() {
|
||||
// TODO: Add parameter and test both?
|
||||
// TODO: Eventually the 'true' variant will be deprecated
|
||||
return new GdbManagerImpl();
|
||||
}
|
||||
|
||||
@ -143,6 +142,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Execute a console loop in this thread
|
||||
*
|
||||
* <p>
|
||||
* Note this does not follow the asynchronous pattern.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
@ -157,6 +157,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Check if GDB is alive
|
||||
*
|
||||
* <p>
|
||||
* Note this is not about the state of inferiors in GDB. If the GDB controlling process is
|
||||
* alive, GDB is alive.
|
||||
*
|
||||
@ -197,6 +198,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Add a listener for target output
|
||||
*
|
||||
* <p>
|
||||
* Note: depending on the target, its output may not be communicated via this listener. Local
|
||||
* targets, e.g., tend to just print output to GDB's controlling TTY. See
|
||||
* {@link GdbInferior#setTty(String)} for a means to more reliably interact with a target's
|
||||
@ -231,6 +233,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get a thread by its GDB-assigned ID
|
||||
*
|
||||
* <p>
|
||||
* GDB numbers its threads using a global counter. These IDs are unrelated to the OS-assigned
|
||||
* TID. This method can retrieve a thread by its ID no matter which inferior it belongs to.
|
||||
*
|
||||
@ -242,6 +245,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get an inferior by its GDB-assigned ID
|
||||
*
|
||||
* <p>
|
||||
* GDB numbers inferiors incrementally. All inferiors and created and destroyed by the user. See
|
||||
* {@link #addInferior()}.
|
||||
*
|
||||
@ -261,6 +265,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get all inferiors known to the manager
|
||||
*
|
||||
* <p>
|
||||
* This does not ask GDB to list its inferiors. Rather it returns a read-only view of the
|
||||
* manager's understanding of the current inferiors based on its tracking of GDB events.
|
||||
*
|
||||
@ -271,6 +276,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get all threads known to the manager
|
||||
*
|
||||
* <p>
|
||||
* This does not ask GDB to lists its known threads. Rather it returns a read-only view of the
|
||||
* manager's understanding of the current threads based on its tracking of GDB events.
|
||||
*
|
||||
@ -281,6 +287,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get all breakpoints known to the manager
|
||||
*
|
||||
* <p>
|
||||
* This does not ask GDB to list its breakpoints. Rather it returns a read-only view of the
|
||||
* manager's understanding of the current breakpoints based on its tracking of GDB events.
|
||||
*
|
||||
@ -291,6 +298,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Send an interrupt to GDB regardless of other queued commands
|
||||
*
|
||||
* <p>
|
||||
* This may be useful if the manager's command queue is stalled because an inferior is running.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
@ -301,6 +309,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Get the state of the GDB session
|
||||
*
|
||||
* <p>
|
||||
* In all-stop mode, if any thread is running, GDB is said to be in the running state and is
|
||||
* unable to process commands. Otherwise, if all threads are stopped, then GDB is said to be in
|
||||
* the stopped state and can accept and process commands. This manager has not been tested in
|
||||
@ -321,6 +330,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Wait for GDB to present a prompt
|
||||
*
|
||||
* <p>
|
||||
* This waits for a prompt from GDB unless the last line printed is already a prompt. This is
|
||||
* generally not necessary following normal commands, but may be necessary after interrupting a
|
||||
* running inferior, or after waiting for an inferior to reach a stopped state.
|
||||
@ -332,6 +342,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* A dummy command which claims as cause a stopped event and waits for the next prompt
|
||||
*
|
||||
* <p>
|
||||
* This is used to squelch normal processing of a stopped event until the next prompt
|
||||
*
|
||||
* @return a future which completes when the "command" has finished execution
|
||||
@ -341,6 +352,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Add an inferior
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command: {@code add-inferior}.
|
||||
*
|
||||
* @return a future which completes with the handle to the new inferior
|
||||
@ -350,8 +362,10 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Remove an inferior
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command: {@code remove-inferior}.
|
||||
*
|
||||
* <p>
|
||||
* Note that unlike the CLI, it is possible to remove the current inferior, in which case, the
|
||||
* lowest-id inferior is selected. Like the CLI, it is not possible to remove an active inferior
|
||||
* or the last inferior.
|
||||
@ -364,6 +378,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Execute an arbitrary CLI command, printing output to the CLI console
|
||||
*
|
||||
* <p>
|
||||
* Note: to ensure a certain thread or inferior has focus for a console command, see
|
||||
* {@link GdbThread#console(String)} and {@link GdbInferior#console(String)}.
|
||||
*
|
||||
@ -375,6 +390,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Execute an arbitrary CLI command, capturing its console output
|
||||
*
|
||||
* <p>
|
||||
* The output will not be printed to the CLI console. To ensure a certain thread or inferior has
|
||||
* focus for a console command, see {@link GdbThread#consoleCapture(String)} and
|
||||
* {@link GdbInferior#consoleCapture(String)}.
|
||||
@ -387,10 +403,12 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Interrupt the GDB session
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to typing Ctrl-C in the CLI. This typically results in the target being
|
||||
* interrupted, either because GDB and the target have the same controlling TTY, or because GDB
|
||||
* will "forward" the interrupt to the target.
|
||||
*
|
||||
* <p>
|
||||
* For whatever reason, interrupting the session does not always reliably interrupt the target.
|
||||
* The manager will send Ctrl-C to the pseudo-terminal up to three times, waiting about 10ms
|
||||
* between each, until GDB issues a stopped event and presents a new prompt.
|
||||
@ -402,6 +420,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* List GDB's inferiors
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command: {@code inferiors}.
|
||||
*
|
||||
* @return a future that completes with a map of inferior IDs to inferior handles
|
||||
@ -411,6 +430,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* List information for all breakpoints
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command {@code info break}.
|
||||
*
|
||||
* @return a future that completes with a list of information for all breakpoints
|
||||
@ -420,6 +440,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Disable the given breakpoints
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command {@code disable breakpoint [NUMBER]}.
|
||||
*
|
||||
* @param numbers the GDB-assigned breakpoint numbers
|
||||
@ -430,6 +451,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Enable the given breakpoints
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command {@code enable breakpoint [NUMBER]}.
|
||||
*
|
||||
* @param numbers the GDB-assigned breakpoint numbers
|
||||
@ -440,6 +462,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Delete a breakpoint
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command {@code delete breakpoint [NUMBER]}.
|
||||
*
|
||||
* @param numbers the GDB-assigned breakpoint numbers
|
||||
@ -457,6 +480,7 @@ public interface GdbManager extends AutoCloseable, GdbBreakpointInsertions {
|
||||
/**
|
||||
* Gather information about the host OS
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to the CLI command: {@code info os [TYPE]}.
|
||||
*
|
||||
* @param type the type of OS information to gather
|
||||
|
@ -27,7 +27,6 @@ import org.python.core.PyDictionary;
|
||||
import org.python.util.InteractiveConsole;
|
||||
|
||||
import agent.gdb.ffi.linux.Pty;
|
||||
import agent.gdb.ffi.linux.PtyMaster;
|
||||
import agent.gdb.manager.*;
|
||||
import agent.gdb.manager.GdbCause.Causes;
|
||||
import agent.gdb.manager.breakpoint.GdbBreakpointInfo;
|
||||
@ -87,6 +86,60 @@ public class GdbManagerImpl implements GdbManager {
|
||||
public static final int INTERRUPT_MAX_RETRIES = 3;
|
||||
public static final int INTERRUPT_RETRY_PERIOD_MILLIS = 100;
|
||||
|
||||
class PtyThread extends Thread {
|
||||
final Pty pty;
|
||||
final BufferedReader reader;
|
||||
final Channel channel;
|
||||
|
||||
Interpreter interpreter;
|
||||
PrintWriter writer;
|
||||
CompletableFuture<Void> hasWriter;
|
||||
|
||||
PtyThread(Pty pty, Channel channel, Interpreter interpreter) {
|
||||
this.pty = pty;
|
||||
this.channel = channel;
|
||||
this.reader =
|
||||
new BufferedReader(new InputStreamReader(pty.getMaster().getInputStream()));
|
||||
this.interpreter = interpreter;
|
||||
hasWriter = new CompletableFuture<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String line;
|
||||
while (isAlive() && null != (line = reader.readLine())) {
|
||||
String l = line;
|
||||
if (interpreter == null) {
|
||||
if (l.startsWith("=") || l.startsWith("~")) {
|
||||
interpreter = Interpreter.MI2;
|
||||
}
|
||||
else {
|
||||
interpreter = Interpreter.CLI;
|
||||
}
|
||||
}
|
||||
if (writer == null) {
|
||||
writer = new PrintWriter(pty.getMaster().getOutputStream());
|
||||
hasWriter.complete(null);
|
||||
}
|
||||
//Msg.debug(this, channel + ": " + line);
|
||||
submit(() -> {
|
||||
if (LOG_IO) {
|
||||
DBG_LOG.println("<" + interpreter + ": " + l);
|
||||
DBG_LOG.flush();
|
||||
}
|
||||
processLine(l, channel, interpreter);
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
terminate();
|
||||
Msg.debug(this, channel + "," + interpreter + " reader exiting because " + e);
|
||||
//throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final AsyncReference<GdbState, GdbCause> state =
|
||||
new AsyncReference<>(GdbState.NOT_STARTED);
|
||||
// A copy of state, which is updated on the eventThread.
|
||||
@ -98,15 +151,12 @@ public class GdbManagerImpl implements GdbManager {
|
||||
private final HandlerMap<GdbEvent<?>, Void, Void> handlerMap = new HandlerMap<>();
|
||||
private final AtomicBoolean exited = new AtomicBoolean(false);
|
||||
|
||||
private Pty cliPty;
|
||||
private Pty mi2Pty;
|
||||
private Process gdb;
|
||||
private Thread gdbWaiter;
|
||||
|
||||
private Thread cliReader;
|
||||
private Thread mi2Reader;
|
||||
private PrintWriter cliWriter;
|
||||
private PrintWriter mi2Writer;
|
||||
private PtyThread iniThread;
|
||||
private PtyThread cliThread;
|
||||
private PtyThread mi2Thread;
|
||||
|
||||
private final AsyncLock cmdLock = new AsyncLock();
|
||||
private final AtomicReference<AsyncLock.Hold> cmdLockHold = new AtomicReference<>(null);
|
||||
@ -500,41 +550,50 @@ public class GdbManagerImpl implements GdbManager {
|
||||
state.set(GdbState.STARTING, Causes.UNCLAIMED);
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
mi2Pty = Pty.openpty();
|
||||
if (gdbCmd != null) {
|
||||
cliPty = Pty.openpty();
|
||||
iniThread = new PtyThread(Pty.openpty(), Channel.STDOUT, null);
|
||||
gdb = iniThread.pty.getSlave().session(fullargs.toArray(new String[] {}), null);
|
||||
iniThread.start();
|
||||
try {
|
||||
gdb = cliPty.getSlave().session(fullargs.toArray(new String[] {}), null);
|
||||
iniThread.hasWriter.get(10, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// TODO: Seems I should declare this, but it makes client code ugly :(
|
||||
throw new RuntimeException(e);
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new IOException("Could not detect GDB's interpreter mode");
|
||||
}
|
||||
switch (iniThread.interpreter) {
|
||||
case CLI:
|
||||
cliThread = iniThread;
|
||||
cliThread.setName("GDB Read CLI");
|
||||
|
||||
PtyMaster cliMaster = cliPty.getMaster();
|
||||
cliReader = new Thread(
|
||||
() -> readStream(cliMaster.getInputStream(), Channel.STDOUT, Interpreter.CLI),
|
||||
"GDB Read CLI");
|
||||
cliReader.start();
|
||||
cliWriter = new PrintWriter(cliMaster.getOutputStream());
|
||||
mi2Thread = new PtyThread(Pty.openpty(), Channel.STDOUT, Interpreter.MI2);
|
||||
mi2Thread.setName("GDB Read MI2");
|
||||
mi2Thread.start();
|
||||
cliThread.writer.println("new-ui mi2 " + mi2Thread.pty.getSlave().getFile());
|
||||
cliThread.writer.flush();
|
||||
try {
|
||||
mi2Thread.hasWriter.get(2, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||
throw new IOException(
|
||||
"Could not obtain GDB/MI2 interpreter. Try " + gdbCmd + " -i mi2");
|
||||
}
|
||||
break;
|
||||
case MI2:
|
||||
mi2Thread = iniThread;
|
||||
mi2Thread.setName("GDB Read MI2");
|
||||
break;
|
||||
}
|
||||
|
||||
gdbWaiter = new Thread(this::waitGdbExit, "GDB WaitExit");
|
||||
gdbWaiter.start();
|
||||
|
||||
cliWriter.println("new-ui mi2 " + mi2Pty.getSlave().getFile());
|
||||
cliWriter.flush();
|
||||
}
|
||||
else {
|
||||
System.out.println(
|
||||
"Agent is waiting for GDB/MI v2 interpreter at " + mi2Pty.getSlave().getFile());
|
||||
mi2Thread = new PtyThread(Pty.openpty(), Channel.STDOUT, Interpreter.MI2);
|
||||
mi2Thread.setName("GDB Read MI2");
|
||||
Msg.info(this, "Agent is waiting for GDB/MI v2 interpreter at " +
|
||||
mi2Thread.pty.getSlave().getFile());
|
||||
mi2Thread.start();
|
||||
}
|
||||
|
||||
PtyMaster mi2Master = mi2Pty.getMaster();
|
||||
mi2Reader = new Thread(
|
||||
() -> readStream(mi2Master.getInputStream(), Channel.STDOUT, Interpreter.MI2),
|
||||
"GDB Read MI2");
|
||||
mi2Reader.start();
|
||||
mi2Writer = new PrintWriter(mi2Master.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -597,22 +656,24 @@ public class GdbManagerImpl implements GdbManager {
|
||||
checkStarted();
|
||||
exited.set(true);
|
||||
executor.shutdownNow();
|
||||
if (cliPty != null) {
|
||||
if (gdbWaiter != null) {
|
||||
gdbWaiter.interrupt();
|
||||
cliReader.interrupt();
|
||||
}
|
||||
mi2Reader.interrupt();
|
||||
if (gdb != null) {
|
||||
gdb.destroyForcibly();
|
||||
}
|
||||
try {
|
||||
if (cliPty != null) {
|
||||
cliPty.close();
|
||||
if (cliThread != null) {
|
||||
cliThread.interrupt();
|
||||
cliThread.pty.close();
|
||||
}
|
||||
if (mi2Thread != null) {
|
||||
mi2Thread.interrupt();
|
||||
mi2Thread.pty.close();
|
||||
}
|
||||
mi2Pty.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
Msg.error(this, "Problem closing PTYs to GDB.");
|
||||
}
|
||||
if (gdb != null) {
|
||||
gdb.destroyForcibly();
|
||||
}
|
||||
cmdLock.dispose("GDB is terminating");
|
||||
state.dispose("GDB is terminating");
|
||||
@ -679,9 +740,9 @@ public class GdbManagerImpl implements GdbManager {
|
||||
protected PrintWriter getWriter(Interpreter interpreter) {
|
||||
switch (interpreter) {
|
||||
case CLI:
|
||||
return cliWriter;
|
||||
return cliThread == null ? null : cliThread.writer;
|
||||
case MI2:
|
||||
return mi2Writer;
|
||||
return mi2Thread == null ? null : mi2Thread.writer;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
@ -790,7 +851,7 @@ public class GdbManagerImpl implements GdbManager {
|
||||
*/
|
||||
protected void processStdOut(GdbConsoleOutputEvent evt, Void v) {
|
||||
String out = evt.getOutput();
|
||||
System.out.print(out);
|
||||
//System.out.print(out);
|
||||
if (!evt.isStolen()) {
|
||||
listenersConsoleOutput.fire.output(Channel.STDOUT, out);
|
||||
}
|
||||
@ -820,7 +881,7 @@ public class GdbManagerImpl implements GdbManager {
|
||||
*/
|
||||
protected void processStdErr(GdbDebugOutputEvent evt, Void v) {
|
||||
String out = evt.getOutput();
|
||||
System.err.print(out);
|
||||
//System.err.print(out);
|
||||
if (!evt.isStolen()) {
|
||||
listenersConsoleOutput.fire.output(Channel.STDERR, out);
|
||||
}
|
||||
@ -1379,14 +1440,16 @@ public class GdbManagerImpl implements GdbManager {
|
||||
public void sendInterruptNow() throws IOException {
|
||||
checkStarted();
|
||||
Msg.info(this, "Interrupting");
|
||||
if (hasCli()) {
|
||||
OutputStream os = cliPty.getMaster().getOutputStream();
|
||||
if (cliThread != null) {
|
||||
OutputStream os = cliThread.pty.getMaster().getOutputStream();
|
||||
os.write(3);
|
||||
os.flush();
|
||||
}
|
||||
if (mi2Thread != null) {
|
||||
OutputStream os = mi2Thread.pty.getMaster().getOutputStream();
|
||||
os.write(3);
|
||||
os.flush();
|
||||
}
|
||||
OutputStream os = mi2Pty.getMaster().getOutputStream();
|
||||
os.write(3);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1488,11 +1551,11 @@ public class GdbManagerImpl implements GdbManager {
|
||||
|
||||
@Override
|
||||
public String getMi2PtyName() {
|
||||
return mi2Pty.getSlave().getFile().getAbsolutePath();
|
||||
return mi2Thread.pty.getSlave().getFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
public boolean hasCli() {
|
||||
return cliWriter != null;
|
||||
return cliThread != null && cliThread.pty != null;
|
||||
}
|
||||
|
||||
public Interpreter getRunningInterpreter() {
|
||||
|
@ -23,6 +23,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import agent.gdb.manager.*;
|
||||
import agent.gdb.manager.impl.cmd.GdbCommandError;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerModelClosedReason;
|
||||
import ghidra.dbg.agent.AbstractDebuggerObjectModel;
|
||||
import ghidra.dbg.error.DebuggerUserException;
|
||||
import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibility;
|
||||
@ -74,6 +75,7 @@ public class GdbModelImpl extends AbstractDebuggerObjectModel {
|
||||
}
|
||||
|
||||
// TODO: Place make this a model method?
|
||||
@Override
|
||||
public AddressFactory getAddressFactory() {
|
||||
return addressFactory;
|
||||
}
|
||||
@ -122,6 +124,7 @@ public class GdbModelImpl extends AbstractDebuggerObjectModel {
|
||||
}
|
||||
|
||||
public void terminate() throws IOException {
|
||||
listeners.fire.modelClosed(DebuggerModelClosedReason.NORMAL);
|
||||
session.invalidateSubtree("GDB is terminating");
|
||||
gdb.terminate();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
|
||||
public class SpawnedGdbManagerTest extends AbstractGdbManagerTest {
|
||||
public class SpawnedCliGdbManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
@ -0,0 +1,34 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package agent.gdb.manager.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
|
||||
public class SpawnedMi2Gdb7Dot6Dot1ManagerTest extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start("/opt/gdb-7.6.1/bin/gdb", "-i", "mi2");
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package agent.gdb.manager.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.gdb.manager.GdbManager;
|
||||
|
||||
public class SpawnedMi2GdbManagerTest2 extends AbstractGdbManagerTest {
|
||||
@Override
|
||||
protected CompletableFuture<Void> startManager(GdbManager manager) {
|
||||
try {
|
||||
manager.start(GdbManager.DEFAULT_GDB_CMD, "-i", "mi2");
|
||||
return manager.runRC();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,14 +20,15 @@ import java.net.SocketAddress;
|
||||
import java.nio.channels.AsynchronousSocketChannel;
|
||||
|
||||
import ghidra.comm.service.AbstractAsyncServer;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.*;
|
||||
import ghidra.dbg.gadp.error.GadpErrorException;
|
||||
import ghidra.dbg.gadp.protocol.Gadp;
|
||||
import ghidra.dbg.gadp.protocol.Gadp.ErrorCode;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public abstract class AbstractGadpServer
|
||||
extends AbstractAsyncServer<AbstractGadpServer, GadpClientHandler> {
|
||||
extends AbstractAsyncServer<AbstractGadpServer, GadpClientHandler>
|
||||
implements DebuggerModelListener {
|
||||
public static final String LISTENING_ON = "GADP Server listening on ";
|
||||
|
||||
protected final DebuggerObjectModel model;
|
||||
@ -36,6 +37,8 @@ public abstract class AbstractGadpServer
|
||||
super(addr);
|
||||
this.model = model;
|
||||
System.out.println(LISTENING_ON + getLocalAddress());
|
||||
|
||||
model.addModelListener(this);
|
||||
}
|
||||
|
||||
public DebuggerObjectModel getModel() {
|
||||
@ -63,4 +66,10 @@ public abstract class AbstractGadpServer
|
||||
// Note, +1 accounted for in how Ghidra AddressRanges work (inclusive of end)
|
||||
return new AddressRangeImpl(min, min.add(Integer.toUnsignedLong(range.getExtend())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modelClosed(DebuggerModelClosedReason reason) {
|
||||
System.err.println("Model closed: " + reason);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package ghidra.dbg;
|
||||
* A reason given for a closed connection
|
||||
*/
|
||||
public interface DebuggerModelClosedReason {
|
||||
DebuggerModelClosedReason NORMAL = DebuggerNormalModelClosedReason.INSTANCE;
|
||||
DebuggerModelClosedReason NORMAL = DebuggerNormalModelClosedReason.NORMAL;
|
||||
|
||||
static DebuggerModelClosedReason normal() {
|
||||
return NORMAL;
|
||||
|
@ -16,7 +16,7 @@
|
||||
package ghidra.dbg;
|
||||
|
||||
enum DebuggerNormalModelClosedReason implements DebuggerModelClosedReason {
|
||||
INSTANCE;
|
||||
NORMAL;
|
||||
|
||||
@Override
|
||||
public boolean hasException() {
|
||||
|
Loading…
Reference in New Issue
Block a user