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| .project||NONE||reviewed||END|
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
build.gradle||GHIDRA||||END| build.gradle||GHIDRA||||END|
hs_err_pid5696.mdmp||GHIDRA||||END|
src/javaprovider/def/javaprovider.def||GHIDRA||||END| src/javaprovider/def/javaprovider.def||GHIDRA||||END|
src/javaprovider/rc/javaprovider.rc||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 ghidra.lifecycle.Unfinished.TODO;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.lang.invoke.MethodHandles;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -31,22 +32,17 @@ import agent.dbgeng.dbgeng.DbgEngTest;
import agent.dbgeng.model.iface1.DbgModelTargetLauncher; import agent.dbgeng.model.iface1.DbgModelTargetLauncher;
import agent.dbgeng.testutil.DummyProc; import agent.dbgeng.testutil.DummyProc;
import ghidra.async.*; import ghidra.async.*;
import ghidra.dbg.DebugModelConventions; import ghidra.dbg.*;
import ghidra.dbg.DebugModelConventions.AllRequiredAccess; import ghidra.dbg.DebugModelConventions.AllRequiredAccess;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.attributes.TargetObjectList;
import ghidra.dbg.error.DebuggerModelNoSuchPathException; import ghidra.dbg.error.DebuggerModelNoSuchPathException;
import ghidra.dbg.error.DebuggerModelTypeException; import ghidra.dbg.error.DebuggerModelTypeException;
import ghidra.dbg.target.*; 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.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetConsole.Channel; import ghidra.dbg.target.TargetConsole.Channel;
import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener;
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
import ghidra.dbg.target.TargetObject.TargetObjectListener;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.dbg.target.schema.XmlSchemaContext; import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.dbg.util.AllTargetObjectListenerAdapter;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest; import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
@ -623,12 +619,13 @@ public abstract class AbstractModelForDbgTest
AtomicReference<AllRequiredAccess> access = new AtomicReference<>(); AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
AsyncReference<String, Void> lastOut = new AsyncReference<>(); AsyncReference<String, Void> lastOut = new AsyncReference<>();
AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { DebuggerModelListener l = new DebuggerModelListener() {
@Override @Override
public void consoleOutput(TargetObject interpreter, Channel channel, public void consoleOutput(TargetObject interpreter, Channel channel,
String out) { byte[] out) {
Msg.debug(this, "Got " + channel + " output: " + out); String str = new String(out);
lastOut.set(out, null); 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<TargetObject> root = new AtomicReference<>();
AtomicReference<AllRequiredAccess> access = new AtomicReference<>(); AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
AllTargetObjectListenerAdapter l = new AllTargetObjectListenerAdapter() { DebuggerModelListener l = new DebuggerModelListener() {
@Override @Override
public void consoleOutput(TargetObject interpreter, Channel channel, public void consoleOutput(TargetObject interpreter, Channel channel,
String out) { String out) {
Msg.debug(this, "Got " + channel + " output: " + out); String str = new String(out);
if (!out.contains("test")) { Msg.debug(this, "Got " + channel + " output: " + str);
if (!str.contains("test")) {
return; 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<TargetObject> root = new AtomicReference<>();
AtomicReference<AllRequiredAccess> access = new AtomicReference<>(); AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>(); AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
m.init().handle(seq::next); m.init().handle(seq::next);
@ -721,7 +719,7 @@ public abstract class AbstractModelForDbgTest
access.get().waitValue(true).handle(seq::next); access.get().waitValue(true).handle(seq::next);
}).then(seq -> { }).then(seq -> {
Msg.debug(this, "Finding breakpoint container..."); Msg.debug(this, "Finding breakpoint container...");
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
.handle(seq::next); .handle(seq::next);
}, breaks).then(seq -> { }, breaks).then(seq -> {
Msg.debug(this, "Got: " + breaks); Msg.debug(this, "Got: " + breaks);
@ -744,7 +742,7 @@ public abstract class AbstractModelForDbgTest
AtomicReference<TargetObject> root = new AtomicReference<>(); AtomicReference<TargetObject> root = new AtomicReference<>();
AtomicReference<AllRequiredAccess> access = new AtomicReference<>(); AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
AtomicReference<DbgModelTargetLauncher> launcher = new AtomicReference<>(); AtomicReference<DbgModelTargetLauncher> launcher = new AtomicReference<>();
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>(); AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
AtomicReference<TargetBreakpointLocation> loc = new AtomicReference<>(); AtomicReference<TargetBreakpointLocation> loc = new AtomicReference<>();
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
@ -770,13 +768,13 @@ public abstract class AbstractModelForDbgTest
.handle(seq::nextIgnore); .handle(seq::nextIgnore);
}).then(seq -> { }).then(seq -> {
Msg.debug(this, "Finding breakpoint container..."); Msg.debug(this, "Finding breakpoint container...");
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
.handle(seq::next); .handle(seq::next);
}, breaks).then(seq -> { }, breaks).then(seq -> {
Msg.debug(this, "Placing breakpoint..."); Msg.debug(this, "Placing breakpoint...");
breaks.get() breaks.get()
.placeBreakpoint("0x7ff7d52c8987", .placeBreakpoint("0x7ff7d52c8987",
Set.of(TargetBreakpointKind.SOFTWARE)) Set.of(TargetBreakpointKind.SW_EXECUTE))
.handle(seq::next); .handle(seq::next);
}).then(seq -> { }).then(seq -> {
Msg.debug(this, "Getting breakpoint specs..."); Msg.debug(this, "Getting breakpoint specs...");
@ -794,9 +792,6 @@ public abstract class AbstractModelForDbgTest
loc.set(es.iterator().next()); loc.set(es.iterator().next());
Address addr = loc.get().getAddress(); Address addr = loc.get().getAddress();
Msg.debug(this, "Got address: " + addr); Msg.debug(this, "Got address: " + addr);
TargetObjectList<?> list = loc.get().getAffects();
Msg.debug(this, "Got affects: " + list);
assertEquals(1, list.size());
seq.exit(); seq.exit();
}).finish()); }).finish());
} }
@ -809,7 +804,7 @@ public abstract class AbstractModelForDbgTest
AtomicReference<TargetObject> root = new AtomicReference<>(); AtomicReference<TargetObject> root = new AtomicReference<>();
AtomicReference<AllRequiredAccess> access = new AtomicReference<>(); AtomicReference<AllRequiredAccess> access = new AtomicReference<>();
AtomicReference<TargetBreakpointContainer> breaks = new AtomicReference<>(); AtomicReference<TargetBreakpointSpecContainer> breaks = new AtomicReference<>();
AtomicReference<TargetLauncher> launcher = new AtomicReference<>(); AtomicReference<TargetLauncher> launcher = new AtomicReference<>();
AtomicReference<TargetObject> obj = new AtomicReference<>(); AtomicReference<TargetObject> obj = new AtomicReference<>();
waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
@ -838,13 +833,13 @@ public abstract class AbstractModelForDbgTest
access.get().waitValue(true).handle(seq::next); access.get().waitValue(true).handle(seq::next);
}).then(seq -> { }).then(seq -> {
Msg.debug(this, "Finding breakpoint container..."); Msg.debug(this, "Finding breakpoint container...");
DebugModelConventions.findSuitable(TargetBreakpointContainer.class, root.get()) DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get())
.handle(seq::next); .handle(seq::next);
}, breaks).then(seq -> { }, breaks).then(seq -> {
Msg.debug(this, "Placing breakpoint..."); Msg.debug(this, "Placing breakpoint...");
breaks.get() breaks.get()
.placeBreakpoint("0x7ff7d52c8987", .placeBreakpoint("0x7ff7d52c8987",
Set.of(TargetBreakpointKind.SOFTWARE)) Set.of(TargetBreakpointKind.SW_EXECUTE))
.handle(seq::next); .handle(seq::next);
}).then(seq -> { }).then(seq -> {
Msg.debug(this, "Getting Process 1..."); Msg.debug(this, "Getting Process 1...");
@ -970,16 +965,17 @@ public abstract class AbstractModelForDbgTest
AsyncReference<List<String>, Void> focusProcPath = new AsyncReference<>(); AsyncReference<List<String>, Void> focusProcPath = new AsyncReference<>();
AsyncReference<Integer, Void> processCount = new AsyncReference<>(); AsyncReference<Integer, Void> processCount = new AsyncReference<>();
TargetObjectListener procListener = new TargetObjectListener() { DebuggerModelListener procListener = new DebuggerModelListener() {
@Override @Override
public void elementsChanged(TargetObject parent, Collection<String> removed, public void elementsChanged(TargetObject parent, Collection<String> removed,
Map<String, ? extends TargetObject> added) { Map<String, ? extends TargetObject> added) {
processCount.set(processes.get().getCachedElements().size(), null); processCount.set(processes.get().getCachedElements().size(), null);
} }
}; };
TargetFocusScopeListener focusListener = new TargetFocusScopeListener() { DebuggerModelListener focusListener =
@Override new AnnotatedDebuggerAttributeListener(MethodHandles.lookup()) {
public void focusChanged(TargetFocusScope object, TargetObject focused) { @AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME)
public void focusChanged(TargetObject object, TargetObject focused) {
// Truncate the path to the parent process // Truncate the path to the parent process
focusProcPath.set(focused.getPath().subList(0, 2), null); focusProcPath.set(focused.getPath().subList(0, 2), null);
} }

View file

@ -24,8 +24,8 @@ import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
import ghidra.util.classfinder.ExtensionPointProperties; import ghidra.util.classfinder.ExtensionPointProperties;
/** /**
* Note this is in the testing source because it's not meant to be shipped in the release.... That * Note this is in the testing source because it's not meant to be shipped in
* may change if it proves stable, though, no? * the release.... That may change if it proves stable, though, no?
*/ */
@FactoryDescription( // @FactoryDescription( //
brief = "IN-VM MS dbgeng local debugger", // brief = "IN-VM MS dbgeng local debugger", //
@ -34,6 +34,8 @@ import ghidra.util.classfinder.ExtensionPointProperties;
@ExtensionPointProperties(priority = 80) @ExtensionPointProperties(priority = 80)
public class DbgEngInJvmDebuggerModelFactory implements LocalDebuggerModelFactory { public class DbgEngInJvmDebuggerModelFactory implements LocalDebuggerModelFactory {
// TODO remoteTransport option?
@Override @Override
public CompletableFuture<? extends DebuggerObjectModel> build() { public CompletableFuture<? extends DebuggerObjectModel> build() {
DbgModelImpl model = new DbgModelImpl(); DbgModelImpl model = new DbgModelImpl();

View file

@ -108,10 +108,23 @@ public interface DebugBreakpoint {
void setFlags(BreakFlags... flags); 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); void setOffset(long offset);
String getOffsetExpression();
void setOffsetExpression(String expression);
BreakDataParameters getDataParameters(); BreakDataParameters getDataParameters();
void setDataParameters(BreakDataParameters params); void setDataParameters(BreakDataParameters params);

View file

@ -254,6 +254,7 @@ public interface DebugControl extends DebugControlReentrant {
/** /**
* Shortcut to retrieve all breakpoints for the current process. * Shortcut to retrieve all breakpoints for the current process.
* *
* <p>
* Uses {@link #getNumberBreakpoints()} and {@link #getBreakpointByIndex(int)} to enumerate all * Uses {@link #getNumberBreakpoints()} and {@link #getBreakpointByIndex(int)} to enumerate all
* breakpoints for the current process. * breakpoints for the current process.
* *
@ -287,10 +288,54 @@ public interface DebugControl extends DebugControlReentrant {
*/ */
DebugBreakpoint getBreakpointById(int id); 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); 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); 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); void waitForEvent(int timeout);
DebugEventInformation getLastEventInformation(); DebugEventInformation getLastEventInformation();
@ -311,5 +356,4 @@ public interface DebugControl extends DebugControlReentrant {
int getExecutingProcessorType(); int getExecutingProcessorType();
int getDebuggeeType(); int getDebuggeeType();
} }

View file

@ -26,18 +26,18 @@ public class DebugModuleInfo {
public final long imageFileHandle; public final long imageFileHandle;
public final long baseOffset; public final long baseOffset;
public final int moduleSize; public final int moduleSize;
public final String moduleName;
public final String imageName;
public final int checkSum; public final int checkSum;
public final int timeDateStamp; public final int timeDateStamp;
private String moduleName;
private String imageName;
public DebugModuleInfo(long imageFileHandle, long baseOffset, int moduleSize, String moduleName, public DebugModuleInfo(long imageFileHandle, long baseOffset, int moduleSize, String moduleName,
String imageName, int checkSum, int timeDateStamp) { String imageName, int checkSum, int timeDateStamp) {
this.imageFileHandle = imageFileHandle; this.imageFileHandle = imageFileHandle;
this.baseOffset = baseOffset; this.baseOffset = baseOffset;
this.moduleSize = moduleSize; this.moduleSize = moduleSize;
this.moduleName = moduleName; this.setModuleName(moduleName);
this.imageName = imageName; this.setImageName(imageName);
this.checkSum = checkSum; this.checkSum = checkSum;
this.timeDateStamp = timeDateStamp; // TODO: Convert to DateTime? this.timeDateStamp = timeDateStamp; // TODO: Convert to DateTime?
} }
@ -45,4 +45,20 @@ public class DebugModuleInfo {
public String toString() { public String toString() {
return Long.toHexString(baseOffset); 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) { default DebugValue getValueByName(String name) {
int indexByName = getIndexByName(name); int indexByName = getIndexByName(name);
if (indexByName > 0) { if (indexByName >= 0) {
return getValue(indexByName); return getValue(indexByName);
} }
return null; 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. * 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 * 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). * 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 * 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 * we can always create a new thread and client, using the existing client's reentrant
* methods. * methods.
* *
* <p>
* As stated in the MSDN, this thread repeatedly calls {@code DispatchEvents} in order to * 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 * 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 * 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. * Schedule a task with a given priority.
* *
* <p>
* Smaller priority values indicate earlier execution. The default priority is * Smaller priority values indicate earlier execution. The default priority is
* {@link #DEFAULT_PRIORITY}. * {@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. * 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 * This is a convenience which spares a call to {@link #getClient()}. See
* {@link #execute(int, Runnable)} about priority. * {@link #execute(int, Runnable)} about priority.
* *

View file

@ -15,13 +15,16 @@
*/ */
package agent.dbgeng.impl.dbgeng.breakpoint; 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.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils; import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.ptr.PointerByReference; import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DbgEng; import agent.dbgeng.dbgeng.DbgEng;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable; import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal; import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal; import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine; import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine;
@ -124,9 +127,14 @@ public class DebugBreakpointImpl1 implements DebugBreakpointInternal {
} }
@Override @Override
public long getOffset() { public Long getOffset() {
ULONGLONGByReference pullOffset = new ULONGLONGByReference(); 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(); return pullOffset.getValue().longValue();
} }
@ -136,6 +144,21 @@ public class DebugBreakpointImpl1 implements DebugBreakpointInternal {
COMUtils.checkRC(jnaBreakpoint.SetOffset(ullOffset)); 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 @Override
public BreakDataParameters getDataParameters() { public BreakDataParameters getDataParameters() {
ULONGByReference pulSize = new ULONGByReference(); ULONGByReference pulSize = new ULONGByReference();

View file

@ -15,6 +15,12 @@
*/ */
package agent.dbgeng.impl.dbgeng.breakpoint; 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; import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint2;
public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 { public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 {
@ -25,4 +31,20 @@ public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 {
super(jnaBreakpoint); super(jnaBreakpoint);
this.jnaBreakpoint = 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; package agent.dbgeng.impl.dbgeng.control;
import javax.help.UnsupportedOperationException;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.*; import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinError; import com.sun.jna.platform.win32.WinError;
@ -211,6 +213,16 @@ public class DebugControlImpl1 implements DebugControlInternal {
return doAddBreakpoint(type, DbgEngUtil.DEBUG_ANY_ID); 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 @Override
public void removeBreakpoint(IDebugBreakpoint comBpt) { public void removeBreakpoint(IDebugBreakpoint comBpt) {
COMUtils.checkRC(jnaControl.RemoveBreakpoint(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.WString;
import com.sun.jna.platform.win32.WinDef.ULONG; import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference; 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.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; import ghidra.comm.util.BitmaskSet;
public class DebugControlImpl4 extends DebugControlImpl3 { public class DebugControlImpl4 extends DebugControlImpl3 {
@ -92,4 +97,26 @@ public class DebugControlImpl4 extends DebugControlImpl3 {
public void returnInput(String input) { public void returnInput(String input) {
COMUtils.checkRC(jnaControl.ReturnInputWide(new WString(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 @Override
public HRESULT SetOffsetExpressionWide(WString Expression) { 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.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference; import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT; 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.DbgEngNative.DEBUG_VALUE;
import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex; 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 ReturnInputWide(WString Buffer);
HRESULT OutputWide(ULONG Mask, WString Format, Object... objects); 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.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference; import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT; 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.DbgEngNative.DEBUG_VALUE;
@ -35,6 +36,11 @@ public class WrapIDebugControl4 extends WrapIDebugControl3 implements IDebugCont
super(pvInstance); super(pvInstance);
} }
@Override
public HRESULT AddBreakpoint2(ULONG Type, ULONG DesiredId, PointerByReference Bp) {
return _invokeHR(VTIndices4.ADD_BREAKPOINT2, getPointer(), Type, DesiredId, Bp);
}
@Override @Override
public HRESULT ReturnInputWide(WString Buffer) { public HRESULT ReturnInputWide(WString Buffer) {
return _invokeHR(VTIndices4.RETURN_INPUT_WIDE, getPointer(), Buffer); return _invokeHR(VTIndices4.RETURN_INPUT_WIDE, getPointer(), Buffer);

View file

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

View file

@ -74,6 +74,15 @@ public enum DbgState {
public boolean isAlive() { public boolean isAlive() {
return false; return false;
} }
},
/**
* Dbg or the process has exited
*/
SESSION_EXIT {
@Override
public boolean isAlive() {
return false;
}
}; };
public abstract boolean isAlive(); public abstract boolean isAlive();

View file

@ -15,11 +15,10 @@
*/ */
package agent.dbgeng.manager.breakpoint; 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.DebugBreakpoint.*; import agent.dbgeng.dbgeng.DebugBreakpoint.*;
import agent.dbgeng.dbgeng.DebugProcessId;
import agent.dbgeng.manager.DbgProcess; import agent.dbgeng.manager.DbgProcess;
import agent.dbgeng.manager.DbgThread; import agent.dbgeng.manager.DbgThread;
import ghidra.comm.util.BitmaskSet; import ghidra.comm.util.BitmaskSet;
@ -39,8 +38,9 @@ public class DbgBreakpointInfo {
private final long number; private final long number;
private boolean enabled; private boolean enabled;
private final String location; private Long offset;
private final List<DbgBreakpointLocation> locations; private String expression;
//private final List<DbgBreakpointLocation> locations;
/** /**
* Construct Dbg breakpoint information * Construct Dbg breakpoint information
@ -64,28 +64,28 @@ public class DbgBreakpointInfo {
} }
public DbgBreakpointInfo(DebugBreakpoint bp, DbgProcess process, DbgThread thread) { public DbgBreakpointInfo(DebugBreakpoint bp, DbgProcess process, DbgThread thread) {
this.bpt = bp; this.setBreakpoint(bp);
this.proc = process; this.proc = process;
this.eventThread = thread; this.eventThread = thread;
this.number = bpt.getId(); this.number = bpt.getId();
this.bptType = bpt.getType(); this.bptType = bpt.getType();
this.flags = bpt.getFlags(); this.flags = bpt.getFlags();
//this.parameters = bpt.getDataParameters(); if (bpt.getType().breakType.equals(BreakType.DATA)) {
//this.access = parameters.access; this.parameters = bpt.getDataParameters();
//this.size = parameters.size; }
this.location = Long.toHexString(bpt.getOffset()); this.access = parameters.access;
List<DbgBreakpointLocation> locs = new ArrayList<>(); this.size = parameters.size;
List<DebugProcessId> ids = new ArrayList<>(); this.offset = bpt.getOffset();
ids.add(proc.getId()); this.expression = bpt.getOffsetExpression();
locs.add(new DbgBreakpointLocation(bpt.getId(), 1, true, location, ids));
this.locations = Collections.unmodifiableList(locs);
} }
@Override @Override
public int hashCode() { 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() { public String toString() {
return Integer.toHexString(bpt.getId()); return Integer.toHexString(bpt.getId());
} }
@ -110,10 +110,16 @@ public class DbgBreakpointInfo {
if (this.getFlags() != that.getFlags()) { if (this.getFlags() != that.getFlags()) {
return false; return false;
} }
if (this.size != that.size) { if (this.getSize() != that.getSize()) {
return false; 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; return false;
} }
if (this.enabled != that.enabled) { 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 * @return the location
*/ */
public String getLocation() { public String getExpression() {
return location; 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() { public Long getOffset() {
return Long.parseUnsignedLong(location, 16); return offset;
} }
/** /**
@ -238,15 +248,16 @@ public class DbgBreakpointInfo {
/** /**
* Get a list of resolved addresses * Get a list of resolved addresses
* *
* <p>
* The effective locations may change for a variety of reasons. Most notable, a new module may * 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 * 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. * addresses within will become new effective locations of this breakpoint.
* *
* @return the list of locations at the time the breakpoint information was captured * @return the list of locations at the time the breakpoint information was captured
*/ */
public List<DbgBreakpointLocation> getLocations() { /*public List<DbgBreakpointLocation> getLocations() {
return locations; return locations;
} }*/
public DbgBreakpointInfo withEnabled(@SuppressWarnings("hiding") boolean enabled) { public DbgBreakpointInfo withEnabled(@SuppressWarnings("hiding") boolean enabled) {
if (isEnabled() == enabled) { if (isEnabled() == enabled) {
@ -271,7 +282,20 @@ public class DbgBreakpointInfo {
return eventThread; return eventThread;
} }
public long getAddressAsLong() { public void setBreakpoint(DebugBreakpoint bpt) {
return locations.get(0).addrAsLong(); 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 java.util.Collection;
import agent.dbgeng.dbgeng.DebugClient; import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.manager.DbgEvent; import agent.dbgeng.manager.DbgCause;
import agent.dbgeng.manager.DbgProcess; import agent.dbgeng.manager.DbgProcess;
import agent.dbgeng.manager.evt.AbstractDbgCompletedCommandEvent;
import agent.dbgeng.manager.evt.DbgThreadExitedEvent;
import agent.dbgeng.manager.impl.*; import agent.dbgeng.manager.impl.*;
/** /**
@ -44,6 +42,7 @@ public class DbgDetachCommand extends AbstractDbgCommand<Void> {
manager.fireThreadExited(t.getId(), process, pending); manager.fireThreadExited(t.getId(), process, pending);
t.remove(); t.remove();
} }
manager.getEventListeners().fire.processRemoved(process.getId(), DbgCause.Causes.UNCLAIMED);
return null; return null;
} }

View file

@ -15,11 +15,9 @@
*/ */
package agent.dbgeng.manager.cmd; package agent.dbgeng.manager.cmd;
import java.util.ArrayList; import agent.dbgeng.dbgeng.DebugBreakpoint;
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.breakpoint.*;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import ghidra.comm.util.BitmaskSet; import ghidra.comm.util.BitmaskSet;
@ -28,41 +26,33 @@ import ghidra.comm.util.BitmaskSet;
* Implementation of {@link DbgBreakpointInsertions#insertBreakpoint(String)} * Implementation of {@link DbgBreakpointInsertions#insertBreakpoint(String)}
*/ */
public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpointInfo> { public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpointInfo> {
private List<Long> locations; //private List<Long> locations;
private final DbgBreakpointType type; private final DbgBreakpointType type;
private DbgBreakpointInfo bkpt; private DbgBreakpointInfo bkpt;
private int len; private int len;
private final String expression;
private final Long loc;
public DbgInsertBreakpointCommand(DbgManagerImpl manager, String expression, public DbgInsertBreakpointCommand(DbgManagerImpl manager, String expression,
DbgBreakpointType type) { DbgBreakpointType type) {
super(manager); 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.type = type;
this.expression = expression;
this.loc = null;
} }
public DbgInsertBreakpointCommand(DbgManagerImpl manager, long loc, int len, public DbgInsertBreakpointCommand(DbgManagerImpl manager, long loc, int len,
DbgBreakpointType type) { DbgBreakpointType type) {
super(manager); super(manager);
locations = new ArrayList<>();
locations.add(loc);
this.len = len; this.len = len;
this.type = type; this.type = type;
this.expression = null;
this.loc = loc;
} }
@Override @Override
public DbgBreakpointInfo complete(DbgPendingCommand<?> pending) { public DbgBreakpointInfo complete(DbgPendingCommand<?> pending) {
manager.doBreakpointCreated(bkpt, pending); //manager.doBreakpointCreated(bkpt, pending);
return bkpt; return bkpt;
} }
@ -73,8 +63,8 @@ public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpoint
if (type.equals(DbgBreakpointType.BREAKPOINT)) { if (type.equals(DbgBreakpointType.BREAKPOINT)) {
bt = BreakType.CODE; bt = BreakType.CODE;
} }
DebugBreakpoint bp = control.addBreakpoint(bt); // 2 for BU, 1 for BP
bp.addFlags(BreakFlags.ENABLED); DebugBreakpoint bp = control.addBreakpoint/*2*/(bt);
if (bt.equals(BreakType.DATA)) { if (bt.equals(BreakType.DATA)) {
BitmaskSet<BreakAccess> access = BitmaskSet.of(BreakAccess.EXECUTE); BitmaskSet<BreakAccess> access = BitmaskSet.of(BreakAccess.EXECUTE);
if (type.equals(DbgBreakpointType.ACCESS_WATCHPOINT)) { if (type.equals(DbgBreakpointType.ACCESS_WATCHPOINT)) {
@ -92,9 +82,14 @@ public class DbgInsertBreakpointCommand extends AbstractDbgCommand<DbgBreakpoint
} }
bp.setDataParameters(len, access); bp.setDataParameters(len, access);
} }
for (Long loc : locations) { if (loc != null) {
bp.setOffset(loc); bp.setOffset(loc);
}
else {
bp.setOffsetExpression(expression);
}
bp.addFlags(BreakFlags.ENABLED);
bkpt = new DbgBreakpointInfo(bp, manager.getCurrentProcess()); 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); Msg.warn(this, "Resync: Was missing thread: " + id);
so.setCurrentThreadId(id); so.setCurrentThreadId(id);
int tid = so.getCurrentThreadSystemId(); int tid = so.getCurrentThreadSystemId();
DbgThreadImpl thread = manager.getThreadComputeIfAbsent(id, process, tid); manager.getThreadComputeIfAbsent(id, process, tid);
thread.add();
} }
for (DebugThreadId id : new ArrayList<>(cur)) { for (DebugThreadId id : new ArrayList<>(cur)) {
if (updatedThreadIds.contains(id)) { if (updatedThreadIds.contains(id)) {

View file

@ -18,6 +18,7 @@ package agent.dbgeng.manager.cmd;
import java.util.*; import java.util.*;
import agent.dbgeng.dbgeng.*; import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
import agent.dbgeng.manager.DbgModule; import agent.dbgeng.manager.DbgModule;
import agent.dbgeng.manager.impl.*; import agent.dbgeng.manager.impl.*;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -62,6 +63,10 @@ public class DbgListModulesCommand extends AbstractDbgCommand<Map<String, DbgMod
DebugSymbols symbols = manager.getSymbols(); DebugSymbols symbols = manager.getSymbols();
for (DebugModule module : symbols.iterateModules(0)) { for (DebugModule module : symbols.iterateModules(0)) {
DebugModuleInfo info = symbols.getModuleParameters(1, module.getIndex()); 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); updatedModules.put(info.toString(), module);
moduleInfo.put(module, info); 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.DebugSystemObjects;
import agent.dbgeng.dbgeng.DebugThreadId; import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.manager.DbgThread; 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; import ghidra.util.Msg;
public class DbgListThreadsCommand extends AbstractDbgCommand<Map<DebugThreadId, DbgThread>> { public class DbgListThreadsCommand extends AbstractDbgCommand<Map<DebugThreadId, DbgThread>> {
@ -45,8 +46,7 @@ public class DbgListThreadsCommand extends AbstractDbgCommand<Map<DebugThreadId,
DebugSystemObjects so = manager.getSystemObjects(); DebugSystemObjects so = manager.getSystemObjects();
so.setCurrentThreadId(id); so.setCurrentThreadId(id);
int tid = so.getCurrentThreadSystemId(); int tid = so.getCurrentThreadSystemId();
DbgThreadImpl thread = manager.getThreadComputeIfAbsent(id, process, tid); manager.getThreadComputeIfAbsent(id, process, tid);
thread.add();
} }
for (DebugThreadId id : new ArrayList<>(cur)) { for (DebugThreadId id : new ArrayList<>(cur)) {
if (updatedThreadIds.contains(id)) { if (updatedThreadIds.contains(id)) {

View file

@ -34,28 +34,35 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
this.manager = manager; this.manager = manager;
} }
protected DebugStatus checkInterrupt(DebugStatus normal) {
if (manager.getControl().getInterrupt()) {
return DebugStatus.BREAK;
}
return normal;
}
@Override @Override
public DebugStatus breakpoint(DebugBreakpoint bp) { public DebugStatus breakpoint(DebugBreakpoint bp) {
Msg.info(this, "***Breakpoint: " + bp.getId()); Msg.info(this, "***Breakpoint: " + bp.getId());
return manager.processEvent(new DbgBreakpointEvent(bp)); return checkInterrupt(manager.processEvent(new DbgBreakpointEvent(bp)));
} }
@Override @Override
public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) { public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) {
Msg.info(this, "***Exception: " + exception + ", first=" + firstChance); Msg.info(this, "***Exception: " + exception + ", first=" + firstChance);
return manager.processEvent(new DbgExceptionEvent(exception)); return checkInterrupt(manager.processEvent(new DbgExceptionEvent(exception)));
} }
@Override @Override
public DebugStatus createThread(DebugThreadInfo threadInfo) { public DebugStatus createThread(DebugThreadInfo threadInfo) {
Msg.info(this, "***Thread created: " + Long.toHexString(threadInfo.handle)); Msg.info(this, "***Thread created: " + Long.toHexString(threadInfo.handle));
return manager.processEvent(new DbgThreadCreatedEvent(threadInfo)); return checkInterrupt(manager.processEvent(new DbgThreadCreatedEvent(threadInfo)));
} }
@Override @Override
public DebugStatus exitThread(int exitCode) { public DebugStatus exitThread(int exitCode) {
Msg.info(this, "***Thread exited: " + exitCode); Msg.info(this, "***Thread exited: " + exitCode);
return manager.processEvent(new DbgThreadExitedEvent(exitCode)); return checkInterrupt(manager.processEvent(new DbgThreadExitedEvent(exitCode)));
} }
@Override @Override
@ -63,20 +70,20 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
Msg.info(this, "***Process created: " + Long.toHexString(processInfo.handle)); Msg.info(this, "***Process created: " + Long.toHexString(processInfo.handle));
Msg.info(this, Msg.info(this,
" **Thread created: " + Long.toHexString(processInfo.initialThreadInfo.handle)); " **Thread created: " + Long.toHexString(processInfo.initialThreadInfo.handle));
return manager.processEvent(new DbgProcessCreatedEvent(processInfo)); return checkInterrupt(manager.processEvent(new DbgProcessCreatedEvent(processInfo)));
} }
@Override @Override
public DebugStatus exitProcess(int exitCode) { public DebugStatus exitProcess(int exitCode) {
Msg.info(this, "***Process exited: " + exitCode); Msg.info(this, "***Process exited: " + exitCode);
Msg.info(this, " **Thread exited"); Msg.info(this, " **Thread exited");
return manager.processEvent(new DbgProcessExitedEvent(exitCode)); return checkInterrupt(manager.processEvent(new DbgProcessExitedEvent(exitCode)));
} }
@Override @Override
public DebugStatus loadModule(DebugModuleInfo moduleInfo) { public DebugStatus loadModule(DebugModuleInfo moduleInfo) {
Msg.info(this, "***Module Loaded: " + moduleInfo); Msg.info(this, "***Module Loaded: " + moduleInfo);
return manager.processEvent(new DbgModuleLoadedEvent(moduleInfo)); return checkInterrupt(manager.processEvent(new DbgModuleLoadedEvent(moduleInfo)));
} }
@Override @Override
@ -85,7 +92,7 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
"***Module Unloaded: " + imageBaseName + ", " + Long.toHexString(baseOffset)); "***Module Unloaded: " + imageBaseName + ", " + Long.toHexString(baseOffset));
DebugModuleInfo info = DebugModuleInfo info =
new DebugModuleInfo(0L, baseOffset, 0, basename(imageBaseName), imageBaseName, 0, 0); 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) { private String basename(String path) {
@ -103,27 +110,27 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
DebugStatus status = DebugStatus.fromArgument(argument); DebugStatus status = DebugStatus.fromArgument(argument);
Msg.info(this, "***ExecutionStatus: " + status); Msg.info(this, "***ExecutionStatus: " + status);
if (status.equals(DebugStatus.NO_DEBUGGEE)) { 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)) { if (flags.contains(ChangeEngineState.BREAKPOINTS)) {
Msg.info(this, "***BreakpointChanged: " + flags + ", " + argument + " on " + Msg.info(this, "***BreakpointChanged: " + flags + ", " + argument + " on " +
Thread.currentThread()); Thread.currentThread());
return manager.processEvent(event); return checkInterrupt(manager.processEvent(event));
} }
if (flags.contains(ChangeEngineState.CURRENT_THREAD)) { if (flags.contains(ChangeEngineState.CURRENT_THREAD)) {
Msg.info(this, "***CurrentThread: " + argument); Msg.info(this, "***CurrentThread: " + argument);
if (argument < 0) { if (argument < 0) {
return manager.processEvent(event); return checkInterrupt(manager.processEvent(event));
} }
} }
if (flags.contains(ChangeEngineState.SYSTEMS)) { if (flags.contains(ChangeEngineState.SYSTEMS)) {
Msg.info(this, "***Systems: " + argument); Msg.info(this, "***Systems: " + argument);
event.setState(DbgState.RUNNING); event.setState(DbgState.RUNNING);
return manager.processEvent(event); return checkInterrupt(manager.processEvent(event));
} }
return DebugStatus.NO_CHANGE; return checkInterrupt(DebugStatus.NO_CHANGE);
} }
//@Override //@Override

View file

@ -15,7 +15,7 @@
*/ */
package agent.dbgeng.manager.impl; package agent.dbgeng.manager.impl;
import static ghidra.async.AsyncUtils.sequence; import static ghidra.async.AsyncUtils.*;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; 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.AbstractClientThreadExecutor;
import agent.dbgeng.gadp.impl.DbgEngClientThreadExecutor; import agent.dbgeng.gadp.impl.DbgEngClientThreadExecutor;
import agent.dbgeng.impl.dbgeng.DbgEngUtil; import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.jna.dbgeng.WinNTExtra;
import agent.dbgeng.manager.*; import agent.dbgeng.manager.*;
import agent.dbgeng.manager.DbgCause.Causes; import agent.dbgeng.manager.DbgCause.Causes;
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
@ -42,7 +43,6 @@ import agent.dbgeng.manager.cmd.*;
import agent.dbgeng.manager.evt.*; import agent.dbgeng.manager.evt.*;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import ghidra.async.*; import ghidra.async.*;
import ghidra.async.seq.AsyncSequenceHandlerForRunner;
import ghidra.comm.util.BitmaskSet; import ghidra.comm.util.BitmaskSet;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.util.HandlerMap; import ghidra.dbg.util.HandlerMap;
@ -237,7 +237,7 @@ public class DbgManagerImpl implements DbgManager {
public DbgSessionImpl getSessionComputeIfAbsent(DebugSessionId id) { public DbgSessionImpl getSessionComputeIfAbsent(DebugSessionId id) {
synchronized (sessions) { synchronized (sessions) {
if (!sessions.containsKey(id)) { if (!sessions.containsKey(id) && id.id >= 0) {
DbgSessionImpl session = new DbgSessionImpl(this, id); DbgSessionImpl session = new DbgSessionImpl(this, id);
session.add(); session.add();
} }
@ -425,19 +425,17 @@ public class DbgManagerImpl implements DbgManager {
//} //}
if (engThread.isCurrentThread()) { if (engThread.isCurrentThread()) {
sequence(TypeSpec.VOID).then((seq) -> { try {
addCommand(cmd, pcmd, seq); addCommand(cmd, pcmd);
seq.exit(); }
}).finish().exceptionally((exc) -> { catch (Throwable exc) {
pcmd.completeExceptionally(exc); pcmd.completeExceptionally(exc);
return null; }
});
} }
else { else {
sequence(TypeSpec.VOID).then(engThread, (seq) -> { CompletableFuture.runAsync(() -> {
addCommand(cmd, pcmd, seq); addCommand(cmd, pcmd);
seq.exit(); }, engThread).exceptionally((exc) -> {
}).finish().exceptionally((exc) -> {
pcmd.completeExceptionally(exc); pcmd.completeExceptionally(exc);
return null; return null;
}); });
@ -445,8 +443,7 @@ public class DbgManagerImpl implements DbgManager {
return pcmd; return pcmd;
} }
private <T> void addCommand(DbgCommand<? extends T> cmd, DbgPendingCommand<T> pcmd, private <T> void addCommand(DbgCommand<? extends T> cmd, DbgPendingCommand<T> pcmd) {
AsyncSequenceHandlerForRunner<Void> seq) {
synchronized (this) { synchronized (this) {
if (!cmd.validInState(state.get())) { if (!cmd.validInState(state.get())) {
throw new DbgCommandError("Command " + cmd + " is not valid while " + state.get()); throw new DbgCommandError("Command " + cmd + " is not valid while " + state.get());
@ -519,7 +516,7 @@ public class DbgManagerImpl implements DbgManager {
synchronized (this) { synchronized (this) {
boolean waitState = isWaiting(); boolean waitState = isWaiting();
waiting = false; waiting = false;
DebugStatus ret = handlerMap.handle(evt, null); DebugStatus ret = evt.isStolen() ? null : handlerMap.handle(evt, null);
if (ret == null) { if (ret == null) {
ret = DebugStatus.NO_CHANGE; ret = DebugStatus.NO_CHANGE;
} }
@ -602,11 +599,18 @@ public class DbgManagerImpl implements DbgManager {
DebugThreadId etid = so.getEventThread(); DebugThreadId etid = so.getEventThread();
DebugProcessId epid = so.getEventProcess(); DebugProcessId epid = so.getEventProcess();
DebugSessionId esid = so.getCurrentSystemId(); DebugSessionId esid = so.getCurrentSystemId();
so.setCurrentProcessId(epid);
so.setCurrentThreadId(etid);
DebugControl control = dbgeng.getControl(); 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 = control.getLastEventInformation();
lastEventInformation.setSession(esid); lastEventInformation.setSession(esid);
lastEventInformation.setExecutingProcessorType(execType); lastEventInformation.setExecutingProcessorType(execType);
@ -687,6 +691,7 @@ public class DbgManagerImpl implements DbgManager {
DbgProcessImpl process = getCurrentProcess(); DbgProcessImpl process = getCurrentProcess();
int tid = so.getCurrentThreadSystemId(); int tid = so.getCurrentThreadSystemId();
DbgThreadImpl thread = getThreadComputeIfAbsent(eventId, process, tid); DbgThreadImpl thread = getThreadComputeIfAbsent(eventId, process, tid);
getEventListeners().fire.threadCreated(thread, DbgCause.Causes.UNCLAIMED);
getEventListeners().fire.threadSelected(thread, null, evt.getCause()); getEventListeners().fire.threadSelected(thread, null, evt.getCause());
String key = Integer.toHexString(eventId.id); String key = Integer.toHexString(eventId.id);
@ -759,18 +764,17 @@ public class DbgManagerImpl implements DbgManager {
so.setCurrentProcessId(id); so.setCurrentProcessId(id);
int pid = so.getCurrentProcessSystemId(); int pid = so.getCurrentProcessSystemId();
DbgProcessImpl proc = getProcessComputeIfAbsent(id, pid); DbgProcessImpl proc = getProcessComputeIfAbsent(id, pid);
getEventListeners().fire.processAdded(proc, DbgCause.Causes.UNCLAIMED);
getEventListeners().fire.processSelected(proc, evt.getCause()); getEventListeners().fire.processSelected(proc, evt.getCause());
handle = info.initialThreadInfo.handle; handle = info.initialThreadInfo.handle;
DebugThreadId idt = so.getThreadIdByHandle(handle); DebugThreadId idt = so.getThreadIdByHandle(handle);
int tid = so.getCurrentThreadSystemId(); int tid = so.getCurrentThreadSystemId();
DbgThreadImpl thread = new DbgThreadImpl(this, proc, idt, tid); DbgThreadImpl thread = getThreadComputeIfAbsent(idt, proc, tid);
thread.add();
getEventListeners().fire.threadCreated(thread, evt.getCause());
getEventListeners().fire.threadSelected(thread, null, evt.getCause()); getEventListeners().fire.threadSelected(thread, null, evt.getCause());
proc.moduleLoaded(info.moduleInfo); //proc.moduleLoaded(info.moduleInfo);
getEventListeners().fire.moduleLoaded(proc, info.moduleInfo, evt.getCause()); //getEventListeners().fire.moduleLoaded(proc, info.moduleInfo, evt.getCause());
String key = Integer.toHexString(id.id); String key = Integer.toHexString(id.id);
if (statusByNameMap.containsKey(key)) { if (statusByNameMap.containsKey(key)) {
@ -788,20 +792,21 @@ public class DbgManagerImpl implements DbgManager {
*/ */
protected DebugStatus processProcessExited(DbgProcessExitedEvent evt, Void v) { protected DebugStatus processProcessExited(DbgProcessExitedEvent evt, Void v) {
DebugThreadId eventId = updateState(); DebugThreadId eventId = updateState();
DbgProcessImpl process = getCurrentProcess();
process.remove(evt.getCause());
getEventListeners().fire.processRemoved(process.getId(), evt.getCause());
DbgThreadImpl thread = getCurrentThread(); DbgThreadImpl thread = getCurrentThread();
if (thread != null) { DbgProcessImpl process = getCurrentProcess();
thread.remove(); process.setExitCode(Long.valueOf(evt.getInfo()));
}
getEventListeners().fire.threadExited(eventId, process, evt.getCause()); getEventListeners().fire.threadExited(eventId, process, evt.getCause());
getEventListeners().fire.processExited(process, evt.getCause());
for (DebugBreakpoint bpt : getBreakpoints()) { for (DebugBreakpoint bpt : getBreakpoints()) {
breaksById.remove(bpt.getId()); 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); String key = Integer.toHexString(process.getId().id);
if (statusByNameMap.containsKey(key)) { if (statusByNameMap.containsKey(key)) {
@ -844,7 +849,7 @@ public class DbgManagerImpl implements DbgManager {
process.moduleLoaded(info); process.moduleLoaded(info);
getEventListeners().fire.moduleLoaded(process, info, evt.getCause()); getEventListeners().fire.moduleLoaded(process, info, evt.getCause());
String key = info.moduleName; String key = info.getModuleName();
if (statusByNameMap.containsKey(key)) { if (statusByNameMap.containsKey(key)) {
return statusByNameMap.get(key); return statusByNameMap.get(key);
} }
@ -865,7 +870,7 @@ public class DbgManagerImpl implements DbgManager {
process.moduleUnloaded(info); process.moduleUnloaded(info);
getEventListeners().fire.moduleUnloaded(process, info, evt.getCause()); getEventListeners().fire.moduleUnloaded(process, info, evt.getCause());
String key = info.moduleName; String key = info.getModuleName();
if (statusByNameMap.containsKey(key)) { if (statusByNameMap.containsKey(key)) {
return statusByNameMap.get(key); return statusByNameMap.get(key);
} }
@ -908,8 +913,11 @@ public class DbgManagerImpl implements DbgManager {
dbgState = DbgState.RUNNING; dbgState = DbgState.RUNNING;
processEvent(new DbgRunningEvent(eventThread.getId())); processEvent(new DbgRunningEvent(eventThread.getId()));
} }
if (!threads.containsValue(eventThread)) {
dbgState = DbgState.EXIT;
}
// Don't fire // Don't fire
if (dbgState != null) { if (dbgState != null && dbgState != DbgState.EXIT) {
processEvent(new DbgThreadSelectedEvent(dbgState, eventThread, processEvent(new DbgThreadSelectedEvent(dbgState, eventThread,
evt.getFrame(eventThread))); evt.getFrame(eventThread)));
} }
@ -1023,12 +1031,14 @@ public class DbgManagerImpl implements DbgManager {
DbgBreakpointInfo knownBreakpoint = getKnownBreakpoint(bptId); DbgBreakpointInfo knownBreakpoint = getKnownBreakpoint(bptId);
if (knownBreakpoint == null) { if (knownBreakpoint == null) {
breakpointInfo = new DbgBreakpointInfo(bpt, getCurrentProcess()); breakpointInfo = new DbgBreakpointInfo(bpt, getCurrentProcess());
if (!breakpointInfo.getLocation().equals("0")) { if (breakpointInfo.getOffset() != null) {
doBreakpointCreated(breakpointInfo, evt.getCause()); doBreakpointCreated(breakpointInfo, evt.getCause());
} }
return; return;
} }
breakpointInfo = knownBreakpoint; breakpointInfo = knownBreakpoint;
breakpointInfo.setBreakpoint(bpt);
} }
doBreakpointModified(breakpointInfo, evt.getCause()); doBreakpointModified(breakpointInfo, evt.getCause());
} }
@ -1110,6 +1120,13 @@ public class DbgManagerImpl implements DbgManager {
doBreakpointModifiedSameLocations(newInfo, oldInfo, cause); doBreakpointModifiedSameLocations(newInfo, oldInfo, cause);
} }
private long orZero(Long l) {
if (l == null) {
return 0;
}
return l;
}
private void changeBreakpoints() { private void changeBreakpoints() {
Set<Integer> retained = new HashSet<>(); Set<Integer> retained = new HashSet<>();
DebugSystemObjects so = getSystemObjects(); DebugSystemObjects so = getSystemObjects();
@ -1134,7 +1151,7 @@ public class DbgManagerImpl implements DbgManager {
} }
int id = bpt.getId(); int id = bpt.getId();
retained.add(id); retained.add(id);
long newOffset = bpt.getOffset(); long newOffset = orZero(bpt.getOffset());
BreakpointTag tag = breaksById.get(id); BreakpointTag tag = breaksById.get(id);
if (tag == null) { if (tag == null) {
for (DebugThreadId tid : tids) { for (DebugThreadId tid : tids) {
@ -1400,8 +1417,7 @@ public class DbgManagerImpl implements DbgManager {
return execute(new DbgSessionSelectCommand(this, session)); return execute(new DbgSessionSelectCommand(this, session));
} }
public CompletableFuture<Void> requestFocus(DbgModelTargetFocusScope scope, public CompletableFuture<Void> requestFocus(DbgModelTargetFocusScope scope, TargetObject obj) {
TargetObject obj) {
return execute(new DbgRequestFocusCommand(this, scope, obj)); return execute(new DbgRequestFocusCommand(this, scope, obj));
} }

View file

@ -17,25 +17,30 @@ package agent.dbgeng.manager.impl;
public class DbgMinimalSymbol { public class DbgMinimalSymbol {
protected final long index; protected final long index;
protected final String type; protected final int typeId;
protected final String name; protected final String name;
protected final long address; 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.index = index;
this.type = type; this.typeId = typeId;
this.name = name; this.name = name;
this.address = address; this.address = address;
this.size = size;
this.tag = tag;
this.moduleBase = moduleBase;
} }
public long getIndex() { public long getIndex() {
return index; return index;
} }
public String getType() { public int getTypeId() {
// TODO: Interpret these types return typeId;
// Observed: t, T, D, S
return type;
} }
public String getName() { public String getName() {
@ -45,4 +50,16 @@ public class DbgMinimalSymbol {
public long getAddress() { public long getAddress() {
return address; 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.dbgeng.DebugModuleInfo;
import agent.dbgeng.manager.DbgCause.Causes; import agent.dbgeng.manager.DbgCause.Causes;
import agent.dbgeng.manager.DbgModule; import agent.dbgeng.manager.DbgModule;
import agent.dbgeng.manager.cmd.DbgListSymbolsCommand;
import ghidra.async.AsyncLazyValue; import ghidra.async.AsyncLazyValue;
public class DbgModuleImpl implements DbgModule { public class DbgModuleImpl implements DbgModule {
@ -44,7 +45,7 @@ public class DbgModuleImpl implements DbgModule {
this.manager = manager; this.manager = manager;
this.process = process; this.process = process;
this.info = info; this.info = info;
this.name = info.moduleName; this.name = info.getModuleName();
} }
@Override @Override
@ -70,12 +71,12 @@ public class DbgModuleImpl implements DbgModule {
@Override @Override
public String getImageName() { public String getImageName() {
return info == null ? getName() : info.imageName; return info == null ? getName() : info.getImageName();
} }
@Override @Override
public String getModuleName() { public String getModuleName() {
return info == null ? getName() : info.moduleName; return info == null ? getName() : info.getModuleName();
} }
@Override @Override
@ -94,9 +95,7 @@ public class DbgModuleImpl implements DbgModule {
} }
protected CompletableFuture<Map<String, DbgMinimalSymbol>> doGetMinimalSymbols() { protected CompletableFuture<Map<String, DbgMinimalSymbol>> doGetMinimalSymbols() {
// TODO: Apparently, this is using internal GDB-debugging commands.... return manager.execute(new DbgListSymbolsCommand(manager, process, this));
// TODO: Also make methods for "full" symbols (DWARF?)
return null;
} }
@Override @Override

View file

@ -15,7 +15,7 @@
*/ */
package agent.dbgeng.manager.impl; package agent.dbgeng.manager.impl;
import static ghidra.async.AsyncUtils.sequence; import static ghidra.async.AsyncUtils.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@ -109,7 +109,7 @@ public class DbgProcessImpl implements DbgProcess {
*/ */
public void add() { public void add() {
manager.processes.put(id, this); 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); //manager.addProcess(this, cause);
} }
@ -352,7 +352,7 @@ public class DbgProcessImpl implements DbgProcess {
} }
protected void moduleLoaded(DebugModuleInfo info) { protected void moduleLoaded(DebugModuleInfo info) {
if (!modules.containsKey(info.moduleName)) { if (!modules.containsKey(info.getModuleName())) {
DbgModuleImpl module = new DbgModuleImpl(manager, this, info); DbgModuleImpl module = new DbgModuleImpl(manager, this, info);
modules.put(info.toString(), module); modules.put(info.toString(), module);
} }

View file

@ -15,8 +15,6 @@
*/ */
package agent.dbgeng.manager.impl; package agent.dbgeng.manager.impl;
import static ghidra.async.AsyncUtils.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.util.*;
@ -34,7 +32,8 @@ import agent.dbgeng.manager.DbgManager.ExecSuffix;
import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo; import agent.dbgeng.manager.breakpoint.DbgBreakpointInfo;
import agent.dbgeng.manager.breakpoint.DbgBreakpointType; import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
import agent.dbgeng.manager.cmd.*; import agent.dbgeng.manager.cmd.*;
import ghidra.async.*; import ghidra.async.AsyncLazyValue;
import ghidra.async.AsyncReference;
import ghidra.util.Msg; import ghidra.util.Msg;
public class DbgThreadImpl implements DbgThread { public class DbgThreadImpl implements DbgThread {
@ -91,7 +90,7 @@ public class DbgThreadImpl implements DbgThread {
*/ */
public void add() { public void add() {
manager.threads.put(id, this); manager.threads.put(id, this);
manager.getEventListeners().fire.threadCreated(this, DbgCause.Causes.UNCLAIMED); //manager.getEventListeners().fire.threadCreated(this, DbgCause.Causes.UNCLAIMED);
process.addThread(this); process.addThread(this);
state.addChangeListener((oldState, newState, pair) -> { state.addChangeListener((oldState, newState, pair) -> {
this.manager.getEventListeners().fire.threadStateChanged(this, newState, pair.cause, this.manager.getEventListeners().fire.threadStateChanged(this, newState, pair.cause,
@ -147,18 +146,18 @@ public class DbgThreadImpl implements DbgThread {
} }
private CompletableFuture<DbgRegisterSet> doListRegisters() { private CompletableFuture<DbgRegisterSet> doListRegisters() {
return sequence(TypeSpec.cls(DbgRegisterSet.class)).then((seq) -> { CompletableFuture<List<DebugRegisterDescription>> listCmd =
manager.execute(new DbgListRegisterDescriptionsCommand(manager)).handle(seq::next); manager.execute(new DbgListRegisterDescriptionsCommand(manager));
}, TypeSpec.cls(DebugRegisterDescription.class).list()).then((descs, seq) -> { return listCmd.thenApply(descs -> {
if (descs == null) { if (descs == null) {
return; return new DbgRegisterSet(Set.of());
} }
List<DbgRegister> regs = new ArrayList<>(); List<DbgRegister> regs = new ArrayList<>();
for (DebugRegisterDescription desc : descs) { for (DebugRegisterDescription desc : descs) {
regs.add(new DbgRegister(desc)); regs.add(new DbgRegister(desc));
} }
seq.exit(new DbgRegisterSet(regs)); return new DbgRegisterSet(regs);
}).finish(); });
} }
@Override @Override
@ -208,47 +207,37 @@ public class DbgThreadImpl implements DbgThread {
@Override @Override
public CompletableFuture<Void> cont() { public CompletableFuture<Void> cont() {
return sequence(TypeSpec.VOID).then((seq) -> { return select().thenCompose(__ -> {
select().handle(seq::next); return manager.execute(new DbgContinueCommand(manager));
}).then((seq) -> { });
manager.execute(new DbgContinueCommand(manager)).handle(seq::exit);
}).finish();
} }
@Override @Override
public CompletableFuture<Void> step(ExecSuffix suffix) { public CompletableFuture<Void> step(ExecSuffix suffix) {
return sequence(TypeSpec.VOID).then((seq) -> { return select().thenCompose(__ -> {
select().handle(seq::next); return manager.execute(new DbgStepCommand(manager, id, suffix));
}).then((seq) -> { });
manager.execute(new DbgStepCommand(manager, id, suffix)).handle(seq::exit);
}).finish();
} }
@Override @Override
public CompletableFuture<Void> step(Map<String, ?> args) { public CompletableFuture<Void> step(Map<String, ?> args) {
return sequence(TypeSpec.VOID).then((seq) -> { return select().thenCompose(__ -> {
select().handle(seq::next); return manager.execute(new DbgStepCommand(manager, id, args));
}).then((seq) -> { });
manager.execute(new DbgStepCommand(manager, id, args)).handle(seq::exit);
}).finish();
} }
@Override @Override
public CompletableFuture<Void> kill() { public CompletableFuture<Void> kill() {
return sequence(TypeSpec.VOID).then((seq) -> { return select().thenCompose(__ -> {
select().handle(seq::next); return manager.execute(new DbgKillCommand(manager));
}).then((seq) -> { });
manager.execute(new DbgKillCommand(manager)).handle(seq::exit);
}).finish();
} }
@Override @Override
public CompletableFuture<Void> detach() { public CompletableFuture<Void> detach() {
return sequence(TypeSpec.VOID).then((seq) -> { return select().thenCompose(__ -> {
select().handle(seq::next); return manager.execute(new DbgDetachCommand(manager, process));
}).then((seq) -> { });
manager.execute(new DbgDetachCommand(manager, process)).handle(seq::exit);
}).finish();
} }
public DebugEventInformation getInfo() { public DebugEventInformation getInfo() {

View file

@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture;
import agent.dbgeng.manager.DbgManager; import agent.dbgeng.manager.DbgManager;
import agent.dbgeng.model.iface2.DbgModelTargetSession; import agent.dbgeng.model.iface2.DbgModelTargetSession;
import ghidra.dbg.agent.AbstractDebuggerObjectModel; import ghidra.dbg.agent.AbstractDebuggerObjectModel;
import ghidra.dbg.target.TargetObject;
import ghidra.program.model.address.AddressFactory; import ghidra.program.model.address.AddressFactory;
public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel { public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel {
@ -37,4 +38,8 @@ public abstract class AbstractDbgModel extends AbstractDebuggerObjectModel {
public abstract DbgModelTargetSession getSession(); 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. * An interface which indicates this object is capable of launching targets.
* *
* <p>
* The targets this launcher creates ought to appear in its successors. * The targets this launcher creates ought to appear in its successors.
* *
* @param <T> type for this * @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.manager.impl.DbgProcessImpl;
import agent.dbgeng.model.iface2.DbgModelTargetAvailable; import agent.dbgeng.model.iface2.DbgModelTargetAvailable;
import agent.dbgeng.model.iface2.DbgModelTargetObject; import agent.dbgeng.model.iface2.DbgModelTargetObject;
import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
import ghidra.dbg.target.TargetAttachable; import ghidra.dbg.target.TargetAttachable;
import ghidra.dbg.target.TargetAttacher; import ghidra.dbg.target.TargetAttacher;
import ghidra.util.Msg; import ghidra.util.Msg;
@ -30,6 +28,7 @@ import ghidra.util.Msg;
/** /**
* An interface which indicates this object is capable of launching targets. * An interface which indicates this object is capable of launching targets.
* *
* <p>
* The targets this launcher creates ought to appear in its successors. * The targets this launcher creates ought to appear in its successors.
* *
* @param <T> type for this * @param <T> type for this
@ -42,22 +41,20 @@ public interface DbgModelTargetAttacher extends DbgModelTargetObject, TargetAtta
getModel().assertMine(DbgModelTargetAvailable.class, attachable); getModel().assertMine(DbgModelTargetAvailable.class, attachable);
// TODO: This and the below new DbgProcessImpl seem to do the same thing // TODO: This and the below new DbgProcessImpl seem to do the same thing
// Both should be expressed the same way // Both should be expressed the same way
return getManager().addProcess().thenAccept(process -> { return getModel().gateFuture(getManager().addProcess().thenCompose(process -> {
process.attach(available.getPid()); return process.attach(available.getPid());
}).exceptionally((exc) -> { }).exceptionally(exc -> {
Msg.error(this, "attach failed"); Msg.error(this, "attach failed");
return null; return null;
}); })).thenApply(__ -> null);
} }
@Override @Override
public default CompletableFuture<Void> attach(long pid) { public default CompletableFuture<Void> attach(long pid) {
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
DbgProcess process = new DbgProcessImpl(getManager()); DbgProcess process = new DbgProcessImpl(getManager());
process.attach(pid).handle(seq::nextIgnore); return getModel().gateFuture(process.attach(pid).exceptionally(exc -> {
}).finish().exceptionally((exc) -> {
Msg.error(this, "attach failed"); Msg.error(this, "attach failed");
return null; return null;
}); })).thenApply(__ -> null);
} }
} }

View file

@ -46,7 +46,6 @@ public interface DbgModelTargetExecutionStateful
changeAttributes(List.of(), Map.of( // changeAttributes(List.of(), Map.of( //
STATE_ATTRIBUTE_NAME, state // STATE_ATTRIBUTE_NAME, state //
), reason); ), 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) // (but, of course, may then cause change in state)
@Override @Override
public default CompletableFuture<Void> requestFocus(TargetObject obj) { 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) { public default CompletableFuture<Void> doRequestFocus(TargetObject obj) {
if (getManager().isWaiting()) { if (getManager().isWaiting()) {
return CompletableFuture.completedFuture(null); return AsyncUtils.NIL;
} }
getModel().assertMine(TargetObject.class, obj); getModel().assertMine(TargetObject.class, obj);
if (obj.equals(getFocus())) { if (obj.equals(getFocus())) {
return CompletableFuture.completedFuture(null); return AsyncUtils.NIL;
} }
if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) { if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) {
throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope");

View file

@ -31,12 +31,12 @@ public interface DbgModelTargetInterpreter extends DbgModelTargetObject, TargetI
@Override @Override
public default CompletableFuture<Void> execute(String cmd) { public default CompletableFuture<Void> execute(String cmd) {
return getManager().console(cmd); return getModel().gateFuture(getManager().console(cmd));
} }
@Override @Override
public default CompletableFuture<String> executeCapture(String cmd) { 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.manager.DbgProcess;
import agent.dbgeng.model.iface2.DbgModelTargetObject; import agent.dbgeng.model.iface2.DbgModelTargetObject;
import ghidra.async.AsyncUtils;
import ghidra.dbg.target.TargetResumable; import ghidra.dbg.target.TargetResumable;
/** /**
@ -33,6 +34,9 @@ public interface DbgModelTargetResumable extends DbgModelTargetObject, TargetRes
@Override @Override
public default CompletableFuture<Void> resume() { public default CompletableFuture<Void> resume() {
DbgProcess process = getManager().getCurrentProcess(); DbgProcess process = getManager().getCurrentProcess();
if (process == null) {
return AsyncUtils.NIL;
}
return process.cont(); return process.cont();
} }

View file

@ -66,20 +66,21 @@ public interface DbgModelTargetSteppable extends DbgModelTargetObject, TargetSte
default: default:
if (this instanceof DbgModelTargetThread) { if (this instanceof DbgModelTargetThread) {
DbgModelTargetThread targetThread = (DbgModelTargetThread) this; DbgModelTargetThread targetThread = (DbgModelTargetThread) this;
return targetThread.getThread().step(convertToDbg(kind)); return getModel().gateFuture(targetThread.getThread().step(convertToDbg(kind)));
} }
if (this instanceof DbgModelTargetProcess) { if (this instanceof DbgModelTargetProcess) {
DbgModelTargetProcess targetProcess = (DbgModelTargetProcess) this; 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 @Override
default CompletableFuture<Void> step(Map<String, ?> args) { default CompletableFuture<Void> step(Map<String, ?> args) {
DbgThread thread = getManager().getCurrentThread(); 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; import ghidra.dbg.target.TargetAttachable;
public interface DbgModelTargetAvailable extends DbgModelTargetObject, TargetAttachable { public interface DbgModelTargetAvailable extends DbgModelTargetObject, TargetAttachable {
String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid";
// TODO: DESCRIPTION, TYPE, USER?
public long getPid(); public long getPid();

View file

@ -22,8 +22,9 @@ import java.util.function.Function;
import agent.dbgeng.manager.DbgEventsListenerAdapter; import agent.dbgeng.manager.DbgEventsListenerAdapter;
import agent.dbgeng.manager.breakpoint.DbgBreakpointType; import agent.dbgeng.manager.breakpoint.DbgBreakpointType;
import ghidra.async.AsyncFence; import ghidra.async.AsyncFence;
import ghidra.dbg.target.TargetBreakpointContainer; import ghidra.dbg.target.TargetBreakpointLocationContainer;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetBreakpointSpecContainer;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.program.model.address.AddressRange; import ghidra.program.model.address.AddressRange;
@ -34,8 +35,10 @@ import ghidra.program.model.address.AddressRange;
attributes = { attributes = {
@TargetAttributeType(type = Void.class) }, @TargetAttributeType(type = Void.class) },
canonicalContainer = true) canonicalContainer = true)
public interface DbgModelTargetBreakpointContainer public interface DbgModelTargetBreakpointContainer extends DbgModelTargetObject, //
extends DbgModelTargetObject, TargetBreakpointContainer, DbgEventsListenerAdapter { TargetBreakpointSpecContainer, //
TargetBreakpointLocationContainer, //
DbgEventsListenerAdapter {
/* /*
@Override @Override
@ -65,13 +68,13 @@ public interface DbgModelTargetBreakpointContainer
else if (kinds.contains(TargetBreakpointKind.WRITE)) { else if (kinds.contains(TargetBreakpointKind.WRITE)) {
fence.include(placer.apply(DbgBreakpointType.HW_WATCHPOINT)); 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)); fence.include(placer.apply(DbgBreakpointType.HW_BREAKPOINT));
} }
if (kinds.contains(TargetBreakpointKind.SOFTWARE)) { if (kinds.contains(TargetBreakpointKind.SW_EXECUTE)) {
fence.include(placer.apply(DbgBreakpointType.BREAKPOINT)); fence.include(placer.apply(DbgBreakpointType.BREAKPOINT));
} }
return fence.ready(); return getModel().gateFuture(fence.ready());
} }
@Override @Override

View file

@ -15,7 +15,6 @@
*/ */
package agent.dbgeng.model.iface2; package agent.dbgeng.model.iface2;
import ghidra.dbg.attributes.TargetObjectList;
import ghidra.dbg.target.TargetBreakpointLocation; import ghidra.dbg.target.TargetBreakpointLocation;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -25,7 +24,4 @@ public interface DbgModelTargetBreakpointLocation
@Override @Override
public Address getAddress(); 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.manager.breakpoint.DbgBreakpointInfo;
import agent.dbgeng.model.iface1.DbgModelTargetBptHelper; import agent.dbgeng.model.iface1.DbgModelTargetBptHelper;
import ghidra.dbg.attributes.TargetObjectList;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetBreakpointContainer.TargetBreakpointKindSet; import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
public interface DbgModelTargetBreakpointSpec extends // public interface DbgModelTargetBreakpointSpec extends //
@ -42,24 +41,24 @@ public interface DbgModelTargetBreakpointSpec extends //
@Override @Override
public default CompletableFuture<Void> delete() { public default CompletableFuture<Void> delete() {
return getManager().deleteBreakpoints(getNumber()); return getModel().gateFuture(getManager().deleteBreakpoints(getNumber()));
} }
@Override @Override
public default CompletableFuture<Void> disable() { public default CompletableFuture<Void> disable() {
setEnabled(false, "Disabled"); setEnabled(false, "Disabled");
return getManager().disableBreakpoints(getNumber()); return getModel().gateFuture(getManager().disableBreakpoints(getNumber()));
} }
@Override @Override
public default CompletableFuture<Void> enable() { public default CompletableFuture<Void> enable() {
setEnabled(true, "Enabled"); setEnabled(true, "Enabled");
return getManager().enableBreakpoints(getNumber()); return getModel().gateFuture(getManager().enableBreakpoints(getNumber()));
} }
@Override @Override
public default String getExpression() { public default String getExpression() {
return getBreakpointInfo().getLocation(); return getBreakpointInfo().getExpression();
} }
public default long getNumber() { public default long getNumber() {
@ -70,9 +69,9 @@ public interface DbgModelTargetBreakpointSpec extends //
public default TargetBreakpointKindSet getKinds() { public default TargetBreakpointKindSet getKinds() {
switch (getBreakpointInfo().getType()) { switch (getBreakpointInfo().getType()) {
case BREAKPOINT: case BREAKPOINT:
return TargetBreakpointKindSet.of(TargetBreakpointKind.SOFTWARE); return TargetBreakpointKindSet.of(TargetBreakpointKind.SW_EXECUTE);
case HW_BREAKPOINT: case HW_BREAKPOINT:
return TargetBreakpointKindSet.of(TargetBreakpointKind.EXECUTE); return TargetBreakpointKindSet.of(TargetBreakpointKind.HW_EXECUTE);
case HW_WATCHPOINT: case HW_WATCHPOINT:
return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE); return TargetBreakpointKindSet.of(TargetBreakpointKind.WRITE);
case READ_WATCHPOINT: case READ_WATCHPOINT:
@ -106,7 +105,6 @@ public interface DbgModelTargetBreakpointSpec extends //
catch (AddressFormatException e) { catch (AddressFormatException e) {
e.printStackTrace(); e.printStackTrace();
} }
map.put(AFFECTS_ATTRIBUTE_NAME, doGetAffects());
map.put(SPEC_ATTRIBUTE_NAME, this); map.put(SPEC_ATTRIBUTE_NAME, this);
map.put(EXPRESSION_ATTRIBUTE_NAME, addstr); map.put(EXPRESSION_ATTRIBUTE_NAME, addstr);
map.put(KINDS_ATTRIBUTE_NAME, getKinds()); map.put(KINDS_ATTRIBUTE_NAME, getKinds());
@ -124,14 +122,16 @@ public interface DbgModelTargetBreakpointSpec extends //
}); });
} }
public default Address doGetAddress() { private long orZero(Long l) {
DbgBreakpointInfo info = getBreakpointInfo(); if (l == null) {
return getModel().getAddress("ram", info.addrAsLong()); return 0;
}
return l;
} }
public default TargetObjectList<?> doGetAffects() { public default Address doGetAddress() {
DbgModelTargetProcess process = getParentProcess(); DbgBreakpointInfo info = getBreakpointInfo();
return TargetObjectList.of(process); return getModel().getAddress("ram", orZero(info.getOffset()));
} }
public default void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, public default void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo,
@ -157,7 +157,6 @@ public interface DbgModelTargetBreakpointSpec extends //
setBreakpointEnabled(enabled); setBreakpointEnabled(enabled);
changeAttributes(List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled // changeAttributes(List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled //
), reason); ), reason);
getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled);
} }
@Override @Override
@ -176,7 +175,10 @@ public interface DbgModelTargetBreakpointSpec extends //
} }
public default void breakpointHit() { 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; 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 @Override
public CompletableFuture<? extends TargetModule> addSyntheticModule(String name); public CompletableFuture<? extends TargetModule> addSyntheticModule(String name);
public CompletableFuture<DbgModelTargetModule> getTargetModule(String name); public DbgModelTargetModule getTargetModule(String name);
public void libraryLoaded(String name); public void libraryLoaded(String name);

View file

@ -15,6 +15,9 @@
*/ */
package agent.dbgeng.model.iface2; 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.dbgeng.DebugClient.DebugStatus;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.AbstractDbgModel; import agent.dbgeng.model.AbstractDbgModel;
import ghidra.async.AsyncUtils;
import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.agent.InvalidatableTargetObjectIf; import ghidra.dbg.agent.InvalidatableTargetObjectIf;
import ghidra.dbg.agent.SpiTargetObject; import ghidra.dbg.agent.SpiTargetObject;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
@ -54,17 +56,17 @@ public interface DbgModelTargetObject extends SpiTargetObject, InvalidatableTarg
return impl; 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 Delta<?, ?> changeAttributes(List<String> remove, Map<String, ?> add, String reason);
public CompletableFuture<? extends Map<String, ?>> requestNativeAttributes(); 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(); public DbgModelTargetSession getParentSession();

View file

@ -19,8 +19,7 @@ import java.util.concurrent.CompletableFuture;
import agent.dbgeng.dbgeng.DebugProcessId; import agent.dbgeng.dbgeng.DebugProcessId;
import agent.dbgeng.dbgeng.DebugSystemObjects; import agent.dbgeng.dbgeng.DebugSystemObjects;
import agent.dbgeng.manager.DbgEventsListenerAdapter; import agent.dbgeng.manager.*;
import agent.dbgeng.manager.DbgProcess;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.*; import agent.dbgeng.model.iface1.*;
import ghidra.dbg.target.TargetAggregate; import ghidra.dbg.target.TargetAggregate;
@ -46,12 +45,12 @@ public interface DbgModelTargetProcess extends //
public void processStarted(Long pid); public void processStarted(Long pid);
public void processExited(Long exitCode);
public DbgModelTargetThreadContainer getThreads(); public DbgModelTargetThreadContainer getThreads();
public DbgModelTargetModuleContainer getModules(); public DbgModelTargetModuleContainer getModules();
public void threadStateChangedSpecific(DbgThread thread, DbgState state);
public default DbgProcess getProcess() { public default DbgProcess getProcess() {
DbgManagerImpl manager = getManager(); DbgManagerImpl manager = getManager();
DebugSystemObjects so = manager.getSystemObjects(); DebugSystemObjects so = manager.getSystemObjects();
@ -78,4 +77,5 @@ public interface DbgModelTargetProcess extends //
} }
return manager.selectProcess(process); return manager.selectProcess(process);
} }
} }

View file

@ -15,8 +15,11 @@
*/ */
package agent.dbgeng.model.iface2; package agent.dbgeng.model.iface2;
import java.math.BigInteger;
import agent.dbgeng.manager.impl.DbgRegister; import agent.dbgeng.manager.impl.DbgRegister;
import ghidra.dbg.target.TargetRegister; import ghidra.dbg.target.TargetRegister;
import ghidra.dbg.util.ConversionUtils;
public interface DbgModelTargetRegister extends DbgModelTargetObject, TargetRegister { public interface DbgModelTargetRegister extends DbgModelTargetObject, TargetRegister {
@ -25,4 +28,10 @@ public interface DbgModelTargetRegister extends DbgModelTargetObject, TargetRegi
public DbgRegister getRegister(); 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.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import agent.dbgeng.manager.DbgThread; import agent.dbgeng.manager.*;
import agent.dbgeng.manager.impl.*; import agent.dbgeng.manager.impl.*;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec; import ghidra.async.TypeSpec;
import ghidra.dbg.DebuggerModelListener;
import ghidra.dbg.error.DebuggerRegisterAccessException; import ghidra.dbg.error.DebuggerRegisterAccessException;
import ghidra.dbg.target.TargetRegisterBank; import ghidra.dbg.target.TargetRegisterBank;
import ghidra.dbg.util.ConversionUtils; import ghidra.dbg.util.ConversionUtils;
@ -34,6 +36,11 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
public DbgModelTargetRegister getTargetRegister(DbgRegister register); public DbgModelTargetRegister getTargetRegister(DbgRegister register);
public default void threadStateChangedSpecific(DbgState state, DbgReason reason) {
readRegistersNamed(getCachedElements().keySet());
}
// NB: Does anyone call this anymore?
@Override @Override
public default CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed( public default CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
Collection<String> names) { Collection<String> names) {
@ -84,9 +91,9 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
reg.setModified(value.toString(16).equals(oldval)); reg.setModified(value.toString(16).equals(oldval));
} }
} }
ListenerSet<TargetObjectListener> listeners = getListeners(); ListenerSet<DebuggerModelListener> listeners = getListeners();
if (listeners != null) { if (listeners != null) {
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); listeners.fire.registersUpdated(getProxy(), result);
} }
return result; return result;
}); });
@ -96,7 +103,7 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
public default CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) { public default CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) {
DbgThread thread = getParentThread().getThread(); DbgThread thread = getParentThread().getThread();
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
fetchElements().handle(seq::nextIgnore); requestNativeElements().handle(seq::nextIgnore);
}).then(seq -> { }).then(seq -> {
thread.listRegisters().handle(seq::next); thread.listRegisters().handle(seq::next);
}, TypeSpec.cls(DbgRegisterSet.class)).then((regset, seq) -> { }, TypeSpec.cls(DbgRegisterSet.class)).then((regset, seq) -> {
@ -115,8 +122,26 @@ public interface DbgModelTargetRegisterBank extends DbgModelTargetObject, Target
getParentThread().getThread().writeRegisters(toWrite).handle(seq::next); getParentThread().getThread().writeRegisters(toWrite).handle(seq::next);
// TODO: Should probably filter only effective and normalized writes in the callback // TODO: Should probably filter only effective and normalized writes in the callback
}).then(seq -> { }).then(seq -> {
getListeners().fire(TargetRegisterBankListener.class).registersUpdated(this, values); getListeners().fire.registersUpdated(getProxy(), values);
seq.exit(); seq.exit();
}).finish(); }).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.TargetRegisterBank;
import ghidra.dbg.target.TargetRegisterContainer; import ghidra.dbg.target.TargetRegisterContainer;
public interface DbgModelTargetRegisterContainerAndBank public interface DbgModelTargetRegisterContainerAndBank extends //
extends DbgModelTargetObject, TargetRegisterContainer, TargetRegisterBank { DbgModelTargetObject, //
TargetRegisterContainer, //
TargetRegisterBank {
public DbgModelTargetRegister getTargetRegister(DbgRegister register); 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.TargetAggregate;
import ghidra.dbg.target.TargetConsole; import ghidra.dbg.target.TargetConsole;
import ghidra.dbg.target.TargetConsole.Channel; import ghidra.dbg.target.TargetConsole.Channel;
import ghidra.dbg.target.TargetFocusScope.TargetFocusScopeListener;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
public interface DbgModelTargetSession extends // public interface DbgModelTargetSession extends //
@ -38,7 +37,6 @@ public interface DbgModelTargetSession extends //
DbgModelTargetResumable, // DbgModelTargetResumable, //
DbgEventsListenerAdapter, // DbgEventsListenerAdapter, //
DbgModelSelectableObject, // DbgModelSelectableObject, //
TargetFocusScopeListener, //
TargetAggregate { TargetAggregate {
DbgModelTargetProcessContainer getProcesses(); DbgModelTargetProcessContainer getProcesses();
@ -69,7 +67,7 @@ public interface DbgModelTargetSession extends //
if (output.contains("loaded *kernel* extension dll for usermode")) { if (output.contains("loaded *kernel* extension dll for usermode")) {
return; return;
} }
getListeners().fire(TargetInterpreterListener.class).consoleOutput(this, chan, output); getListeners().fire.consoleOutput(getProxy(), chan, output);
} }
@Override @Override

View file

@ -61,21 +61,25 @@ public interface DbgModelTargetStackFrame extends //
if (attrs == null) { if (attrs == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
TargetObject attributes = (TargetObject) attrs.get("Attributes"); DbgModelTargetObject attributes = (DbgModelTargetObject) attrs.get("Attributes");
if (attributes == null) { if (attributes == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
return attributes.fetchAttributes(true); return attributes.requestAugmentedAttributes().thenCompose(ax -> {
}).thenCompose(subattrs -> { Map<String, ?> subattrs = attributes.getCachedAttributes();
if (subattrs == null) { if (subattrs == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
TargetObject frameNumber = (TargetObject) subattrs.get("FrameNumber"); DbgModelTargetObject frameNumber =
return frameNumber.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(noval -> { (DbgModelTargetObject) subattrs.get("FrameNumber");
return frameNumber.requestAugmentedAttributes().thenCompose(bx -> {
Object noval = frameNumber.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
String nostr = noval.toString(); String nostr = noval.toString();
TargetObject instructionOffset = (TargetObject) subattrs.get("InstructionOffset"); DbgModelTargetObject instructionOffset =
return instructionOffset.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(pcval -> { (DbgModelTargetObject) subattrs.get("InstructionOffset");
return instructionOffset.requestAugmentedAttributes().thenAccept(cx -> {
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
Object pcval = instructionOffset.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
String pcstr = pcval.toString(); String pcstr = pcval.toString();
long pc = Long.parseUnsignedLong(pcstr, 16); long pc = Long.parseUnsignedLong(pcstr, 16);
map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc)); map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc));
@ -85,6 +89,7 @@ public interface DbgModelTargetStackFrame extends //
}); });
}); });
}); });
});
} }
public void setFrame(DbgStackFrame frame); public void setFrame(DbgStackFrame frame);

View file

@ -18,8 +18,6 @@ package agent.dbgeng.model.iface2;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import ghidra.dbg.target.TargetObject;
public interface DbgModelTargetTTD extends DbgModelTargetObject { public interface DbgModelTargetTTD extends DbgModelTargetObject {
@Override @Override
@ -28,20 +26,22 @@ public interface DbgModelTargetTTD extends DbgModelTargetObject {
if (attrs == null) { if (attrs == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
TargetObject attributes = (TargetObject) attrs.get("Position"); DbgModelTargetObject attributes = (DbgModelTargetObject) attrs.get("Position");
if (attributes == null) { if (attributes == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
return attributes.fetchAttributes(true); return attributes.requestAugmentedAttributes().thenCompose(ax -> {
}).thenCompose(subattrs -> { Map<String, ?> subattrs = attributes.getCachedAttributes();
if (subattrs == null) { if (subattrs == null) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
TargetObject seq = (TargetObject) subattrs.get("Sequence"); DbgModelTargetObject seq = (DbgModelTargetObject) subattrs.get("Sequence");
return seq.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenCompose(sqval -> { return seq.requestAugmentedAttributes().thenCompose(bx -> {
Object sqval = seq.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
String sqstr = sqval.toString(); String sqstr = sqval.toString();
TargetObject steps = (TargetObject) subattrs.get("Steps"); DbgModelTargetObject steps = (DbgModelTargetObject) subattrs.get("Steps");
return steps.fetchAttribute(VALUE_ATTRIBUTE_NAME).thenAccept(stval -> { return steps.requestAugmentedAttributes().thenAccept(cx -> {
Object stval = steps.getCachedAttribute(VALUE_ATTRIBUTE_NAME);
String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME);
String ststr = stval.toString(); String ststr = stval.toString();
String display = String.format("TTD %s:%s", sqstr, ststr); String display = String.format("TTD %s:%s", sqstr, ststr);
@ -50,5 +50,6 @@ public interface DbgModelTargetTTD extends DbgModelTargetObject {
}); });
}); });
}); });
});
} }
} }

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 @Override
public default CompletableFuture<Void> select() { public default CompletableFuture<Void> select() {

View file

@ -16,21 +16,30 @@
package agent.dbgeng.model.impl; package agent.dbgeng.model.impl;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture; 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.dbgeng.DebugSessionId;
import agent.dbgeng.manager.DbgManager; import agent.dbgeng.manager.DbgManager;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.manager.impl.DbgSessionImpl; import agent.dbgeng.manager.impl.DbgSessionImpl;
import agent.dbgeng.model.AbstractDbgModel; import agent.dbgeng.model.AbstractDbgModel;
import agent.dbgeng.model.iface2.DbgModelTargetSession; import agent.dbgeng.model.iface2.*;
import agent.dbgeng.model.iface2.DbgModelTargetSessionContainer; 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.TargetObject;
import ghidra.dbg.target.schema.AnnotatedSchemaContext; import ghidra.dbg.target.schema.AnnotatedSchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
import ghidra.program.model.address.*; 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. // 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 // The model must convert to and from Ghidra's address space names
protected static final String SPACE_NAME = "ram"; protected static final String SPACE_NAME = "ram";
@ -51,6 +60,8 @@ public class DbgModelImpl extends AbstractDbgModel {
protected final CompletableFuture<DbgModelTargetRootImpl> completedRoot; protected final CompletableFuture<DbgModelTargetRootImpl> completedRoot;
protected Map<Object, TargetObject> objectMap = new HashMap<>();
public DbgModelImpl() { public DbgModelImpl() {
this.dbg = DbgManager.newInstance(); this.dbg = DbgManager.newInstance();
//System.out.println(XmlSchemaContext.serialize(SCHEMA_CTX)); //System.out.println(XmlSchemaContext.serialize(SCHEMA_CTX));
@ -79,7 +90,7 @@ public class DbgModelImpl extends AbstractDbgModel {
@Override @Override
public CompletableFuture<Void> startDbgEng(String[] args) { public CompletableFuture<Void> startDbgEng(String[] args) {
return dbg.start(args); return dbg.start(args).thenApplyAsync(__ -> null, clientExecutor);
} }
@Override @Override
@ -89,6 +100,8 @@ public class DbgModelImpl extends AbstractDbgModel {
@Override @Override
public void terminate() throws IOException { public void terminate() throws IOException {
listeners.fire.modelClosed(DebuggerModelClosedReason.NORMAL);
root.invalidateSubtree(root, "Dbgeng is terminating");
dbg.terminate(); dbg.terminate();
} }
@ -113,6 +126,10 @@ public class DbgModelImpl extends AbstractDbgModel {
terminate(); terminate();
return super.close(); return super.close();
} }
catch (RejectedExecutionException e) {
reportError(this, "Model is already closing", e);
return AsyncUtils.NIL;
}
catch (Throwable t) { catch (Throwable t) {
return CompletableFuture.failedFuture(t); return CompletableFuture.failedFuture(t);
} }
@ -122,4 +139,39 @@ public class DbgModelImpl extends AbstractDbgModel {
public DbgModelTargetSession getSession() { public DbgModelTargetSession getSession() {
return session; 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.manager.DbgProcess;
import agent.dbgeng.model.AbstractDbgModel; import agent.dbgeng.model.AbstractDbgModel;
import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
public enum DbgModelImplUtils { public enum DbgModelImplUtils {
; ;
public static CompletableFuture<Void> launch(AbstractDbgModel impl, DbgProcess process, public static CompletableFuture<Void> launch(AbstractDbgModel impl, DbgProcess process,
List<String> args) { List<String> args) {
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { return process.fileExecAndSymbols(args.get(0));
process.fileExecAndSymbols(args.get(0));
}).finish();
} }
public static <V> V noDupMerge(V first, V second) { 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 agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
import ghidra.util.datastruct.WeakValueHashMap; import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo(name = "AvailableContainer", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = DbgModelTargetAvailableImpl.class) // name = "AvailableContainer",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = DbgModelTargetAvailableImpl.class)
}, canonicalContainer = true) },
elementResync = ResyncMode.ALWAYS,
attributes = {
@TargetAttributeType(type = Void.class)
},
canonicalContainer = true)
public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetAvailableContainer { implements DbgModelTargetAvailableContainer {
@ -40,9 +46,6 @@ public class DbgModelTargetAvailableContainerImpl extends DbgModelTargetObjectIm
public DbgModelTargetAvailableContainerImpl(DbgModelTargetRoot root) { public DbgModelTargetAvailableContainerImpl(DbgModelTargetRoot root) {
super(root.getModel(), root, "Available", "AvailableContainer"); super(root.getModel(), root, "Available", "AvailableContainer");
changeAttributes(List.of(), List.of(), Map.of( //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.SOLICITED //
), "Initialized");
} }
@Override @Override

View file

@ -23,17 +23,17 @@ import agent.dbgeng.model.iface2.DbgModelTargetAvailableContainer;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo(name = "Available", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = Void.class) // name = "Available",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = Void.class)
}) },
attributes = {
@TargetAttributeType(type = Void.class)
})
public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetAvailable { implements DbgModelTargetAvailable {
protected static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid";
// TODO: DESCRIPTION, TYPE, USER?
protected static String indexAttachable(int pid) { protected static String indexAttachable(int pid) {
return Integer.toHexString(pid); return Integer.toHexString(pid);
} }
@ -51,8 +51,7 @@ public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
this.changeAttributes(List.of(), List.of(), Map.of(// this.changeAttributes(List.of(), List.of(), Map.of(//
PID_ATTRIBUTE_NAME, (long) pid, // PID_ATTRIBUTE_NAME, (long) pid, //
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim(), DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) + " : " + name.trim() //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }
@ -62,8 +61,7 @@ public class DbgModelTargetAvailableImpl extends DbgModelTargetObjectImpl
this.changeAttributes(List.of(), List.of(), Map.of(// this.changeAttributes(List.of(), List.of(), Map.of(//
PID_ATTRIBUTE_NAME, (long) pid, // PID_ATTRIBUTE_NAME, (long) pid, //
DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid), // DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }

View file

@ -27,7 +27,6 @@ import agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo(name = "BreakpointContainer", elements = { // @TargetObjectSchemaInfo(name = "BreakpointContainer", elements = { //
@TargetElementType(type = DbgModelTargetBreakpointSpecImpl.class) // @TargetElementType(type = DbgModelTargetBreakpointSpecImpl.class) //
@ -40,8 +39,6 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
protected static final TargetBreakpointKindSet SUPPORTED_KINDS = protected static final TargetBreakpointKindSet SUPPORTED_KINDS =
TargetBreakpointKindSet.of(TargetBreakpointKind.values()); TargetBreakpointKindSet.of(TargetBreakpointKind.values());
private final Map<Long, DbgModelTargetBreakpointSpec> specsByNumber = new WeakValueHashMap<>();
public DbgModelTargetBreakpointContainerImpl(DbgModelTargetDebugContainer debug) { public DbgModelTargetBreakpointContainerImpl(DbgModelTargetDebugContainer debug) {
super(debug.getModel(), debug, "Breakpoints", "BreakpointContainer"); super(debug.getModel(), debug, "Breakpoints", "BreakpointContainer");
@ -66,9 +63,8 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
@Override @Override
public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) { public void breakpointDeleted(DbgBreakpointInfo info, DbgCause cause) {
synchronized (this) { DbgModelImpl impl = (DbgModelImpl) model;
getSpecsByNumber().remove(info.getNumber()); impl.deleteModelObject(info.getDebugBreakpoint());
}
changeElements(List.of( // changeElements(List.of( //
DbgModelTargetBreakpointSpecImpl.indexBreakpoint(info) // DbgModelTargetBreakpointSpecImpl.indexBreakpoint(info) //
), List.of(), Map.of(), "Deleted"); ), List.of(), Map.of(), "Deleted");
@ -76,17 +72,20 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
@Override @Override
public void breakpointHit(DbgBreakpointInfo info, DbgCause cause) { public void breakpointHit(DbgBreakpointInfo info, DbgCause cause) {
DbgModelTargetThread targetThread =
getParentProcess().getThreads().getTargetThread(getManager().getEventThread());
DbgModelTargetBreakpointSpec spec = getTargetBreakpointSpec(info); DbgModelTargetBreakpointSpec spec = getTargetBreakpointSpec(info);
listeners.fire(TargetBreakpointListener.class) listeners.fire.breakpointHit(getProxy(), targetThread, null, spec, spec);
.breakpointHit(this, getParentProcess(), null, spec, spec);
spec.breakpointHit(); spec.breakpointHit();
} }
public DbgModelTargetBreakpointSpec getTargetBreakpointSpec(DbgBreakpointInfo info) { public DbgModelTargetBreakpointSpec getTargetBreakpointSpec(DbgBreakpointInfo info) {
synchronized (this) { DbgModelImpl impl = (DbgModelImpl) model;
return getSpecsByNumber().computeIfAbsent(info.getNumber(), TargetObject modelObject = impl.getModelObject(info.getDebugBreakpoint());
i -> new DbgModelTargetBreakpointSpecImpl(this, info)); if (modelObject != null) {
return (DbgModelTargetBreakpointSpec) modelObject;
} }
return new DbgModelTargetBreakpointSpecImpl(this, info);
} }
@Override @Override
@ -103,8 +102,4 @@ public class DbgModelTargetBreakpointContainerImpl extends DbgModelTargetObjectI
setElements(specs, Map.of(), "Refreshed"); 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.manager.breakpoint.DbgBreakpointInfo;
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer; import agent.dbgeng.model.iface2.DbgModelTargetBreakpointContainer;
import agent.dbgeng.model.iface2.DbgModelTargetBreakpointSpec; 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.TargetAttributeType;
import ghidra.dbg.target.schema.TargetObjectSchemaInfo; import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@ -56,20 +57,18 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
public void changeAttributeSet(String reason) { public void changeAttributeSet(String reason) {
this.changeAttributes(List.of(), List.of(), Map.of( // 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(), // ADDRESS_ATTRIBUTE_NAME, doGetAddress(), //
LENGTH_ATTRIBUTE_NAME, info.getSize(), // LENGTH_ATTRIBUTE_NAME, info.getSize(), //
AFFECTS_ATTRIBUTE_NAME, doGetAffects(), //
SPEC_ATTRIBUTE_NAME, this, // SPEC_ATTRIBUTE_NAME, this, //
EXPRESSION_ATTRIBUTE_NAME, info.getLocation(), // EXPRESSION_ATTRIBUTE_NAME, info.getExpression(), //
KINDS_ATTRIBUTE_NAME, getKinds() // KINDS_ATTRIBUTE_NAME, getKinds() //
), reason); ), reason);
this.changeAttributes(List.of(), List.of(), Map.of( // this.changeAttributes(List.of(), List.of(), Map.of( //
BPT_TYPE_ATTRIBUTE_NAME, info.getType().name(), // BPT_TYPE_ATTRIBUTE_NAME, info.getType().name(), //
BPT_DISP_ATTRIBUTE_NAME, info.getDisp().name(), // BPT_DISP_ATTRIBUTE_NAME, info.getDisp().name(), //
BPT_PENDING_ATTRIBUTE_NAME, info.getPending(), // BPT_PENDING_ATTRIBUTE_NAME, info.getPending(), //
BPT_TIMES_ATTRIBUTE_NAME, info.getTimes(), // BPT_TIMES_ATTRIBUTE_NAME, info.getTimes() //
TargetObject.UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), reason); ), reason);
} }
@ -84,11 +83,13 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
public DbgModelTargetBreakpointSpecImpl(DbgModelTargetBreakpointContainer breakpoints, public DbgModelTargetBreakpointSpecImpl(DbgModelTargetBreakpointContainer breakpoints,
DbgBreakpointInfo info) { DbgBreakpointInfo info) {
super(breakpoints.getModel(), breakpoints, keyBreakpoint(info), "BreakpointSpec"); super(breakpoints.getModel(), breakpoints, keyBreakpoint(info), "BreakpointSpec");
this.setBreakpointInfo(info); this.getModel().addModelObject(info.getDebugBreakpoint(), this);
//this.setBreakpointInfo(info);
updateInfo(null, info, "Created"); updateInfo(null, info, "Created");
} }
@Override
public void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, String reason) { public void updateInfo(DbgBreakpointInfo oldInfo, DbgBreakpointInfo newInfo, String reason) {
synchronized (this) { synchronized (this) {
assert oldInfo == getBreakpointInfo(); assert oldInfo == getBreakpointInfo();
@ -116,18 +117,19 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
/** /**
* Update the enabled field * Update the enabled field
* *
* This does not actually toggle the breakpoint. It just updates the field and calls the proper * This does not actually toggle the breakpoint. It just updates the field
* listeners. To actually toggle the breakpoint, use {@link #toggle(boolean)} instead, which if * and calls the proper listeners. To actually toggle the breakpoint, use
* effective, should eventually cause this method to be called. * {@link #toggle(boolean)} instead, which if effective, should eventually
* cause this method to be called.
* *
* @param enabled true if enabled, false if disabled * @param enabled true if enabled, false if disabled
* @param reason a description of the cause (not really used, yet) * @param reason a description of the cause (not really used, yet)
*/ */
@Override
public void setEnabled(boolean enabled, String reason) { public void setEnabled(boolean enabled, String reason) {
setBreakpointEnabled(enabled); setBreakpointEnabled(enabled);
changeAttributes(List.of(), List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled // changeAttributes(List.of(), List.of(), Map.of(ENABLED_ATTRIBUTE_NAME, enabled //
), reason); ), reason);
getListeners().fire(TargetBreakpointSpecListener.class).breakpointToggled(this, enabled);
} }
@Override @Override
@ -140,6 +142,7 @@ public class DbgModelTargetBreakpointSpecImpl extends DbgModelTargetObjectImpl
this.enabled = enabled; this.enabled = enabled;
} }
@Override
public ListenerSet<TargetBreakpointAction> getActions() { public ListenerSet<TargetBreakpointAction> getActions() {
return actions; 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.TargetAttributeType;
import ghidra.dbg.target.schema.TargetObjectSchemaInfo; import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
@TargetObjectSchemaInfo(name = "ConnectorContainer", attributes = { // @TargetObjectSchemaInfo(
@TargetAttributeType(name = "Launch process", type = DbgModelTargetProcessLaunchConnectorImpl.class, required = true, fixed = true), // name = "ConnectorContainer",
@TargetAttributeType(name = "Attach to process", type = DbgModelTargetProcessAttachConnectorImpl.class, required = true, fixed = true), // attributes = {
@TargetAttributeType(name = "Load trace/dump", type = DbgModelTargetTraceOrDumpConnectorImpl.class, required = true, fixed = true), // @TargetAttributeType(
@TargetAttributeType(name = "Attach to kernel", type = DbgModelTargetKernelConnectorImpl.class, required = true, fixed = true), // name = "Launch process",
@TargetAttributeType(type = Void.class) // type = DbgModelTargetProcessLaunchConnectorImpl.class,
}, canonicalContainer = true) 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 { public class DbgModelTargetConnectorContainerImpl extends DbgModelTargetObjectImpl {
protected final DbgModelTargetRoot root; 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.TargetAttributeType;
import ghidra.dbg.target.schema.TargetObjectSchemaInfo; import ghidra.dbg.target.schema.TargetObjectSchemaInfo;
@TargetObjectSchemaInfo(name = "DebugContainer", attributes = { // @TargetObjectSchemaInfo(
@TargetAttributeType(name = "Breakpoints", type = DbgModelTargetBreakpointContainerImpl.class, required = true, fixed = true), // name = "DebugContainer",
@TargetAttributeType(type = Void.class) // attributes = {
}, canonicalContainer = true) @TargetAttributeType(
name = "Breakpoints",
type = DbgModelTargetBreakpointContainerImpl.class,
required = true,
fixed = true),
@TargetAttributeType(type = Void.class)
},
canonicalContainer = true)
public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetDebugContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetDebugContainer { implements DbgModelTargetDebugContainer {

View file

@ -28,11 +28,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo(name = "KernelConnector", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = Void.class) // name = "KernelConnector",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = Void.class)
}) },
attributes = {
@TargetAttributeType(type = Void.class)
})
public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetConnector { implements DbgModelTargetConnector {
@ -47,8 +50,7 @@ public class DbgModelTargetKernelConnectorImpl extends DbgModelTargetObjectImpl
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
paramDescs = TargetParameterMap.copyOf(computeParameters()), // paramDescs = TargetParameterMap.copyOf(computeParameters()) //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }

View file

@ -27,6 +27,7 @@ import agent.dbgeng.manager.DbgModuleMemory;
import agent.dbgeng.manager.cmd.*; import agent.dbgeng.manager.cmd.*;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.async.AsyncUtils;
import ghidra.dbg.error.DebuggerMemoryAccessException; import ghidra.dbg.error.DebuggerMemoryAccessException;
import ghidra.dbg.error.DebuggerModelAccessException; import ghidra.dbg.error.DebuggerModelAccessException;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
@ -34,15 +35,9 @@ import ghidra.dbg.target.schema.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.util.datastruct.WeakValueHashMap; import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "Memory", elements = {
name = "Memory", @TargetElementType(type = DbgModelTargetMemoryRegionImpl.class) }, attributes = {
elements = { @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetElementType(type = DbgModelTargetMemoryRegionImpl.class)
},
attributes = {
@TargetAttributeType(type = Void.class)
},
canonicalContainer = true)
public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetMemoryContainer { implements DbgModelTargetMemoryContainer {
@ -58,7 +53,10 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public CompletableFuture<Void> requestElements(boolean refresh) { 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 -> { return listMemory().thenAccept(byName -> {
List<TargetObject> sections; List<TargetObject> sections;
synchronized (this) { synchronized (this) {
@ -111,7 +109,7 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
if (range == null) { if (range == null) {
throw new DebuggerMemoryAccessException("Cannot read at " + address); 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())); 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) { private void writeAssist(Address address, byte[] data) {
listeners.fire(TargetMemoryListener.class).memoryUpdated(this, address, data); listeners.fire.memoryUpdated(getProxy(), address, data);
} }
@Override @Override
public CompletableFuture<byte[]> readMemory(Address address, int length) { 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(); DbgManagerImpl manager = getManager();
if (manager.isWaiting()) { if (manager.isWaiting()) {
throw new DebuggerModelAccessException( throw new DebuggerModelAccessException(
@ -197,6 +199,10 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public CompletableFuture<Void> writeMemory(Address address, byte[] data) { 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(); DbgManagerImpl manager = getManager();
if (manager.isWaiting()) { if (manager.isWaiting()) {
throw new DebuggerModelAccessException( throw new DebuggerModelAccessException(
@ -252,16 +258,6 @@ public class DbgModelTargetMemoryContainerImpl extends DbgModelTargetObjectImpl
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
public void invalidateMemoryCaches() {
listeners.fire.invalidateCacheRequested(this);
}
@Override
public void onRunning() {
invalidateMemoryCaches();
setAccessible(false);
}
@Override @Override
protected void update() { protected void update() {
requestElements(true); requestElements(true);

View file

@ -26,22 +26,18 @@ import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@TargetObjectSchemaInfo(name = "MemoryRegion", elements = { // @TargetObjectSchemaInfo(name = "MemoryRegion", elements = {
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class) }, attributes = {
}, attributes = { // @TargetAttributeType(name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, type = DbgModelTargetMemoryContainerImpl.class),
@TargetAttributeType( // @TargetAttributeType(name = "BaseAddress", type = Address.class),
name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, // @TargetAttributeType(name = "EndAddress", type = Address.class),
type = DbgModelTargetMemoryContainerImpl.class), // @TargetAttributeType(name = "RegionSize", type = String.class),
@TargetAttributeType(name = "BaseAddress", type = Address.class), // @TargetAttributeType(name = "AllocationBase", type = Address.class),
@TargetAttributeType(name = "EndAddress", type = Address.class), // @TargetAttributeType(name = "AllocationProtect", type = String.class),
@TargetAttributeType(name = "RegionSize", type = String.class), // @TargetAttributeType(name = "Protect", type = String.class),
@TargetAttributeType(name = "AllocationBase", type = Address.class), // @TargetAttributeType(name = "State", type = String.class),
@TargetAttributeType(name = "AllocationProtect", type = String.class), // @TargetAttributeType(name = "Type", type = String.class),
@TargetAttributeType(name = "Protect", type = String.class), // @TargetAttributeType(type = Void.class) })
@TargetAttributeType(name = "State", type = String.class), //
@TargetAttributeType(name = "Type", type = String.class), //
@TargetAttributeType(type = Void.class) //
})
public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetMemoryRegion { implements DbgModelTargetMemoryRegion {
@ -64,6 +60,7 @@ public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
public DbgModelTargetMemoryRegionImpl(DbgModelTargetMemoryContainer memory, public DbgModelTargetMemoryRegionImpl(DbgModelTargetMemoryContainer memory,
DbgModuleMemory region) { DbgModuleMemory region) {
super(memory.getModel(), memory, keySection(region), "Region"); super(memory.getModel(), memory, keySection(region), "Region");
this.getModel().addModelObject(region, this);
this.section = region; this.section = region;
this.range = doGetRange(section); this.range = doGetRange(section);
@ -92,8 +89,7 @@ public class DbgModelTargetMemoryRegionImpl extends DbgModelTargetObjectImpl
RANGE_ATTRIBUTE_NAME, doGetRange(section), // RANGE_ATTRIBUTE_NAME, doGetRange(section), //
READABLE_ATTRIBUTE_NAME, isReadable(), // READABLE_ATTRIBUTE_NAME, isReadable(), //
WRITABLE_ATTRIBUTE_NAME, isWritable(), // WRITABLE_ATTRIBUTE_NAME, isWritable(), //
EXECUTABLE_ATTRIBUTE_NAME, isExecutable(), // EXECUTABLE_ATTRIBUTE_NAME, isExecutable() //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
AddressSpace space = getModel().getAddressSpace("ram"); AddressSpace space = getModel().getAddressSpace("ram");

View file

@ -22,42 +22,36 @@ import agent.dbgeng.manager.DbgModule;
import agent.dbgeng.manager.DbgProcess; import agent.dbgeng.manager.DbgProcess;
import agent.dbgeng.model.iface2.DbgModelTargetModule; import agent.dbgeng.model.iface2.DbgModelTargetModule;
import agent.dbgeng.model.iface2.DbgModelTargetModuleContainer; import agent.dbgeng.model.iface2.DbgModelTargetModuleContainer;
import ghidra.async.AsyncFence; import ghidra.dbg.target.*;
import ghidra.async.AsyncLazyMap;
import ghidra.dbg.target.TargetModule;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
import ghidra.lifecycle.Internal; import ghidra.lifecycle.Internal;
import ghidra.util.Msg;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "ModuleContainer", elements = { //
name = "ModuleContainer",
elements = { //
@TargetElementType(type = DbgModelTargetModuleImpl.class) // @TargetElementType(type = DbgModelTargetModuleImpl.class) //
}, }, //
elementResync = ResyncMode.ONCE, //
attributes = { // attributes = { //
@TargetAttributeType(type = Void.class) // @TargetAttributeType(type = Void.class) //
}, }, canonicalContainer = true)
canonicalContainer = true)
public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetModuleContainer { implements DbgModelTargetModuleContainer {
// NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO. // NOTE: -file-list-shared-libraries omits the main module and system-supplied DSO.
protected final DbgModelTargetProcessImpl targetProcess;
protected final DbgProcess process; 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) { public DbgModelTargetModuleContainerImpl(DbgModelTargetProcessImpl process) {
super(process.getModel(), process, "Modules", "ModuleContainer"); super(process.getModel(), process, "Modules", "ModuleContainer");
this.targetProcess = process;
this.process = process.process; this.process = process.process;
requestElements(false);
} }
@Override @Override
@Internal @Internal
public void libraryLoaded(String name) { public void libraryLoaded(String name) {
CompletableFuture<DbgModelTargetModule> module; DbgModelTargetModule module;
synchronized (this) { synchronized (this) {
/** /**
* It's not a good idea to remove "stale" entries. If the entry's already present, it's * 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! * sections loaded. Removing it will cause it to load all module sections again!
*/ */
//modulesByName.remove(name); //modulesByName.remove(name);
module = doGetTargetModule(name); module = getTargetModule(name);
} }
module.thenAccept(mod -> { TargetThread eventThread =
changeElements(List.of(), List.of(mod), Map.of(), "Loaded"); (TargetThread) getModel().getModelObject(getManager().getEventThread());
getListeners().fire(TargetEventScopeListener.class) changeElements(List.of(), List.of(module), Map.of(), "Loaded");
.event(this, null, TargetEventType.MODULE_LOADED, "Library " + name + " loaded", getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_LOADED,
List.of(mod)); "Library " + name + " loaded", List.of(module));
}).exceptionally(e -> {
Msg.error(this, "Problem getting module for library load: " + name, e);
return null;
});
} }
@Override @Override
@Internal @Internal
public void libraryUnloaded(String name) { public void libraryUnloaded(String name) {
if (!modulesByName.containsKey(name)) { DbgModelTargetModule targetModule = getTargetModule(name);
return; if (targetModule != null) {
} TargetThread eventThread =
modulesByName.get(name).thenAccept(mod -> { (TargetThread) getModel().getModelObject(getManager().getEventThread());
getListeners().fire(TargetEventScopeListener.class) getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_UNLOADED,
.event(this, null, TargetEventType.MODULE_UNLOADED, "Library " + name + " unloaded", List.of(targetModule));
"Library " + name + " unloaded", List.of(mod)); DbgModelImpl impl = (DbgModelImpl) model;
}); impl.deleteModelObject(targetModule.getDbgModule());
synchronized (this) {
modulesByName.remove(name);
} }
changeElements(List.of(name), List.of(), Map.of(), "Unloaded"); changeElements(List.of(name), List.of(), Map.of(), "Unloaded");
} }
@ -108,34 +96,28 @@ public class DbgModelTargetModuleContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public CompletableFuture<Void> requestElements(boolean refresh) { public CompletableFuture<Void> requestElements(boolean refresh) {
List<TargetObject> result = new ArrayList<>(); List<TargetObject> result = new ArrayList<>();
return process.listModules().thenCompose(byName -> { return process.listModules().thenAccept(byName -> {
AsyncFence fence = new AsyncFence();
synchronized (this) { synchronized (this) {
modulesByName.retainKeys(byName.keySet());
for (Map.Entry<String, DbgModule> ent : byName.entrySet()) { for (Map.Entry<String, DbgModule> ent : byName.entrySet()) {
fence.include(getTargetModule(ent.getKey()).thenAccept(module -> { result.add(getTargetModule(ent.getKey()));
result.add(module);
}));
} }
} }
return fence.ready();
}).thenAccept(__ -> {
changeElements(List.of(), result, Map.of(), "Refreshed"); 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. // Only get here from libraryLoaded or getElements. The known list should be fresh.
DbgModule module = process.getKnownModules().get(name); DbgModule module = process.getKnownModules().get(name);
if (module == null) { if (module == null) {
return CompletableFuture.completedFuture(null); return null;
} }
return CompletableFuture.completedFuture(new DbgModelTargetModuleImpl(this, module)); DbgModelImpl impl = (DbgModelImpl) model;
//TODO: return module.listSections().thenApply(__ -> new DbgModelTargetModule(this, module)); 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.dbg.util.PathUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@TargetObjectSchemaInfo(name = "Module", elements = { // @TargetObjectSchemaInfo(name = "Module", elements = {
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class) }, attributes = {
}, attributes = { // @TargetAttributeType(name = "Symbols", type = DbgModelTargetSymbolContainerImpl.class, required = true, fixed = true),
@TargetAttributeType(name = "Symbols", type = DbgModelTargetSymbolContainerImpl.class, required = true, fixed = true), // @TargetAttributeType(name = "BaseAddress", type = Address.class),
@TargetAttributeType(name = "BaseAddress", type = Address.class), // @TargetAttributeType(name = "ImageName", type = String.class),
@TargetAttributeType(name = "ImageName", type = String.class), // @TargetAttributeType(name = "TimeStamp", type = Integer.class),
@TargetAttributeType(name = "TimeStamp", type = Integer.class), // @TargetAttributeType(name = "Len", type = String.class),
@TargetAttributeType(name = "Len", type = String.class), // @TargetAttributeType(type = Void.class) })
@TargetAttributeType(type = Void.class) //
})
public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetModule { implements DbgModelTargetModule {
protected static String indexModule(DbgModule module) { protected static String indexModule(DbgModule module) {
@ -52,6 +50,7 @@ public class DbgModelTargetModuleImpl extends DbgModelTargetObjectImpl
public DbgModelTargetModuleImpl(DbgModelTargetModuleContainerImpl modules, DbgModule module) { public DbgModelTargetModuleImpl(DbgModelTargetModuleContainerImpl modules, DbgModule module) {
super(modules.getModel(), modules, keyModule(module), "Module"); super(modules.getModel(), modules, keyModule(module), "Module");
this.getModel().addModelObject(module, this);
this.process = modules.process; this.process = modules.process;
this.module = module; this.module = module;

View file

@ -15,28 +15,22 @@
*/ */
package agent.dbgeng.model.impl; package agent.dbgeng.model.impl;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import agent.dbgeng.manager.DbgModule; import agent.dbgeng.manager.DbgModule;
import agent.dbgeng.manager.DbgModuleSection; import agent.dbgeng.manager.DbgModuleSection;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo(name = "SectionContainer", elements = { // @TargetObjectSchemaInfo(name = "SectionContainer", elements = {
@TargetElementType(type = DbgModelTargetModuleSectionImpl.class) // @TargetElementType(type = DbgModelTargetModuleSectionImpl.class) }, attributes = {
}, attributes = { // @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetAttributeType(type = Void.class) //
}, canonicalContainer = true)
public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetModuleSectionContainer { implements DbgModelTargetModuleSectionContainer {
protected final DbgModule module; protected final DbgModule module;
protected final Map<Long, DbgModelTargetModuleSectionImpl> sectionsByStart =
new WeakValueHashMap<>();
public DbgModelTargetModuleSectionContainerImpl(DbgModelTargetModule module) { public DbgModelTargetModuleSectionContainerImpl(DbgModelTargetModule module) {
super(module.getModel(), module, "Sections", "ModuleSections"); super(module.getModel(), module, "Sections", "ModuleSections");
this.module = module.getDbgModule(); this.module = module.getDbgModule();
@ -61,8 +55,12 @@ public class DbgModelTargetModuleSectionContainerImpl extends DbgModelTargetObje
} }
protected synchronized DbgModelTargetModuleSection getModuleSection(DbgModuleSection section) { protected synchronized DbgModelTargetModuleSection getModuleSection(DbgModuleSection section) {
return sectionsByStart.computeIfAbsent(section.getStart(), DbgModelImpl impl = (DbgModelImpl) model;
s -> new DbgModelTargetModuleSectionImpl(this, section)); 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.dbg.target.schema.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "Section", elements = {
name = "Section", @TargetElementType(type = Void.class) }, attributes = {
elements = { // @TargetAttributeType(type = Void.class) })
@TargetElementType(type = Void.class) //
},
attributes = { //
@TargetAttributeType(type = Void.class) //
})
public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetModuleSection { implements DbgModelTargetModuleSection {
protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile"; protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile";
@ -40,6 +35,7 @@ public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
public DbgModelTargetModuleSectionImpl(DbgModelTargetModuleSectionContainerImpl sections, public DbgModelTargetModuleSectionImpl(DbgModelTargetModuleSectionContainerImpl sections,
DbgModuleSection section) { DbgModuleSection section) {
super(sections.getModel(), sections, section.getName(), "Section"); super(sections.getModel(), sections, section.getName(), "Section");
this.getModel().addModelObject(section, this);
AddressSpace space = getModel().getAddressSpace("ram"); AddressSpace space = getModel().getAddressSpace("ram");
Address min = space.getAddress(section.getStart()); Address min = space.getAddress(section.getStart());
@ -50,8 +46,7 @@ public class DbgModelTargetModuleSectionImpl extends DbgModelTargetObjectImpl
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
MODULE_ATTRIBUTE_NAME, sections.getParent(), // MODULE_ATTRIBUTE_NAME, sections.getParent(), //
RANGE_ATTRIBUTE_NAME, range, // RANGE_ATTRIBUTE_NAME, range, //
DISPLAY_ATTRIBUTE_NAME, section.getName(), // DISPLAY_ATTRIBUTE_NAME, section.getName() //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }

View file

@ -26,7 +26,6 @@ import agent.dbgeng.model.iface1.DbgModelTargetExecutionStateful;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.dbg.agent.DefaultTargetObject; import ghidra.dbg.agent.DefaultTargetObject;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.TargetAccessConditioned.TargetAccessibilityListener;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.dbg.target.schema.TargetObjectSchema; import ghidra.dbg.target.schema.TargetObjectSchema;
@ -71,10 +70,6 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, accessible // TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, accessible //
), "Accessibility changed"); ), "Accessibility changed");
DbgModelTargetAccessConditioned accessConditioned =
(DbgModelTargetAccessConditioned) this;
listeners.fire(TargetAccessibilityListener.class)
.accessibilityChanged(accessConditioned, accessible);
} }
} }
@ -135,6 +130,10 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
onExit(); onExit();
break; break;
} }
case SESSION_EXIT: {
getModel().close();
return;
}
} }
if (this instanceof DbgModelTargetExecutionStateful) { if (this instanceof DbgModelTargetExecutionStateful) {
DbgModelTargetExecutionStateful stateful = (DbgModelTargetExecutionStateful) this; DbgModelTargetExecutionStateful stateful = (DbgModelTargetExecutionStateful) this;
@ -147,6 +146,11 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
throw new AssertionError(); // shouldn't ever be here throw new AssertionError(); // shouldn't ever be here
} }
@Override
public CompletableFuture<List<TargetObject>> requestNativeElements() {
throw new AssertionError(); // shouldn't ever be here
}
@Override @Override
public DbgModelTargetSession getParentSession() { public DbgModelTargetSession getParentSession() {
DbgModelTargetObject test = (DbgModelTargetObject) parent; DbgModelTargetObject test = (DbgModelTargetObject) parent;
@ -178,7 +182,6 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
public void setModified(Map<String, Object> map, boolean b) { public void setModified(Map<String, Object> map, boolean b) {
if (modified) { if (modified) {
map.put(MODIFIED_ATTRIBUTE_NAME, 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( // changeAttributes(List.of(), List.of(), Map.of( //
MODIFIED_ATTRIBUTE_NAME, modified // MODIFIED_ATTRIBUTE_NAME, modified //
), "Refreshed"); ), "Refreshed");
listeners.fire.displayChanged(this, getDisplay());
} }
} }
@ -199,4 +201,9 @@ public class DbgModelTargetObjectImpl extends DefaultTargetObject<TargetObject,
), "Refreshed"); ), "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.TargetMethod.TargetParameterMap;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo(name = "ProcessAttachConnector", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = Void.class) // name = "ProcessAttachConnector",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = Void.class)
}) },
attributes = {
@TargetAttributeType(type = Void.class)
})
public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObjectImpl public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetConnector { implements DbgModelTargetConnector {
@ -48,8 +51,7 @@ public class DbgModelTargetProcessAttachConnectorImpl extends DbgModelTargetObje
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
paramDescs = TargetParameterMap.copyOf(computeParameters()), // paramDescs = TargetParameterMap.copyOf(computeParameters()) //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }

View file

@ -25,23 +25,13 @@ import agent.dbgeng.manager.*;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "ProcessContainer", elements = {
name = "ProcessContainer", @TargetElementType(type = DbgModelTargetProcessImpl.class) }, attributes = {
elements = { // @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetElementType(type = DbgModelTargetProcessImpl.class) //
},
attributes = { //
@TargetAttributeType(type = Void.class) //
},
canonicalContainer = true)
public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetProcessContainer { implements DbgModelTargetProcessContainer {
protected final Map<DebugProcessId, DbgModelTargetProcess> processesById =
new WeakValueHashMap<>();
public DbgModelTargetProcessContainerImpl(DbgModelTargetSession session) { public DbgModelTargetProcessContainerImpl(DbgModelTargetSession session) {
super(session.getModel(), session, "Processes", "ProcessContainer"); super(session.getModel(), session, "Processes", "ProcessContainer");
@ -55,9 +45,9 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
DbgModelTargetProcess process = getTargetProcess(proc); DbgModelTargetProcess process = getTargetProcess(proc);
changeElements(List.of(), List.of(process), Map.of(), "Added"); changeElements(List.of(), List.of(process), Map.of(), "Added");
process.processStarted(proc.getPid()); process.processStarted(proc.getPid());
getListeners().fire(TargetEventScopeListener.class) getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_CREATED,
.event(this, null, TargetEventType.PROCESS_CREATED, "Process " + proc.getId() + "Process " + proc.getId() + " started " + process.getName() + "pid=" + proc.getPid(),
" started " + "notepad.exe" + " pid=" + proc.getPid(), List.of(process)); List.of(process));
} }
@Override @Override
@ -66,21 +56,8 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
process.processStarted(proc.getPid()); 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 @Override
public void processRemoved(DebugProcessId processId, DbgCause cause) { public void processRemoved(DebugProcessId processId, DbgCause cause) {
synchronized (this) {
processesById.remove(processId);
}
changeElements(List.of( // changeElements(List.of( //
DbgModelTargetProcessImpl.indexProcess(processId) // DbgModelTargetProcessImpl.indexProcess(processId) //
), List.of(), Map.of(), "Removed"); ), List.of(), Map.of(), "Removed");
@ -96,21 +73,24 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
DbgReason reason) { DbgReason reason) {
DbgModelTargetProcess process = getTargetProcess(thread.getProcess()); DbgModelTargetProcess process = getTargetProcess(thread.getProcess());
process.threadStateChanged(thread, state, cause, reason); process.threadStateChangedSpecific(thread, state);
} }
@Override @Override
public void threadExited(DebugThreadId threadId, DbgProcess proc, DbgCause cause) { public void threadExited(DebugThreadId threadId, DbgProcess proc, DbgCause cause) {
DbgModelTargetProcess targetProcess = processesById.get(proc.getId()); DbgModelTargetProcess process = getTargetProcess(proc);
if (targetProcess != null) { if (process != null) {
targetProcess.getThreads().threadExited(threadId); process.getThreads().threadExited(threadId);
} }
} }
@Override @Override
public void moduleLoaded(DbgProcess proc, DebugModuleInfo info, DbgCause cause) { public void moduleLoaded(DbgProcess proc, DebugModuleInfo info, DbgCause cause) {
DbgModelTargetProcess process = getTargetProcess(proc); DbgModelTargetProcess process = getTargetProcess(proc);
process.getModules().libraryLoaded(info.toString()); DbgModelTargetModuleContainer modules = process.getModules();
if (modules != null) {
modules.libraryLoaded(info.toString());
}
} }
@Override @Override
@ -135,14 +115,22 @@ public class DbgModelTargetProcessContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public synchronized DbgModelTargetProcess getTargetProcess(DebugProcessId id) { public synchronized DbgModelTargetProcess getTargetProcess(DebugProcessId id) {
return processesById.computeIfAbsent(id, DbgModelImpl impl = (DbgModelImpl) model;
i -> new DbgModelTargetProcessImpl(this, getManager().getKnownProcesses().get(id))); TargetObject modelObject = impl.getModelObject(id);
if (modelObject != null) {
return (DbgModelTargetProcess) modelObject;
}
return new DbgModelTargetProcessImpl(this, getManager().getKnownProcesses().get(id));
} }
@Override @Override
public synchronized DbgModelTargetProcess getTargetProcess(DbgProcess process) { public synchronized DbgModelTargetProcess getTargetProcess(DbgProcess process) {
return processesById.computeIfAbsent(process.getId(), DbgModelImpl impl = (DbgModelImpl) model;
i -> new DbgModelTargetProcessImpl(this, process)); 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.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import agent.dbgeng.dbgeng.DebugProcessId; import agent.dbgeng.dbgeng.DebugProcessId;
import agent.dbgeng.manager.*; import agent.dbgeng.manager.*;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import agent.dbgeng.model.iface2.*; 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.*;
import ghidra.dbg.target.TargetEventScope.TargetEventType;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "Process", elements = {
name = "Process", @TargetElementType(type = Void.class) }, attributes = {
elements = { @TargetAttributeType(name = "Debug", type = DbgModelTargetDebugContainerImpl.class, required = true, fixed = true),
@TargetElementType(type = Void.class) @TargetAttributeType(name = "Memory", type = DbgModelTargetMemoryContainerImpl.class, required = true, fixed = true),
}, @TargetAttributeType(name = "Modules", type = DbgModelTargetModuleContainerImpl.class, required = true, fixed = true),
attributes = { @TargetAttributeType(name = "Threads", type = DbgModelTargetThreadContainerImpl.class, required = true, fixed = true),
@TargetAttributeType( @TargetAttributeType(name = DbgModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME, type = Long.class),
name = "Debug", @TargetAttributeType(type = Void.class) })
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)
})
public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetProcess { implements DbgModelTargetProcess {
@ -92,6 +69,8 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
public DbgModelTargetProcessImpl(DbgModelTargetProcessContainer processes, DbgProcess process) { public DbgModelTargetProcessImpl(DbgModelTargetProcessContainer processes, DbgProcess process) {
super(processes.getModel(), processes, keyProcess(process), "Process"); super(processes.getModel(), processes, keyProcess(process), "Process");
this.getModel().addModelObject(process, this);
this.getModel().addModelObject(process.getId(), this);
this.process = process; this.process = process;
this.debug = new DbgModelTargetDebugContainerImpl(this); this.debug = new DbgModelTargetDebugContainerImpl(this);
@ -107,7 +86,7 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
//sections, // //sections, //
threads // threads //
), Map.of( // ), Map.of( //
ACCESSIBLE_ATTRIBUTE_NAME, false, // ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS, // TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS, //
SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, // SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS, //
@ -129,36 +108,28 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
@Override @Override
public void processSelected(DbgProcess eventProcess, DbgCause cause) { public void processSelected(DbgProcess eventProcess, DbgCause cause) {
if (eventProcess.equals(process)) { if (eventProcess.equals(process)) {
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>(); ((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
.handle(seq::next);
}, scope).then(seq -> {
scope.get().setFocus(this);
}).finish();
} }
} }
@Override public void threadStateChangedSpecific(DbgThread thread, DbgState state) {
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
DbgReason reason) {
TargetExecutionState targetState = convertState(state); TargetExecutionState targetState = convertState(state);
setExecutionState(targetState, "ThreadStateChanged"); setExecutionState(targetState, "ThreadStateChanged");
} }
@Override @Override
public CompletableFuture<Void> launch(List<String> args) { public CompletableFuture<Void> launch(List<String> args) {
return DbgModelImplUtils.launch(getModel(), process, args); return model.gateFuture(DbgModelImplUtils.launch(getModel(), process, args));
} }
@Override @Override
public CompletableFuture<Void> resume() { public CompletableFuture<Void> resume() {
return process.cont(); return model.gateFuture(process.cont());
} }
@Override @Override
public CompletableFuture<Void> kill() { public CompletableFuture<Void> kill() {
return process.kill(); return model.gateFuture(process.kill());
} }
@Override @Override
@ -166,22 +137,22 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
getModel().assertMine(TargetObject.class, attachable); getModel().assertMine(TargetObject.class, attachable);
// NOTE: Get the object and type check it myself. // NOTE: Get the object and type check it myself.
// The typed ref could have been unsafely cast // 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 @Override
public CompletableFuture<Void> attach(long pid) { public CompletableFuture<Void> attach(long pid) {
return process.attach(pid).thenApply(set -> null); return model.gateFuture(process.attach(pid)).thenApply(set -> null);
} }
@Override @Override
public CompletableFuture<Void> detach() { public CompletableFuture<Void> detach() {
return process.detach(); return model.gateFuture(process.detach());
} }
@Override @Override
public CompletableFuture<Void> delete() { public CompletableFuture<Void> delete() {
return process.remove(); return model.gateFuture(process.remove());
} }
@Override @Override
@ -192,13 +163,13 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
case ADVANCE: // Why no exec-advance in dbgeng? case ADVANCE: // Why no exec-advance in dbgeng?
throw new UnsupportedOperationException(kind.name()); throw new UnsupportedOperationException(kind.name());
default: default:
return process.step(convertToDbg(kind)); return model.gateFuture(process.step(convertToDbg(kind)));
} }
} }
@Override @Override
public CompletableFuture<Void> step(Map<String, ?> args) { public CompletableFuture<Void> step(Map<String, ?> args) {
return process.step(args); return model.gateFuture(process.step(args));
} }
@Override @Override
@ -213,20 +184,16 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
} }
@Override @Override
public void processExited(Long exitCode) { public void processExited(DbgProcess proc, DbgCause cause) {
if (exitCode != null) { if (proc.equals(this.process)) {
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
EXIT_CODE_ATTRIBUTE_NAME, exitCode // STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, //
EXIT_CODE_ATTRIBUTE_NAME, proc.getExitCode() //
), "Exited"); ), "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 @Override

View file

@ -27,11 +27,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo(name = "ProcessLaunchConnector", elements = { // @TargetObjectSchemaInfo(
name = "ProcessLaunchConnector",
elements = { //
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class) //
}, attributes = { // },
attributes = { //
@TargetAttributeType(type = Void.class) // @TargetAttributeType(type = Void.class) //
}) })
public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObjectImpl public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetConnector { implements DbgModelTargetConnector {
@ -46,8 +49,7 @@ public class DbgModelTargetProcessLaunchConnectorImpl extends DbgModelTargetObje
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
paramDescs = TargetParameterMap.copyOf(computeParameters()), // paramDescs = TargetParameterMap.copyOf(computeParameters()) //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
} }

View file

@ -20,42 +20,36 @@ import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; 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.DbgRegister;
import agent.dbgeng.manager.impl.DbgRegisterSet;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
import ghidra.dbg.error.DebuggerRegisterAccessException; import ghidra.dbg.error.DebuggerRegisterAccessException;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetRegisterBank; import ghidra.dbg.target.TargetRegisterBank;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
import ghidra.dbg.util.ConversionUtils; import ghidra.dbg.util.ConversionUtils;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "RegisterContainer", elements = {
name = "RegisterContainer", @TargetElementType(type = DbgModelTargetRegisterImpl.class) }, elementResync = ResyncMode.ONCE, //
elements = {
@TargetElementType(type = DbgModelTargetRegisterImpl.class)
},
attributes = { attributes = {
@TargetAttributeType( @TargetAttributeType(name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, type = DbgModelTargetRegisterContainerImpl.class),
name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
type = DbgModelTargetRegisterContainerImpl.class),
@TargetAttributeType(type = Void.class)
},
canonicalContainer = true)
public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetRegisterContainerAndBank { implements DbgModelTargetRegisterContainerAndBank {
protected final DbgThread thread; protected final DbgThread thread;
protected final Map<Integer, DbgModelTargetRegister> registersByNumber = new HashMap<>();
protected final Map<String, DbgModelTargetRegister> registersByName = new HashMap<>(); protected final Map<String, DbgModelTargetRegister> registersByName = new HashMap<>();
private Map<String, byte[]> values = new HashMap<>();
public DbgModelTargetRegisterContainerImpl(DbgModelTargetThread thread) { public DbgModelTargetRegisterContainerImpl(DbgModelTargetThread thread) {
super(thread.getModel(), thread, "Registers", "RegisterContainer"); super(thread.getModel(), thread, "Registers", "RegisterContainer");
this.thread = thread.getThread(); this.thread = thread.getThread();
requestElements(false);
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, this // TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, this //
), "Initialized"); ), "Initialized");
@ -64,9 +58,13 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
@Override @Override
public CompletableFuture<Void> requestElements(boolean refresh) { public CompletableFuture<Void> requestElements(boolean refresh) {
return thread.listRegisters().thenAccept(regs -> { return thread.listRegisters().thenAccept(regs -> {
if (regs.size() != registersByNumber.size()) { if (regs.size() != registersByName.size()) {
registersByNumber.clear(); DbgModelImpl impl = (DbgModelImpl) model;
for (DbgRegister reg : regs) {
impl.deleteModelObject(reg);
}
registersByName.clear(); registersByName.clear();
} }
List<TargetObject> registers; List<TargetObject> registers;
synchronized (this) { 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 @Override
public synchronized DbgModelTargetRegister getTargetRegister(DbgRegister register) { public synchronized DbgModelTargetRegister getTargetRegister(DbgRegister register) {
DbgModelTargetRegister reg = registersByNumber.computeIfAbsent(register.getNumber(), DbgModelImpl impl = (DbgModelImpl) model;
n -> new DbgModelTargetRegisterImpl(this, register)); TargetObject modelObject = impl.getModelObject(register);
if (modelObject != null) {
return (DbgModelTargetRegister) modelObject;
}
DbgModelTargetRegister reg = new DbgModelTargetRegisterImpl(this, register);
registersByName.put(register.getName(), reg); registersByName.put(register.getName(), reg);
return reg; return reg;
} }
@ -90,14 +98,12 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
@Override @Override
public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed( public CompletableFuture<? extends Map<String, byte[]>> readRegistersNamed(
Collection<String> names) { Collection<String> names) {
return AsyncUtils.sequence(TypeSpec.map(String.class, byte[].class)).then(seq -> { return model.gateFuture(thread.listRegisters().thenCompose(regs -> {
thread.listRegisters().handle(seq::next); if (regs.size() != registersByName.size() || getCachedElements().isEmpty()) {
}, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> { return requestElements(false);
if (regs.size() != registersByNumber.size() || getCachedElements().isEmpty()) {
requestElements(true).handle(seq::next);
} }
seq.next(null, null); return AsyncUtils.NIL;
}).then(seq -> { }).thenCompose(__ -> {
Set<DbgRegister> toRead = new LinkedHashSet<>(); Set<DbgRegister> toRead = new LinkedHashSet<>();
for (String regname : names) { for (String regname : names) {
DbgModelTargetRegister reg = registersByName.get(regname); DbgModelTargetRegister reg = registersByName.get(regname);
@ -109,11 +115,11 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
//throw new DebuggerRegisterAccessException("No such register: " + regname); //throw new DebuggerRegisterAccessException("No such register: " + regname);
} }
} }
thread.readRegisters(toRead).handle(seq::next); return thread.readRegisters(toRead);
}, TypeSpec.map(DbgRegister.class, BigInteger.class)).then((vals, seq) -> { }).thenApply(vals -> {
Map<String, byte[]> result = new LinkedHashMap<>(); Map<String, byte[]> result = new LinkedHashMap<>();
for (DbgRegister dbgReg : vals.keySet()) { for (DbgRegister dbgReg : vals.keySet()) {
DbgModelTargetRegister reg = registersByNumber.get(dbgReg.getNumber()); DbgModelTargetRegister reg = getTargetRegister(dbgReg);
String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); String oldval = (String) reg.getCachedAttributes().get(VALUE_ATTRIBUTE_NAME);
BigInteger value = vals.get(dbgReg); BigInteger value = vals.get(dbgReg);
byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value); byte[] bytes = ConversionUtils.bigIntegerToBytes(dbgReg.getSize(), value);
@ -129,18 +135,17 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
reg.setModified(!value.toString(16).equals(oldval)); reg.setModified(!value.toString(16).equals(oldval));
} }
} }
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, result); this.values = result;
seq.exit(result); listeners.fire.registersUpdated(getProxy(), result);
}).finish(); return result;
}));
} }
@Override @Override
public CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) { public CompletableFuture<Void> writeRegistersNamed(Map<String, byte[]> values) {
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { return model.gateFuture(thread.listRegisters().thenCompose(regs -> {
thread.listRegisters().handle(seq::next); return requestElements(false);
}, TypeSpec.cls(DbgRegisterSet.class)).then((regs, seq) -> { }).thenCompose(__ -> {
fetchElements().handle(seq::nextIgnore);
}).then(seq -> {
Map<String, ? extends TargetObject> regs = getCachedElements(); Map<String, ? extends TargetObject> regs = getCachedElements();
Map<DbgRegister, BigInteger> toWrite = new LinkedHashMap<>(); Map<DbgRegister, BigInteger> toWrite = new LinkedHashMap<>();
for (Map.Entry<String, byte[]> ent : values.entrySet()) { for (Map.Entry<String, byte[]> ent : values.entrySet()) {
@ -152,32 +157,15 @@ public class DbgModelTargetRegisterContainerImpl extends DbgModelTargetObjectImp
BigInteger val = new BigInteger(1, ent.getValue()); BigInteger val = new BigInteger(1, ent.getValue());
toWrite.put(reg.getRegister(), val); 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 // TODO: Should probably filter only effective and normalized writes in the callback
}).then(seq -> { }).thenAccept(__ -> {
listeners.fire(TargetRegisterBankListener.class).registersUpdated(this, values); listeners.fire.registersUpdated(getProxy(), values);
seq.exit(); }));
}).finish();
} }
/* public Map<String, byte[]> getCachedRegisters() {
public void invalidateRegisterCaches() { return values;
listeners.fire.invalidateCacheRequested(this);
}
*/
@Override
public void onRunning() {
// NB: We don't want to do this apparently
//invalidateRegisterCaches();
setAccessible(false);
} }
@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.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = { // @TargetObjectSchemaInfo(name = "RegisterDescriptor", elements = {
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class) }, attributes = {
}, attributes = { // @TargetAttributeType(name = TargetRegister.CONTAINER_ATTRIBUTE_NAME, type = DbgModelTargetRegisterContainerImpl.class),
@TargetAttributeType( // @TargetAttributeType(type = Void.class) })
name = TargetRegister.CONTAINER_ATTRIBUTE_NAME, //
type = DbgModelTargetRegisterContainerImpl.class), //
@TargetAttributeType(type = Void.class) //
})
public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetRegister { implements DbgModelTargetRegister {
@ -56,6 +52,7 @@ public class DbgModelTargetRegisterImpl extends DbgModelTargetObjectImpl
public DbgModelTargetRegisterImpl(DbgModelTargetRegisterContainerAndBank registers, public DbgModelTargetRegisterImpl(DbgModelTargetRegisterContainerAndBank registers,
DbgRegister register) { DbgRegister register) {
super(registers.getModel(), registers, keyRegister(register), "Register"); super(registers.getModel(), registers, keyRegister(register), "Register");
this.getModel().addModelObject(register, this);
this.registers = registers; this.registers = registers;
this.register = register; 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.iface1.DbgModelSelectableObject;
import agent.dbgeng.model.iface2.DbgModelTargetConnector; import agent.dbgeng.model.iface2.DbgModelTargetConnector;
import agent.dbgeng.model.iface2.DbgModelTargetRoot; import agent.dbgeng.model.iface2.DbgModelTargetRoot;
import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec;
import ghidra.dbg.error.DebuggerUserException; import ghidra.dbg.error.DebuggerUserException;
import ghidra.dbg.target.*; import ghidra.dbg.target.*;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "Debugger", elements = {
name = "Debugger", @TargetElementType(type = Void.class) }, attributes = {
elements = { @TargetAttributeType(name = "Available", type = DbgModelTargetAvailableContainerImpl.class, required = true, fixed = true),
@TargetElementType(type = Void.class) @TargetAttributeType(name = "Connectors", type = DbgModelTargetConnectorContainerImpl.class, required = true, fixed = true),
}, @TargetAttributeType(name = "Sessions", type = DbgModelTargetSessionContainerImpl.class, required = true, fixed = true),
attributes = { @TargetAttributeType(type = Void.class) })
@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 public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
implements DbgModelTargetRoot { implements DbgModelTargetRoot {
@ -77,7 +58,7 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
connectors, // connectors, //
sessions // sessions //
), Map.of( // ), Map.of( //
ACCESSIBLE_ATTRIBUTE_NAME, true, // ACCESSIBLE_ATTRIBUTE_NAME, accessible, //
DISPLAY_ATTRIBUTE_NAME, "Debugger", // DISPLAY_ATTRIBUTE_NAME, "Debugger", //
FOCUS_ATTRIBUTE_NAME, this, // FOCUS_ATTRIBUTE_NAME, this, //
SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, DbgModelTargetProcessImpl.SUPPORTED_KINDS, // SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, DbgModelTargetProcessImpl.SUPPORTED_KINDS, //
@ -117,7 +98,6 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus // TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus //
), "Focus changed"); ), "Focus changed");
listeners.fire(TargetFocusScopeListener.class).focusChanged(this, sel);
} }
return doFire; return doFire;
} }
@ -125,20 +105,15 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
@Override @Override
public CompletableFuture<Void> launch(Map<String, ?> args) { public CompletableFuture<Void> launch(Map<String, ?> args) {
DbgModelTargetConnector targetConnector = connectors.getDefaultConnector(); DbgModelTargetConnector targetConnector = connectors.getDefaultConnector();
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { return model.gateFuture(targetConnector.launch(args)).exceptionally(exc -> {
targetConnector.launch(args).handle(seq::nextIgnore);
//getManager().launch(args).handle(seq::nextIgnore);
}).finish().exceptionally((exc) -> {
throw new DebuggerUserException("Launch failed for " + args); throw new DebuggerUserException("Launch failed for " + args);
}); });
} }
@Override @Override
public CompletableFuture<Void> attach(long pid) { public CompletableFuture<Void> attach(long pid) {
return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
DbgProcess process = new DbgProcessImpl(getManager()); DbgProcess process = new DbgProcessImpl(getManager());
process.attach(pid).handle(seq::nextIgnore); return model.gateFuture(process.attach(pid)).thenApply(__ -> null);
}).finish();
} }
@Override @Override

View file

@ -25,15 +25,15 @@ import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(
name = "SessionAttributes", name = "SessionAttributes",
elements = { // elements = {
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class)
}, },
attributes = { // attributes = {
@TargetAttributeType( @TargetAttributeType(
name = "Machine", name = "Machine",
type = DbgModelTargetSessionAttributesMachineImpl.class, type = DbgModelTargetSessionAttributesMachineImpl.class,
fixed = true), // fixed = true),
@TargetAttributeType(type = Void.class) // @TargetAttributeType(type = Void.class)
}) })
public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImpl public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSessionAttributes { implements DbgModelTargetSessionAttributes {
@ -51,7 +51,7 @@ public class DbgModelTargetSessionAttributesImpl extends DbgModelTargetObjectImp
ARCH_ATTRIBUTE_NAME, "x86_64", // ARCH_ATTRIBUTE_NAME, "x86_64", //
DEBUGGER_ATTRIBUTE_NAME, "dbgeng", // DEBUGGER_ATTRIBUTE_NAME, "dbgeng", //
OS_ATTRIBUTE_NAME, "Windows", // OS_ATTRIBUTE_NAME, "Windows", //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // ENDIAN_ATTRIBUTE_NAME, "little" //
), "Initialized"); ), "Initialized");
getManager().addEventsListener(this); getManager().addEventsListener(this);

View file

@ -30,15 +30,18 @@ import ghidra.async.AsyncUtils;
import ghidra.async.TypeSpec; import ghidra.async.TypeSpec;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo(name = "SessionAttributesMachine", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = Void.class) // name = "SessionAttributesMachine",
}, attributes = { // elements = {
@TargetAttributeType(name = "Arch", type = String.class), // @TargetElementType(type = Void.class)
@TargetAttributeType(name = "Debugger", type = String.class), // },
@TargetAttributeType(name = "OS", type = String.class), // attributes = {
@TargetAttributeType(name = "Mode", type = String.class), // @TargetAttributeType(name = "Arch", type = String.class),
@TargetAttributeType(type = Void.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 public class DbgModelTargetSessionAttributesMachineImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSessionAttributesMachine { implements DbgModelTargetSessionAttributesMachine {

View file

@ -23,20 +23,15 @@ import agent.dbgeng.dbgeng.DebugSessionId;
import agent.dbgeng.manager.DbgCause; import agent.dbgeng.manager.DbgCause;
import agent.dbgeng.manager.DbgSession; import agent.dbgeng.manager.DbgSession;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo(name = "SessionContainer", elements = { // @TargetObjectSchemaInfo(name = "SessionContainer", elements = {
@TargetElementType(type = DbgModelTargetSessionImpl.class) // @TargetElementType(type = DbgModelTargetSessionImpl.class) }, attributes = {
}, attributes = { // @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetAttributeType(type = Void.class) //
}, canonicalContainer = true)
public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSessionContainer { implements DbgModelTargetSessionContainer {
protected final Map<DebugSessionId, DbgModelTargetSession> sessionsById =
new WeakValueHashMap<>();
public DbgModelTargetSessionContainerImpl(DbgModelTargetRoot root) { public DbgModelTargetSessionContainerImpl(DbgModelTargetRoot root) {
super(root.getModel(), root, "Sessions", "SessionContainer"); super(root.getModel(), root, "Sessions", "SessionContainer");
@ -51,9 +46,9 @@ public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public void sessionRemoved(DebugSessionId sessionId, DbgCause cause) { public void sessionRemoved(DebugSessionId sessionId, DbgCause cause) {
synchronized (this) { //synchronized (this) {
sessionsById.remove(sessionId); // sessionsById.remove(sessionId);
} //}
changeElements(List.of( // changeElements(List.of( //
DbgModelTargetSessionImpl.indexSession(sessionId) // DbgModelTargetSessionImpl.indexSession(sessionId) //
), List.of(), Map.of(), "Removed"); ), List.of(), Map.of(), "Removed");
@ -61,8 +56,12 @@ public class DbgModelTargetSessionContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public synchronized DbgModelTargetSession getTargetSession(DbgSession session) { public synchronized DbgModelTargetSession getTargetSession(DbgSession session) {
DebugSessionId id = session.getId(); DbgModelImpl impl = (DbgModelImpl) model;
return sessionsById.computeIfAbsent(id, i -> new DbgModelTargetSessionImpl(this, session)); TargetObject modelObject = impl.getModelObject(session);
if (modelObject != null) {
return (DbgModelTargetSession) modelObject;
}
return new DbgModelTargetSessionImpl(this, session);
} }
@Override @Override

View file

@ -30,8 +30,7 @@ import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(
name = "Session", name = "Session",
elements = { elements = {
@TargetElementType(type = Void.class) @TargetElementType(type = Void.class) },
},
attributes = { attributes = {
@TargetAttributeType( @TargetAttributeType(
name = "Attributes", name = "Attributes",
@ -42,8 +41,7 @@ import ghidra.dbg.util.PathUtils;
type = DbgModelTargetProcessContainerImpl.class, type = DbgModelTargetProcessContainerImpl.class,
required = true, required = true,
fixed = true), fixed = true),
@TargetAttributeType(type = Void.class) @TargetAttributeType(type = Void.class) })
})
public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSession { implements DbgModelTargetSession {
@ -71,6 +69,7 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
public DbgModelTargetSessionImpl(DbgModelTargetSessionContainerImpl sessions, public DbgModelTargetSessionImpl(DbgModelTargetSessionContainerImpl sessions,
DbgSession session) { DbgSession session) {
super(sessions.getModel(), sessions, keySession(session), "Session"); super(sessions.getModel(), sessions, keySession(session), "Session");
this.getModel().addModelObject(session, this);
this.attributes = new DbgModelTargetSessionAttributesImpl(this); this.attributes = new DbgModelTargetSessionAttributesImpl(this);
this.processes = new DbgModelTargetProcessContainerImpl(this); this.processes = new DbgModelTargetProcessContainerImpl(this);
@ -79,10 +78,9 @@ public class DbgModelTargetSessionImpl extends DbgModelTargetObjectImpl
attributes, // attributes, //
processes // processes //
), Map.of( // ), Map.of( //
ACCESSIBLE_ATTRIBUTE_NAME, true, // ACCESSIBLE_ATTRIBUTE_NAME, accessible, //
PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, // PROMPT_ATTRIBUTE_NAME, DBG_PROMPT, //
STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "Initialized");
getManager().addEventsListener(this); getManager().addEventsListener(this);

View file

@ -19,61 +19,31 @@ import java.math.BigInteger;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import agent.dbgeng.manager.*; import agent.dbgeng.manager.*;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.async.AsyncUtils; import ghidra.dbg.target.TargetFocusScope;
import ghidra.async.TypeSpec;
import ghidra.dbg.DebugModelConventions;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "StackFrame", elements = {
name = "StackFrame", @TargetElementType(type = Void.class) }, attributes = {
elements = { @TargetAttributeType(name = DbgModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, type = String.class),
@TargetElementType(type = Void.class) @TargetAttributeType(name = DbgModelTargetStackFrame.FUNC_TABLE_ENTRY_ATTRIBUTE_NAME, type = String.class),
}, @TargetAttributeType(name = DbgModelTargetStackFrame.INST_OFFSET_ATTRIBUTE_NAME, type = String.class),
attributes = { @TargetAttributeType(name = DbgModelTargetStackFrame.FRAME_OFFSET_ATTRIBUTE_NAME, type = String.class),
@TargetAttributeType( @TargetAttributeType(name = DbgModelTargetStackFrame.RETURN_OFFSET_ATTRIBUTE_NAME, type = String.class),
name = DbgModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, @TargetAttributeType(name = DbgModelTargetStackFrame.STACK_OFFSET_ATTRIBUTE_NAME, type = String.class),
type = String.class), @TargetAttributeType(name = DbgModelTargetStackFrame.VIRTUAL_ATTRIBUTE_NAME, type = Boolean.class),
@TargetAttributeType( @TargetAttributeType(name = DbgModelTargetStackFrame.PARAM0_ATTRIBUTE_NAME, type = String.class),
name = DbgModelTargetStackFrame.FUNC_TABLE_ENTRY_ATTRIBUTE_NAME, @TargetAttributeType(name = DbgModelTargetStackFrame.PARAM1_ATTRIBUTE_NAME, type = String.class),
type = String.class), @TargetAttributeType(name = DbgModelTargetStackFrame.PARAM2_ATTRIBUTE_NAME, type = String.class),
@TargetAttributeType( @TargetAttributeType(name = DbgModelTargetStackFrame.PARAM3_ATTRIBUTE_NAME, type = String.class),
name = DbgModelTargetStackFrame.INST_OFFSET_ATTRIBUTE_NAME, @TargetAttributeType(type = Void.class) })
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 public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetStackFrame { implements DbgModelTargetStackFrame {
@ -102,6 +72,7 @@ public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
public DbgModelTargetStackFrameImpl(DbgModelTargetStack stack, DbgModelTargetThread thread, public DbgModelTargetStackFrameImpl(DbgModelTargetStack stack, DbgModelTargetThread thread,
DbgStackFrame frame) { DbgStackFrame frame) {
super(stack.getModel(), stack, keyFrame(frame), "StackFrame"); super(stack.getModel(), stack, keyFrame(frame), "StackFrame");
this.getModel().addModelObject(frame, this);
this.thread = thread; this.thread = thread;
this.pc = getModel().getAddressSpace("ram").getAddress(-1); this.pc = getModel().getAddressSpace("ram").getAddress(-1);
@ -125,13 +96,7 @@ public class DbgModelTargetStackFrameImpl extends DbgModelTargetObjectImpl
@Override @Override
public void threadSelected(DbgThread eventThread, DbgStackFrame eventFrame, DbgCause cause) { public void threadSelected(DbgThread eventThread, DbgStackFrame eventFrame, DbgCause cause) {
if (eventFrame != null && eventFrame.equals(frame)) { if (eventFrame != null && eventFrame.equals(frame)) {
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>(); ((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
.handle(seq::next);
}, scope).then(seq -> {
scope.get().setFocus(this);
}).finish();
} }
} }

View file

@ -24,20 +24,21 @@ import agent.dbgeng.manager.impl.DbgMinimalSymbol;
import agent.dbgeng.model.iface2.DbgModelTargetSymbolContainer; import agent.dbgeng.model.iface2.DbgModelTargetSymbolContainer;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap; import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode;
@TargetObjectSchemaInfo(name = "SymbolContainer", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = DbgModelTargetSymbolImpl.class) // name = "SymbolContainer",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = DbgModelTargetSymbolImpl.class) },
}, canonicalContainer = true) elementResync = ResyncMode.ONCE,
attributes = {
@TargetAttributeType(type = Void.class) },
canonicalContainer = true)
public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSymbolContainer { implements DbgModelTargetSymbolContainer {
protected final DbgModelTargetModuleImpl module; protected final DbgModelTargetModuleImpl module;
protected final Map<String, DbgModelTargetSymbolImpl> symbolsByName = new WeakValueHashMap<>();
public DbgModelTargetSymbolContainerImpl(DbgModelTargetModuleImpl module) { public DbgModelTargetSymbolContainerImpl(DbgModelTargetModuleImpl module) {
super(module.getModel(), module, "Symbols", "SymbolContainer"); super(module.getModel(), module, "Symbols", "SymbolContainer");
this.module = module; this.module = module;
@ -59,7 +60,11 @@ public class DbgModelTargetSymbolContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public synchronized DbgModelTargetSymbolImpl getTargetSymbol(DbgMinimalSymbol symbol) { public synchronized DbgModelTargetSymbolImpl getTargetSymbol(DbgMinimalSymbol symbol) {
return symbolsByName.computeIfAbsent(symbol.getName(), DbgModelImpl impl = (DbgModelImpl) model;
n -> new DbgModelTargetSymbolImpl(this, symbol)); 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.manager.impl.DbgMinimalSymbol;
import agent.dbgeng.model.iface2.DbgModelTargetSymbol; import agent.dbgeng.model.iface2.DbgModelTargetSymbol;
import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.TargetSymbol; import ghidra.dbg.target.TargetSymbol;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@TargetObjectSchemaInfo(name = "Symbol", elements = { // @TargetObjectSchemaInfo(name = "Symbol", elements = {
@TargetElementType(type = Void.class) // @TargetElementType(type = Void.class) }, attributes = {
}, attributes = { // @TargetAttributeType(name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, type = DbgModelTargetSymbolContainerImpl.class),
@TargetAttributeType( // @TargetAttributeType(name = TargetObject.VALUE_ATTRIBUTE_NAME, type = Address.class),
name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, // @TargetAttributeType(name = TargetSymbol.SIZE_ATTRIBUTE_NAME, type = long.class),
type = DbgModelTargetSymbolContainerImpl.class), // @TargetAttributeType(name = "Name", type = String.class),
@TargetAttributeType(type = Void.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 public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetSymbol { implements DbgModelTargetSymbol {
protected static String indexSymbol(DbgMinimalSymbol symbol) { protected static String indexSymbol(DbgMinimalSymbol symbol) {
@ -45,21 +48,25 @@ public class DbgModelTargetSymbolImpl extends DbgModelTargetObjectImpl
protected final boolean constant; protected final boolean constant;
protected final Address value; protected final Address value;
protected final int size; protected final long size;
public DbgModelTargetSymbolImpl(DbgModelTargetSymbolContainerImpl symbols, public DbgModelTargetSymbolImpl(DbgModelTargetSymbolContainerImpl symbols,
DbgMinimalSymbol symbol) { DbgMinimalSymbol symbol) {
super(symbols.getModel(), symbols, keySymbol(symbol), "Symbol"); super(symbols.getModel(), symbols, keySymbol(symbol), "Symbol");
this.getModel().addModelObject(symbol, this);
this.constant = false; this.constant = false;
this.value = symbols.getModel().getAddressSpace("ram").getAddress(symbol.getAddress()); this.value = symbols.getModel().getAddressSpace("ram").getAddress(symbol.getAddress());
this.size = 0; this.size = symbol.getSize();
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
// TODO: DATA_TYPE // TODO: DATA_TYPE
NAMESPACE_ATTRIBUTE_NAME, symbols, // NAMESPACE_ATTRIBUTE_NAME, symbols, //
VALUE_ATTRIBUTE_NAME, value, // VALUE_ATTRIBUTE_NAME, value, //
SIZE_ATTRIBUTE_NAME, size, // SIZE_ATTRIBUTE_NAME, size, //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED // "Name", symbol.getName(), //
"Size", size, //
"TypeId", symbol.getTypeId(), //
"Tag", symbol.getTag() //
), "Initialized"); ), "Initialized");
} }

View file

@ -26,26 +26,21 @@ import agent.dbgeng.manager.reason.*;
import agent.dbgeng.model.iface2.*; import agent.dbgeng.model.iface2.*;
import ghidra.dbg.target.TargetObject; import ghidra.dbg.target.TargetObject;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.util.datastruct.WeakValueHashMap;
@TargetObjectSchemaInfo(name = "ThreadContainer", elements = { // @TargetObjectSchemaInfo(name = "ThreadContainer", elements = {
@TargetElementType(type = DbgModelTargetThreadImpl.class) // @TargetElementType(type = DbgModelTargetThreadImpl.class) }, attributes = {
}, attributes = { // @TargetAttributeType(type = Void.class) }, canonicalContainer = true)
@TargetAttributeType(type = Void.class) //
}, canonicalContainer = true)
public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetThreadContainer { implements DbgModelTargetThreadContainer {
protected final DbgProcess process; protected final DbgProcess process;
protected final Map<DebugThreadId, DbgModelTargetThreadImpl> threadsById =
new WeakValueHashMap<>();
public DbgModelTargetThreadContainerImpl(DbgModelTargetProcessImpl process) { public DbgModelTargetThreadContainerImpl(DbgModelTargetProcessImpl process) {
super(process.getModel(), process, "Threads", "ThreadContainer"); super(process.getModel(), process, "Threads", "ThreadContainer");
this.process = process.process; this.process = process.process;
getManager().addEventsListener(this); getManager().addEventsListener(this);
requestElements(false);
} }
@Override @Override
@ -53,9 +48,8 @@ public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created"); changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created");
DbgModelTargetThread targetThread = getTargetThread(thread); DbgModelTargetThread targetThread = getTargetThread(thread);
changeElements(List.of(), List.of(targetThread), Map.of(), "Created"); changeElements(List.of(), List.of(targetThread), Map.of(), "Created");
targetThread.threadStateChanged(DbgState.STARTING, DbgReason.getReason(null)); targetThread.threadStateChangedSpecific(DbgState.STARTING, DbgReason.getReason(null));
getListeners().fire(TargetEventScopeListener.class) getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_CREATED,
.event(this, targetThread, TargetEventType.THREAD_CREATED,
"Thread " + thread.getId() + " started", List.of(targetThread)); "Thread " + thread.getId() + " started", List.of(targetThread));
} }
@ -63,24 +57,23 @@ public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause, public void threadStateChanged(DbgThread thread, DbgState state, DbgCause cause,
DbgReason reason) { DbgReason reason) {
DbgModelTargetThread targetThread = getTargetThread(thread); DbgModelTargetThread targetThread = getTargetThread(thread);
targetThread.threadStateChanged(state, reason);
TargetEventType eventType = getEventType(state, cause, reason); TargetEventType eventType = getEventType(state, cause, reason);
getListeners().fire(TargetEventScopeListener.class) getListeners().fire.event(getProxy(), targetThread, eventType,
.event(this, targetThread, eventType, "Thread " + thread.getId() + " state changed", "Thread " + thread.getId() + " state changed", List.of(targetThread));
List.of(targetThread)); targetThread.threadStateChangedSpecific(state, reason);
} }
@Override @Override
public void threadExited(DebugThreadId threadId) { public void threadExited(DebugThreadId threadId) {
DbgModelTargetThread targetThread = threadsById.get(threadId); DbgModelImpl impl = (DbgModelImpl) model;
DbgModelTargetThread targetThread = (DbgModelTargetThread) impl.getModelObject(threadId);
if (targetThread != null) { if (targetThread != null) {
getListeners().fire(TargetEventScopeListener.class) getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_EXITED,
.event(this, targetThread, TargetEventType.THREAD_EXITED,
"Thread " + threadId + " exited", List.of(targetThread)); "Thread " + threadId + " exited", List.of(targetThread));
} }
synchronized (this) { //synchronized (this) {
threadsById.remove(threadId); // threadsById.remove(threadId);
} //}
changeElements(List.of( // changeElements(List.of( //
DbgModelTargetThreadImpl.indexThread(threadId) // DbgModelTargetThreadImpl.indexThread(threadId) //
), List.of(), Map.of(), "Exited"); ), List.of(), Map.of(), "Exited");
@ -125,8 +118,12 @@ public class DbgModelTargetThreadContainerImpl extends DbgModelTargetObjectImpl
@Override @Override
public synchronized DbgModelTargetThread getTargetThread(DbgThread thread) { public synchronized DbgModelTargetThread getTargetThread(DbgThread thread) {
return threadsById.computeIfAbsent(thread.getId(), DbgModelImpl impl = (DbgModelImpl) model;
i -> new DbgModelTargetThreadImpl(this, (DbgModelTargetProcess) parent, thread)); 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.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import agent.dbgeng.dbgeng.DebugThreadId; import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.manager.*; import agent.dbgeng.manager.*;
@ -26,32 +25,17 @@ import agent.dbgeng.manager.cmd.DbgThreadSelectCommand;
import agent.dbgeng.manager.impl.DbgManagerImpl; import agent.dbgeng.manager.impl.DbgManagerImpl;
import agent.dbgeng.model.iface1.DbgModelTargetFocusScope; import agent.dbgeng.model.iface1.DbgModelTargetFocusScope;
import agent.dbgeng.model.iface2.*; 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.TargetEnvironment;
import ghidra.dbg.target.TargetFocusScope;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
import ghidra.dbg.util.PathUtils; import ghidra.dbg.util.PathUtils;
@TargetObjectSchemaInfo( @TargetObjectSchemaInfo(name = "Thread", elements = {
name = "Thread", @TargetElementType(type = Void.class) }, attributes = {
elements = { @TargetAttributeType(name = "Registers", type = DbgModelTargetRegisterContainerImpl.class, required = true, fixed = true),
@TargetElementType(type = Void.class) @TargetAttributeType(name = "Stack", type = DbgModelTargetStackImpl.class, required = true, fixed = true),
},
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(name = TargetEnvironment.ARCH_ATTRIBUTE_NAME, type = String.class),
@TargetAttributeType(type = Void.class) @TargetAttributeType(type = Void.class) })
})
public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetThread { implements DbgModelTargetThread {
@ -87,6 +71,8 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
public DbgModelTargetThreadImpl(DbgModelTargetThreadContainer threads, public DbgModelTargetThreadImpl(DbgModelTargetThreadContainer threads,
DbgModelTargetProcess process, DbgThread thread) { DbgModelTargetProcess process, DbgThread thread) {
super(threads.getModel(), threads, keyThread(thread), "Thread"); super(threads.getModel(), threads, keyThread(thread), "Thread");
this.getModel().addModelObject(thread, this);
this.getModel().addModelObject(thread.getId(), this);
this.process = process; this.process = process;
this.thread = thread; this.thread = thread;
@ -97,7 +83,7 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
registers, // registers, //
stack // stack //
), Map.of( // ), Map.of( //
ACCESSIBLE_ATTRIBUTE_NAME, false, // ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS //
), "Initialized"); ), "Initialized");
@ -118,18 +104,12 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
@Override @Override
public void threadSelected(DbgThread eventThread, DbgStackFrame frame, DbgCause cause) { public void threadSelected(DbgThread eventThread, DbgStackFrame frame, DbgCause cause) {
if (eventThread.equals(thread)) { if (eventThread.equals(thread)) {
AtomicReference<DbgModelTargetFocusScope> scope = new AtomicReference<>(); ((DbgModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this);
AsyncUtils.sequence(TypeSpec.VOID).then(seq -> {
DebugModelConventions.findSuitable(DbgModelTargetFocusScope.class, this)
.handle(seq::next);
}, scope).then(seq -> {
scope.get().setFocus(this);
}).finish();
} }
} }
@Override @Override
public void threadStateChanged(DbgState state, DbgReason reason) { public void threadStateChangedSpecific(DbgState state, DbgReason reason) {
TargetExecutionState targetState = convertState(state); TargetExecutionState targetState = convertState(state);
String executionType = thread.getExecutingProcessorType().description; String executionType = thread.getExecutingProcessorType().description;
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
@ -137,6 +117,7 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType // TargetEnvironment.ARCH_ATTRIBUTE_NAME, executionType //
), reason.desc()); ), reason.desc());
setExecutionState(targetState, reason.desc()); setExecutionState(targetState, reason.desc());
registers.threadStateChangedSpecific(state, reason);
} }
@Override @Override
@ -147,13 +128,13 @@ public class DbgModelTargetThreadImpl extends DbgModelTargetObjectImpl
case ADVANCE: // Why no exec-advance in GDB/MI? case ADVANCE: // Why no exec-advance in GDB/MI?
return thread.console("advance"); return thread.console("advance");
default: default:
return thread.step(convertToDbg(kind)); return model.gateFuture(thread.step(convertToDbg(kind)));
} }
} }
@Override @Override
public CompletableFuture<Void> step(Map<String, ?> args) { public CompletableFuture<Void> step(Map<String, ?> args) {
return thread.step(args); return model.gateFuture(thread.step(args));
} }
@Override @Override

View file

@ -27,11 +27,14 @@ import ghidra.dbg.target.TargetMethod.ParameterDescription;
import ghidra.dbg.target.TargetMethod.TargetParameterMap; import ghidra.dbg.target.TargetMethod.TargetParameterMap;
import ghidra.dbg.target.schema.*; import ghidra.dbg.target.schema.*;
@TargetObjectSchemaInfo(name = "TraceOrDumpConnector", elements = { // @TargetObjectSchemaInfo(
@TargetElementType(type = Void.class) // name = "TraceOrDumpConnector",
}, attributes = { // elements = {
@TargetAttributeType(type = Void.class) // @TargetElementType(type = Void.class)
}) },
attributes = {
@TargetAttributeType(type = Void.class)
})
public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObjectImpl public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObjectImpl
implements DbgModelTargetConnector { implements DbgModelTargetConnector {
@ -46,8 +49,7 @@ public class DbgModelTargetTraceOrDumpConnectorImpl extends DbgModelTargetObject
changeAttributes(List.of(), List.of(), Map.of( // changeAttributes(List.of(), List.of(), Map.of( //
DISPLAY_ATTRIBUTE_NAME, getDisplay(), // DISPLAY_ATTRIBUTE_NAME, getDisplay(), //
TargetMethod.PARAMETERS_ATTRIBUTE_NAME, TargetMethod.PARAMETERS_ATTRIBUTE_NAME,
paramDescs = TargetParameterMap.copyOf(computeParameters()), // paramDescs = TargetParameterMap.copyOf(computeParameters()) //
UPDATE_MODE_ATTRIBUTE_NAME, TargetUpdateMode.FIXED //
), "Initialized"); ), "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