mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-13 06:00:17 +00:00
GP-739,741,742,666,681,823: combine listener interfaces, remove attribute-specific callbacks, update-mode schema, recorder refactor, model testing, and double-launch fix
This commit is contained in:
parent
e83a893493
commit
015858b5d3
@ -4,5 +4,6 @@
|
||||
.project||NONE||reviewed||END|
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
hs_err_pid5696.mdmp||GHIDRA||||END|
|
||||
src/javaprovider/def/javaprovider.def||GHIDRA||||END|
|
||||
src/javaprovider/rc/javaprovider.rc||GHIDRA||||END|
|
||||
|
@ -19,6 +19,7 @@ import static agent.dbgeng.testutil.DummyProc.runProc;
|
||||
import static ghidra.lifecycle.Unfinished.TODO;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.*;
|
||||
@ -31,22 +32,17 @@ import agent.dbgeng.dbgeng.DbgEngTest;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetLauncher;
|
||||
import agent.dbgeng.testutil.DummyProc;
|
||||
import ghidra.async.*;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.*;
|
||||
import ghidra.dbg.DebugModelConventions.AllRequiredAccess;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.attributes.TargetObjectList;
|
||||
import ghidra.dbg.error.DebuggerModelNoSuchPathException;
|
||||
import ghidra.dbg.error.DebuggerModelTypeException;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet;
|
||||
import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.target.TargetConsole.Channel;
|
||||
import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener;
|
||||
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
|
||||
import ghidra.dbg.target.TargetObject.TargetObjectListener;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.dbg.target.schema.XmlSchemaContext;
|
||||
import ghidra.dbg.util.AllTargetObjectListenerAdapter;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
@ -623,12 +619,13 @@ public abstract class AbstractModelForDbgTest
|
||||
AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
|
||||
AsyncReference<String, Void> lastOut = new AsyncReference<>();
|
||||
|
||||
AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() {
|
||||
DebuggerModelListener l = new DebuggerModelListener() {
|
||||
@Override
|
||||
public void consoleOutput(TargetObject interpreter, Channel channel,
|
||||
String out) {
|
||||
Msg.debug(this, "Got " + channel + " output: " + out);
|
||||
lastOut.set(out, null);
|
||||
byte[] out) {
|
||||
String str = new String(out);
|
||||
Msg.debug(this, "Got " + channel + " output: " + str);
|
||||
lastOut.set(str, null);
|
||||
}
|
||||
};
|
||||
|
||||
@ -663,15 +660,16 @@ public abstract class AbstractModelForDbgTest
|
||||
AtomicReference<TargetObject> root = new AtomicReference<>();
|
||||
AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
|
||||
|
||||
AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() {
|
||||
DebuggerModelListener l = new DebuggerModelListener() {
|
||||
@Override
|
||||
public void consoleOutput(TargetObject interpreter, Channel channel,
|
||||
String out) {
|
||||
Msg.debug(this, "Got " + channel + " output: " + out);
|
||||
if (!out.contains("test")) {
|
||||
String str = new String(out);
|
||||
Msg.debug(this, "Got " + channel + " output: " + str);
|
||||
if (!str.contains("test")) {
|
||||
return;
|
||||
}
|
||||
offThread.catching(() -> fail("Unexpected output:" + out));
|
||||
offThread.catching(() -> fail("Unexpected output:" + str));
|
||||
}
|
||||
};
|
||||
|
||||
@ -706,7 +704,7 @@ public abstract class AbstractModelForDbgTest
|
||||
|
||||
AtomicReference<TargetObject> root = new AtomicReference<>();
|
||||
AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
|
||||
|
||||
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
m.init().handle(seq::next);
|
||||
@ -721,7 +719,7 @@ public abstract class AbstractModelForDbgTest
|
||||
access.get().waitValue(true).handle(seq::next);
|
||||
}).then(seq -> {
|
||||
Msg.debug(this, "Finding breakpoint container...");
|
||||
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get())
|
||||
DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
|
||||
.handle(seq::next);
|
||||
}, breaks).then(seq -> {
|
||||
Msg.debug(this, "Got: " + breaks);
|
||||
@ -744,7 +742,7 @@ public abstract class AbstractModelForDbgTest
|
||||
AtomicReference<TargetObject> root = new AtomicReference<>();
|
||||
AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
|
||||
AtomicReference<DbgModelTargetLauncher> launcher = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointLocation> loc = new AtomicReference<>();
|
||||
|
||||
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
@ -770,13 +768,13 @@ public abstract class AbstractModelForDbgTest
|
||||
.handle(seq::nextIgnore);
|
||||
}).then(seq -> {
|
||||
Msg.debug(this, "Finding breakpoint container...");
|
||||
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get())
|
||||
DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
|
||||
.handle(seq::next);
|
||||
}, breaks).then(seq -> {
|
||||
Msg.debug(this, "Placing breakpoint...");
|
||||
breaks.get()
|
||||
.placeBreakpoint("0x7ff7d52c8987",
|
||||
Set.of(TargetBreakpointKind.SOFTWARE))
|
||||
Set.of(TargetBreakpointKind.SW_EXECUTE))
|
||||
.handle(seq::next);
|
||||
}).then(seq -> {
|
||||
Msg.debug(this, "Getting breakpoint specs...");
|
||||
@ -794,9 +792,6 @@ public abstract class AbstractModelForDbgTest
|
||||
loc.set(es.iterator().next());
|
||||
Address addr = loc.get().getAddress();
|
||||
Msg.debug(this, "Got address: " + addr);
|
||||
TargetObjectList<?> list = loc.get().getAffects();
|
||||
Msg.debug(this, "Got affects: " + list);
|
||||
assertEquals(1, list.size());
|
||||
seq.exit();
|
||||
}).finish());
|
||||
}
|
||||
@ -809,7 +804,7 @@ public abstract class AbstractModelForDbgTest
|
||||
|
||||
AtomicReference<TargetObject> root = new AtomicReference<>();
|
||||
AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>();
|
||||
AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
|
||||
AtomicReference<TargetLauncher> launcher = new AtomicReference<>();
|
||||
AtomicReference<TargetObject> obj = new AtomicReference<>();
|
||||
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
@ -838,13 +833,13 @@ public abstract class AbstractModelForDbgTest
|
||||
access.get().waitValue(true).handle(seq::next);
|
||||
}).then(seq -> {
|
||||
Msg.debug(this, "Finding breakpoint container...");
|
||||
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get())
|
||||
DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
|
||||
.handle(seq::next);
|
||||
}, breaks).then(seq -> {
|
||||
Msg.debug(this, "Placing breakpoint...");
|
||||
breaks.get()
|
||||
.placeBreakpoint("0x7ff7d52c8987",
|
||||
Set.of(TargetBreakpointKind.SOFTWARE))
|
||||
Set.of(TargetBreakpointKind.SW_EXECUTE))
|
||||
.handle(seq::next);
|
||||
}).then(seq -> {
|
||||
Msg.debug(this, "Getting Process 1...");
|
||||
@ -970,20 +965,21 @@ public abstract class AbstractModelForDbgTest
|
||||
AsyncReference<List<String>, Void> focusProcPath = new AsyncReference<>();
|
||||
AsyncReference<Integer, Void> processCount = new AsyncReference<>();
|
||||
|
||||
TargetObjectListener procListener = new TargetObjectListener() {
|
||||
DebuggerModelListener procListener = new DebuggerModelListener() {
|
||||
@Override
|
||||
public void elementsChanged(TargetObject parent, Collection<String> removed,
|
||||
Map<String, ? extends TargetObject> added) {
|
||||
processCount.set(processes.get().getCachedElements().size(), null);
|
||||
}
|
||||
};
|
||||
TargetFocusScopeListener focusListener = new TargetFocusScopeListener() {
|
||||
@Override
|
||||
public void focusChanged(TargetFocusScope object, TargetObject focused) {
|
||||
// Truncate the path to the parent process
|
||||
focusProcPath.set(focused.getPath().subList(0, 2), null);
|
||||
}
|
||||
};
|
||||
DebuggerModelListener focusListener =
|
||||
new AnnotatedDebuggerAttributeListener(MethodHandles.lookup()) {
|
||||
@AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME)
|
||||
public void focusChanged(TargetObject object, TargetObject focused) {
|
||||
// Truncate the path to the parent process
|
||||
focusProcPath.set(focused.getPath().subList(0, 2), null);
|
||||
}
|
||||
};
|
||||
|
||||
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
m.init().handle(seq::next);
|
@ -24,16 +24,18 @@ import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
||||
|
||||
/**
|
||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
||||
* may change if it proves stable, though, no?
|
||||
* Note this is in the testing source because it's not meant to be shipped in
|
||||
* the release.... That may change if it proves stable, though, no?
|
||||
*/
|
||||
@FactoryDescription( //
|
||||
brief = "IN-VM MS dbgeng local debugger", //
|
||||
htmlDetails = "Launch a dbgeng session in this same JVM" //
|
||||
brief = "IN-VM MS dbgeng local debugger", //
|
||||
htmlDetails = "Launch a dbgeng session in this same JVM" //
|
||||
)
|
||||
@ExtensionPointProperties(priority = 80)
|
||||
public class DbgEngInJvmDebuggerModelFactory implements LocalDebuggerModelFactory {
|
||||
|
||||
// TODO remoteTransport option?
|
||||
|
||||
@Override
|
||||
public CompletableFuture<? extends DebuggerObjectModel> build() {
|
||||
DbgModelImpl model = new DbgModelImpl();
|
||||
|
@ -108,10 +108,23 @@ public interface DebugBreakpoint {
|
||||
|
||||
void setFlags(BreakFlags... flags);
|
||||
|
||||
long getOffset();
|
||||
/**
|
||||
* Get the location on target that triggers the breakpoint
|
||||
*
|
||||
* <p>
|
||||
* If the breakpoint is deferred, this will return {@code null}. In that case, use
|
||||
* {@link #getOffsetExpression()}.
|
||||
*
|
||||
* @return the offset, or {@code null}
|
||||
*/
|
||||
Long getOffset();
|
||||
|
||||
void setOffset(long offset);
|
||||
|
||||
String getOffsetExpression();
|
||||
|
||||
void setOffsetExpression(String expression);
|
||||
|
||||
BreakDataParameters getDataParameters();
|
||||
|
||||
void setDataParameters(BreakDataParameters params);
|
||||
|
@ -254,6 +254,7 @@ public interface DebugControl extends DebugControlReentrant {
|
||||
/**
|
||||
* Shortcut to retrieve all breakpoints for the current process.
|
||||
*
|
||||
* <p>
|
||||
* Uses {@link #getNumberBreakpoints()} and {@link #getBreakpointByIndex(int)} to enumerate all
|
||||
* breakpoints for the current process.
|
||||
*
|
||||
@ -287,10 +288,54 @@ public interface DebugControl extends DebugControlReentrant {
|
||||
*/
|
||||
DebugBreakpoint getBreakpointById(int id);
|
||||
|
||||
/**
|
||||
* Add a (resolved) breakpoint with the given type and desired id
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent, in part, to the {@code bp} command.
|
||||
*
|
||||
* @param type the type
|
||||
* @param desiredId the desired id
|
||||
* @return the breakpoint, disabled at offset 0
|
||||
*/
|
||||
DebugBreakpoint addBreakpoint(BreakType type, int desiredId);
|
||||
|
||||
/**
|
||||
* Add a (resolved) breakpoint with the given type and any id
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent, in part, to the {@code bp} command.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the breakpoint, disable at offset 0
|
||||
*/
|
||||
DebugBreakpoint addBreakpoint(BreakType type);
|
||||
|
||||
/**
|
||||
* Add an unresolved breakpoint with the given type and desired id
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent, in part, to the {@code bu} command. See the MSDN for a comparison of
|
||||
* {@code bu} and {@code bp}.
|
||||
*
|
||||
* @param type the type
|
||||
* @param desiredId the desired id
|
||||
* @return the breakpoint, disabled at offset 0
|
||||
*/
|
||||
DebugBreakpoint addBreakpoint2(BreakType type, int desiredId);
|
||||
|
||||
/**
|
||||
* Add an unresolved breakpoint with the given type and any id
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent, in part, to the {@code bu} command. See the MSDN for a comparison of
|
||||
* {@code bu} and {@code bp}.
|
||||
*
|
||||
* @param desiredId the desired id
|
||||
* @return the breakpoint, disabled at offset 0
|
||||
*/
|
||||
DebugBreakpoint addBreakpoint2(BreakType type);
|
||||
|
||||
void waitForEvent(int timeout);
|
||||
|
||||
DebugEventInformation getLastEventInformation();
|
||||
@ -311,5 +356,4 @@ public interface DebugControl extends DebugControlReentrant {
|
||||
int getExecutingProcessorType();
|
||||
|
||||
int getDebuggeeType();
|
||||
|
||||
}
|
||||
|
@ -26,18 +26,18 @@ public class DebugModuleInfo {
|
||||
public final long imageFileHandle;
|
||||
public final long baseOffset;
|
||||
public final int moduleSize;
|
||||
public final String moduleName;
|
||||
public final String imageName;
|
||||
public final int checkSum;
|
||||
public final int timeDateStamp;
|
||||
private String moduleName;
|
||||
private String imageName;
|
||||
|
||||
public DebugModuleInfo(long imageFileHandle, long baseOffset, int moduleSize, String moduleName,
|
||||
String imageName, int checkSum, int timeDateStamp) {
|
||||
this.imageFileHandle = imageFileHandle;
|
||||
this.baseOffset = baseOffset;
|
||||
this.moduleSize = moduleSize;
|
||||
this.moduleName = moduleName;
|
||||
this.imageName = imageName;
|
||||
this.setModuleName(moduleName);
|
||||
this.setImageName(imageName);
|
||||
this.checkSum = checkSum;
|
||||
this.timeDateStamp = timeDateStamp; // TODO: Convert to DateTime?
|
||||
}
|
||||
@ -45,4 +45,20 @@ public class DebugModuleInfo {
|
||||
public String toString() {
|
||||
return Long.toHexString(baseOffset);
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public void setModuleName(String moduleName) {
|
||||
this.moduleName = moduleName;
|
||||
}
|
||||
|
||||
public String getImageName() {
|
||||
return imageName;
|
||||
}
|
||||
|
||||
public void setImageName(String imageName) {
|
||||
this.imageName = imageName;
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ public interface DebugRegisters {
|
||||
*/
|
||||
default DebugValue getValueByName(String name) {
|
||||
int indexByName = getIndexByName(name);
|
||||
if (indexByName > 0) {
|
||||
if (indexByName >= 0) {
|
||||
return getValue(indexByName);
|
||||
}
|
||||
return null;
|
||||
|
@ -31,6 +31,7 @@ import ghidra.util.Msg;
|
||||
/**
|
||||
* A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client.
|
||||
*
|
||||
* <p>
|
||||
* The executor also has a priority mechanism, so that callbacks may register follow-on handlers
|
||||
* which take precedence over other tasks in the queue (which could trigger additional callbacks).
|
||||
* This is required since certain operation are not allowed during normal callback processing. For
|
||||
@ -102,6 +103,7 @@ public abstract class AbstractClientThreadExecutor extends AbstractExecutorServi
|
||||
* we can always create a new thread and client, using the existing client's reentrant
|
||||
* methods.
|
||||
*
|
||||
* <p>
|
||||
* As stated in the MSDN, this thread repeatedly calls {@code DispatchEvents} in order to
|
||||
* receive callbacks regarding events caused by other clients. If, however, an wait is
|
||||
* registered, or the current engine state indicates that a wait is proper, the thread calls
|
||||
@ -196,6 +198,7 @@ public abstract class AbstractClientThreadExecutor extends AbstractExecutorServi
|
||||
/**
|
||||
* Schedule a task with a given priority.
|
||||
*
|
||||
* <p>
|
||||
* Smaller priority values indicate earlier execution. The default priority is
|
||||
* {@link #DEFAULT_PRIORITY}.
|
||||
*
|
||||
@ -224,6 +227,7 @@ public abstract class AbstractClientThreadExecutor extends AbstractExecutorServi
|
||||
/**
|
||||
* Schedule a task with the given priority, taking a reference to the client.
|
||||
*
|
||||
* <p>
|
||||
* This is a convenience which spares a call to {@link #getClient()}. See
|
||||
* {@link #execute(int, Runnable)} about priority.
|
||||
*
|
||||
|
@ -15,13 +15,16 @@
|
||||
*/
|
||||
package agent.dbgeng.impl.dbgeng.breakpoint;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinDef.*;
|
||||
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
||||
import com.sun.jna.platform.win32.COM.COMUtils;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
import agent.dbgeng.dbgeng.DbgEng;
|
||||
import agent.dbgeng.dbgeng.DebugClient;
|
||||
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
|
||||
import agent.dbgeng.dbgeng.DebugClient;
|
||||
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
|
||||
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
|
||||
import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine;
|
||||
@ -124,9 +127,14 @@ public class DebugBreakpointImpl1 implements DebugBreakpointInternal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOffset() {
|
||||
public Long getOffset() {
|
||||
ULONGLONGByReference pullOffset = new ULONGLONGByReference();
|
||||
COMUtils.checkRC(jnaBreakpoint.GetOffset(pullOffset));
|
||||
HRESULT getOffset = jnaBreakpoint.GetOffset(pullOffset);
|
||||
if (getOffset.longValue() == Kernel32.E_NOINTERFACE) {
|
||||
// Per MSDN, this means the placement is deferred
|
||||
return null;
|
||||
}
|
||||
COMUtils.checkRC(getOffset);
|
||||
return pullOffset.getValue().longValue();
|
||||
}
|
||||
|
||||
@ -136,6 +144,21 @@ public class DebugBreakpointImpl1 implements DebugBreakpointInternal {
|
||||
COMUtils.checkRC(jnaBreakpoint.SetOffset(ullOffset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOffsetExpression() {
|
||||
ULONGByReference pulExpressionSize = new ULONGByReference();
|
||||
COMUtils.checkRC(jnaBreakpoint.GetOffsetExpression(null, new ULONG(0), pulExpressionSize));
|
||||
byte[] buffer = new byte[pulExpressionSize.getValue().intValue()];
|
||||
COMUtils.checkRC(
|
||||
jnaBreakpoint.GetOffsetExpression(buffer, pulExpressionSize.getValue(), null));
|
||||
return Native.toString(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffsetExpression(String expression) {
|
||||
COMUtils.checkRC(jnaBreakpoint.SetOffsetExpression(expression));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BreakDataParameters getDataParameters() {
|
||||
ULONGByReference pulSize = new ULONGByReference();
|
||||
|
@ -15,6 +15,12 @@
|
||||
*/
|
||||
package agent.dbgeng.impl.dbgeng.breakpoint;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.WString;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
||||
import com.sun.jna.platform.win32.COM.COMUtils;
|
||||
|
||||
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint2;
|
||||
|
||||
public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 {
|
||||
@ -25,4 +31,20 @@ public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 {
|
||||
super(jnaBreakpoint);
|
||||
this.jnaBreakpoint = jnaBreakpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOffsetExpression() {
|
||||
ULONGByReference pulExpressionSize = new ULONGByReference();
|
||||
COMUtils.checkRC(
|
||||
jnaBreakpoint.GetOffsetExpressionWide(null, new ULONG(0), pulExpressionSize));
|
||||
char[] buffer = new char[pulExpressionSize.getValue().intValue()];
|
||||
COMUtils.checkRC(
|
||||
jnaBreakpoint.GetOffsetExpressionWide(buffer, pulExpressionSize.getValue(), null));
|
||||
return Native.toString(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffsetExpression(String expression) {
|
||||
COMUtils.checkRC(jnaBreakpoint.SetOffsetExpressionWide(new WString(expression)));
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package agent.dbgeng.impl.dbgeng.control;
|
||||
|
||||
import javax.help.UnsupportedOperationException;
|
||||
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.platform.win32.WinDef.*;
|
||||
import com.sun.jna.platform.win32.WinError;
|
||||
@ -211,6 +213,16 @@ public class DebugControlImpl1 implements DebugControlInternal {
|
||||
return doAddBreakpoint(type, DbgEngUtil.DEBUG_ANY_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugBreakpoint addBreakpoint2(BreakType type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugBreakpoint addBreakpoint2(BreakType type, int desiredId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBreakpoint(IDebugBreakpoint comBpt) {
|
||||
COMUtils.checkRC(jnaControl.RemoveBreakpoint(comBpt));
|
||||
|
@ -19,13 +19,18 @@ import com.sun.jna.Native;
|
||||
import com.sun.jna.WString;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
|
||||
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
|
||||
import agent.dbgeng.jna.dbgeng.control.IDebugControl4;
|
||||
|
||||
import com.sun.jna.platform.win32.COM.COMUtils;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint;
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType;
|
||||
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
|
||||
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
|
||||
import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal;
|
||||
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
|
||||
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint;
|
||||
import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint;
|
||||
import agent.dbgeng.jna.dbgeng.control.IDebugControl4;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
|
||||
public class DebugControlImpl4 extends DebugControlImpl3 {
|
||||
@ -92,4 +97,26 @@ public class DebugControlImpl4 extends DebugControlImpl3 {
|
||||
public void returnInput(String input) {
|
||||
COMUtils.checkRC(jnaControl.ReturnInputWide(new WString(input)));
|
||||
}
|
||||
|
||||
public DebugBreakpoint doAddBreakpoint2(BreakType type, ULONG ulDesiredId) {
|
||||
ULONG ulType = new ULONG(type.ordinal());
|
||||
PointerByReference ppBp = new PointerByReference();
|
||||
COMUtils.checkRC(jnaControl.AddBreakpoint2(ulType, ulDesiredId, ppBp));
|
||||
IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue());
|
||||
DebugBreakpoint bpt =
|
||||
DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface);
|
||||
// AddRef or no? Probably not.
|
||||
return bpt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugBreakpoint addBreakpoint2(BreakType type, int desiredId) {
|
||||
return doAddBreakpoint2(type, new ULONG(desiredId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugBreakpoint addBreakpoint2(BreakType type) {
|
||||
return doAddBreakpoint2(type, DbgEngUtil.DEBUG_ANY_ID);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,6 @@ public class WrapIDebugBreakpoint2 extends WrapIDebugBreakpoint implements IDebu
|
||||
|
||||
@Override
|
||||
public HRESULT SetOffsetExpressionWide(WString Expression) {
|
||||
return _invokeHR(VTIndices2.SET_COMMAND_WIDE, getPointer(), Expression);
|
||||
return _invokeHR(VTIndices2.SET_OFFSET_EXPRESSION_WIDE, getPointer(), Expression);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import com.sun.jna.platform.win32.Guid.IID;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
||||
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
|
||||
import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex;
|
||||
@ -91,6 +92,8 @@ public interface IDebugControl4 extends IDebugControl3 {
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT AddBreakpoint2(ULONG Type, ULONG DesiredId, PointerByReference Bp);
|
||||
|
||||
HRESULT ReturnInputWide(WString Buffer);
|
||||
|
||||
HRESULT OutputWide(ULONG Mask, WString Format, Object... objects);
|
||||
|
@ -21,6 +21,7 @@ import com.sun.jna.*;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
||||
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
||||
import com.sun.jna.ptr.PointerByReference;
|
||||
|
||||
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
|
||||
|
||||
@ -35,6 +36,11 @@ public class WrapIDebugControl4 extends WrapIDebugControl3 implements IDebugCont
|
||||
super(pvInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRESULT AddBreakpoint2(ULONG Type, ULONG DesiredId, PointerByReference Bp) {
|
||||
return _invokeHR(VTIndices4.ADD_BREAKPOINT2, getPointer(), Type, DesiredId, Bp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HRESULT ReturnInputWide(WString Buffer) {
|
||||
return _invokeHR(VTIndices4.RETURN_INPUT_WIDE, getPointer(), Buffer);
|
||||
|
@ -32,8 +32,7 @@ public interface DbgReason {
|
||||
|
||||
@Override
|
||||
public String desc() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,15 @@ public enum DbgState {
|
||||
public boolean isAlive() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Dbg or the process has exited
|
||||
*/
|
||||
SESSION_EXIT {
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public abstract boolean isAlive();
|
||||
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package agent.dbgeng.manager.breakpoint;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Objects;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint;
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint.*;
|
||||
import agent.dbgeng.dbgeng.DebugProcessId;
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
@ -39,8 +38,9 @@ public class DbgBreakpointInfo {
|
||||
private final long number;
|
||||
private boolean enabled;
|
||||
|
||||
private final String location;
|
||||
private final List<DbgBreakpointLocation> locations;
|
||||
private Long offset;
|
||||
private String expression;
|
||||
//private final List<DbgBreakpointLocation> locations;
|
||||
|
||||
/**
|
||||
* Construct Dbg breakpoint information
|
||||
@ -64,28 +64,28 @@ public class DbgBreakpointInfo {
|
||||
}
|
||||
|
||||
public DbgBreakpointInfo(DebugBreakpoint bp, DbgProcess process, DbgThread thread) {
|
||||
this.bpt = bp;
|
||||
this.setBreakpoint(bp);
|
||||
this.proc = process;
|
||||
this.eventThread = thread;
|
||||
this.number = bpt.getId();
|
||||
this.bptType = bpt.getType();
|
||||
this.flags = bpt.getFlags();
|
||||
//this.parameters = bpt.getDataParameters();
|
||||
//this.access = parameters.access;
|
||||
//this.size = parameters.size;
|
||||
this.location = Long.toHexString(bpt.getOffset());
|
||||
List<DbgBreakpointLocation> locs = new ArrayList<>();
|
||||
List<DebugProcessId> ids = new ArrayList<>();
|
||||
ids.add(proc.getId());
|
||||
locs.add(new DbgBreakpointLocation(bpt.getId(), 1, true, location, ids));
|
||||
this.locations = Collections.unmodifiableList(locs);
|
||||
if (bpt.getType().breakType.equals(BreakType.DATA)) {
|
||||
this.parameters = bpt.getDataParameters();
|
||||
}
|
||||
this.access = parameters.access;
|
||||
this.size = parameters.size;
|
||||
this.offset = bpt.getOffset();
|
||||
this.expression = bpt.getOffsetExpression();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(number, bptType, getFlags(), location, enabled, access, size);
|
||||
return Objects.hash(number, bptType, getFlags(), /*location,*/ enabled, access, getSize(),
|
||||
offset, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toHexString(bpt.getId());
|
||||
}
|
||||
@ -110,10 +110,16 @@ public class DbgBreakpointInfo {
|
||||
if (this.getFlags() != that.getFlags()) {
|
||||
return false;
|
||||
}
|
||||
if (this.size != that.size) {
|
||||
if (this.getSize() != that.getSize()) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.location, that.location)) {
|
||||
/*if (!Objects.equals(this.location, that.location)) {
|
||||
return false;
|
||||
}*/
|
||||
if (!Objects.equals(this.expression, that.expression)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.offset, that.offset)) {
|
||||
return false;
|
||||
}
|
||||
if (this.enabled != that.enabled) {
|
||||
@ -173,12 +179,12 @@ public class DbgBreakpointInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location of the breakpoint
|
||||
* Get the offset expression of the breakpoint
|
||||
*
|
||||
* @return the location
|
||||
*/
|
||||
public String getLocation() {
|
||||
return location;
|
||||
public String getExpression() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,12 +206,16 @@ public class DbgBreakpointInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming the location is an address, get it as a long
|
||||
* Get the offset of this breakpoint
|
||||
*
|
||||
* @return the address
|
||||
* <p>
|
||||
* Note if the offset was given as an expression, but it hasn't been resolved, this will return
|
||||
* {@code null}.
|
||||
*
|
||||
* @return the offset, or {@code null}
|
||||
*/
|
||||
public long addrAsLong() {
|
||||
return Long.parseUnsignedLong(location, 16);
|
||||
public Long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,15 +248,16 @@ public class DbgBreakpointInfo {
|
||||
/**
|
||||
* Get a list of resolved addresses
|
||||
*
|
||||
* <p>
|
||||
* The effective locations may change for a variety of reasons. Most notable, a new module may
|
||||
* be loaded, having location(s) that match the desired location of this breakpoint. The binary
|
||||
* addresses within will become new effective locations of this breakpoint.
|
||||
*
|
||||
* @return the list of locations at the time the breakpoint information was captured
|
||||
*/
|
||||
public List<DbgBreakpointLocation> getLocations() {
|
||||
/*public List<DbgBreakpointLocation> getLocations() {
|
||||
return locations;
|
||||
}
|
||||
}*/
|
||||
|
||||
public DbgBreakpointInfo withEnabled(@SuppressWarnings("hiding") boolean enabled) {
|
||||
if (isEnabled() == enabled) {
|
||||
@ -271,7 +282,20 @@ public class DbgBreakpointInfo {
|
||||
return eventThread;
|
||||
}
|
||||
|
||||
public long getAddressAsLong() {
|
||||
return locations.get(0).addrAsLong();
|
||||
public void setBreakpoint(DebugBreakpoint bpt) {
|
||||
this.bpt = bpt;
|
||||
this.bptType = bpt.getType();
|
||||
this.flags = bpt.getFlags();
|
||||
this.offset = bpt.getOffset();
|
||||
this.expression = bpt.getOffsetExpression();
|
||||
if (bptType.breakType.equals(BreakType.DATA)) {
|
||||
BreakDataParameters p = bpt.getDataParameters();
|
||||
this.access = p.access;
|
||||
this.size = p.size;
|
||||
}
|
||||
}
|
||||
|
||||
/*public long getAddressAsLong() {
|
||||
return locations.get(0).addrAsLong();
|
||||
}*/ // getOffset instead
|
||||
}
|
||||
|
@ -19,10 +19,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugClient;
|
||||
import agent.dbgeng.manager.DbgEvent;
|
||||
import agent.dbgeng.manager.DbgCause;
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent;
|
||||
import agent.dbgeng.manager.evt.DbgThreadExitedEvent;
|
||||
import agent.dbgeng.manager.impl.*;
|
||||
|
||||
/**
|
||||
@ -44,6 +42,7 @@ public class DbgDetachCommand extends AbstractDbgCommand<Void> {
|
||||
manager.fireThreadExited(t.getId(), process, pending);
|
||||
t.remove();
|
||||
}
|
||||
manager.getEventListeners().fire.processRemoved(process.getId(), DbgCause.Causes.UNCLAIMED);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,9 @@
|
||||
*/
|
||||
package agent.dbgeng.manager.cmd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import agent.dbgeng.dbgeng.*;
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint;
|
||||
import agent.dbgeng.dbgeng.DebugBreakpoint.*;
|
||||
import agent.dbgeng.dbgeng.DebugControl;
|
||||
import agent.dbgeng.manager.breakpoint.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
@ -28,41 +26,33 @@ import ghidra.comm.util.BitmaskSet;
|
||||
* Implementation of {@link DbgBreakpointInsertions#insertBreakpoint(String)}
|
||||
*/
|
||||
public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpointInfo> {
|
||||
private List<Long> locations;
|
||||
//private List<Long> locations;
|
||||
private final DbgBreakpointType type;
|
||||
private DbgBreakpointInfo bkpt;
|
||||
private int len;
|
||||
private final String expression;
|
||||
private final Long loc;
|
||||
|
||||
public DbgInsertBreakpointCommand(DbgManagerImpl manager, String expression,
|
||||
DbgBreakpointType type) {
|
||||
super(manager);
|
||||
locations = new ArrayList<>();
|
||||
DebugSymbols symbols = manager.getSymbols();
|
||||
List<DebugSymbolId> ids = symbols.getSymbolIdsByName(expression);
|
||||
if (ids.isEmpty()) {
|
||||
locations.add(Long.decode(expression));
|
||||
}
|
||||
else {
|
||||
for (DebugSymbolId id : ids) {
|
||||
DebugSymbolEntry entry = symbols.getSymbolEntry(id);
|
||||
locations.add(entry.offset);
|
||||
}
|
||||
}
|
||||
this.type = type;
|
||||
this.expression = expression;
|
||||
this.loc = null;
|
||||
}
|
||||
|
||||
public DbgInsertBreakpointCommand(DbgManagerImpl manager, long loc, int len,
|
||||
DbgBreakpointType type) {
|
||||
super(manager);
|
||||
locations = new ArrayList<>();
|
||||
locations.add(loc);
|
||||
this.len = len;
|
||||
this.type = type;
|
||||
this.expression = null;
|
||||
this.loc = loc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbgBreakpointInfo complete(DbgPendingCommand<?> pending) {
|
||||
manager.doBreakpointCreated(bkpt, pending);
|
||||
//manager.doBreakpointCreated(bkpt, pending);
|
||||
return bkpt;
|
||||
}
|
||||
|
||||
@ -73,8 +63,8 @@ public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpoint
|
||||
if (type.equals(DbgBreakpointType.BREAKPOINT)) {
|
||||
bt = BreakType.CODE;
|
||||
}
|
||||
DebugBreakpoint bp = control.addBreakpoint(bt);
|
||||
bp.addFlags(BreakFlags.ENABLED);
|
||||
// 2 for BU, 1 for BP
|
||||
DebugBreakpoint bp = control.addBreakpoint/*2*/(bt);
|
||||
if (bt.equals(BreakType.DATA)) {
|
||||
BitmaskSet<BreakAccess> access = BitmaskSet.of(BreakAccess.EXECUTE);
|
||||
if (type.equals(DbgBreakpointType.ACCESS_WATCHPOINT)) {
|
||||
@ -92,9 +82,14 @@ public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpoint
|
||||
}
|
||||
bp.setDataParameters(len, access);
|
||||
}
|
||||
for (Long loc : locations) {
|
||||
if (loc != null) {
|
||||
bp.setOffset(loc);
|
||||
bkpt = new DbgBreakpointInfo(bp, manager.getCurrentProcess());
|
||||
}
|
||||
else {
|
||||
bp.setOffsetExpression(expression);
|
||||
}
|
||||
bp.addFlags(BreakFlags.ENABLED);
|
||||
|
||||
bkpt = new DbgBreakpointInfo(bp, manager.getCurrentProcess());
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,7 @@ public class DbgListMappingsCommand extends AbstractDbgCommand<Map<Long, DbgSect
|
||||
Msg.warn(this, "Resync: Was missing thread: " + id);
|
||||
so.setCurrentThreadId(id);
|
||||
int tid = so.getCurrentThreadSystemId();
|
||||
DbgThreadImpl thread = manager.getThreadComputeIfAbsent(id, process, tid);
|
||||
thread.add();
|
||||
manager.getThreadComputeIfAbsent(id, process, tid);
|
||||
}
|
||||
for (DebugThreadId id : new ArrayList<>(cur)) {
|
||||
if (updatedThreadIds.contains(id)) {
|
||||
|
@ -18,6 +18,7 @@ package agent.dbgeng.manager.cmd;
|
||||
import java.util.*;
|
||||
|
||||
import agent.dbgeng.dbgeng.*;
|
||||
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
|
||||
import agent.dbgeng.manager.DbgModule;
|
||||
import agent.dbgeng.manager.impl.*;
|
||||
import ghidra.util.Msg;
|
||||
@ -62,6 +63,10 @@ public class DbgListModulesCommand extends AbstractDbgCommand<Map<String, DbgMod
|
||||
DebugSymbols symbols = manager.getSymbols();
|
||||
for (DebugModule module : symbols.iterateModules(0)) {
|
||||
DebugModuleInfo info = symbols.getModuleParameters(1, module.getIndex());
|
||||
String imageName = module.getName(DebugModuleName.IMAGE);
|
||||
String moduleName = module.getName(DebugModuleName.MODULE);
|
||||
info.setImageName(imageName);
|
||||
info.setModuleName(moduleName);
|
||||
updatedModules.put(info.toString(), module);
|
||||
moduleInfo.put(module, info);
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
/* ###
|
||||
* 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.cmd;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import agent.dbgeng.dbgeng.*;
|
||||
import agent.dbgeng.manager.impl.*;
|
||||
|
||||
public class DbgListSymbolsCommand extends AbstractDbgCommand<Map<String, DbgMinimalSymbol>> {
|
||||
protected final DbgProcessImpl process;
|
||||
protected final DbgModuleImpl module;
|
||||
|
||||
private Map<DebugSymbolId, DebugSymbolEntry> symbolEntries = new HashMap<>();
|
||||
|
||||
public DbgListSymbolsCommand(DbgManagerImpl manager, DbgProcessImpl process,
|
||||
DbgModuleImpl module) {
|
||||
super(manager);
|
||||
this.process = process;
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, DbgMinimalSymbol> complete(DbgPendingCommand<?> pending) {
|
||||
Map<String, DbgMinimalSymbol> symbolMap = new HashMap<>();
|
||||
for (Entry<DebugSymbolId, DebugSymbolEntry> entry : symbolEntries.entrySet()) {
|
||||
DebugSymbolEntry value = entry.getValue();
|
||||
DbgMinimalSymbol minSymbol = new DbgMinimalSymbol(entry.getKey().symbolIndex,
|
||||
value.typeId, value.name, value.offset, value.size, value.tag, value.moduleBase);
|
||||
symbolMap.put(entry.getKey().toString(), minSymbol);
|
||||
}
|
||||
return symbolMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke() {
|
||||
DebugSystemObjects so = manager.getSystemObjects();
|
||||
so.setCurrentProcessId(process.getId());
|
||||
DebugSymbols symbols = manager.getSymbols();
|
||||
|
||||
for (DebugSymbolName symbol : symbols.iterateSymbolMatches(module.getName() + "!*")) {
|
||||
List<DebugSymbolId> symbolIdsByName = symbols.getSymbolIdsByName(symbol.name);
|
||||
for (DebugSymbolId symbolId : symbolIdsByName) {
|
||||
DebugSymbolEntry symbolEntry = symbols.getSymbolEntry(symbolId);
|
||||
symbolEntries.put(symbolId, symbolEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,8 @@ import java.util.*;
|
||||
import agent.dbgeng.dbgeng.DebugSystemObjects;
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.manager.impl.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.manager.impl.DbgProcessImpl;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DbgListThreadsCommand extends AbstractDbgCommand<Map<DebugThreadId, DbgThread>> {
|
||||
@ -45,8 +46,7 @@ public class DbgListThreadsCommand extends AbstractDbgCommand<Map<DebugThreadId,
|
||||
DebugSystemObjects so = manager.getSystemObjects();
|
||||
so.setCurrentThreadId(id);
|
||||
int tid = so.getCurrentThreadSystemId();
|
||||
DbgThreadImpl thread = manager.getThreadComputeIfAbsent(id, process, tid);
|
||||
thread.add();
|
||||
manager.getThreadComputeIfAbsent(id, process, tid);
|
||||
}
|
||||
for (DebugThreadId id : new ArrayList<>(cur)) {
|
||||
if (updatedThreadIds.contains(id)) {
|
||||
|
@ -34,28 +34,35 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
protected DebugStatus checkInterrupt(DebugStatus normal) {
|
||||
if (manager.getControl().getInterrupt()) {
|
||||
return DebugStatus.BREAK;
|
||||
}
|
||||
return normal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus breakpoint(DebugBreakpoint bp) {
|
||||
Msg.info(this, "***Breakpoint: " + bp.getId());
|
||||
return manager.processEvent(new DbgBreakpointEvent(bp));
|
||||
return checkInterrupt(manager.processEvent(new DbgBreakpointEvent(bp)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) {
|
||||
Msg.info(this, "***Exception: " + exception + ", first=" + firstChance);
|
||||
return manager.processEvent(new DbgExceptionEvent(exception));
|
||||
return checkInterrupt(manager.processEvent(new DbgExceptionEvent(exception)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus createThread(DebugThreadInfo threadInfo) {
|
||||
Msg.info(this, "***Thread created: " + Long.toHexString(threadInfo.handle));
|
||||
return manager.processEvent(new DbgThreadCreatedEvent(threadInfo));
|
||||
return checkInterrupt(manager.processEvent(new DbgThreadCreatedEvent(threadInfo)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus exitThread(int exitCode) {
|
||||
Msg.info(this, "***Thread exited: " + exitCode);
|
||||
return manager.processEvent(new DbgThreadExitedEvent(exitCode));
|
||||
return checkInterrupt(manager.processEvent(new DbgThreadExitedEvent(exitCode)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,20 +70,20 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
||||
Msg.info(this, "***Process created: " + Long.toHexString(processInfo.handle));
|
||||
Msg.info(this,
|
||||
" **Thread created: " + Long.toHexString(processInfo.initialThreadInfo.handle));
|
||||
return manager.processEvent(new DbgProcessCreatedEvent(processInfo));
|
||||
return checkInterrupt(manager.processEvent(new DbgProcessCreatedEvent(processInfo)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus exitProcess(int exitCode) {
|
||||
Msg.info(this, "***Process exited: " + exitCode);
|
||||
Msg.info(this, " **Thread exited");
|
||||
return manager.processEvent(new DbgProcessExitedEvent(exitCode));
|
||||
return checkInterrupt(manager.processEvent(new DbgProcessExitedEvent(exitCode)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus loadModule(DebugModuleInfo moduleInfo) {
|
||||
Msg.info(this, "***Module Loaded: " + moduleInfo);
|
||||
return manager.processEvent(new DbgModuleLoadedEvent(moduleInfo));
|
||||
return checkInterrupt(manager.processEvent(new DbgModuleLoadedEvent(moduleInfo)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -85,7 +92,7 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
||||
"***Module Unloaded: " + imageBaseName + ", " + Long.toHexString(baseOffset));
|
||||
DebugModuleInfo info =
|
||||
new DebugModuleInfo(0L, baseOffset, 0, basename(imageBaseName), imageBaseName, 0, 0);
|
||||
return manager.processEvent(new DbgModuleUnloadedEvent(info));
|
||||
return checkInterrupt(manager.processEvent(new DbgModuleUnloadedEvent(info)));
|
||||
}
|
||||
|
||||
private String basename(String path) {
|
||||
@ -103,27 +110,27 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
||||
DebugStatus status = DebugStatus.fromArgument(argument);
|
||||
Msg.info(this, "***ExecutionStatus: " + status);
|
||||
if (status.equals(DebugStatus.NO_DEBUGGEE)) {
|
||||
event.setState(DbgState.EXIT);
|
||||
event.setState(DbgState.SESSION_EXIT);
|
||||
}
|
||||
return manager.processEvent(event);
|
||||
return checkInterrupt(manager.processEvent(event));
|
||||
}
|
||||
if (flags.contains(ChangeEngineState.BREAKPOINTS)) {
|
||||
Msg.info(this, "***BreakpointChanged: " + flags + ", " + argument + " on " +
|
||||
Thread.currentThread());
|
||||
return manager.processEvent(event);
|
||||
return checkInterrupt(manager.processEvent(event));
|
||||
}
|
||||
if (flags.contains(ChangeEngineState.CURRENT_THREAD)) {
|
||||
Msg.info(this, "***CurrentThread: " + argument);
|
||||
if (argument < 0) {
|
||||
return manager.processEvent(event);
|
||||
return checkInterrupt(manager.processEvent(event));
|
||||
}
|
||||
}
|
||||
if (flags.contains(ChangeEngineState.SYSTEMS)) {
|
||||
Msg.info(this, "***Systems: " + argument);
|
||||
event.setState(DbgState.RUNNING);
|
||||
return manager.processEvent(event);
|
||||
return checkInterrupt(manager.processEvent(event));
|
||||
}
|
||||
return DebugStatus.NO_CHANGE;
|
||||
return checkInterrupt(DebugStatus.NO_CHANGE);
|
||||
}
|
||||
|
||||
//@Override
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package agent.dbgeng.manager.impl;
|
||||
|
||||
import static ghidra.async.AsyncUtils.sequence;
|
||||
import static ghidra.async.AsyncUtils.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -34,6 +34,7 @@ import agent.dbgeng.dbgeng.DebugControl.DebugInterrupt;
|
||||
import agent.dbgeng.gadp.impl.AbstractClientThreadExecutor;
|
||||
import agent.dbgeng.gadp.impl.DbgEngClientThreadExecutor;
|
||||
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
|
||||
import agent.dbgeng.jna.dbgeng.WinNTExtra;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.DbgCause.Causes;
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
||||
@ -42,7 +43,6 @@ import agent.dbgeng.manager.cmd.*;
|
||||
import agent.dbgeng.manager.evt.*;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
import ghidra.async.*;
|
||||
import ghidra.async.seq.AsyncSequenceHandlerForRunner;
|
||||
import ghidra.comm.util.BitmaskSet;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.util.HandlerMap;
|
||||
@ -237,7 +237,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
|
||||
public DbgSessionImpl getSessionComputeIfAbsent(DebugSessionId id) {
|
||||
synchronized (sessions) {
|
||||
if (!sessions.containsKey(id)) {
|
||||
if (!sessions.containsKey(id) && id.id >= 0) {
|
||||
DbgSessionImpl session = new DbgSessionImpl(this, id);
|
||||
session.add();
|
||||
}
|
||||
@ -425,19 +425,17 @@ public class DbgManagerImpl implements DbgManager {
|
||||
//}
|
||||
|
||||
if (engThread.isCurrentThread()) {
|
||||
sequence(TypeSpec.VOID).then((seq) -> {
|
||||
addCommand(cmd, pcmd, seq);
|
||||
seq.exit();
|
||||
}).finish().exceptionally((exc) -> {
|
||||
try {
|
||||
addCommand(cmd, pcmd);
|
||||
}
|
||||
catch (Throwable exc) {
|
||||
pcmd.completeExceptionally(exc);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
sequence(TypeSpec.VOID).then(engThread, (seq) -> {
|
||||
addCommand(cmd, pcmd, seq);
|
||||
seq.exit();
|
||||
}).finish().exceptionally((exc) -> {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
addCommand(cmd, pcmd);
|
||||
}, engThread).exceptionally((exc) -> {
|
||||
pcmd.completeExceptionally(exc);
|
||||
return null;
|
||||
});
|
||||
@ -445,8 +443,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
return pcmd;
|
||||
}
|
||||
|
||||
private <T> void addCommand(DbgCommand<? extends T> cmd, DbgPendingCommand<T> pcmd,
|
||||
AsyncSequenceHandlerForRunner<Void> seq) {
|
||||
private <T> void addCommand(DbgCommand<? extends T> cmd, DbgPendingCommand<T> pcmd) {
|
||||
synchronized (this) {
|
||||
if (!cmd.validInState(state.get())) {
|
||||
throw new DbgCommandError("Command " + cmd + " is not valid while " + state.get());
|
||||
@ -519,7 +516,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
synchronized (this) {
|
||||
boolean waitState = isWaiting();
|
||||
waiting = false;
|
||||
DebugStatus ret = handlerMap.handle(evt, null);
|
||||
DebugStatus ret = evt.isStolen() ? null : handlerMap.handle(evt, null);
|
||||
if (ret == null) {
|
||||
ret = DebugStatus.NO_CHANGE;
|
||||
}
|
||||
@ -602,11 +599,18 @@ public class DbgManagerImpl implements DbgManager {
|
||||
DebugThreadId etid = so.getEventThread();
|
||||
DebugProcessId epid = so.getEventProcess();
|
||||
DebugSessionId esid = so.getCurrentSystemId();
|
||||
so.setCurrentProcessId(epid);
|
||||
so.setCurrentThreadId(etid);
|
||||
|
||||
DebugControl control = dbgeng.getControl();
|
||||
int execType = control.getExecutingProcessorType();
|
||||
int execType = WinNTExtra.Machine.IMAGE_FILE_MACHINE_AMD64.val;
|
||||
try {
|
||||
so.setCurrentProcessId(epid);
|
||||
so.setCurrentThreadId(etid);
|
||||
execType = control.getExecutingProcessorType();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Ignore for now
|
||||
}
|
||||
|
||||
lastEventInformation = control.getLastEventInformation();
|
||||
lastEventInformation.setSession(esid);
|
||||
lastEventInformation.setExecutingProcessorType(execType);
|
||||
@ -687,6 +691,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
DbgProcessImpl process = getCurrentProcess();
|
||||
int tid = so.getCurrentThreadSystemId();
|
||||
DbgThreadImpl thread = getThreadComputeIfAbsent(eventId, process, tid);
|
||||
getEventListeners().fire.threadCreated(thread, DbgCause.Causes.UNCLAIMED);
|
||||
getEventListeners().fire.threadSelected(thread, null, evt.getCause());
|
||||
|
||||
String key = Integer.toHexString(eventId.id);
|
||||
@ -759,18 +764,17 @@ public class DbgManagerImpl implements DbgManager {
|
||||
so.setCurrentProcessId(id);
|
||||
int pid = so.getCurrentProcessSystemId();
|
||||
DbgProcessImpl proc = getProcessComputeIfAbsent(id, pid);
|
||||
getEventListeners().fire.processAdded(proc, DbgCause.Causes.UNCLAIMED);
|
||||
getEventListeners().fire.processSelected(proc, evt.getCause());
|
||||
|
||||
handle = info.initialThreadInfo.handle;
|
||||
DebugThreadId idt = so.getThreadIdByHandle(handle);
|
||||
int tid = so.getCurrentThreadSystemId();
|
||||
DbgThreadImpl thread = new DbgThreadImpl(this, proc, idt, tid);
|
||||
thread.add();
|
||||
getEventListeners().fire.threadCreated(thread, evt.getCause());
|
||||
DbgThreadImpl thread = getThreadComputeIfAbsent(idt, proc, tid);
|
||||
getEventListeners().fire.threadSelected(thread, null, evt.getCause());
|
||||
|
||||
proc.moduleLoaded(info.moduleInfo);
|
||||
getEventListeners().fire.moduleLoaded(proc, info.moduleInfo, evt.getCause());
|
||||
//proc.moduleLoaded(info.moduleInfo);
|
||||
//getEventListeners().fire.moduleLoaded(proc, info.moduleInfo, evt.getCause());
|
||||
|
||||
String key = Integer.toHexString(id.id);
|
||||
if (statusByNameMap.containsKey(key)) {
|
||||
@ -788,20 +792,21 @@ public class DbgManagerImpl implements DbgManager {
|
||||
*/
|
||||
protected DebugStatus processProcessExited(DbgProcessExitedEvent evt, Void v) {
|
||||
DebugThreadId eventId = updateState();
|
||||
|
||||
DbgProcessImpl process = getCurrentProcess();
|
||||
process.remove(evt.getCause());
|
||||
getEventListeners().fire.processRemoved(process.getId(), evt.getCause());
|
||||
|
||||
DbgThreadImpl thread = getCurrentThread();
|
||||
if (thread != null) {
|
||||
thread.remove();
|
||||
}
|
||||
DbgProcessImpl process = getCurrentProcess();
|
||||
process.setExitCode(Long.valueOf(evt.getInfo()));
|
||||
getEventListeners().fire.threadExited(eventId, process, evt.getCause());
|
||||
|
||||
getEventListeners().fire.processExited(process, evt.getCause());
|
||||
|
||||
for (DebugBreakpoint bpt : getBreakpoints()) {
|
||||
breaksById.remove(bpt.getId());
|
||||
}
|
||||
if (thread != null) {
|
||||
thread.remove();
|
||||
}
|
||||
process.remove(evt.getCause());
|
||||
getEventListeners().fire.processRemoved(process.getId(), evt.getCause());
|
||||
|
||||
String key = Integer.toHexString(process.getId().id);
|
||||
if (statusByNameMap.containsKey(key)) {
|
||||
@ -844,7 +849,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
process.moduleLoaded(info);
|
||||
getEventListeners().fire.moduleLoaded(process, info, evt.getCause());
|
||||
|
||||
String key = info.moduleName;
|
||||
String key = info.getModuleName();
|
||||
if (statusByNameMap.containsKey(key)) {
|
||||
return statusByNameMap.get(key);
|
||||
}
|
||||
@ -865,7 +870,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
process.moduleUnloaded(info);
|
||||
getEventListeners().fire.moduleUnloaded(process, info, evt.getCause());
|
||||
|
||||
String key = info.moduleName;
|
||||
String key = info.getModuleName();
|
||||
if (statusByNameMap.containsKey(key)) {
|
||||
return statusByNameMap.get(key);
|
||||
}
|
||||
@ -908,8 +913,11 @@ public class DbgManagerImpl implements DbgManager {
|
||||
dbgState = DbgState.RUNNING;
|
||||
processEvent(new DbgRunningEvent(eventThread.getId()));
|
||||
}
|
||||
if (!threads.containsValue(eventThread)) {
|
||||
dbgState = DbgState.EXIT;
|
||||
}
|
||||
// Don't fire
|
||||
if (dbgState != null) {
|
||||
if (dbgState != null && dbgState != DbgState.EXIT) {
|
||||
processEvent(new DbgThreadSelectedEvent(dbgState, eventThread,
|
||||
evt.getFrame(eventThread)));
|
||||
}
|
||||
@ -1023,12 +1031,14 @@ public class DbgManagerImpl implements DbgManager {
|
||||
DbgBreakpointInfo knownBreakpoint = getKnownBreakpoint(bptId);
|
||||
if (knownBreakpoint == null) {
|
||||
breakpointInfo = new DbgBreakpointInfo(bpt, getCurrentProcess());
|
||||
if (!breakpointInfo.getLocation().equals("0")) {
|
||||
if (breakpointInfo.getOffset() != null) {
|
||||
doBreakpointCreated(breakpointInfo, evt.getCause());
|
||||
}
|
||||
return;
|
||||
}
|
||||
breakpointInfo = knownBreakpoint;
|
||||
breakpointInfo.setBreakpoint(bpt);
|
||||
|
||||
}
|
||||
doBreakpointModified(breakpointInfo, evt.getCause());
|
||||
}
|
||||
@ -1110,6 +1120,13 @@ public class DbgManagerImpl implements DbgManager {
|
||||
doBreakpointModifiedSameLocations(newInfo, oldInfo, cause);
|
||||
}
|
||||
|
||||
private long orZero(Long l) {
|
||||
if (l == null) {
|
||||
return 0;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
private void changeBreakpoints() {
|
||||
Set<Integer> retained = new HashSet<>();
|
||||
DebugSystemObjects so = getSystemObjects();
|
||||
@ -1134,7 +1151,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
}
|
||||
int id = bpt.getId();
|
||||
retained.add(id);
|
||||
long newOffset = bpt.getOffset();
|
||||
long newOffset = orZero(bpt.getOffset());
|
||||
BreakpointTag tag = breaksById.get(id);
|
||||
if (tag == null) {
|
||||
for (DebugThreadId tid : tids) {
|
||||
@ -1400,8 +1417,7 @@ public class DbgManagerImpl implements DbgManager {
|
||||
return execute(new DbgSessionSelectCommand(this, session));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> requestFocus(DbgModelTargetFocusScope scope,
|
||||
TargetObject obj) {
|
||||
public CompletableFuture<Void> requestFocus(DbgModelTargetFocusScope scope, TargetObject obj) {
|
||||
return execute(new DbgRequestFocusCommand(this, scope, obj));
|
||||
}
|
||||
|
||||
|
@ -17,25 +17,30 @@ package agent.dbgeng.manager.impl;
|
||||
|
||||
public class DbgMinimalSymbol {
|
||||
protected final long index;
|
||||
protected final String type;
|
||||
protected final int typeId;
|
||||
protected final String name;
|
||||
protected final long address;
|
||||
protected final long size;
|
||||
private final int tag;
|
||||
private final long moduleBase;
|
||||
|
||||
public DbgMinimalSymbol(long index, String type, String name, long address) {
|
||||
public DbgMinimalSymbol(long index, int typeId, String name, long address, long size, int tag,
|
||||
long moduleBase) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
this.typeId = typeId;
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.size = size;
|
||||
this.tag = tag;
|
||||
this.moduleBase = moduleBase;
|
||||
}
|
||||
|
||||
public long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
// TODO: Interpret these types
|
||||
// Observed: t, T, D, S
|
||||
return type;
|
||||
public int getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -45,4 +50,16 @@ public class DbgMinimalSymbol {
|
||||
public long getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public int getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public long getModuleBase() {
|
||||
return moduleBase;
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import agent.dbgeng.dbgeng.DebugModuleInfo;
|
||||
import agent.dbgeng.manager.DbgCause.Causes;
|
||||
import agent.dbgeng.manager.DbgModule;
|
||||
import agent.dbgeng.manager.cmd.DbgListSymbolsCommand;
|
||||
import ghidra.async.AsyncLazyValue;
|
||||
|
||||
public class DbgModuleImpl implements DbgModule {
|
||||
@ -44,7 +45,7 @@ public class DbgModuleImpl implements DbgModule {
|
||||
this.manager = manager;
|
||||
this.process = process;
|
||||
this.info = info;
|
||||
this.name = info.moduleName;
|
||||
this.name = info.getModuleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,12 +71,12 @@ public class DbgModuleImpl implements DbgModule {
|
||||
|
||||
@Override
|
||||
public String getImageName() {
|
||||
return info == null ? getName() : info.imageName;
|
||||
return info == null ? getName() : info.getImageName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return info == null ? getName() : info.moduleName;
|
||||
return info == null ? getName() : info.getModuleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -94,9 +95,7 @@ public class DbgModuleImpl implements DbgModule {
|
||||
}
|
||||
|
||||
protected CompletableFuture<Map<String, DbgMinimalSymbol>> doGetMinimalSymbols() {
|
||||
// TODO: Apparently, this is using internal GDB-debugging commands....
|
||||
// TODO: Also make methods for "full" symbols (DWARF?)
|
||||
return null;
|
||||
return manager.execute(new DbgListSymbolsCommand(manager, process, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package agent.dbgeng.manager.impl;
|
||||
|
||||
import static ghidra.async.AsyncUtils.sequence;
|
||||
import static ghidra.async.AsyncUtils.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
@ -109,7 +109,7 @@ public class DbgProcessImpl implements DbgProcess {
|
||||
*/
|
||||
public void add() {
|
||||
manager.processes.put(id, this);
|
||||
manager.getEventListeners().fire.processAdded(this, DbgCause.Causes.UNCLAIMED);
|
||||
//manager.getEventListeners().fire.processAdded(this, DbgCause.Causes.UNCLAIMED);
|
||||
//manager.addProcess(this, cause);
|
||||
}
|
||||
|
||||
@ -352,7 +352,7 @@ public class DbgProcessImpl implements DbgProcess {
|
||||
}
|
||||
|
||||
protected void moduleLoaded(DebugModuleInfo info) {
|
||||
if (!modules.containsKey(info.moduleName)) {
|
||||
if (!modules.containsKey(info.getModuleName())) {
|
||||
DbgModuleImpl module = new DbgModuleImpl(manager, this, info);
|
||||
modules.put(info.toString(), module);
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package agent.dbgeng.manager.impl;
|
||||
|
||||
import static ghidra.async.AsyncUtils.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
@ -34,7 +32,8 @@ import agent.dbgeng.manager.DbgManager.ExecSuffix;
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
|
||||
import agent.dbgeng.manager.cmd.*;
|
||||
import ghidra.async.*;
|
||||
import ghidra.async.AsyncLazyValue;
|
||||
import ghidra.async.AsyncReference;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DbgThreadImpl implements DbgThread {
|
||||
@ -91,7 +90,7 @@ public class DbgThreadImpl implements DbgThread {
|
||||
*/
|
||||
public void add() {
|
||||
manager.threads.put(id, this);
|
||||
manager.getEventListeners().fire.threadCreated(this, DbgCause.Causes.UNCLAIMED);
|
||||
//manager.getEventListeners().fire.threadCreated(this, DbgCause.Causes.UNCLAIMED);
|
||||
process.addThread(this);
|
||||
state.addChangeListener((oldState, newState, pair) -> {
|
||||
this.manager.getEventListeners().fire.threadStateChanged(this, newState, pair.cause,
|
||||
@ -147,18 +146,18 @@ public class DbgThreadImpl implements DbgThread {
|
||||
}
|
||||
|
||||
private CompletableFuture<DbgRegisterSet> doListRegisters() {
|
||||
return sequence(TypeSpec.cls(DbgRegisterSet.class)).then((seq) -> {
|
||||
manager.execute(new DbgListRegisterDescriptionsCommand(manager)).handle(seq::next);
|
||||
}, TypeSpec.cls(DebugRegisterDescription.class).list()).then((descs, seq) -> {
|
||||
CompletableFuture<List<DebugRegisterDescription>> listCmd =
|
||||
manager.execute(new DbgListRegisterDescriptionsCommand(manager));
|
||||
return listCmd.thenApply(descs -> {
|
||||
if (descs == null) {
|
||||
return;
|
||||
return new DbgRegisterSet(Set.of());
|
||||
}
|
||||
List<DbgRegister> regs = new ArrayList<>();
|
||||
for (DebugRegisterDescription desc : descs) {
|
||||
regs.add(new DbgRegister(desc));
|
||||
}
|
||||
seq.exit(new DbgRegisterSet(regs));
|
||||
}).finish();
|
||||
return new DbgRegisterSet(regs);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -208,47 +207,37 @@ public class DbgThreadImpl implements DbgThread {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> cont() {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgContinueCommand(manager)).handle(seq::exit);
|
||||
}).finish();
|
||||
return select().thenCompose(__ -> {
|
||||
return manager.execute(new DbgContinueCommand(manager));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(ExecSuffix suffix) {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, id, suffix)).handle(seq::exit);
|
||||
}).finish();
|
||||
return select().thenCompose(__ -> {
|
||||
return manager.execute(new DbgStepCommand(manager, id, suffix));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgStepCommand(manager, id, args)).handle(seq::exit);
|
||||
}).finish();
|
||||
return select().thenCompose(__ -> {
|
||||
return manager.execute(new DbgStepCommand(manager, id, args));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> kill() {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgKillCommand(manager)).handle(seq::exit);
|
||||
}).finish();
|
||||
return select().thenCompose(__ -> {
|
||||
return manager.execute(new DbgKillCommand(manager));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> detach() {
|
||||
return sequence(TypeSpec.VOID).then((seq) -> {
|
||||
select().handle(seq::next);
|
||||
}).then((seq) -> {
|
||||
manager.execute(new DbgDetachCommand(manager, process)).handle(seq::exit);
|
||||
}).finish();
|
||||
return select().thenCompose(__ -> {
|
||||
return manager.execute(new DbgDetachCommand(manager, process));
|
||||
});
|
||||
}
|
||||
|
||||
public DebugEventInformation getInfo() {
|
||||
|
@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import agent.dbgeng.manager.DbgManager;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSession;
|
||||
import ghidra.dbg.agent.AbstractDebuggerObjectModel;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
|
||||
public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel {
|
||||
@ -37,4 +38,8 @@ public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel {
|
||||
|
||||
public abstract DbgModelTargetSession getSession();
|
||||
|
||||
public abstract void addModelObject(Object object, TargetObject targetObject);
|
||||
|
||||
public abstract TargetObject getModelObject(Object object);
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import ghidra.dbg.target.TargetAttachable;
|
||||
/**
|
||||
* An interface which indicates this object is capable of launching targets.
|
||||
*
|
||||
* <p>
|
||||
* The targets this launcher creates ought to appear in its successors.
|
||||
*
|
||||
* @param <T> type for this
|
||||
|
@ -21,8 +21,6 @@ import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.manager.impl.DbgProcessImpl;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetAvailable;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetObject;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.target.TargetAttachable;
|
||||
import ghidra.dbg.target.TargetAttacher;
|
||||
import ghidra.util.Msg;
|
||||
@ -30,6 +28,7 @@ import ghidra.util.Msg;
|
||||
/**
|
||||
* An interface which indicates this object is capable of launching targets.
|
||||
*
|
||||
* <p>
|
||||
* The targets this launcher creates ought to appear in its successors.
|
||||
*
|
||||
* @param <T> type for this
|
||||
@ -42,22 +41,20 @@ public interface DbgModelTargetAttacher extends DbgModelTargetObject, TargetAtta
|
||||
getModel().assertMine(DbgModelTargetAvailable.class, attachable);
|
||||
// TODO: This and the below new DbgProcessImpl seem to do the same thing
|
||||
// Both should be expressed the same way
|
||||
return getManager().addProcess().thenAccept(process -> {
|
||||
process.attach(available.getPid());
|
||||
}).exceptionally((exc) -> {
|
||||
return getModel().gateFuture(getManager().addProcess().thenCompose(process -> {
|
||||
return process.attach(available.getPid());
|
||||
}).exceptionally(exc -> {
|
||||
Msg.error(this, "attach failed");
|
||||
return null;
|
||||
});
|
||||
})).thenApply(__ -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> attach(long pid) {
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
DbgProcess process = new DbgProcessImpl(getManager());
|
||||
process.attach(pid).handle(seq::nextIgnore);
|
||||
}).finish().exceptionally((exc) -> {
|
||||
DbgProcess process = new DbgProcessImpl(getManager());
|
||||
return getModel().gateFuture(process.attach(pid).exceptionally(exc -> {
|
||||
Msg.error(this, "attach failed");
|
||||
return null;
|
||||
});
|
||||
})).thenApply(__ -> null);
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ public interface DbgModelTargetExecutionStateful
|
||||
changeAttributes(List.of(), Map.of( //
|
||||
STATE_ATTRIBUTE_NAME, state //
|
||||
), reason);
|
||||
getListeners().fire(TargetExecutionStateListener.class).executionStateChanged(this, state);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,16 +43,16 @@ public interface DbgModelTargetFocusScope extends DbgModelTargetObject, TargetFo
|
||||
// (but, of course, may then cause change in state)
|
||||
@Override
|
||||
public default CompletableFuture<Void> requestFocus(TargetObject obj) {
|
||||
return getManager().requestFocus(this, obj);
|
||||
return getModel().gateFuture(getManager().requestFocus(this, obj));
|
||||
}
|
||||
|
||||
public default CompletableFuture<Void> doRequestFocus(TargetObject obj) {
|
||||
if (getManager().isWaiting()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
getModel().assertMine(TargetObject.class, obj);
|
||||
if (obj.equals(getFocus())) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) {
|
||||
throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope");
|
||||
|
@ -31,12 +31,12 @@ public interface DbgModelTargetInterpreter extends DbgModelTargetObject, TargetI
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> execute(String cmd) {
|
||||
return getManager().console(cmd);
|
||||
return getModel().gateFuture(getManager().console(cmd));
|
||||
}
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<String> executeCapture(String cmd) {
|
||||
return getManager().consoleCapture(cmd);
|
||||
return getModel().gateFuture(getManager().consoleCapture(cmd));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetObject;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.target.TargetResumable;
|
||||
|
||||
/**
|
||||
@ -33,6 +34,9 @@ public interface DbgModelTargetResumable extends DbgModelTargetObject, TargetRes
|
||||
@Override
|
||||
public default CompletableFuture<Void> resume() {
|
||||
DbgProcess process = getManager().getCurrentProcess();
|
||||
if (process == null) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return process.cont();
|
||||
}
|
||||
|
||||
|
@ -66,20 +66,21 @@ public interface DbgModelTargetSteppable extends DbgModelTargetObject, TargetSte
|
||||
default:
|
||||
if (this instanceof DbgModelTargetThread) {
|
||||
DbgModelTargetThread targetThread = (DbgModelTargetThread) this;
|
||||
return targetThread.getThread().step(convertToDbg(kind));
|
||||
return getModel().gateFuture(targetThread.getThread().step(convertToDbg(kind)));
|
||||
}
|
||||
if (this instanceof DbgModelTargetProcess) {
|
||||
DbgModelTargetProcess targetProcess = (DbgModelTargetProcess) this;
|
||||
return targetProcess.getProcess().step(convertToDbg(kind));
|
||||
return getModel()
|
||||
.gateFuture(targetProcess.getProcess().step(convertToDbg(kind)));
|
||||
}
|
||||
return thread.step(convertToDbg(kind));
|
||||
return getModel().gateFuture(thread.step(convertToDbg(kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
DbgThread thread = getManager().getCurrentThread();
|
||||
return thread.step(args);
|
||||
return getModel().gateFuture(thread.step(args));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ package agent.dbgeng.model.iface2;
|
||||
import ghidra.dbg.target.TargetAttachable;
|
||||
|
||||
public interface DbgModelTargetAvailable extends DbgModelTargetObject, TargetAttachable {
|
||||
String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid";
|
||||
// TODO: DESCRIPTION, TYPE, USER?
|
||||
|
||||
public long getPid();
|
||||
|
||||
|
@ -22,8 +22,9 @@ import java.util.function.Function;
|
||||
import agent.dbgeng.manager.DbgEventsListenerAdapter;
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.dbg.target.TargetBreakpointContainer;
|
||||
import ghidra.dbg.target.TargetBreakpointLocationContainer;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.target.TargetBreakpointSpecContainer;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
|
||||
@ -34,8 +35,10 @@ import ghidra.program.model.address.AddressRange;
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class) },
|
||||
canonicalContainer = true)
|
||||
public interface DbgModelTargetBreakpointContainer
|
||||
extends DbgModelTargetObject, TargetBreakpointContainer, DbgEventsListenerAdapter {
|
||||
public interface DbgModelTargetBreakpointContainer extends DbgModelTargetObject, //
|
||||
TargetBreakpointSpecContainer, //
|
||||
TargetBreakpointLocationContainer, //
|
||||
DbgEventsListenerAdapter {
|
||||
|
||||
/*
|
||||
@Override
|
||||
@ -65,13 +68,13 @@ public interface DbgModelTargetBreakpointContainer
|
||||
else if (kinds.contains(TargetBreakpointKind.WRITE)) {
|
||||
fence.include(placer.apply(DbgBreakpointType.HW_WATCHPOINT));
|
||||
}
|
||||
if (kinds.contains(TargetBreakpointKind.EXECUTE)) {
|
||||
if (kinds.contains(TargetBreakpointKind.HW_EXECUTE)) {
|
||||
fence.include(placer.apply(DbgBreakpointType.HW_BREAKPOINT));
|
||||
}
|
||||
if (kinds.contains(TargetBreakpointKind.SOFTWARE)) {
|
||||
if (kinds.contains(TargetBreakpointKind.SW_EXECUTE)) {
|
||||
fence.include(placer.apply(DbgBreakpointType.BREAKPOINT));
|
||||
}
|
||||
return fence.ready();
|
||||
return getModel().gateFuture(fence.ready());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package agent.dbgeng.model.iface2;
|
||||
|
||||
import ghidra.dbg.attributes.TargetObjectList;
|
||||
import ghidra.dbg.target.TargetBreakpointLocation;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
@ -25,7 +24,4 @@ public interface DbgModelTargetBreakpointLocation
|
||||
@Override
|
||||
public Address getAddress();
|
||||
|
||||
@Override
|
||||
public TargetObjectList<?> getAffects();
|
||||
|
||||
}
|
||||
|
@ -21,9 +21,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetBptHelper;
|
||||
import ghidra.dbg.attributes.TargetObjectList;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet;
|
||||
import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public interface DbgModelTargetBreakpointSpec extends //
|
||||
@ -42,24 +41,24 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> delete() {
|
||||
return getManager().deleteBreakpoints(getNumber());
|
||||
return getModel().gateFuture(getManager().deleteBreakpoints(getNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> disable() {
|
||||
setEnabled(false, "Disabled");
|
||||
return getManager().disableBreakpoints(getNumber());
|
||||
return getModel().gateFuture(getManager().disableBreakpoints(getNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> enable() {
|
||||
setEnabled(true, "Enabled");
|
||||
return getManager().enableBreakpoints(getNumber());
|
||||
return getModel().gateFuture(getManager().enableBreakpoints(getNumber()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public default String getExpression() {
|
||||
return getBreakpointInfo().getLocation();
|
||||
return getBreakpointInfo().getExpression();
|
||||
}
|
||||
|
||||
public default long getNumber() {
|
||||
@ -70,9 +69,9 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
public default TargetBreakpointKindSet getKinds() {
|
||||
switch (getBreakpointInfo().getType()) {
|
||||
case BREAKPOINT:
|
||||
return TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE);
|
||||
return TargetBreakpointKindSet.of(TargetBreakpointKind.SW_EXECUTE);
|
||||
case HW_BREAKPOINT:
|
||||
return TargetBreakpointKindSet.of(TargetBreakpointKind.EXECUTE);
|
||||
return TargetBreakpointKindSet.of(TargetBreakpointKind.HW_EXECUTE);
|
||||
case HW_WATCHPOINT:
|
||||
return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE);
|
||||
case READ_WATCHPOINT:
|
||||
@ -106,7 +105,6 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
catch (AddressFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
map.put(AFFECTS_ATTRIBUTE_NAME, doGetAffects());
|
||||
map.put(SPEC_ATTRIBUTE_NAME, this);
|
||||
map.put(EXPRESSION_ATTRIBUTE_NAME, addstr);
|
||||
map.put(KINDS_ATTRIBUTE_NAME, getKinds());
|
||||
@ -124,14 +122,16 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
});
|
||||
}
|
||||
|
||||
public default Address doGetAddress() {
|
||||
DbgBreakpointInfo info = getBreakpointInfo();
|
||||
return getModel().getAddress("ram", info.addrAsLong());
|
||||
private long orZero(Long l) {
|
||||
if (l == null) {
|
||||
return 0;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
public default TargetObjectList<?> doGetAffects() {
|
||||
DbgModelTargetProcess process = getParentProcess();
|
||||
return TargetObjectList.of(process);
|
||||
public default Address doGetAddress() {
|
||||
DbgBreakpointInfo info = getBreakpointInfo();
|
||||
return getModel().getAddress("ram", orZero(info.getOffset()));
|
||||
}
|
||||
|
||||
public default void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo,
|
||||
@ -157,7 +157,6 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
setBreakpointEnabled(enabled);
|
||||
changeAttributes(List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled //
|
||||
), reason);
|
||||
getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -176,7 +175,10 @@ public interface DbgModelTargetBreakpointSpec extends //
|
||||
}
|
||||
|
||||
public default void breakpointHit() {
|
||||
getActions().fire.breakpointHit(this, getParentProcess(), null, this);
|
||||
DbgModelTargetThread targetThread =
|
||||
getParentProcess().getThreads().getTargetThread(getManager().getEventThread());
|
||||
getActions().fire.breakpointHit((DbgModelTargetBreakpointSpec) getProxy(), targetThread,
|
||||
null, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package agent.dbgeng.model.iface2;
|
||||
|
||||
public interface DbgModelTargetDebugContainer extends DbgModelTargetObject {
|
||||
import ghidra.dbg.target.TargetAggregate;
|
||||
|
||||
public interface DbgModelTargetDebugContainer extends DbgModelTargetObject, TargetAggregate {
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public interface DbgModelTargetModuleContainer
|
||||
@Override
|
||||
public CompletableFuture<? extends TargetModule> addSyntheticModule(String name);
|
||||
|
||||
public CompletableFuture<DbgModelTargetModule> getTargetModule(String name);
|
||||
public DbgModelTargetModule getTargetModule(String name);
|
||||
|
||||
public void libraryLoaded(String name);
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package agent.dbgeng.model.iface2;
|
||||
|
||||
public interface DbgModelTargetModuleSectionContainer extends DbgModelTargetObject {
|
||||
import ghidra.dbg.target.TargetSectionContainer;
|
||||
|
||||
public interface DbgModelTargetModuleSectionContainer
|
||||
extends DbgModelTargetObject, TargetSectionContainer {
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.AbstractDbgModel;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerModelListener;
|
||||
import ghidra.dbg.agent.InvalidatableTargetObjectIf;
|
||||
import ghidra.dbg.agent.SpiTargetObject;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
@ -54,17 +56,17 @@ public interface DbgModelTargetObject extends SpiTargetObject, InvalidatableTarg
|
||||
return impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<? extends Map<String, ? extends TargetObject>> fetchElements();
|
||||
|
||||
@Override
|
||||
public CompletableFuture<? extends Map<String, ?>> fetchAttributes();
|
||||
|
||||
public Delta<?, ?> changeAttributes(List<String> remove, Map<String, ?> add, String reason);
|
||||
|
||||
public CompletableFuture<? extends Map<String, ?>> requestNativeAttributes();
|
||||
|
||||
public ListenerSet<TargetObjectListener> getListeners();
|
||||
public default CompletableFuture<Void> requestAugmentedAttributes() {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
|
||||
public CompletableFuture<List<TargetObject>> requestNativeElements();
|
||||
|
||||
public ListenerSet<DebuggerModelListener> getListeners();
|
||||
|
||||
public DbgModelTargetSession getParentSession();
|
||||
|
||||
|
@ -19,8 +19,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugProcessId;
|
||||
import agent.dbgeng.dbgeng.DebugSystemObjects;
|
||||
import agent.dbgeng.manager.DbgEventsListenerAdapter;
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface1.*;
|
||||
import ghidra.dbg.target.TargetAggregate;
|
||||
@ -46,12 +45,12 @@ public interface DbgModelTargetProcess extends //
|
||||
|
||||
public void processStarted(Long pid);
|
||||
|
||||
public void processExited(Long exitCode);
|
||||
|
||||
public DbgModelTargetThreadContainer getThreads();
|
||||
|
||||
public DbgModelTargetModuleContainer getModules();
|
||||
|
||||
public void threadStateChangedSpecific(DbgThread thread, DbgState state);
|
||||
|
||||
public default DbgProcess getProcess() {
|
||||
DbgManagerImpl manager = getManager();
|
||||
DebugSystemObjects so = manager.getSystemObjects();
|
||||
@ -78,4 +77,5 @@ public interface DbgModelTargetProcess extends //
|
||||
}
|
||||
return manager.selectProcess(process);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,11 @@
|
||||
*/
|
||||
package agent.dbgeng.model.iface2;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import agent.dbgeng.manager.impl.DbgRegister;
|
||||
import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.util.ConversionUtils;
|
||||
|
||||
public interface DbgModelTargetRegister extends DbgModelTargetObject, TargetRegister {
|
||||
|
||||
@ -25,4 +28,10 @@ public interface DbgModelTargetRegister extends DbgModelTargetObject, TargetRegi
|
||||
|
||||
public DbgRegister getRegister();
|
||||
|
||||
public default byte[] getBytes() {
|
||||
String val = (String) getCachedAttributes().get(VALUE_ATTRIBUTE_NAME);
|
||||
BigInteger value = new BigInteger(val, 16);
|
||||
return ConversionUtils.bigIntegerToBytes(16, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,13 +17,15 @@ package agent.dbgeng.model.iface2;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.impl.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.DebuggerModelListener;
|
||||
import ghidra.dbg.error.DebuggerRegisterAccessException;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.dbg.util.ConversionUtils;
|
||||
@ -34,6 +36,11 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
|
||||
|
||||
public DbgModelTargetRegister getTargetRegister(DbgRegister register);
|
||||
|
||||
public default void threadStateChangedSpecific(DbgState state, DbgReason reason) {
|
||||
readRegistersNamed(getCachedElements().keySet());
|
||||
}
|
||||
|
||||
// NB: Does anyone call this anymore?
|
||||
@Override
|
||||
public default CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
|
||||
Collection<String> names) {
|
||||
@ -84,9 +91,9 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
|
||||
reg.setModified(value.toString(16).equals(oldval));
|
||||
}
|
||||
}
|
||||
ListenerSet<TargetObjectListener> listeners = getListeners();
|
||||
ListenerSet<DebuggerModelListener> listeners = getListeners();
|
||||
if (listeners != null) {
|
||||
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result);
|
||||
listeners.fire.registersUpdated(getProxy(), result);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@ -96,7 +103,7 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
|
||||
public default CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) {
|
||||
DbgThread thread = getParentThread().getThread();
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
fetchElements().handle(seq::nextIgnore);
|
||||
requestNativeElements().handle(seq::nextIgnore);
|
||||
}).then(seq -> {
|
||||
thread.listRegisters().handle(seq::next);
|
||||
}, TypeSpec.cls(DbgRegisterSet.class)).then((regset, seq) -> {
|
||||
@ -115,8 +122,26 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
|
||||
getParentThread().getThread().writeRegisters(toWrite).handle(seq::next);
|
||||
// TODO: Should probably filter only effective and normalized writes in the callback
|
||||
}).then(seq -> {
|
||||
getListeners().fire(TargetRegisterBankListener.class).registersUpdated(this, values);
|
||||
getListeners().fire.registersUpdated(getProxy(), values);
|
||||
seq.exit();
|
||||
}).finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public default Map<String, byte[]> getCachedRegisters() {
|
||||
return getValues();
|
||||
}
|
||||
|
||||
public default Map<String, byte[]> getValues() {
|
||||
Map<String, byte[]> result = new HashMap<>();
|
||||
for (Entry<String, ?> entry : this.getCachedAttributes().entrySet()) {
|
||||
if (entry.getValue() instanceof DbgModelTargetRegister) {
|
||||
DbgModelTargetRegister reg = (DbgModelTargetRegister) entry.getValue();
|
||||
byte[] bytes = reg.getBytes();
|
||||
result.put(entry.getKey(), bytes);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ import agent.dbgeng.manager.impl.DbgRegister;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.dbg.target.TargetRegisterContainer;
|
||||
|
||||
public interface DbgModelTargetRegisterContainerAndBank
|
||||
extends DbgModelTargetObject, TargetRegisterContainer, TargetRegisterBank {
|
||||
public interface DbgModelTargetRegisterContainerAndBank extends //
|
||||
DbgModelTargetObject, //
|
||||
TargetRegisterContainer, //
|
||||
TargetRegisterBank {
|
||||
|
||||
public DbgModelTargetRegister getTargetRegister(DbgRegister register);
|
||||
|
||||
|
@ -26,7 +26,6 @@ import agent.dbgeng.model.iface1.*;
|
||||
import ghidra.dbg.target.TargetAggregate;
|
||||
import ghidra.dbg.target.TargetConsole;
|
||||
import ghidra.dbg.target.TargetConsole.Channel;
|
||||
import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public interface DbgModelTargetSession extends //
|
||||
@ -38,7 +37,6 @@ public interface DbgModelTargetSession extends //
|
||||
DbgModelTargetResumable, //
|
||||
DbgEventsListenerAdapter, //
|
||||
DbgModelSelectableObject, //
|
||||
TargetFocusScopeListener, //
|
||||
TargetAggregate {
|
||||
|
||||
DbgModelTargetProcessContainer getProcesses();
|
||||
@ -69,7 +67,7 @@ public interface DbgModelTargetSession extends //
|
||||
if (output.contains("loaded *kernel* extension dll for usermode")) {
|
||||
return;
|
||||
}
|
||||
getListeners().fire(TargetInterpreterListener.class).consoleOutput(this, chan, output);
|
||||
getListeners().fire.consoleOutput(getProxy(), chan, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,27 +61,32 @@ public interface DbgModelTargetStackFrame extends //
|
||||
if (attrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
TargetObject attributes = (TargetObject) attrs.get("Attributes");
|
||||
DbgModelTargetObject attributes = (DbgModelTargetObject) attrs.get("Attributes");
|
||||
if (attributes == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
return attributes.fetchAttributes(true);
|
||||
}).thenCompose(subattrs -> {
|
||||
if (subattrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
TargetObject frameNumber = (TargetObject) subattrs.get("FrameNumber");
|
||||
return frameNumber.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(noval -> {
|
||||
String nostr = noval.toString();
|
||||
TargetObject instructionOffset = (TargetObject) subattrs.get("InstructionOffset");
|
||||
return instructionOffset.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(pcval -> {
|
||||
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
|
||||
String pcstr = pcval.toString();
|
||||
long pc = Long.parseUnsignedLong(pcstr, 16);
|
||||
map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc));
|
||||
String display = String.format("#%s 0x%s", nostr, pcstr);
|
||||
map.put(DISPLAY_ATTRIBUTE_NAME, display);
|
||||
setModified(map, !display.equals(oldval));
|
||||
return attributes.requestAugmentedAttributes().thenCompose(ax -> {
|
||||
Map<String, ?> subattrs = attributes.getCachedAttributes();
|
||||
if (subattrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
DbgModelTargetObject frameNumber =
|
||||
(DbgModelTargetObject) subattrs.get("FrameNumber");
|
||||
return frameNumber.requestAugmentedAttributes().thenCompose(bx -> {
|
||||
Object noval = frameNumber.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
|
||||
String nostr = noval.toString();
|
||||
DbgModelTargetObject instructionOffset =
|
||||
(DbgModelTargetObject) subattrs.get("InstructionOffset");
|
||||
return instructionOffset.requestAugmentedAttributes().thenAccept(cx -> {
|
||||
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
|
||||
Object pcval = instructionOffset.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
|
||||
String pcstr = pcval.toString();
|
||||
long pc = Long.parseUnsignedLong(pcstr, 16);
|
||||
map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc));
|
||||
String display = String.format("#%s 0x%s", nostr, pcstr);
|
||||
map.put(DISPLAY_ATTRIBUTE_NAME, display);
|
||||
setModified(map, !display.equals(oldval));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -18,8 +18,6 @@ package agent.dbgeng.model.iface2;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
||||
public interface DbgModelTargetTTD extends DbgModelTargetObject {
|
||||
|
||||
@Override
|
||||
@ -28,25 +26,28 @@ public interface DbgModelTargetTTD extends DbgModelTargetObject {
|
||||
if (attrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
TargetObject attributes = (TargetObject) attrs.get("Position");
|
||||
DbgModelTargetObject attributes = (DbgModelTargetObject) attrs.get("Position");
|
||||
if (attributes == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
return attributes.fetchAttributes(true);
|
||||
}).thenCompose(subattrs -> {
|
||||
if (subattrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
TargetObject seq = (TargetObject) subattrs.get("Sequence");
|
||||
return seq.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(sqval -> {
|
||||
String sqstr = sqval.toString();
|
||||
TargetObject steps = (TargetObject) subattrs.get("Steps");
|
||||
return steps.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(stval -> {
|
||||
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
|
||||
String ststr = stval.toString();
|
||||
String display = String.format("TTD %s:%s", sqstr, ststr);
|
||||
map.put(DISPLAY_ATTRIBUTE_NAME, display);
|
||||
setModified(map, !display.equals(oldval));
|
||||
return attributes.requestAugmentedAttributes().thenCompose(ax -> {
|
||||
Map<String, ?> subattrs = attributes.getCachedAttributes();
|
||||
if (subattrs == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
DbgModelTargetObject seq = (DbgModelTargetObject) subattrs.get("Sequence");
|
||||
return seq.requestAugmentedAttributes().thenCompose(bx -> {
|
||||
Object sqval = seq.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
|
||||
String sqstr = sqval.toString();
|
||||
DbgModelTargetObject steps = (DbgModelTargetObject) subattrs.get("Steps");
|
||||
return steps.requestAugmentedAttributes().thenAccept(cx -> {
|
||||
Object stval = steps.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
|
||||
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
|
||||
String ststr = stval.toString();
|
||||
String display = String.format("TTD %s:%s", sqstr, ststr);
|
||||
map.put(DISPLAY_ATTRIBUTE_NAME, display);
|
||||
setModified(map, !display.equals(oldval));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -55,7 +55,7 @@ public interface DbgModelTargetThread extends //
|
||||
}
|
||||
}
|
||||
|
||||
public void threadStateChanged(DbgState state, DbgReason reason);
|
||||
public void threadStateChangedSpecific(DbgState state, DbgReason reason);
|
||||
|
||||
@Override
|
||||
public default CompletableFuture<Void> select() {
|
||||
|
@ -16,21 +16,30 @@
|
||||
package agent.dbgeng.model.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugSessionId;
|
||||
import agent.dbgeng.manager.DbgManager;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.manager.impl.DbgSessionImpl;
|
||||
import agent.dbgeng.model.AbstractDbgModel;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSession;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSessionContainer;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.DebuggerModelClosedReason;
|
||||
import ghidra.dbg.DebuggerObjectModelWithMemory;
|
||||
import ghidra.dbg.error.DebuggerModelTerminatingException;
|
||||
import ghidra.dbg.target.TargetMemory;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.AnnotatedSchemaContext;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class DbgModelImpl extends AbstractDbgModel {
|
||||
public class DbgModelImpl extends AbstractDbgModel implements DebuggerObjectModelWithMemory {
|
||||
// TODO: Need some minimal memory modeling per architecture on the model/agent side.
|
||||
// The model must convert to and from Ghidra's address space names
|
||||
protected static final String SPACE_NAME = "ram";
|
||||
@ -51,6 +60,8 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||
|
||||
protected final CompletableFuture<DbgModelTargetRootImpl> completedRoot;
|
||||
|
||||
protected Map<Object, TargetObject> objectMap = new HashMap<>();
|
||||
|
||||
public DbgModelImpl() {
|
||||
this.dbg = DbgManager.newInstance();
|
||||
//System.out.println(XmlSchemaContext.serialize(SCHEMA_CTX));
|
||||
@ -79,7 +90,7 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> startDbgEng(String[] args) {
|
||||
return dbg.start(args);
|
||||
return dbg.start(args).thenApplyAsync(__ -> null, clientExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -89,6 +100,8 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||
|
||||
@Override
|
||||
public void terminate() throws IOException {
|
||||
listeners.fire.modelClosed(DebuggerModelClosedReason.NORMAL);
|
||||
root.invalidateSubtree(root, "Dbgeng is terminating");
|
||||
dbg.terminate();
|
||||
}
|
||||
|
||||
@ -113,6 +126,10 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||
terminate();
|
||||
return super.close();
|
||||
}
|
||||
catch (RejectedExecutionException e) {
|
||||
reportError(this, "Model is already closing", e);
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
return CompletableFuture.failedFuture(t);
|
||||
}
|
||||
@ -122,4 +139,39 @@ public class DbgModelImpl extends AbstractDbgModel {
|
||||
public DbgModelTargetSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetMemory getMemory(TargetObject target, Address address, int length) {
|
||||
if (target instanceof DbgModelTargetProcess) {
|
||||
DbgModelTargetProcess process = (DbgModelTargetProcess) target;
|
||||
return new DbgModelTargetMemoryContainerImpl(process);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addModelObject(Object object, TargetObject targetObject) {
|
||||
objectMap.put(object, targetObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetObject getModelObject(Object object) {
|
||||
return objectMap.get(object);
|
||||
}
|
||||
|
||||
public void deleteModelObject(Object object) {
|
||||
objectMap.remove(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CompletableFuture<T> gateFuture(CompletableFuture<T> future) {
|
||||
return super.gateFuture(future).exceptionally(ex -> {
|
||||
for (Throwable cause = ex; cause != null; cause = cause.getCause()) {
|
||||
if (cause instanceof RejectedExecutionException) {
|
||||
throw new DebuggerModelTerminatingException("dbgeng is terminating", ex);
|
||||
}
|
||||
}
|
||||
return ExceptionUtils.rethrow(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,16 +20,12 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.model.AbstractDbgModel;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
|
||||
public enum DbgModelImplUtils {
|
||||
;
|
||||
public static CompletableFuture<Void> launch(AbstractDbgModel impl, DbgProcess process,
|
||||
List<String> args) {
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
process.fileExecAndSymbols(args.get(0));
|
||||
}).finish();
|
||||
return process.fileExecAndSymbols(args.get(0));
|
||||
}
|
||||
|
||||
public static <V> V noDupMerge(V first, V second) {
|
||||
|
@ -25,13 +25,19 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "AvailableContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetAvailableImpl.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "AvailableContainer",
|
||||
elements = {
|
||||
@TargetElementType(type = DbgModelTargetAvailableImpl.class)
|
||||
},
|
||||
elementResync = ResyncMode.ALWAYS,
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
},
|
||||
canonicalContainer = true)
|
||||
public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetAvailableContainer {
|
||||
|
||||
@ -40,9 +46,6 @@ public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectIm
|
||||
|
||||
public DbgModelTargetAvailableContainerImpl(DbgModelTargetRoot root) {
|
||||
super(root.getModel(), root, "Available", "AvailableContainer");
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.SOLICITED //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,17 +23,17 @@ import agent.dbgeng.model.iface2.DbgModelTargetAvailableContainer;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "Available", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Available",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetAvailable {
|
||||
|
||||
protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid";
|
||||
// TODO: DESCRIPTION, TYPE, USER?
|
||||
|
||||
protected static String indexAttachable(int pid) {
|
||||
return Integer.toHexString(pid);
|
||||
}
|
||||
@ -51,8 +51,7 @@ public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
this.changeAttributes(List.of(), List.of(), Map.of(//
|
||||
PID_ATTRIBUTE_NAME, (long) pid, //
|
||||
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim(),
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim() //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
@ -62,8 +61,7 @@ public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
this.changeAttributes(List.of(), List.of(), Map.of(//
|
||||
PID_ATTRIBUTE_NAME, (long) pid, //
|
||||
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "BreakpointContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetBreakpointSpecImpl.class) //
|
||||
@ -40,8 +39,6 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
|
||||
protected static final TargetBreakpointKindSet SUPPORTED_KINDS =
|
||||
TargetBreakpointKindSet.of(TargetBreakpointKind.values());
|
||||
|
||||
private final Map<Long, DbgModelTargetBreakpointSpec> specsByNumber = new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetBreakpointContainerImpl(DbgModelTargetDebugContainer debug) {
|
||||
super(debug.getModel(), debug, "Breakpoints", "BreakpointContainer");
|
||||
|
||||
@ -66,9 +63,8 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
|
||||
|
||||
@Override
|
||||
public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) {
|
||||
synchronized (this) {
|
||||
getSpecsByNumber().remove(info.getNumber());
|
||||
}
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
impl.deleteModelObject(info.getDebugBreakpoint());
|
||||
changeElements(List.of( //
|
||||
DbgModelTargetBreakpointSpecImpl.indexBreakpoint(info) //
|
||||
), List.of(), Map.of(), "Deleted");
|
||||
@ -76,17 +72,20 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
|
||||
|
||||
@Override
|
||||
public void breakpointHit(DbgBreakpointInfo info, DbgCause cause) {
|
||||
DbgModelTargetThread targetThread =
|
||||
getParentProcess().getThreads().getTargetThread(getManager().getEventThread());
|
||||
DbgModelTargetBreakpointSpec spec = getTargetBreakpointSpec(info);
|
||||
listeners.fire(TargetBreakpointListener.class)
|
||||
.breakpointHit(this, getParentProcess(), null, spec, spec);
|
||||
listeners.fire.breakpointHit(getProxy(), targetThread, null, spec, spec);
|
||||
spec.breakpointHit();
|
||||
}
|
||||
|
||||
public DbgModelTargetBreakpointSpec getTargetBreakpointSpec(DbgBreakpointInfo info) {
|
||||
synchronized (this) {
|
||||
return getSpecsByNumber().computeIfAbsent(info.getNumber(),
|
||||
i -> new DbgModelTargetBreakpointSpecImpl(this, info));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(info.getDebugBreakpoint());
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetBreakpointSpec) modelObject;
|
||||
}
|
||||
return new DbgModelTargetBreakpointSpecImpl(this, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,8 +102,4 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
|
||||
setElements(specs, Map.of(), "Refreshed");
|
||||
});
|
||||
}
|
||||
|
||||
public Map<Long, DbgModelTargetBreakpointSpec> getSpecsByNumber() {
|
||||
return specsByNumber;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointSpec;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetBreakpointLocation;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec;
|
||||
import ghidra.dbg.target.schema.TargetAttributeType;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
@ -56,20 +57,18 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
public void changeAttributeSet(String reason) {
|
||||
this.changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, "[" + info.getNumber() + "] " + info.getLocation(), //
|
||||
DISPLAY_ATTRIBUTE_NAME, "[" + info.getNumber() + "] " + info.getExpression(), //
|
||||
ADDRESS_ATTRIBUTE_NAME, doGetAddress(), //
|
||||
LENGTH_ATTRIBUTE_NAME, info.getSize(), //
|
||||
AFFECTS_ATTRIBUTE_NAME, doGetAffects(), //
|
||||
SPEC_ATTRIBUTE_NAME, this, //
|
||||
EXPRESSION_ATTRIBUTE_NAME, info.getLocation(), //
|
||||
EXPRESSION_ATTRIBUTE_NAME, info.getExpression(), //
|
||||
KINDS_ATTRIBUTE_NAME, getKinds() //
|
||||
), reason);
|
||||
this.changeAttributes(List.of(), List.of(), Map.of( //
|
||||
BPT_TYPE_ATTRIBUTE_NAME, info.getType().name(), //
|
||||
BPT_DISP_ATTRIBUTE_NAME, info.getDisp().name(), //
|
||||
BPT_PENDING_ATTRIBUTE_NAME, info.getPending(), //
|
||||
BPT_TIMES_ATTRIBUTE_NAME, info.getTimes(), //
|
||||
TargetObject.UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
BPT_TIMES_ATTRIBUTE_NAME, info.getTimes() //
|
||||
), reason);
|
||||
}
|
||||
|
||||
@ -84,11 +83,13 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetBreakpointSpecImpl(DbgModelTargetBreakpointContainer breakpoints,
|
||||
DbgBreakpointInfo info) {
|
||||
super(breakpoints.getModel(), breakpoints, keyBreakpoint(info), "BreakpointSpec");
|
||||
this.setBreakpointInfo(info);
|
||||
this.getModel().addModelObject(info.getDebugBreakpoint(), this);
|
||||
//this.setBreakpointInfo(info);
|
||||
|
||||
updateInfo(null, info, "Created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, String reason) {
|
||||
synchronized (this) {
|
||||
assert oldInfo == getBreakpointInfo();
|
||||
@ -116,18 +117,19 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
||||
/**
|
||||
* Update the enabled field
|
||||
*
|
||||
* This does not actually toggle the breakpoint. It just updates the field and calls the proper
|
||||
* listeners. To actually toggle the breakpoint, use {@link #toggle(boolean)} instead, which if
|
||||
* effective, should eventually cause this method to be called.
|
||||
* This does not actually toggle the breakpoint. It just updates the field
|
||||
* and calls the proper listeners. To actually toggle the breakpoint, use
|
||||
* {@link #toggle(boolean)} instead, which if effective, should eventually
|
||||
* cause this method to be called.
|
||||
*
|
||||
* @param enabled true if enabled, false if disabled
|
||||
* @param reason a description of the cause (not really used, yet)
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean enabled, String reason) {
|
||||
setBreakpointEnabled(enabled);
|
||||
changeAttributes(List.of(), List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled //
|
||||
), reason);
|
||||
getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,6 +142,7 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerSet<TargetBreakpointAction> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
@ -23,13 +23,32 @@ import agent.dbgeng.model.iface2.DbgModelTargetRoot;
|
||||
import ghidra.dbg.target.schema.TargetAttributeType;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "ConnectorContainer", attributes = { //
|
||||
@TargetAttributeType(name = "Launch process", type = DbgModelTargetProcessLaunchConnectorImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(name = "Attach to process", type = DbgModelTargetProcessAttachConnectorImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(name = "Load trace/dump", type = DbgModelTargetTraceOrDumpConnectorImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(name = "Attach to kernel", type = DbgModelTargetKernelConnectorImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "ConnectorContainer",
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Launch process",
|
||||
type = DbgModelTargetProcessLaunchConnectorImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Attach to process",
|
||||
type = DbgModelTargetProcessAttachConnectorImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Load trace/dump",
|
||||
type = DbgModelTargetTraceOrDumpConnectorImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Attach to kernel",
|
||||
type = DbgModelTargetKernelConnectorImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
},
|
||||
canonicalContainer = true)
|
||||
public class DbgModelTargetConnectorContainerImpl extends DbgModelTargetObjectImpl {
|
||||
|
||||
protected final DbgModelTargetRoot root;
|
||||
|
@ -23,10 +23,17 @@ import agent.dbgeng.model.iface2.DbgModelTargetProcess;
|
||||
import ghidra.dbg.target.schema.TargetAttributeType;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "DebugContainer", attributes = { //
|
||||
@TargetAttributeType(name = "Breakpoints", type = DbgModelTargetBreakpointContainerImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "DebugContainer",
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Breakpoints",
|
||||
type = DbgModelTargetBreakpointContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
},
|
||||
canonicalContainer = true)
|
||||
public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetDebugContainer {
|
||||
|
||||
|
@ -28,11 +28,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "KernelConnector", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "KernelConnector",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetConnector {
|
||||
|
||||
@ -47,8 +50,7 @@ public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()) //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import agent.dbgeng.manager.DbgModuleMemory;
|
||||
import agent.dbgeng.manager.cmd.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||
import ghidra.dbg.error.DebuggerModelAccessException;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
@ -34,15 +35,9 @@ import ghidra.dbg.target.schema.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Memory",
|
||||
elements = {
|
||||
@TargetElementType(type = DbgModelTargetMemoryRegionImpl.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
},
|
||||
canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "Memory", elements = {
|
||||
@TargetElementType(type = DbgModelTargetMemoryRegionImpl.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetMemoryContainer {
|
||||
|
||||
@ -58,7 +53,10 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> requestElements(boolean refresh) {
|
||||
//return CompletableFuture.completedFuture(null);
|
||||
DbgModelTargetProcess targetProcess = getParentProcess();
|
||||
if (!targetProcess.getProcess().equals(getManager().getCurrentProcess())) {
|
||||
return AsyncUtils.NIL;
|
||||
}
|
||||
return listMemory().thenAccept(byName -> {
|
||||
List<TargetObject> sections;
|
||||
synchronized (this) {
|
||||
@ -111,7 +109,7 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
if (range == null) {
|
||||
throw new DebuggerMemoryAccessException("Cannot read at " + address);
|
||||
}
|
||||
listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, buf.array());
|
||||
listeners.fire.memoryUpdated(getProxy(), address, buf.array());
|
||||
return Arrays.copyOf(buf.array(), (int) (range.upperEndpoint() - range.lowerEndpoint()));
|
||||
}
|
||||
|
||||
@ -128,11 +126,15 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
}
|
||||
|
||||
private void writeAssist(Address address, byte[] data) {
|
||||
listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data);
|
||||
listeners.fire.memoryUpdated(getProxy(), address, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> readMemory(Address address, int length) {
|
||||
return model.gateFuture(doReadMemory(address, length));
|
||||
}
|
||||
|
||||
protected CompletableFuture<byte[]> doReadMemory(Address address, int length) {
|
||||
DbgManagerImpl manager = getManager();
|
||||
if (manager.isWaiting()) {
|
||||
throw new DebuggerModelAccessException(
|
||||
@ -197,6 +199,10 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeMemory(Address address, byte[] data) {
|
||||
return model.gateFuture(doWriteMemory(address, data));
|
||||
}
|
||||
|
||||
protected CompletableFuture<Void> doWriteMemory(Address address, byte[] data) {
|
||||
DbgManagerImpl manager = getManager();
|
||||
if (manager.isWaiting()) {
|
||||
throw new DebuggerModelAccessException(
|
||||
@ -252,16 +258,6 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
public void invalidateMemoryCaches() {
|
||||
listeners.fire.invalidateCacheRequested(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunning() {
|
||||
invalidateMemoryCaches();
|
||||
setAccessible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update() {
|
||||
requestElements(true);
|
||||
|
@ -26,22 +26,18 @@ import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "MemoryRegion", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType( //
|
||||
name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, //
|
||||
type = DbgModelTargetMemoryContainerImpl.class), //
|
||||
@TargetAttributeType(name = "BaseAddress", type = Address.class), //
|
||||
@TargetAttributeType(name = "EndAddress", type = Address.class), //
|
||||
@TargetAttributeType(name = "RegionSize", type = String.class), //
|
||||
@TargetAttributeType(name = "AllocationBase", type = Address.class), //
|
||||
@TargetAttributeType(name = "AllocationProtect", type = String.class), //
|
||||
@TargetAttributeType(name = "Protect", type = String.class), //
|
||||
@TargetAttributeType(name = "State", type = String.class), //
|
||||
@TargetAttributeType(name = "Type", type = String.class), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "MemoryRegion", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, type = DbgModelTargetMemoryContainerImpl.class),
|
||||
@TargetAttributeType(name = "BaseAddress", type = Address.class),
|
||||
@TargetAttributeType(name = "EndAddress", type = Address.class),
|
||||
@TargetAttributeType(name = "RegionSize", type = String.class),
|
||||
@TargetAttributeType(name = "AllocationBase", type = Address.class),
|
||||
@TargetAttributeType(name = "AllocationProtect", type = String.class),
|
||||
@TargetAttributeType(name = "Protect", type = String.class),
|
||||
@TargetAttributeType(name = "State", type = String.class),
|
||||
@TargetAttributeType(name = "Type", type = String.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetMemoryRegion {
|
||||
|
||||
@ -64,6 +60,7 @@ public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetMemoryRegionImpl(DbgModelTargetMemoryContainer memory,
|
||||
DbgModuleMemory region) {
|
||||
super(memory.getModel(), memory, keySection(region), "Region");
|
||||
this.getModel().addModelObject(region, this);
|
||||
this.section = region;
|
||||
|
||||
this.range = doGetRange(section);
|
||||
@ -92,8 +89,7 @@ public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
|
||||
RANGE_ATTRIBUTE_NAME, doGetRange(section), //
|
||||
READABLE_ATTRIBUTE_NAME, isReadable(), //
|
||||
WRITABLE_ATTRIBUTE_NAME, isWritable(), //
|
||||
EXECUTABLE_ATTRIBUTE_NAME, isExecutable(), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
EXECUTABLE_ATTRIBUTE_NAME, isExecutable() //
|
||||
), "Initialized");
|
||||
|
||||
AddressSpace space = getModel().getAddressSpace("ram");
|
||||
|
@ -22,42 +22,36 @@ import agent.dbgeng.manager.DbgModule;
|
||||
import agent.dbgeng.manager.DbgProcess;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetModule;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetModuleContainer;
|
||||
import ghidra.async.AsyncFence;
|
||||
import ghidra.async.AsyncLazyMap;
|
||||
import ghidra.dbg.target.TargetModule;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
|
||||
import ghidra.lifecycle.Internal;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "ModuleContainer",
|
||||
elements = { //
|
||||
@TargetElementType(type = DbgModelTargetModuleImpl.class) //
|
||||
},
|
||||
attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
},
|
||||
canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "ModuleContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetModuleImpl.class) //
|
||||
}, //
|
||||
elementResync = ResyncMode.ONCE, //
|
||||
attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetModuleContainer {
|
||||
// NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO.
|
||||
|
||||
protected final DbgModelTargetProcessImpl targetProcess;
|
||||
protected final DbgProcess process;
|
||||
|
||||
// TODO: Is it possible to load the same object twice?
|
||||
protected final AsyncLazyMap<String, DbgModelTargetModule> modulesByName =
|
||||
new AsyncLazyMap<String, DbgModelTargetModule>(new HashMap<>(), this::doGetTargetModule);
|
||||
|
||||
public DbgModelTargetModuleContainerImpl(DbgModelTargetProcessImpl process) {
|
||||
super(process.getModel(), process, "Modules", "ModuleContainer");
|
||||
this.targetProcess = process;
|
||||
this.process = process.process;
|
||||
requestElements(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Internal
|
||||
public void libraryLoaded(String name) {
|
||||
CompletableFuture<DbgModelTargetModule> module;
|
||||
DbgModelTargetModule module;
|
||||
synchronized (this) {
|
||||
/**
|
||||
* It's not a good idea to remove "stale" entries. If the entry's already present, it's
|
||||
@ -65,32 +59,26 @@ public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl
|
||||
* sections loaded. Removing it will cause it to load all module sections again!
|
||||
*/
|
||||
//modulesByName.remove(name);
|
||||
module = doGetTargetModule(name);
|
||||
module = getTargetModule(name);
|
||||
}
|
||||
module.thenAccept(mod -> {
|
||||
changeElements(List.of(), List.of(mod), Map.of(), "Loaded");
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, null, TargetEventType.MODULE_LOADED, "Library " + name + " loaded",
|
||||
List.of(mod));
|
||||
}).exceptionally(e -> {
|
||||
Msg.error(this, "Problem getting module for library load: " + name, e);
|
||||
return null;
|
||||
});
|
||||
TargetThread eventThread =
|
||||
(TargetThread) getModel().getModelObject(getManager().getEventThread());
|
||||
changeElements(List.of(), List.of(module), Map.of(), "Loaded");
|
||||
getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_LOADED,
|
||||
"Library " + name + " loaded", List.of(module));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Internal
|
||||
public void libraryUnloaded(String name) {
|
||||
if (!modulesByName.containsKey(name)) {
|
||||
return;
|
||||
}
|
||||
modulesByName.get(name).thenAccept(mod -> {
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, null, TargetEventType.MODULE_UNLOADED,
|
||||
"Library " + name + " unloaded", List.of(mod));
|
||||
});
|
||||
synchronized (this) {
|
||||
modulesByName.remove(name);
|
||||
DbgModelTargetModule targetModule = getTargetModule(name);
|
||||
if (targetModule != null) {
|
||||
TargetThread eventThread =
|
||||
(TargetThread) getModel().getModelObject(getManager().getEventThread());
|
||||
getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_UNLOADED,
|
||||
"Library " + name + " unloaded", List.of(targetModule));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
impl.deleteModelObject(targetModule.getDbgModule());
|
||||
}
|
||||
changeElements(List.of(name), List.of(), Map.of(), "Unloaded");
|
||||
}
|
||||
@ -108,34 +96,28 @@ public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl
|
||||
@Override
|
||||
public CompletableFuture<Void> requestElements(boolean refresh) {
|
||||
List<TargetObject> result = new ArrayList<>();
|
||||
return process.listModules().thenCompose(byName -> {
|
||||
AsyncFence fence = new AsyncFence();
|
||||
return process.listModules().thenAccept(byName -> {
|
||||
synchronized (this) {
|
||||
modulesByName.retainKeys(byName.keySet());
|
||||
for (Map.Entry<String, DbgModule> ent : byName.entrySet()) {
|
||||
fence.include(getTargetModule(ent.getKey()).thenAccept(module -> {
|
||||
result.add(module);
|
||||
}));
|
||||
result.add(getTargetModule(ent.getKey()));
|
||||
}
|
||||
}
|
||||
return fence.ready();
|
||||
}).thenAccept(__ -> {
|
||||
changeElements(List.of(), result, Map.of(), "Refreshed");
|
||||
});
|
||||
}
|
||||
|
||||
protected CompletableFuture<DbgModelTargetModule> doGetTargetModule(String name) {
|
||||
public DbgModelTargetModule getTargetModule(String name) {
|
||||
// Only get here from libraryLoaded or getElements. The known list should be fresh.
|
||||
DbgModule module = process.getKnownModules().get(name);
|
||||
if (module == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return null;
|
||||
}
|
||||
return CompletableFuture.completedFuture(new DbgModelTargetModuleImpl(this, module));
|
||||
//TODO: return module.listSections().thenApply(__ -> new DbgModelTargetModule(this, module));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(module);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetModule) modelObject;
|
||||
}
|
||||
return new DbgModelTargetModuleImpl(this, module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DbgModelTargetModule> getTargetModule(String name) {
|
||||
return modulesByName.get(name);
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,14 @@ import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "Module", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(name = "Symbols", type = DbgModelTargetSymbolContainerImpl.class, required = true, fixed = true), //
|
||||
@TargetAttributeType(name = "BaseAddress", type = Address.class), //
|
||||
@TargetAttributeType(name = "ImageName", type = String.class), //
|
||||
@TargetAttributeType(name = "TimeStamp", type = Integer.class), //
|
||||
@TargetAttributeType(name = "Len", type = String.class), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "Module", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Symbols", type = DbgModelTargetSymbolContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "BaseAddress", type = Address.class),
|
||||
@TargetAttributeType(name = "ImageName", type = String.class),
|
||||
@TargetAttributeType(name = "TimeStamp", type = Integer.class),
|
||||
@TargetAttributeType(name = "Len", type = String.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetModule {
|
||||
protected static String indexModule(DbgModule module) {
|
||||
@ -52,6 +50,7 @@ public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
public DbgModelTargetModuleImpl(DbgModelTargetModuleContainerImpl modules, DbgModule module) {
|
||||
super(modules.getModel(), modules, keyModule(module), "Module");
|
||||
this.getModel().addModelObject(module, this);
|
||||
this.process = modules.process;
|
||||
this.module = module;
|
||||
|
||||
|
@ -15,28 +15,22 @@
|
||||
*/
|
||||
package agent.dbgeng.model.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import agent.dbgeng.manager.DbgModule;
|
||||
import agent.dbgeng.manager.DbgModuleSection;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "SectionContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetModuleSectionImpl.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "SectionContainer", elements = {
|
||||
@TargetElementType(type = DbgModelTargetModuleSectionImpl.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetModuleSectionContainer {
|
||||
|
||||
protected final DbgModule module;
|
||||
|
||||
protected final Map<Long, DbgModelTargetModuleSectionImpl> sectionsByStart =
|
||||
new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetModuleSectionContainerImpl(DbgModelTargetModule module) {
|
||||
super(module.getModel(), module, "Sections", "ModuleSections");
|
||||
this.module = module.getDbgModule();
|
||||
@ -61,8 +55,12 @@ public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObje
|
||||
}
|
||||
|
||||
protected synchronized DbgModelTargetModuleSection getModuleSection(DbgModuleSection section) {
|
||||
return sectionsByStart.computeIfAbsent(section.getStart(),
|
||||
s -> new DbgModelTargetModuleSectionImpl(this, section));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(section);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetModuleSection) modelObject;
|
||||
}
|
||||
return new DbgModelTargetModuleSectionImpl(this, section);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,14 +23,9 @@ import agent.dbgeng.model.iface2.DbgModelTargetModuleSection;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Section",
|
||||
elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
},
|
||||
attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "Section", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetModuleSection {
|
||||
protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile";
|
||||
@ -40,6 +35,7 @@ public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetModuleSectionImpl(DbgModelTargetModuleSectionContainerImpl sections,
|
||||
DbgModuleSection section) {
|
||||
super(sections.getModel(), sections, section.getName(), "Section");
|
||||
this.getModel().addModelObject(section, this);
|
||||
|
||||
AddressSpace space = getModel().getAddressSpace("ram");
|
||||
Address min = space.getAddress(section.getStart());
|
||||
@ -50,8 +46,7 @@ public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
MODULE_ATTRIBUTE_NAME, sections.getParent(), //
|
||||
RANGE_ATTRIBUTE_NAME, range, //
|
||||
DISPLAY_ATTRIBUTE_NAME, section.getName(), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
DISPLAY_ATTRIBUTE_NAME, section.getName() //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ import agent.dbgeng.model.iface1.DbgModelTargetExecutionStateful;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.agent.DefaultTargetObject;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibilityListener;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema;
|
||||
|
||||
@ -71,10 +70,6 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, accessible //
|
||||
), "Accessibility changed");
|
||||
DbgModelTargetAccessConditioned accessConditioned =
|
||||
(DbgModelTargetAccessConditioned) this;
|
||||
listeners.fire(TargetAccessibilityListener.class)
|
||||
.accessibilityChanged(accessConditioned, accessible);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,6 +130,10 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
onExit();
|
||||
break;
|
||||
}
|
||||
case SESSION_EXIT: {
|
||||
getModel().close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this instanceof DbgModelTargetExecutionStateful) {
|
||||
DbgModelTargetExecutionStateful stateful = (DbgModelTargetExecutionStateful) this;
|
||||
@ -147,6 +146,11 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
throw new AssertionError(); // shouldn't ever be here
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<TargetObject>> requestNativeElements() {
|
||||
throw new AssertionError(); // shouldn't ever be here
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbgModelTargetSession getParentSession() {
|
||||
DbgModelTargetObject test = (DbgModelTargetObject) parent;
|
||||
@ -178,7 +182,6 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
public void setModified(Map<String, Object> map, boolean b) {
|
||||
if (modified) {
|
||||
map.put(MODIFIED_ATTRIBUTE_NAME, modified);
|
||||
listeners.fire.displayChanged(this, getDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,7 +191,6 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
MODIFIED_ATTRIBUTE_NAME, modified //
|
||||
), "Refreshed");
|
||||
listeners.fire.displayChanged(this, getDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,4 +201,9 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
|
||||
), "Refreshed");
|
||||
}
|
||||
|
||||
public TargetObject searchForSuitable(Class<? extends TargetObject> type) {
|
||||
List<String> pathToClass = model.getRootSchema().searchForSuitable(type, path);
|
||||
return model.getModelObject(pathToClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,11 +29,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "ProcessAttachConnector", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "ProcessAttachConnector",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetConnector {
|
||||
|
||||
@ -48,8 +51,7 @@ public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObje
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()) //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -25,23 +25,13 @@ import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "ProcessContainer",
|
||||
elements = { //
|
||||
@TargetElementType(type = DbgModelTargetProcessImpl.class) //
|
||||
},
|
||||
attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
},
|
||||
canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "ProcessContainer", elements = {
|
||||
@TargetElementType(type = DbgModelTargetProcessImpl.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetProcessContainer {
|
||||
|
||||
protected final Map<DebugProcessId, DbgModelTargetProcess> processesById =
|
||||
new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetProcessContainerImpl(DbgModelTargetSession session) {
|
||||
super(session.getModel(), session, "Processes", "ProcessContainer");
|
||||
|
||||
@ -55,9 +45,9 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
|
||||
DbgModelTargetProcess process = getTargetProcess(proc);
|
||||
changeElements(List.of(), List.of(process), Map.of(), "Added");
|
||||
process.processStarted(proc.getPid());
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, null, TargetEventType.PROCESS_CREATED, "Process " + proc.getId() +
|
||||
" started " + "notepad.exe" + " pid=" + proc.getPid(), List.of(process));
|
||||
getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_CREATED,
|
||||
"Process " + proc.getId() + " started " + process.getName() + "pid=" + proc.getPid(),
|
||||
List.of(process));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,21 +56,8 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
|
||||
process.processStarted(proc.getPid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processExited(DbgProcess proc, DbgCause cause) {
|
||||
DbgModelTargetProcess process = getTargetProcess(proc);
|
||||
process.processExited(proc.getExitCode());
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, null, TargetEventType.PROCESS_EXITED,
|
||||
"Process " + proc.getId() + " exited code=" + proc.getExitCode(),
|
||||
List.of(process));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRemoved(DebugProcessId processId, DbgCause cause) {
|
||||
synchronized (this) {
|
||||
processesById.remove(processId);
|
||||
}
|
||||
changeElements(List.of( //
|
||||
DbgModelTargetProcessImpl.indexProcess(processId) //
|
||||
), List.of(), Map.of(), "Removed");
|
||||
@ -96,21 +73,24 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
|
||||
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
|
||||
DbgReason reason) {
|
||||
DbgModelTargetProcess process = getTargetProcess(thread.getProcess());
|
||||
process.threadStateChanged(thread, state, cause, reason);
|
||||
process.threadStateChangedSpecific(thread, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadExited(DebugThreadId threadId, DbgProcess proc, DbgCause cause) {
|
||||
DbgModelTargetProcess targetProcess = processesById.get(proc.getId());
|
||||
if (targetProcess != null) {
|
||||
targetProcess.getThreads().threadExited(threadId);
|
||||
DbgModelTargetProcess process = getTargetProcess(proc);
|
||||
if (process != null) {
|
||||
process.getThreads().threadExited(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moduleLoaded(DbgProcess proc, DebugModuleInfo info, DbgCause cause) {
|
||||
DbgModelTargetProcess process = getTargetProcess(proc);
|
||||
process.getModules().libraryLoaded(info.toString());
|
||||
DbgModelTargetModuleContainer modules = process.getModules();
|
||||
if (modules != null) {
|
||||
modules.libraryLoaded(info.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -135,14 +115,22 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetProcess getTargetProcess(DebugProcessId id) {
|
||||
return processesById.computeIfAbsent(id,
|
||||
i -> new DbgModelTargetProcessImpl(this, getManager().getKnownProcesses().get(id)));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(id);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetProcess) modelObject;
|
||||
}
|
||||
return new DbgModelTargetProcessImpl(this, getManager().getKnownProcesses().get(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetProcess getTargetProcess(DbgProcess process) {
|
||||
return processesById.computeIfAbsent(process.getId(),
|
||||
i -> new DbgModelTargetProcessImpl(this, process));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(process);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetProcess) modelObject;
|
||||
}
|
||||
return new DbgModelTargetProcessImpl(this, process);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,48 +18,25 @@ package agent.dbgeng.model.impl;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugProcessId;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetEventScope.TargetEventType;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Process",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Debug",
|
||||
type = DbgModelTargetDebugContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Memory",
|
||||
type = DbgModelTargetMemoryContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Modules",
|
||||
type = DbgModelTargetModuleContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Threads",
|
||||
type = DbgModelTargetThreadContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "Process", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Debug", type = DbgModelTargetDebugContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Memory", type = DbgModelTargetMemoryContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Modules", type = DbgModelTargetModuleContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Threads", type = DbgModelTargetThreadContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = DbgModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME, type = Long.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetProcess {
|
||||
|
||||
@ -92,6 +69,8 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
public DbgModelTargetProcessImpl(DbgModelTargetProcessContainer processes, DbgProcess process) {
|
||||
super(processes.getModel(), processes, keyProcess(process), "Process");
|
||||
this.getModel().addModelObject(process, this);
|
||||
this.getModel().addModelObject(process.getId(), this);
|
||||
this.process = process;
|
||||
|
||||
this.debug = new DbgModelTargetDebugContainerImpl(this);
|
||||
@ -107,7 +86,7 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
//sections, //
|
||||
threads //
|
||||
), Map.of( //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, false, //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS, //
|
||||
SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, //
|
||||
@ -129,36 +108,28 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
@Override
|
||||
public void processSelected(DbgProcess eventProcess, DbgCause cause) {
|
||||
if (eventProcess.equals(process)) {
|
||||
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>();
|
||||
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
|
||||
.handle(seq::next);
|
||||
}, scope).then(seq -> {
|
||||
scope.get().setFocus(this);
|
||||
}).finish();
|
||||
((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
|
||||
DbgReason reason) {
|
||||
public void threadStateChangedSpecific(DbgThread thread, DbgState state) {
|
||||
TargetExecutionState targetState = convertState(state);
|
||||
setExecutionState(targetState, "ThreadStateChanged");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> launch(List<String> args) {
|
||||
return DbgModelImplUtils.launch(getModel(), process, args);
|
||||
return model.gateFuture(DbgModelImplUtils.launch(getModel(), process, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> resume() {
|
||||
return process.cont();
|
||||
return model.gateFuture(process.cont());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> kill() {
|
||||
return process.kill();
|
||||
return model.gateFuture(process.kill());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -166,22 +137,22 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
getModel().assertMine(TargetObject.class, attachable);
|
||||
// NOTE: Get the object and type check it myself.
|
||||
// The typed ref could have been unsafely cast
|
||||
return process.reattach(attachable).thenApply(set -> null);
|
||||
return model.gateFuture(process.reattach(attachable)).thenApply(set -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> attach(long pid) {
|
||||
return process.attach(pid).thenApply(set -> null);
|
||||
return model.gateFuture(process.attach(pid)).thenApply(set -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> detach() {
|
||||
return process.detach();
|
||||
return model.gateFuture(process.detach());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> delete() {
|
||||
return process.remove();
|
||||
return model.gateFuture(process.remove());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -192,13 +163,13 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
case ADVANCE: // Why no exec-advance in dbgeng?
|
||||
throw new UnsupportedOperationException(kind.name());
|
||||
default:
|
||||
return process.step(convertToDbg(kind));
|
||||
return model.gateFuture(process.step(convertToDbg(kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return process.step(args);
|
||||
return model.gateFuture(process.step(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -213,20 +184,16 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processExited(Long exitCode) {
|
||||
if (exitCode != null) {
|
||||
public void processExited(DbgProcess proc, DbgCause cause) {
|
||||
if (proc.equals(this.process)) {
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
EXIT_CODE_ATTRIBUTE_NAME, exitCode //
|
||||
STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, //
|
||||
EXIT_CODE_ATTRIBUTE_NAME, proc.getExitCode() //
|
||||
), "Exited");
|
||||
getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_EXITED,
|
||||
"Process " + proc.getId() + " exited code=" + proc.getExitCode(),
|
||||
List.of(getProxy()));
|
||||
}
|
||||
setExecutionState(TargetExecutionState.TERMINATED, "Exited");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExit() {
|
||||
super.onExit();
|
||||
DbgModelTargetProcessContainer processes = (DbgModelTargetProcessContainer) getParent();
|
||||
processes.processRemoved(process.getId(), DbgCause.Causes.UNCLAIMED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,11 +27,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "ProcessLaunchConnector", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "ProcessLaunchConnector",
|
||||
elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
},
|
||||
attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetConnector {
|
||||
|
||||
@ -46,8 +49,7 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()) //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -20,42 +20,36 @@ import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import agent.dbgeng.manager.DbgThread;
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.impl.DbgRegister;
|
||||
import agent.dbgeng.manager.impl.DbgRegisterSet;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.error.DebuggerRegisterAccessException;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetRegisterBank;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
|
||||
import ghidra.dbg.util.ConversionUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "RegisterContainer",
|
||||
elements = {
|
||||
@TargetElementType(type = DbgModelTargetRegisterImpl.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME,
|
||||
type = DbgModelTargetRegisterContainerImpl.class),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
},
|
||||
canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "RegisterContainer", elements = {
|
||||
@TargetElementType(type = DbgModelTargetRegisterImpl.class) }, elementResync = ResyncMode.ONCE, //
|
||||
attributes = {
|
||||
@TargetAttributeType(name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, type = DbgModelTargetRegisterContainerImpl.class),
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetRegisterContainerAndBank {
|
||||
|
||||
protected final DbgThread thread;
|
||||
|
||||
protected final Map<Integer, DbgModelTargetRegister> registersByNumber = new HashMap<>();
|
||||
protected final Map<String, DbgModelTargetRegister> registersByName = new HashMap<>();
|
||||
|
||||
private Map<String, byte[]> values = new HashMap<>();
|
||||
|
||||
public DbgModelTargetRegisterContainerImpl(DbgModelTargetThread thread) {
|
||||
super(thread.getModel(), thread, "Registers", "RegisterContainer");
|
||||
this.thread = thread.getThread();
|
||||
|
||||
requestElements(false);
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, this //
|
||||
), "Initialized");
|
||||
@ -64,9 +58,13 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
@Override
|
||||
public CompletableFuture<Void> requestElements(boolean refresh) {
|
||||
return thread.listRegisters().thenAccept(regs -> {
|
||||
if (regs.size() != registersByNumber.size()) {
|
||||
registersByNumber.clear();
|
||||
if (regs.size() != registersByName.size()) {
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
for (DbgRegister reg : regs) {
|
||||
impl.deleteModelObject(reg);
|
||||
}
|
||||
registersByName.clear();
|
||||
|
||||
}
|
||||
List<TargetObject> registers;
|
||||
synchronized (this) {
|
||||
@ -79,10 +77,20 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
});
|
||||
}
|
||||
|
||||
public void threadStateChangedSpecific(DbgState state, DbgReason reason) {
|
||||
if (state.equals(DbgState.STOPPED)) {
|
||||
readRegistersNamed(getCachedElements().keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetRegister getTargetRegister(DbgRegister register) {
|
||||
DbgModelTargetRegister reg = registersByNumber.computeIfAbsent(register.getNumber(),
|
||||
n -> new DbgModelTargetRegisterImpl(this, register));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(register);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetRegister) modelObject;
|
||||
}
|
||||
DbgModelTargetRegister reg = new DbgModelTargetRegisterImpl(this, register);
|
||||
registersByName.put(register.getName(), reg);
|
||||
return reg;
|
||||
}
|
||||
@ -90,14 +98,12 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
@Override
|
||||
public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
|
||||
Collection<String> names) {
|
||||
return AsyncUtils.sequence(TypeSpec.map(String.class, byte[].class)).then(seq -> {
|
||||
thread.listRegisters().handle(seq::next);
|
||||
}, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> {
|
||||
if (regs.size() != registersByNumber.size() || getCachedElements().isEmpty()) {
|
||||
requestElements(true).handle(seq::next);
|
||||
return model.gateFuture(thread.listRegisters().thenCompose(regs -> {
|
||||
if (regs.size() != registersByName.size() || getCachedElements().isEmpty()) {
|
||||
return requestElements(false);
|
||||
}
|
||||
seq.next(null, null);
|
||||
}).then(seq -> {
|
||||
return AsyncUtils.NIL;
|
||||
}).thenCompose(__ -> {
|
||||
Set<DbgRegister> toRead = new LinkedHashSet<>();
|
||||
for (String regname : names) {
|
||||
DbgModelTargetRegister reg = registersByName.get(regname);
|
||||
@ -109,11 +115,11 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
//throw new DebuggerRegisterAccessException("No such register: " + regname);
|
||||
}
|
||||
}
|
||||
thread.readRegisters(toRead).handle(seq::next);
|
||||
}, TypeSpec.map(DbgRegister.class, BigInteger.class)).then((vals, seq) -> {
|
||||
return thread.readRegisters(toRead);
|
||||
}).thenApply(vals -> {
|
||||
Map<String, byte[]> result = new LinkedHashMap<>();
|
||||
for (DbgRegister dbgReg : vals.keySet()) {
|
||||
DbgModelTargetRegister reg = registersByNumber.get(dbgReg.getNumber());
|
||||
DbgModelTargetRegister reg = getTargetRegister(dbgReg);
|
||||
String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME);
|
||||
BigInteger value = vals.get(dbgReg);
|
||||
byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value);
|
||||
@ -129,18 +135,17 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
reg.setModified(!value.toString(16).equals(oldval));
|
||||
}
|
||||
}
|
||||
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result);
|
||||
seq.exit(result);
|
||||
}).finish();
|
||||
this.values = result;
|
||||
listeners.fire.registersUpdated(getProxy(), result);
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) {
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
thread.listRegisters().handle(seq::next);
|
||||
}, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> {
|
||||
fetchElements().handle(seq::nextIgnore);
|
||||
}).then(seq -> {
|
||||
return model.gateFuture(thread.listRegisters().thenCompose(regs -> {
|
||||
return requestElements(false);
|
||||
}).thenCompose(__ -> {
|
||||
Map<String, ? extends TargetObject> regs = getCachedElements();
|
||||
Map<DbgRegister, BigInteger> toWrite = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, byte[]> ent : values.entrySet()) {
|
||||
@ -152,32 +157,15 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
|
||||
BigInteger val = new BigInteger(1, ent.getValue());
|
||||
toWrite.put(reg.getRegister(), val);
|
||||
}
|
||||
thread.writeRegisters(toWrite).handle(seq::next);
|
||||
return thread.writeRegisters(toWrite);
|
||||
// TODO: Should probably filter only effective and normalized writes in the callback
|
||||
}).then(seq -> {
|
||||
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, values);
|
||||
seq.exit();
|
||||
}).finish();
|
||||
}).thenAccept(__ -> {
|
||||
listeners.fire.registersUpdated(getProxy(), values);
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
public void invalidateRegisterCaches() {
|
||||
listeners.fire.invalidateCacheRequested(this);
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void onRunning() {
|
||||
// NB: We don't want to do this apparently
|
||||
//invalidateRegisterCaches();
|
||||
setAccessible(false);
|
||||
public Map<String, byte[]> getCachedRegisters() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopped() {
|
||||
setAccessible(true);
|
||||
if (thread.equals(getManager().getEventThread())) {
|
||||
readRegistersNamed(getCachedElements().keySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,10 @@ import ghidra.dbg.target.TargetRegister;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType( //
|
||||
name = TargetRegister.CONTAINER_ATTRIBUTE_NAME, //
|
||||
type = DbgModelTargetRegisterContainerImpl.class), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = TargetRegister.CONTAINER_ATTRIBUTE_NAME, type = DbgModelTargetRegisterContainerImpl.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetRegister {
|
||||
|
||||
@ -56,6 +52,7 @@ public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetRegisterImpl(DbgModelTargetRegisterContainerAndBank registers,
|
||||
DbgRegister register) {
|
||||
super(registers.getModel(), registers, keyRegister(register), "Register");
|
||||
this.getModel().addModelObject(register, this);
|
||||
this.registers = registers;
|
||||
this.register = register;
|
||||
|
||||
|
@ -23,36 +23,17 @@ import agent.dbgeng.manager.impl.DbgProcessImpl;
|
||||
import agent.dbgeng.model.iface1.DbgModelSelectableObject;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetConnector;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetRoot;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.error.DebuggerUserException;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Debugger",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Available",
|
||||
type = DbgModelTargetAvailableContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Connectors",
|
||||
type = DbgModelTargetConnectorContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Sessions",
|
||||
type = DbgModelTargetSessionContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "Debugger", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Available", type = DbgModelTargetAvailableContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Connectors", type = DbgModelTargetConnectorContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Sessions", type = DbgModelTargetSessionContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
||||
implements DbgModelTargetRoot {
|
||||
|
||||
@ -77,7 +58,7 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
||||
connectors, //
|
||||
sessions //
|
||||
), Map.of( //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, true, //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, accessible, //
|
||||
DISPLAY_ATTRIBUTE_NAME, "Debugger", //
|
||||
FOCUS_ATTRIBUTE_NAME, this, //
|
||||
SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, DbgModelTargetProcessImpl.SUPPORTED_KINDS, //
|
||||
@ -117,7 +98,6 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus //
|
||||
), "Focus changed");
|
||||
listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel);
|
||||
}
|
||||
return doFire;
|
||||
}
|
||||
@ -125,20 +105,15 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
||||
@Override
|
||||
public CompletableFuture<Void> launch(Map<String, ?> args) {
|
||||
DbgModelTargetConnector targetConnector = connectors.getDefaultConnector();
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
targetConnector.launch(args).handle(seq::nextIgnore);
|
||||
//getManager().launch(args).handle(seq::nextIgnore);
|
||||
}).finish().exceptionally((exc) -> {
|
||||
return model.gateFuture(targetConnector.launch(args)).exceptionally(exc -> {
|
||||
throw new DebuggerUserException("Launch failed for " + args);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> attach(long pid) {
|
||||
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
DbgProcess process = new DbgProcessImpl(getManager());
|
||||
process.attach(pid).handle(seq::nextIgnore);
|
||||
}).finish();
|
||||
DbgProcess process = new DbgProcessImpl(getManager());
|
||||
return model.gateFuture(process.attach(pid)).thenApply(__ -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,15 +25,15 @@ import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "SessionAttributes",
|
||||
elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = { //
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Machine",
|
||||
type = DbgModelTargetSessionAttributesMachineImpl.class,
|
||||
fixed = true), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSessionAttributes {
|
||||
@ -51,7 +51,7 @@ public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImp
|
||||
ARCH_ATTRIBUTE_NAME, "x86_64", //
|
||||
DEBUGGER_ATTRIBUTE_NAME, "dbgeng", //
|
||||
OS_ATTRIBUTE_NAME, "Windows", //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
ENDIAN_ATTRIBUTE_NAME, "little" //
|
||||
), "Initialized");
|
||||
|
||||
getManager().addEventsListener(this);
|
||||
|
@ -30,15 +30,18 @@ import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "SessionAttributesMachine", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(name = "Arch", type = String.class), //
|
||||
@TargetAttributeType(name = "Debugger", type = String.class), //
|
||||
@TargetAttributeType(name = "OS", type = String.class), //
|
||||
@TargetAttributeType(name = "Mode", type = String.class), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "SessionAttributesMachine",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(name = "Arch", type = String.class),
|
||||
@TargetAttributeType(name = "Debugger", type = String.class),
|
||||
@TargetAttributeType(name = "OS", type = String.class),
|
||||
@TargetAttributeType(name = "Mode", type = String.class),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetSessionAttributesMachineImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSessionAttributesMachine {
|
||||
|
||||
|
@ -23,20 +23,15 @@ import agent.dbgeng.dbgeng.DebugSessionId;
|
||||
import agent.dbgeng.manager.DbgCause;
|
||||
import agent.dbgeng.manager.DbgSession;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "SessionContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetSessionImpl.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "SessionContainer", elements = {
|
||||
@TargetElementType(type = DbgModelTargetSessionImpl.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSessionContainer {
|
||||
|
||||
protected final Map<DebugSessionId, DbgModelTargetSession> sessionsById =
|
||||
new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetSessionContainerImpl(DbgModelTargetRoot root) {
|
||||
super(root.getModel(), root, "Sessions", "SessionContainer");
|
||||
|
||||
@ -51,9 +46,9 @@ public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public void sessionRemoved(DebugSessionId sessionId, DbgCause cause) {
|
||||
synchronized (this) {
|
||||
sessionsById.remove(sessionId);
|
||||
}
|
||||
//synchronized (this) {
|
||||
// sessionsById.remove(sessionId);
|
||||
//}
|
||||
changeElements(List.of( //
|
||||
DbgModelTargetSessionImpl.indexSession(sessionId) //
|
||||
), List.of(), Map.of(), "Removed");
|
||||
@ -61,8 +56,12 @@ public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetSession getTargetSession(DbgSession session) {
|
||||
DebugSessionId id = session.getId();
|
||||
return sessionsById.computeIfAbsent(id, i -> new DbgModelTargetSessionImpl(this, session));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(session);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetSession) modelObject;
|
||||
}
|
||||
return new DbgModelTargetSessionImpl(this, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,8 +30,7 @@ import ghidra.dbg.util.PathUtils;
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Session",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
@TargetElementType(type = Void.class) },
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Attributes",
|
||||
@ -42,8 +41,7 @@ import ghidra.dbg.util.PathUtils;
|
||||
type = DbgModelTargetProcessContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSession {
|
||||
|
||||
@ -71,6 +69,7 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetSessionImpl(DbgModelTargetSessionContainerImpl sessions,
|
||||
DbgSession session) {
|
||||
super(sessions.getModel(), sessions, keySession(session), "Session");
|
||||
this.getModel().addModelObject(session, this);
|
||||
|
||||
this.attributes = new DbgModelTargetSessionAttributesImpl(this);
|
||||
this.processes = new DbgModelTargetProcessContainerImpl(this);
|
||||
@ -79,10 +78,9 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
|
||||
attributes, //
|
||||
processes //
|
||||
), Map.of( //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, true, //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, accessible, //
|
||||
PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, //
|
||||
STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE //
|
||||
), "Initialized");
|
||||
|
||||
getManager().addEventsListener(this);
|
||||
|
@ -19,61 +19,31 @@ import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import agent.dbgeng.manager.*;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.target.TargetFocusScope;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "StackFrame",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.FUNC_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.FUNC_TABLE_ENTRY_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.INST_OFFSET_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.FRAME_OFFSET_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.RETURN_OFFSET_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.STACK_OFFSET_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.VIRTUAL_ATTRIBUTE_NAME,
|
||||
type = Boolean.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.PARAM0_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.PARAM1_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.PARAM2_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(
|
||||
name = DbgModelTargetStackFrame.PARAM3_ATTRIBUTE_NAME,
|
||||
type = String.class),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "StackFrame", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.FUNC_TABLE_ENTRY_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.INST_OFFSET_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.FRAME_OFFSET_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.RETURN_OFFSET_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.STACK_OFFSET_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.VIRTUAL_ATTRIBUTE_NAME, type = Boolean.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.PARAM0_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.PARAM1_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.PARAM2_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(name = DbgModelTargetStackFrame.PARAM3_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetStackFrame {
|
||||
|
||||
@ -102,6 +72,7 @@ public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetStackFrameImpl(DbgModelTargetStack stack, DbgModelTargetThread thread,
|
||||
DbgStackFrame frame) {
|
||||
super(stack.getModel(), stack, keyFrame(frame), "StackFrame");
|
||||
this.getModel().addModelObject(frame, this);
|
||||
this.thread = thread;
|
||||
this.pc = getModel().getAddressSpace("ram").getAddress(-1);
|
||||
|
||||
@ -125,13 +96,7 @@ public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
|
||||
@Override
|
||||
public void threadSelected(DbgThread eventThread, DbgStackFrame eventFrame, DbgCause cause) {
|
||||
if (eventFrame != null && eventFrame.equals(frame)) {
|
||||
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>();
|
||||
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
|
||||
.handle(seq::next);
|
||||
}, scope).then(seq -> {
|
||||
scope.get().setFocus(this);
|
||||
}).finish();
|
||||
((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,20 +24,21 @@ import agent.dbgeng.manager.impl.DbgMinimalSymbol;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSymbolContainer;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "SymbolContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetSymbolImpl.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "SymbolContainer",
|
||||
elements = {
|
||||
@TargetElementType(type = DbgModelTargetSymbolImpl.class) },
|
||||
elementResync = ResyncMode.ONCE,
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class) },
|
||||
canonicalContainer = true)
|
||||
public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSymbolContainer {
|
||||
|
||||
protected final DbgModelTargetModuleImpl module;
|
||||
|
||||
protected final Map<String, DbgModelTargetSymbolImpl> symbolsByName = new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetSymbolContainerImpl(DbgModelTargetModuleImpl module) {
|
||||
super(module.getModel(), module, "Symbols", "SymbolContainer");
|
||||
this.module = module;
|
||||
@ -59,7 +60,11 @@ public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetSymbolImpl getTargetSymbol(DbgMinimalSymbol symbol) {
|
||||
return symbolsByName.computeIfAbsent(symbol.getName(),
|
||||
n -> new DbgModelTargetSymbolImpl(this, symbol));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(symbol);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetSymbolImpl) modelObject;
|
||||
}
|
||||
return new DbgModelTargetSymbolImpl(this, symbol);
|
||||
}
|
||||
}
|
||||
|
@ -20,19 +20,22 @@ import java.util.Map;
|
||||
|
||||
import agent.dbgeng.manager.impl.DbgMinimalSymbol;
|
||||
import agent.dbgeng.model.iface2.DbgModelTargetSymbol;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetSymbol;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "Symbol", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType( //
|
||||
name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, //
|
||||
type = DbgModelTargetSymbolContainerImpl.class), //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(name = "Symbol", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, type = DbgModelTargetSymbolContainerImpl.class),
|
||||
@TargetAttributeType(name = TargetObject.VALUE_ATTRIBUTE_NAME, type = Address.class),
|
||||
@TargetAttributeType(name = TargetSymbol.SIZE_ATTRIBUTE_NAME, type = long.class),
|
||||
@TargetAttributeType(name = "Name", type = String.class),
|
||||
@TargetAttributeType(name = "Size", type = long.class),
|
||||
@TargetAttributeType(name = "TypeId", type = int.class),
|
||||
@TargetAttributeType(name = "Tag", type = int.class),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetSymbol {
|
||||
protected static String indexSymbol(DbgMinimalSymbol symbol) {
|
||||
@ -45,21 +48,25 @@ public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
protected final boolean constant;
|
||||
protected final Address value;
|
||||
protected final int size;
|
||||
protected final long size;
|
||||
|
||||
public DbgModelTargetSymbolImpl(DbgModelTargetSymbolContainerImpl symbols,
|
||||
DbgMinimalSymbol symbol) {
|
||||
super(symbols.getModel(), symbols, keySymbol(symbol), "Symbol");
|
||||
this.getModel().addModelObject(symbol, this);
|
||||
this.constant = false;
|
||||
this.value = symbols.getModel().getAddressSpace("ram").getAddress(symbol.getAddress());
|
||||
this.size = 0;
|
||||
this.size = symbol.getSize();
|
||||
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
// TODO: DATA_TYPE
|
||||
NAMESPACE_ATTRIBUTE_NAME, symbols, //
|
||||
VALUE_ATTRIBUTE_NAME, value, //
|
||||
SIZE_ATTRIBUTE_NAME, size, //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
"Name", symbol.getName(), //
|
||||
"Size", size, //
|
||||
"TypeId", symbol.getTypeId(), //
|
||||
"Tag", symbol.getTag() //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -26,26 +26,21 @@ import agent.dbgeng.manager.reason.*;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "ThreadContainer", elements = { //
|
||||
@TargetElementType(type = DbgModelTargetThreadImpl.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
}, canonicalContainer = true)
|
||||
@TargetObjectSchemaInfo(name = "ThreadContainer", elements = {
|
||||
@TargetElementType(type = DbgModelTargetThreadImpl.class) }, attributes = {
|
||||
@TargetAttributeType(type = Void.class) }, canonicalContainer = true)
|
||||
public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetThreadContainer {
|
||||
|
||||
protected final DbgProcess process;
|
||||
|
||||
protected final Map<DebugThreadId, DbgModelTargetThreadImpl> threadsById =
|
||||
new WeakValueHashMap<>();
|
||||
|
||||
public DbgModelTargetThreadContainerImpl(DbgModelTargetProcessImpl process) {
|
||||
super(process.getModel(), process, "Threads", "ThreadContainer");
|
||||
this.process = process.process;
|
||||
|
||||
getManager().addEventsListener(this);
|
||||
requestElements(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,34 +48,32 @@ public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
|
||||
changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created");
|
||||
DbgModelTargetThread targetThread = getTargetThread(thread);
|
||||
changeElements(List.of(), List.of(targetThread), Map.of(), "Created");
|
||||
targetThread.threadStateChanged(DbgState.STARTING, DbgReason.getReason(null));
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, targetThread, TargetEventType.THREAD_CREATED,
|
||||
"Thread " + thread.getId() + " started", List.of(targetThread));
|
||||
targetThread.threadStateChangedSpecific(DbgState.STARTING, DbgReason.getReason(null));
|
||||
getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_CREATED,
|
||||
"Thread " + thread.getId() + " started", List.of(targetThread));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
|
||||
DbgReason reason) {
|
||||
DbgModelTargetThread targetThread = getTargetThread(thread);
|
||||
targetThread.threadStateChanged(state, reason);
|
||||
TargetEventType eventType = getEventType(state, cause, reason);
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, targetThread, eventType, "Thread " + thread.getId() + " state changed",
|
||||
List.of(targetThread));
|
||||
getListeners().fire.event(getProxy(), targetThread, eventType,
|
||||
"Thread " + thread.getId() + " state changed", List.of(targetThread));
|
||||
targetThread.threadStateChangedSpecific(state, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadExited(DebugThreadId threadId) {
|
||||
DbgModelTargetThread targetThread = threadsById.get(threadId);
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
DbgModelTargetThread targetThread = (DbgModelTargetThread) impl.getModelObject(threadId);
|
||||
if (targetThread != null) {
|
||||
getListeners().fire(TargetEventScopeListener.class)
|
||||
.event(this, targetThread, TargetEventType.THREAD_EXITED,
|
||||
"Thread " + threadId + " exited", List.of(targetThread));
|
||||
}
|
||||
synchronized (this) {
|
||||
threadsById.remove(threadId);
|
||||
getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_EXITED,
|
||||
"Thread " + threadId + " exited", List.of(targetThread));
|
||||
}
|
||||
//synchronized (this) {
|
||||
// threadsById.remove(threadId);
|
||||
//}
|
||||
changeElements(List.of( //
|
||||
DbgModelTargetThreadImpl.indexThread(threadId) //
|
||||
), List.of(), Map.of(), "Exited");
|
||||
@ -125,8 +118,12 @@ public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
|
||||
|
||||
@Override
|
||||
public synchronized DbgModelTargetThread getTargetThread(DbgThread thread) {
|
||||
return threadsById.computeIfAbsent(thread.getId(),
|
||||
i -> new DbgModelTargetThreadImpl(this, (DbgModelTargetProcess) parent, thread));
|
||||
DbgModelImpl impl = (DbgModelImpl) model;
|
||||
TargetObject modelObject = impl.getModelObject(thread);
|
||||
if (modelObject != null) {
|
||||
return (DbgModelTargetThread) modelObject;
|
||||
}
|
||||
return new DbgModelTargetThreadImpl(this, (DbgModelTargetProcess) parent, thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package agent.dbgeng.model.impl;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import agent.dbgeng.dbgeng.DebugThreadId;
|
||||
import agent.dbgeng.manager.*;
|
||||
@ -26,32 +25,17 @@ import agent.dbgeng.manager.cmd.DbgThreadSelectCommand;
|
||||
import agent.dbgeng.manager.impl.DbgManagerImpl;
|
||||
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
|
||||
import agent.dbgeng.model.iface2.*;
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.async.TypeSpec;
|
||||
import ghidra.dbg.DebugModelConventions;
|
||||
import ghidra.dbg.target.TargetEnvironment;
|
||||
import ghidra.dbg.target.TargetFocusScope;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Thread",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Registers",
|
||||
type = DbgModelTargetRegisterContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Stack",
|
||||
type = DbgModelTargetStackImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetObjectSchemaInfo(name = "Thread", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Registers", type = DbgModelTargetRegisterContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Stack", type = DbgModelTargetStackImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = TargetEnvironment.ARCH_ATTRIBUTE_NAME, type = String.class),
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetThread {
|
||||
|
||||
@ -87,6 +71,8 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
public DbgModelTargetThreadImpl(DbgModelTargetThreadContainer threads,
|
||||
DbgModelTargetProcess process, DbgThread thread) {
|
||||
super(threads.getModel(), threads, keyThread(thread), "Thread");
|
||||
this.getModel().addModelObject(thread, this);
|
||||
this.getModel().addModelObject(thread.getId(), this);
|
||||
this.process = process;
|
||||
this.thread = thread;
|
||||
|
||||
@ -97,7 +83,7 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
registers, //
|
||||
stack //
|
||||
), Map.of( //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, false, //
|
||||
ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS //
|
||||
), "Initialized");
|
||||
@ -118,18 +104,12 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
@Override
|
||||
public void threadSelected(DbgThread eventThread, DbgStackFrame frame, DbgCause cause) {
|
||||
if (eventThread.equals(thread)) {
|
||||
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>();
|
||||
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
|
||||
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
|
||||
.handle(seq::next);
|
||||
}, scope).then(seq -> {
|
||||
scope.get().setFocus(this);
|
||||
}).finish();
|
||||
((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStateChanged(DbgState state, DbgReason reason) {
|
||||
public void threadStateChangedSpecific(DbgState state, DbgReason reason) {
|
||||
TargetExecutionState targetState = convertState(state);
|
||||
String executionType = thread.getExecutingProcessorType().description;
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
@ -137,6 +117,7 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType //
|
||||
), reason.desc());
|
||||
setExecutionState(targetState, reason.desc());
|
||||
registers.threadStateChangedSpecific(state, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -147,13 +128,13 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
|
||||
case ADVANCE: // Why no exec-advance in GDB/MI?
|
||||
return thread.console("advance");
|
||||
default:
|
||||
return thread.step(convertToDbg(kind));
|
||||
return model.gateFuture(thread.step(convertToDbg(kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> step(Map<String, ?> args) {
|
||||
return thread.step(args);
|
||||
return model.gateFuture(thread.step(args));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,11 +27,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.target.schema.*;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "TraceOrDumpConnector", elements = { //
|
||||
@TargetElementType(type = Void.class) //
|
||||
}, attributes = { //
|
||||
@TargetAttributeType(type = Void.class) //
|
||||
})
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "TraceOrDumpConnector",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class)
|
||||
},
|
||||
attributes = {
|
||||
@TargetAttributeType(type = Void.class)
|
||||
})
|
||||
public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObjectImpl
|
||||
implements DbgModelTargetConnector {
|
||||
|
||||
@ -46,8 +49,7 @@ public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObject
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
|
||||
TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()), //
|
||||
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
|
||||
paramDescs = TargetParameterMap.copyOf(computeParameters()) //
|
||||
), "Initialized");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.dbg.test.AbstractModelHost;
|
||||
|
||||
public abstract class AbstractDbgengModelHost extends AbstractModelHost {
|
||||
@Override
|
||||
public Map<String, Object> getFactoryOptions() {
|
||||
return Map.ofEntries();
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet;
|
||||
import ghidra.dbg.test.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public abstract class AbstractModelForDbgengBreakpointsTest
|
||||
extends AbstractDebuggerModelBreakpointsTest implements ProvidesTargetViaLaunchSpecimen {
|
||||
|
||||
@Override
|
||||
public AbstractDebuggerModelTest getTest() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> seedPath() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerTestSpecimen getLaunchSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedBreakpointContainerPath(List<String> targetPath) {
|
||||
return PathUtils.extend(targetPath, PathUtils.parse("Debug.Breakpoints"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetBreakpointKindSet getExpectedSupportedKinds() {
|
||||
return TargetBreakpointKindSet.of( //
|
||||
TargetBreakpointKind.SW_EXECUTE, //
|
||||
TargetBreakpointKind.HW_EXECUTE, //
|
||||
TargetBreakpointKind.READ, //
|
||||
TargetBreakpointKind.WRITE); //
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getSuitableRangeForBreakpoint(TargetObject target,
|
||||
TargetBreakpointKind kind) throws Throwable {
|
||||
TargetStackFrame frame = retry(() -> {
|
||||
TargetStackFrame f = findAnyStackFrame(target.getPath());
|
||||
assertNotNull(f);
|
||||
return f;
|
||||
}, List.of(AssertionError.class));
|
||||
waitOn(frame.fetchAttributes());
|
||||
Address pc = frame.getProgramCounter();
|
||||
switch (kind) {
|
||||
case SW_EXECUTE:
|
||||
case HW_EXECUTE:
|
||||
return new AddressRangeImpl(pc, pc);
|
||||
case READ:
|
||||
case WRITE:
|
||||
return new AddressRangeImpl(pc, 4);
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.dbg.test.AbstractDebuggerModelFactoryTest;
|
||||
|
||||
public abstract class AbstractModelForDbgengFactoryTest extends AbstractDebuggerModelFactoryTest {
|
||||
@Override
|
||||
protected Map<String, Object> getFailingFactoryOptions() {
|
||||
// TODO: No options to test for IN-VM variant
|
||||
return Map.ofEntries();
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelFocusTest;
|
||||
|
||||
public abstract class AbstractModelForDbgengFrameFocusTest
|
||||
extends AbstractDebuggerModelFocusTest {
|
||||
|
||||
protected DebuggerTestSpecimen getSpecimen() {
|
||||
return WindowsSpecimen.STACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<TargetObject> getFocusableThings() throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getSpecimen();
|
||||
TargetLauncher launcher = findLauncher(); // root launcher should generate new inferiors
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
|
||||
TargetProcess process = retry(() -> {
|
||||
TargetProcess p = m.findAny(TargetProcess.class, seedPath());
|
||||
assertNotNull(p);
|
||||
return p;
|
||||
}, List.of(AssertionError.class));
|
||||
|
||||
trapAt("expStack!break_here", process);
|
||||
|
||||
return retry(() -> {
|
||||
Map<List<String>, TargetStackFrame> frames =
|
||||
m.findAll(TargetStackFrame.class, seedPath());
|
||||
assertTrue(frames.size() >= 3);
|
||||
return Set.copyOf(frames.values());
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.dbg.test.AbstractDebuggerModelInterpreterTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengInterpreterTest
|
||||
extends AbstractDebuggerModelInterpreterTest {
|
||||
|
||||
@Override
|
||||
protected List<String> seedPath() {
|
||||
return PathUtils.parse("Sessions[0]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedInterpreterPath() {
|
||||
return PathUtils.parse("Sessions[0]");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getEchoCommand(String msg) {
|
||||
return ".echo " + msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getQuitCommand() {
|
||||
return "q";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAttachCommand() {
|
||||
return ".attach " + Long.toHexString(dummy.pid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerTestSpecimen getAttachSpecimen() {
|
||||
return WindowsSpecimen.NOTEPAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerTestSpecimen getLaunchSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelFocusTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengProcessFocusTest
|
||||
extends AbstractDebuggerModelFocusTest {
|
||||
|
||||
protected int getCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
protected DebuggerTestSpecimen getSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<TargetObject> getFocusableThings() throws Throwable {
|
||||
DebuggerTestSpecimen specimen = getSpecimen();
|
||||
TargetLauncher launcher = findLauncher();
|
||||
int count = getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
waitOn(launcher.launch(specimen.getLauncherArgs()));
|
||||
}
|
||||
return retry(() -> {
|
||||
Map<List<String>, TargetProcess> found =
|
||||
m.findAll(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
assertEquals(count, found.size());
|
||||
return Set.copyOf(found.values());
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import agent.dbgeng.model.invm.InVmDbgengModelHost;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelAttacherTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengRootAttacherTest
|
||||
extends AbstractDebuggerModelAttacherTest {
|
||||
|
||||
public class InVmModelForDbgengRootAttacherTest extends AbstractModelForDbgengRootAttacherTest {
|
||||
@Override
|
||||
public ModelHost modelHost() throws Throwable {
|
||||
return new InVmDbgengModelHost();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedAttachableContainerPath() {
|
||||
return List.of("Available");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedProcessesContainerPath() {
|
||||
return PathUtils.parse("Sessions[0].Processes");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedAttacherPath() {
|
||||
return PathUtils.parse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerTestSpecimen getAttachSpecimen() {
|
||||
return WindowsSpecimen.NOTEPAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetParameterMap getExpectedAttachParameters() {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEnvironment(TargetEnvironment environment) {
|
||||
assertEquals("x86_64", environment.getArchitecture());
|
||||
assertEquals("Windows", environment.getOperatingSystem());
|
||||
assertEquals("little", environment.getEndian());
|
||||
assertTrue(environment.getDebugger().toLowerCase().contains("dbgeng"));
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||
import ghidra.dbg.target.TargetMethod.TargetParameterMap;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelLauncherTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengRootLauncherTest
|
||||
extends AbstractDebuggerModelLauncherTest {
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedProcessesContainerPath() {
|
||||
return PathUtils.parse("Sessions[0].Processes");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExpectedLauncherPath() {
|
||||
return PathUtils.parse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebuggerTestSpecimen getLaunchSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
|
||||
@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"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEnvironment(TargetEnvironment environment) {
|
||||
assertEquals("x86_64", environment.getArchitecture());
|
||||
assertEquals("Windows", environment.getOperatingSystem());
|
||||
assertEquals("little", environment.getEndian());
|
||||
assertTrue(environment.getDebugger().toLowerCase().contains("dbgeng"));
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
import ghidra.dbg.target.TargetProcess;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelScenarioCloneExitTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengScenarioCloneExitTest
|
||||
extends AbstractDebuggerModelScenarioCloneExitTest {
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTestSpecimen getSpecimen() {
|
||||
return WindowsSpecimen.CREATE_THREAD_EXIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBreakpointExpression() {
|
||||
return "expCreateThreadExit!work";
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelScenarioForkExitTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
public abstract class AbstractModelForDbgengScenarioForkExitTest
|
||||
extends AbstractDebuggerModelScenarioForkExitTest {
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DebuggerTestSpecimen getSpecimen() {
|
||||
return WindowsSpecimen.CREATE_PROCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getParentBreakpointExpression() {
|
||||
return "expCreateProcess!func";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getChildBreakpointExpression() {
|
||||
return "expCreateProcess!func";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEnvironment(TargetEnvironment environment) {
|
||||
assertEquals("x86_64", environment.getArchitecture());
|
||||
assertEquals("Windows", environment.getOperatingSystem());
|
||||
assertEquals("little", environment.getEndian());
|
||||
assertTrue(environment.getDebugger().toLowerCase().contains("dbgeng"));
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/* ###
|
||||
* 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.model;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import agent.dbgeng.model.impl.DbgModelTargetProcessImpl;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.test.AbstractDebuggerModelScenarioMemoryTest;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public abstract class AbstractModelForDbgengScenarioMemoryTest
|
||||
extends AbstractDebuggerModelScenarioMemoryTest {
|
||||
|
||||
@Override
|
||||
protected TargetObject findProcessContainer() throws Throwable {
|
||||
return m.findContainer(TargetProcess.class, PathUtils.parse("Sessions[0]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WindowsSpecimen getSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
|
||||
protected String getSymbolName() {
|
||||
return "overwrite";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Address getAddressToWrite(TargetProcess process) throws Throwable {
|
||||
// It seems this is the only test case that exercises module symbols.
|
||||
List<String> modulePath = PathUtils.extend(process.getPath(),
|
||||
PathUtils.parse("Modules[" + getSpecimen().getBinModuleKey() + "]"));
|
||||
TargetObject container =
|
||||
Objects.requireNonNull(m.findContainer(TargetSymbol.class, modulePath));
|
||||
TargetSymbol symbol =
|
||||
waitOn(container.fetchElements()).get(getSymbolName()).as(TargetSymbol.class);
|
||||
return symbol.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getBytesToWrite() {
|
||||
return "Speak".getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getExpectedBytes() {
|
||||
return "Speak, World!".getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyExpectedEffect(TargetProcess process) throws Throwable {
|
||||
// TODO: Should (optional) exitCode be standardized on all models?
|
||||
retryVoid(() -> {
|
||||
long status = process.getTypedAttributeNowByName(
|
||||
DbgModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME, Long.class, 0L);
|
||||
assertEquals('S', status);
|
||||
}, List.of(AssertionError.class));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user