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:
Dan 2021-04-01 10:15:17 -04:00
parent e83a893493
commit 015858b5d3
533 changed files with 29293 additions and 8011 deletions

View File

@ -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|

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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.
*

View File

@ -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();

View File

@ -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)));
}
}

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);

View File

@ -32,8 +32,7 @@ public interface DbgReason {
@Override
public String desc() {
// TODO Auto-generated method stub
return null;
return "Unknown";
}
}

View File

@ -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();

View File

@ -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
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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);
}
}
}
}

View File

@ -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)) {

View File

@ -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

View File

@ -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));
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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() {

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -46,7 +46,6 @@ public interface DbgModelTargetExecutionStateful
changeAttributes(List.of(), Map.of( //
STATE_ATTRIBUTE_NAME, state //
), reason);
getListeners().fire(TargetExecutionStateListener.class).executionStateChanged(this, state);
}
}

View File

@ -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");

View File

@ -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));
}
}

View File

@ -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();
}

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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 {
}

View File

@ -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);

View File

@ -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 {
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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));
});
});
});
});

View File

@ -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));
});
});
});
});

View File

@ -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() {

View File

@ -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);
});
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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");
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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");
}

View File

@ -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);

View File

@ -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");

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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");
}

View File

@ -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());
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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");
}

View File

@ -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();
}
}

View File

@ -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]"));
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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"));
}
}

View File

@ -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"));
}
}

View File

@ -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";
}
}

View File

@ -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"));
}
}

View File

@ -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