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 16a767f994..81cd9e5325 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 @@ -60,7 +60,6 @@ public class GdbModelImpl extends AbstractDebuggerObjectModel { new DefaultAddressFactory(new AddressSpace[] { space }); protected final GdbManager gdb; - protected boolean noStarti = false; protected final GdbModelTargetSession session; protected final CompletableFuture completedSession; diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java index 7be1eaa9fa..1fd72e4ee9 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelImplUtils.java @@ -25,27 +25,13 @@ import ghidra.dbg.util.ShellUtils; public enum GdbModelImplUtils { ; public static CompletableFuture launch(GdbModelImpl impl, GdbInferior inferior, - List args) { + List args, boolean useStarti) { // Queue all these up to avoid other commands getting between. CompletableFuture feas = inferior.fileExecAndSymbols(args.get(0)); CompletableFuture sargs = inferior.setVar("args", ShellUtils.generateLine(args.subList(1, args.size()))); - CompletableFuture both = CompletableFuture.allOf(feas, sargs); - if (impl.noStarti) { - return both.thenCombine(inferior.start(), (__, t) -> t); - } - else { - return both.thenCombine(inferior.starti(), (__, t) -> t).exceptionally(ex -> { - impl.noStarti = true; - // TODO: Check that the error is actually Undefined command: "starti" - return null; - }).thenCompose(thread -> { - if (thread == null) { - return inferior.start(); - } - return CompletableFuture.completedStage(thread); - }); - } + CompletableFuture start = useStarti ? inferior.starti() : inferior.start(); + return feas.thenCombine(sargs, (v1, v2) -> v2).thenCombine(start, (v, t) -> t); } public static V noDupMerge(V first, V second) { diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java index 81680f9011..799c53938a 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetInferior.java @@ -26,7 +26,8 @@ import ghidra.async.AsyncFence; import ghidra.dbg.agent.DefaultTargetObject; import ghidra.dbg.target.*; import ghidra.dbg.target.TargetEventScope.TargetEventType; -import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.schema.*; import ghidra.dbg.util.PathUtils; import ghidra.lifecycle.Internal; @@ -40,11 +41,19 @@ import ghidra.lifecycle.Internal; public class GdbModelTargetInferior extends DefaultTargetObject implements TargetProcess, TargetAggregate, TargetExecutionStateful, TargetAttacher, - TargetDeletable, TargetDetachable, TargetKillable, TargetCmdLineLauncher, TargetResumable, + TargetDeletable, TargetDetachable, TargetKillable, TargetLauncher, TargetResumable, TargetSteppable, GdbModelSelectableObject { public static final String EXIT_CODE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_code"; + public static final ParameterDescription PARAMETER_STARTI = + ParameterDescription.create(Boolean.class, "starti", false, false, + "Break on first instruction (use starti)", + "true to use starti, false to use start. Requires GDB 8.1 or later."); + + public static final TargetParameterMap PARAMETERS = + TargetMethod.makeParameters(TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS, PARAMETER_STARTI); + protected static final TargetAttachKindSet SUPPORTED_KINDS = TargetAttachKindSet.of( // TargetAttachKind.BY_OBJECT_REF, TargetAttachKind.BY_ID); @@ -108,12 +117,23 @@ public class GdbModelTargetInferior Map.of( // STATE_ATTRIBUTE_NAME, state = realState, // DISPLAY_ATTRIBUTE_NAME, updateDisplay(), // - TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetCmdLineLauncher.PARAMETERS, // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS, // SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, // SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, GdbModelTargetThread.SUPPORTED_KINDS), // "Initialized"); } + protected TargetParameterMap computeParams() { + return TargetMethod.makeParameters( + TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS, + PARAMETER_STARTI); + } + + @Override + public TargetParameterMap getParameters() { + return PARAMETERS; + } + @TargetAttributeType(name = GdbModelTargetEnvironment.NAME, required = true, fixed = true) public GdbModelTargetEnvironment getEnvironment() { return environment; @@ -150,9 +170,12 @@ public class GdbModelTargetInferior } @Override - public CompletableFuture launch(List args) { - return impl - .gateFuture(GdbModelImplUtils.launch(impl, inferior, args).thenApply(__ -> null)); + public CompletableFuture launch(Map args) { + List cmdLineArgs = + CmdLineParser.tokenize(TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.get(args)); + Boolean useStarti = PARAMETER_STARTI.get(args); + return impl.gateFuture( + GdbModelImplUtils.launch(impl, inferior, cmdLineArgs, useStarti).thenApply(__ -> null)); } @Override diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java index 26c2b37241..6df63f0918 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetSession.java @@ -27,7 +27,6 @@ import ghidra.async.AsyncUtils; import ghidra.dbg.agent.DefaultTargetModelRoot; import ghidra.dbg.error.DebuggerIllegalArgumentException; import ghidra.dbg.target.*; -import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; import ghidra.dbg.target.schema.*; import ghidra.dbg.util.PathUtils; import ghidra.util.Msg; @@ -48,7 +47,7 @@ import ghidra.util.Msg; @TargetAttributeType(type = Void.class) }) public class GdbModelTargetSession extends DefaultTargetModelRoot implements TargetAccessConditioned, TargetAttacher, TargetInterpreter, TargetInterruptible, - TargetCmdLineLauncher, TargetActiveScope, TargetEventScope, TargetFocusScope, + TargetLauncher, TargetActiveScope, TargetEventScope, TargetFocusScope, GdbConsoleOutputListener, GdbEventsListenerAdapter { protected static final String GDB_PROMPT = "(gdb)"; @@ -205,9 +204,12 @@ public class GdbModelTargetSession extends DefaultTargetModelRoot } @Override - public CompletableFuture launch(List args) { + public CompletableFuture launch(Map args) { + List cmdLineArgs = + CmdLineParser.tokenize(TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.get(args)); + Boolean useStarti = GdbModelTargetInferior.PARAMETER_STARTI.get(args); return impl.gateFuture(impl.gdb.availableInferior().thenCompose(inf -> { - return GdbModelImplUtils.launch(impl, inf, args); + return GdbModelImplUtils.launch(impl, inf, cmdLineArgs, useStarti); }).thenApply(__ -> null)); } diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbLauncherTest.java b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbLauncherTest.java index c3d2ad8a95..b64b95884c 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbLauncherTest.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/test/java/agent/gdb/model/AbstractModelForGdbLauncherTest.java @@ -18,10 +18,8 @@ package agent.gdb.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.util.Map; - +import agent.gdb.model.impl.GdbModelTargetInferior; import ghidra.dbg.target.TargetEnvironment; -import ghidra.dbg.target.TargetMethod.ParameterDescription; import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.test.AbstractDebuggerModelLauncherTest; @@ -34,9 +32,7 @@ public abstract class AbstractModelForGdbLauncherTest extends AbstractDebuggerMo @Override public TargetParameterMap getExpectedLauncherParameters() { - return TargetParameterMap.copyOf(Map.ofEntries( - Map.entry("args", ParameterDescription.create(String.class, "args", true, "", - "Command Line", "space-separated command-line arguments")))); + return GdbModelTargetInferior.PARAMETERS; } @Override