mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-23 21:50:49 +00:00
GP-638: Implementing multi-line commands for dbgeng
This commit is contained in:
parent
2d0743b785
commit
1695e3ae64
|
@ -15,17 +15,15 @@
|
|||
*/
|
||||
package agent.dbgeng.dbgeng;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The interface for receiving input callbacks via {@code IDebugInputCallbacks} or a newer variant.
|
||||
*
|
||||
* <p>
|
||||
* Note: The wrapper implementation will select the appropriate native interface version.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DebugInputCallbacks {
|
||||
CompletableFuture<String> startInput();
|
||||
|
||||
public void startInput(long bufferSize);
|
||||
|
||||
default void endInput() {
|
||||
// Optional implementation
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
*/
|
||||
package agent.dbgeng.impl.dbgeng.io;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.platform.win32.Guid.REFIID;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||
|
@ -30,15 +26,12 @@ import com.sun.jna.ptr.PointerByReference;
|
|||
import agent.dbgeng.dbgeng.DebugInputCallbacks;
|
||||
import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1;
|
||||
import agent.dbgeng.jna.dbgeng.io.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCallbacks {
|
||||
private final DebugClientImpl1 client;
|
||||
private final DebugInputCallbacks cb;
|
||||
private ListenerIDebugInputCallbacks listener;
|
||||
|
||||
private final Set<CompletableFuture<String>> futures = new HashSet<>(); // TODO: Just one?
|
||||
|
||||
public WrapCallbackIDebugInputCallbacks(DebugClientImpl1 client, DebugInputCallbacks cb) {
|
||||
this.client = client;
|
||||
this.cb = cb;
|
||||
|
@ -82,23 +75,7 @@ public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCall
|
|||
@Override
|
||||
public HRESULT StartInput(ULONG BufferSize) {
|
||||
try {
|
||||
CompletableFuture<String> future = cb.startInput();
|
||||
if (future == null) {
|
||||
return WinError.S_OK;
|
||||
}
|
||||
future.handle((input, exc) -> {
|
||||
if (exc == null) {
|
||||
client.getControl().returnInput(input);
|
||||
}
|
||||
else if (exc instanceof CancelledException) {
|
||||
// Normal if another client provides input
|
||||
}
|
||||
else {
|
||||
client.getControl().errln("ERROR getting input: " + exc.getMessage());
|
||||
}
|
||||
futures.remove(future);
|
||||
return null;
|
||||
});
|
||||
cb.startInput(BufferSize.longValue());
|
||||
return WinError.S_OK;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
|
@ -109,9 +86,6 @@ public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCall
|
|||
@Override
|
||||
public HRESULT EndInput() {
|
||||
try {
|
||||
for (CompletableFuture<String> future : futures) {
|
||||
future.cancel(true);
|
||||
}
|
||||
cb.endInput();
|
||||
return WinError.S_OK;
|
||||
}
|
||||
|
|
|
@ -244,4 +244,10 @@ public interface DbgEventsListener {
|
|||
* @param mask class of output
|
||||
*/
|
||||
void consoleOutput(String output, int mask);
|
||||
|
||||
/**
|
||||
* @param prompt for console output
|
||||
*/
|
||||
void promptChanged(String prompt);
|
||||
|
||||
}
|
||||
|
|
|
@ -142,4 +142,10 @@ public interface DbgEventsListenerAdapter extends DbgEventsListener {
|
|||
public default void consoleOutput(String output, int mask) {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
@Override
|
||||
public default void promptChanged(String prompt) {
|
||||
// Extension point
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* 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.dbgeng.manager.impl;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugInputCallbacks;
|
||||
|
||||
public class DbgDebugInputCallbacks implements DebugInputCallbacks {
|
||||
private DbgManagerImpl manager;
|
||||
|
||||
public DbgDebugInputCallbacks(DbgManagerImpl manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startInput(long bufsize) {
|
||||
manager.getEventListeners().fire.promptChanged(">>>");
|
||||
CompletableFuture<String> cf = new CompletableFuture<String>();
|
||||
try {
|
||||
manager.setContinuation(cf);
|
||||
manager.getControl().returnInput(cf.get());
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -43,6 +43,7 @@ import agent.dbgeng.manager.cmd.*;
|
|||
import agent.dbgeng.manager.evt.*;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetActiveScope;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetInterpreter;
|
||||
import ghidra.async.*;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
@ -110,6 +111,7 @@ public class DbgManagerImpl implements DbgManager {
|
|||
private DbgThread eventThread;
|
||||
private volatile boolean waiting = false;
|
||||
private boolean kernelMode = false;
|
||||
private CompletableFuture<String> continuation;
|
||||
|
||||
/**
|
||||
* Instantiate a new manager
|
||||
|
@ -370,6 +372,7 @@ public class DbgManagerImpl implements DbgManager {
|
|||
status = dbgeng.getControl().getExecutionStatus();
|
||||
dbgeng.setOutputCallbacks(new DbgDebugOutputCallbacks(this));
|
||||
dbgeng.setEventCallbacks(new DbgDebugEventCallbacksAdapter(this));
|
||||
dbgeng.setInputCallbacks(new DbgDebugInputCallbacks(this));
|
||||
dbgeng.flushCallbacks();
|
||||
|
||||
if (!create) {
|
||||
|
@ -1429,6 +1432,13 @@ public class DbgManagerImpl implements DbgManager {
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Void> console(String command) {
|
||||
if (continuation != null) {
|
||||
String prompt = command.equals("") ? DbgModelTargetInterpreter.DBG_PROMPT : ">>>";
|
||||
getEventListeners().fire.promptChanged(prompt);
|
||||
continuation.complete(command);
|
||||
setContinuation(null);
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return execute(
|
||||
new DbgConsoleExecCommand(this, command, DbgConsoleExecCommand.Output.CONSOLE))
|
||||
.thenApply(e -> null);
|
||||
|
@ -1498,4 +1508,7 @@ public class DbgManagerImpl implements DbgManager {
|
|||
this.kernelMode = kernelMode;
|
||||
}
|
||||
|
||||
public void setContinuation(CompletableFuture<String> continuation) {
|
||||
this.continuation = continuation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import ghidra.dbg.target.TargetInterpreter;
|
|||
*/
|
||||
public interface DbgModelTargetInterpreter extends DbgModelTargetObject, TargetInterpreter {
|
||||
|
||||
public static final String DBG_PROMPT = "(kd)";
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> execute(String cmd) {
|
||||
return getModel().gateFuture(getManager().console(cmd));
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package agent.dbgeng.model.iface2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugClient.DebugOutputFlags;
|
||||
|
@ -70,6 +72,13 @@ public interface DbgModelTargetSession extends //
|
|||
getListeners().fire.consoleOutput(getProxy(), chan, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public default void promptChanged(String prompt) {
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
PROMPT_ATTRIBUTE_NAME, prompt //
|
||||
), "Refreshed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> setActive() {
|
||||
DbgManagerImpl manager = getManager();
|
||||
|
|
|
@ -22,15 +22,26 @@ import java.util.concurrent.CompletableFuture;
|
|||
import agent.dbgeng.dbgeng.DebugSessionId;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.model.iface1.DbgModelSelectableObject;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetInterpreter;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetProcessContainer;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSession;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "Session", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Attributes", type = DbgModelTargetSessionAttributesImpl.class, fixed = true),
|
||||
@TargetAttributeType(name = "Processes", type = DbgModelTargetProcessContainerImpl.class, required = true, fixed = true),
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Session",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class) },
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Attributes",
|
||||
type = DbgModelTargetSessionAttributesImpl.class,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Processes",
|
||||
type = DbgModelTargetProcessContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSession {
|
||||
|
@ -71,7 +82,7 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
|
|||
processes //
|
||||
), Map.of( //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, accessible, //
|
||||
PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, //
|
||||
PROMPT_ATTRIBUTE_NAME, DbgModelTargetInterpreter.DBG_PROMPT, //
|
||||
STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE //
|
||||
), "Initialized");
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user