mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-2623: Improve connect dialog and factory descriptions
This commit is contained in:
parent
5195aaebc1
commit
8dbf2341b2
30 changed files with 565 additions and 333 deletions
|
@ -23,15 +23,16 @@ import agent.dbgeng.model.impl.DbgModelImpl;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
@FactoryDescription(
|
||||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
brief = "MS dbgeng.dll (WinDbg)",
|
||||||
* may change if it proves stable, though, no?
|
htmlDetails = """
|
||||||
*/
|
Connect to the Microsoft Debug Engine.
|
||||||
@FactoryDescription( //
|
This is the same engine that powers WinDbg.
|
||||||
brief = "IN-VM MS dbgeng local debugger", //
|
This is best for most Windows userspace and kernel targets.
|
||||||
htmlDetails = "Launch a dbgeng session in this same JVM" //
|
Kernel debugging is still experimental.
|
||||||
)
|
This will access the native API, which may put Ghidra's JVM at risk.""")
|
||||||
public class DbgEngInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
public class DbgEngInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
protected String remote = "none"; // Require user to start server
|
protected String remote = "none"; // Require user to start server
|
||||||
|
@ -53,8 +54,18 @@ public class DbgEngInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
return System.getProperty("os.name").toLowerCase().contains("windows");
|
// TODO: Might instead look for the DLL
|
||||||
|
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAgentTransport() {
|
public String getAgentTransport() {
|
||||||
|
|
|
@ -19,14 +19,17 @@ import java.util.List;
|
||||||
|
|
||||||
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
@FactoryDescription( //
|
@FactoryDescription(
|
||||||
brief = "MS dbgeng.dll (WinDbg) local agent via GADP/TCP", //
|
brief = "MS dbgeng.dll (WinDbg) via GADP",
|
||||||
htmlDetails = "Launch a new agent using the Microsoft Debug Engine." //
|
htmlDetails = """
|
||||||
)
|
Connect to the Microsoft Debug Engine.
|
||||||
@ExtensionPointProperties(priority = 100)
|
This is the same engine that powers WinDbg.
|
||||||
public class DbgEngLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
This is best for most Windows userspace and kernel targets.
|
||||||
|
Kernel debugging is still experimental.
|
||||||
|
This will protect Ghidra's JVM by using a subprocess to access the native API.""")
|
||||||
|
public class DbgEngGadpDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
||||||
|
|
||||||
protected String remote = "none"; // Require user to start server
|
protected String remote = "none"; // Require user to start server
|
||||||
@FactoryOption("DebugConnect options (.server)")
|
@FactoryOption("DebugConnect options (.server)")
|
||||||
|
@ -39,9 +42,18 @@ public class DbgEngLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerMo
|
||||||
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
|
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
// TODO: Might instead look for the DLL
|
// TODO: Might instead look for the DLL
|
||||||
return System.getProperty("os.name").toLowerCase().contains("windows");
|
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAgentTransport() {
|
public String getAgentTransport() {
|
|
@ -16,7 +16,7 @@
|
||||||
package agent.dbgeng.model.gadp;
|
package agent.dbgeng.model.gadp;
|
||||||
|
|
||||||
import agent.dbgeng.dbgeng.DbgEngTest;
|
import agent.dbgeng.dbgeng.DbgEngTest;
|
||||||
import agent.dbgeng.gadp.DbgEngLocalDebuggerModelFactory;
|
import agent.dbgeng.gadp.DbgEngGadpDebuggerModelFactory;
|
||||||
import agent.dbgeng.model.AbstractDbgengModelHost;
|
import agent.dbgeng.model.AbstractDbgengModelHost;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
|
|
||||||
|
@ -24,6 +24,6 @@ public class GadpDbgengModelHost extends AbstractDbgengModelHost {
|
||||||
@Override
|
@Override
|
||||||
public DebuggerModelFactory getModelFactory() {
|
public DebuggerModelFactory getModelFactory() {
|
||||||
DbgEngTest.assumeDbgengDLLLoadable();
|
DbgEngTest.assumeDbgengDLLLoadable();
|
||||||
return new DbgEngLocalDebuggerModelFactory();
|
return new DbgEngGadpDebuggerModelFactory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,14 @@ import agent.dbgmodel.model.impl.DbgModel2Impl;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
@FactoryDescription(
|
||||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
brief = "MS dbgmodel.dll (WinDbg Preview)",
|
||||||
* may change if it proves stable, though, no?
|
htmlDetails = """
|
||||||
*/
|
Connect to the Microsoft Debug Model.
|
||||||
@FactoryDescription( //
|
This is the same engine that powers WinDbg 2.
|
||||||
brief = "IN-VM MS dbgmodel local debugger", //
|
This will access the native API, which may put Ghidra's JVM at risk.""")
|
||||||
htmlDetails = "Launch a dbgmodel session in this same JVM" //
|
|
||||||
)
|
|
||||||
public class DbgModelInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
public class DbgModelInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
protected String remote = "none"; // Require user to start server
|
protected String remote = "none"; // Require user to start server
|
||||||
|
@ -53,8 +52,18 @@ public class DbgModelInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
return System.getProperty("os.name").toLowerCase().contains("windows");
|
// TODO: Might instead look for the DLL
|
||||||
|
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 70;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAgentTransport() {
|
public String getAgentTransport() {
|
||||||
|
|
|
@ -15,16 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package agent.dbgmodel.gadp;
|
package agent.dbgmodel.gadp;
|
||||||
|
|
||||||
import agent.dbgeng.gadp.DbgEngLocalDebuggerModelFactory;
|
import agent.dbgeng.gadp.DbgEngGadpDebuggerModelFactory;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
@FactoryDescription( //
|
@FactoryDescription(
|
||||||
brief = "MS dbgmodel.dll (WinDbg 2) local agent via GADP/TCP", //
|
brief = "MS dbgmodel.dll (WinDbg Preview) via GADP/TCP",
|
||||||
htmlDetails = "Launch a new agent using the Microsoft Debug Model (best for WinDbg 2)." //
|
htmlDetails = """
|
||||||
)
|
Connect to the Microsoft Debug Model.
|
||||||
@ExtensionPointProperties(priority = 90)
|
This is the same engine that powers WinDbg 2.
|
||||||
public class DbgModelLocalDebuggerModelFactory extends DbgEngLocalDebuggerModelFactory {
|
This will protect Ghidra's JVM by using a subprocess to access the native API.""")
|
||||||
|
public class DbgModelGadpDebuggerModelFactory extends DbgEngGadpDebuggerModelFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getThreadName() {
|
protected String getThreadName() {
|
||||||
|
@ -35,4 +36,19 @@ public class DbgModelLocalDebuggerModelFactory extends DbgEngLocalDebuggerModelF
|
||||||
protected Class<?> getServerClass() {
|
protected Class<?> getServerClass() {
|
||||||
return DbgModelGadpServer.class;
|
return DbgModelGadpServer.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority(Program program) {
|
||||||
|
// TODO: Might instead look for the DLL
|
||||||
|
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 50;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,17 +21,14 @@ import agent.frida.model.impl.FridaModelImpl;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
@FactoryDescription(
|
||||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
brief = "PROTOTYPE: Frida",
|
||||||
* may change if it proves stable, though, no?
|
htmlDetails = """
|
||||||
*/
|
Connect to Frida.
|
||||||
@FactoryDescription( //
|
This is an experimental connector. Use at your own risk.
|
||||||
brief = "IN-VM Frida local debugger", //
|
This will access the native API, which may put Ghidra's JVM at risk.""")
|
||||||
htmlDetails = "Launch a Frida session in this same JVM" //
|
|
||||||
)
|
|
||||||
@ExtensionPointProperties(priority = 80)
|
|
||||||
public class FridaInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
public class FridaInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,9 +38,18 @@ public class FridaInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
String osname = System.getProperty("os.name");
|
String osname = System.getProperty("os.name").toLowerCase();
|
||||||
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
|
if (!(osname.contains("mac os x") || osname.contains("linux") ||
|
||||||
|
osname.contains("windows"))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.frida.gadp;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
||||||
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
@FactoryDescription(
|
||||||
|
brief = "PROTOTYPE: Frida via GADP",
|
||||||
|
htmlDetails = """
|
||||||
|
Connect to Frida.
|
||||||
|
This is an experimental connector. Use at your own risk.
|
||||||
|
This will protect Ghidra's JVM by using a subprocess to access the native API.""")
|
||||||
|
public class FridaGadpDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority(Program program) {
|
||||||
|
String osname = System.getProperty("os.name").toLowerCase();
|
||||||
|
if (!(osname.contains("mac os x") || osname.contains("linux") ||
|
||||||
|
osname.contains("windows"))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getThreadName() {
|
||||||
|
return "Local Frida Agent stdout";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<?> getServerClass() {
|
||||||
|
return FridaGadpServer.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeCommandLine(List<String> cmd) {
|
||||||
|
cmd.add(getServerClass().getCanonicalName());
|
||||||
|
cmd.addAll(List.of("-H", host));
|
||||||
|
cmd.addAll(List.of("-p", Integer.toString(port)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,78 +0,0 @@
|
||||||
/* ###
|
|
||||||
* 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.frida.gadp;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
|
||||||
|
|
||||||
@FactoryDescription( //
|
|
||||||
brief = "Frida local agent via GADP/TCP", //
|
|
||||||
htmlDetails = "Launch a new agent using Frida." //
|
|
||||||
)
|
|
||||||
@ExtensionPointProperties(priority = 100)
|
|
||||||
public class FridaLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
|
||||||
|
|
||||||
protected String remote = "none"; // Require user to start server
|
|
||||||
@FactoryOption("DebugConnect options (.server)")
|
|
||||||
public final Property<String> agentRemoteOption =
|
|
||||||
Property.fromAccessors(String.class, this::getAgentRemote, this::setAgentRemote);
|
|
||||||
|
|
||||||
protected String transport = "none"; // Require user to start server
|
|
||||||
@FactoryOption("Remote process server options (untested)")
|
|
||||||
public final Property<String> agentTransportOption =
|
|
||||||
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompatible() {
|
|
||||||
String osname = System.getProperty("os.name");
|
|
||||||
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAgentTransport() {
|
|
||||||
return transport;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgentTransport(String transport) {
|
|
||||||
this.transport = transport;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAgentRemote() {
|
|
||||||
return remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgentRemote(String remote) {
|
|
||||||
this.remote = remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getThreadName() {
|
|
||||||
return "Local Frida Agent stdout";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<?> getServerClass() {
|
|
||||||
return FridaGadpServer.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeCommandLine(List<String> cmd) {
|
|
||||||
cmd.add(getServerClass().getCanonicalName());
|
|
||||||
cmd.addAll(List.of("-H", host));
|
|
||||||
cmd.addAll(List.of("-p", Integer.toString(port)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,7 @@ package agent.frida.model.gadp;
|
||||||
|
|
||||||
import static org.junit.Assume.assumeFalse;
|
import static org.junit.Assume.assumeFalse;
|
||||||
|
|
||||||
import agent.frida.gadp.FridaLocalDebuggerModelFactory;
|
import agent.frida.gadp.FridaGadpDebuggerModelFactory;
|
||||||
import agent.frida.model.AbstractFridaModelHost;
|
import agent.frida.model.AbstractFridaModelHost;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
@ -26,6 +26,6 @@ class GadpFridaModelHost extends AbstractFridaModelHost {
|
||||||
@Override
|
@Override
|
||||||
public DebuggerModelFactory getModelFactory() {
|
public DebuggerModelFactory getModelFactory() {
|
||||||
assumeFalse("Not ready for CI", SystemUtilities.isInTestingBatchMode());
|
assumeFalse("Not ready for CI", SystemUtilities.isInTestingBatchMode());
|
||||||
return new FridaLocalDebuggerModelFactory();
|
return new FridaGadpDebuggerModelFactory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,16 @@ import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.dbg.util.ShellUtils;
|
import ghidra.dbg.util.ShellUtils;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
@FactoryDescription(
|
||||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
brief = "gdb",
|
||||||
* may change if it proves stable, though, no?
|
htmlDetails = """
|
||||||
*/
|
Connect to gdb.
|
||||||
@FactoryDescription( //
|
This is best for most Linux and Unix userspace targets, and many embedded targets.
|
||||||
brief = "IN-VM GNU gdb local debugger", //
|
It may also be used with gdbserver by connecting to gdb, then using <code>target remote
|
||||||
htmlDetails = "Launch a GDB session in this same JVM" //
|
...</code>.
|
||||||
)
|
This will access the native API, which may put Ghidra's JVM at risk.""")
|
||||||
public class GdbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
public class GdbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
private String gdbCmd = GdbManager.DEFAULT_GDB_CMD;
|
private String gdbCmd = GdbManager.DEFAULT_GDB_CMD;
|
||||||
|
@ -59,8 +60,17 @@ public class GdbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
return GdbCompatibility.INSTANCE.isCompatible(gdbCmd);
|
if (!GdbCompatibility.INSTANCE.isCompatible(gdbCmd)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGdbCommand() {
|
public String getGdbCommand() {
|
||||||
|
|
|
@ -24,10 +24,14 @@ import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ShellUtils;
|
import ghidra.dbg.util.ShellUtils;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
@FactoryDescription(
|
@FactoryDescription(
|
||||||
brief = "GNU gdb via SSH",
|
brief = "gdb via SSH",
|
||||||
htmlDetails = "Launch a GDB session over an SSH connection")
|
htmlDetails = """
|
||||||
|
Connect to gdb using SSH.
|
||||||
|
This is best for remote Linux and Unix userspace targets when gdb is installed on the
|
||||||
|
remote host.""")
|
||||||
public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory {
|
public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
private String gdbCmd = "/usr/bin/gdb";
|
private String gdbCmd = "/usr/bin/gdb";
|
||||||
|
@ -91,8 +95,14 @@ public class GdbOverSshDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
return true;
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 75;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGdbCommand() {
|
public String getGdbCommand() {
|
||||||
|
|
|
@ -22,14 +22,17 @@ import agent.gdb.manager.GdbManager;
|
||||||
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.dbg.util.ShellUtils;
|
import ghidra.dbg.util.ShellUtils;
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
@FactoryDescription( //
|
@FactoryDescription(
|
||||||
brief = "GNU gdb local agent via GADP/TCP", //
|
brief = "gdb via GADP",
|
||||||
htmlDetails = "Launch a new agent using GDB. This may start a new session or join an existing one." //
|
htmlDetails = """
|
||||||
)
|
Connect to gdb.
|
||||||
@ExtensionPointProperties(priority = 100)
|
This is best for most Linux and Unix userspace targets, and many embedded targets.
|
||||||
public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
This will protect Ghidra's JVM by using a subprocess to access the native API.
|
||||||
|
If you are using <b>gdbserver</b>, you must connect to gdb first (consider the non-GADP
|
||||||
|
connector), then use <code>target remote ...</code> to connect to your target.""")
|
||||||
|
public class GdbGadpDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
||||||
|
|
||||||
private String gdbCmd = GdbManager.DEFAULT_GDB_CMD;
|
private String gdbCmd = GdbManager.DEFAULT_GDB_CMD;
|
||||||
@FactoryOption("GDB launch command")
|
@FactoryOption("GDB launch command")
|
||||||
|
@ -44,9 +47,17 @@ public class GdbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModel
|
||||||
// TODO: newLine option?
|
// TODO: newLine option?
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
// TODO: Could potentially support GDB on Windows, but the pty thing would need porting.
|
if (!GdbCompatibility.INSTANCE.isCompatible(gdbCmd)) {
|
||||||
return GdbCompatibility.INSTANCE.isCompatible(gdbCmd);
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGdbCommand() {
|
public String getGdbCommand() {
|
|
@ -20,7 +20,7 @@ import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import agent.gdb.gadp.GdbLocalDebuggerModelFactory;
|
import agent.gdb.gadp.GdbGadpDebuggerModelFactory;
|
||||||
import agent.gdb.model.AbstractGdbModelHost;
|
import agent.gdb.model.AbstractGdbModelHost;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
|
@ -30,6 +30,6 @@ class GadpGdbModelHost extends AbstractGdbModelHost {
|
||||||
public DebuggerModelFactory getModelFactory() {
|
public DebuggerModelFactory getModelFactory() {
|
||||||
assumeFalse("Not ready for CI", SystemUtilities.isInTestingBatchMode());
|
assumeFalse("Not ready for CI", SystemUtilities.isInTestingBatchMode());
|
||||||
assumeTrue("GDB cannot be found", new File("/usr/bin/gdb").canExecute());
|
assumeTrue("GDB cannot be found", new File("/usr/bin/gdb").canExecute());
|
||||||
return new GdbLocalDebuggerModelFactory();
|
return new GdbGadpDebuggerModelFactory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,21 +21,16 @@ import agent.lldb.model.impl.LldbModelImpl;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
/**
|
@FactoryDescription(
|
||||||
* Note this is in the testing source because it's not meant to be shipped in the release.... That
|
brief = "lldb",
|
||||||
* may change if it proves stable, though, no?
|
htmlDetails = """
|
||||||
*/
|
Connect to lldb.
|
||||||
@FactoryDescription( //
|
This is best for most macOS and iOS targets, but supports many others.
|
||||||
brief = "IN-VM lldb local debugger", //
|
This will access the native API, which may put Ghidra's JVM at risk.""")
|
||||||
htmlDetails = "Launch a lldb session in this same JVM" //
|
|
||||||
)
|
|
||||||
@ExtensionPointProperties(priority = 80)
|
|
||||||
public class LldbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
public class LldbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
// TODO remoteTransport option?
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<? extends DebuggerObjectModel> build() {
|
public CompletableFuture<? extends DebuggerObjectModel> build() {
|
||||||
LldbModelImpl model = new LldbModelImpl();
|
LldbModelImpl model = new LldbModelImpl();
|
||||||
|
@ -43,9 +38,19 @@ public class LldbInJvmDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCompatible() {
|
public int getPriority(Program program) {
|
||||||
String osname = System.getProperty("os.name");
|
String osname = System.getProperty("os.name").toLowerCase();
|
||||||
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
|
if (!(osname.contains("mac os x") || osname.contains("linux") ||
|
||||||
|
osname.contains("windows"))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.lldb.gadp;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
||||||
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
|
@FactoryDescription(
|
||||||
|
brief = "lldb via GADP",
|
||||||
|
htmlDetails = """
|
||||||
|
Connect to lldb.
|
||||||
|
This is best for most macOS and iOS targets, but supports many others.
|
||||||
|
This will protect Ghidra's JVM by using a subprocess to access the native API.""")
|
||||||
|
public class LldbGadpDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority(Program program) {
|
||||||
|
String osname = System.getProperty("os.name").toLowerCase();
|
||||||
|
if (!(osname.contains("mac os x") || osname.contains("linux") ||
|
||||||
|
osname.contains("windows"))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (program != null) {
|
||||||
|
String exe = program.getExecutablePath();
|
||||||
|
if (exe == null || exe.isBlank()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getThreadName() {
|
||||||
|
return "Local LLDB Agent stdout";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<?> getServerClass() {
|
||||||
|
return LldbGadpServer.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void completeCommandLine(List<String> cmd) {
|
||||||
|
cmd.add(getServerClass().getCanonicalName());
|
||||||
|
cmd.addAll(List.of("-H", host));
|
||||||
|
cmd.addAll(List.of("-p", Integer.toString(port)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,78 +0,0 @@
|
||||||
/* ###
|
|
||||||
* 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.lldb.gadp;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
|
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
|
||||||
import ghidra.util.classfinder.ExtensionPointProperties;
|
|
||||||
|
|
||||||
@FactoryDescription( //
|
|
||||||
brief = "LLVM lldb local agent via GADP/TCP", //
|
|
||||||
htmlDetails = "Launch a new agent using LLVM's lldb." //
|
|
||||||
)
|
|
||||||
@ExtensionPointProperties(priority = 100)
|
|
||||||
public class LldbLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
|
|
||||||
|
|
||||||
protected String remote = "none"; // Require user to start server
|
|
||||||
@FactoryOption("DebugConnect options (.server)")
|
|
||||||
public final Property<String> agentRemoteOption =
|
|
||||||
Property.fromAccessors(String.class, this::getAgentRemote, this::setAgentRemote);
|
|
||||||
|
|
||||||
protected String transport = "none"; // Require user to start server
|
|
||||||
@FactoryOption("Remote process server options (untested)")
|
|
||||||
public final Property<String> agentTransportOption =
|
|
||||||
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCompatible() {
|
|
||||||
String osname = System.getProperty("os.name");
|
|
||||||
return osname.contains("Mac OS X") || osname.contains("Linux") || osname.contains("Windows");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAgentTransport() {
|
|
||||||
return transport;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgentTransport(String transport) {
|
|
||||||
this.transport = transport;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAgentRemote() {
|
|
||||||
return remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAgentRemote(String remote) {
|
|
||||||
this.remote = remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getThreadName() {
|
|
||||||
return "Local LLDB Agent stdout";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<?> getServerClass() {
|
|
||||||
return LldbGadpServer.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeCommandLine(List<String> cmd) {
|
|
||||||
cmd.add(getServerClass().getCanonicalName());
|
|
||||||
cmd.addAll(List.of("-H", host));
|
|
||||||
cmd.addAll(List.of("-p", Integer.toString(port)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,10 +26,12 @@ import ghidra.async.TypeSpec;
|
||||||
import ghidra.dbg.DebuggerModelFactory;
|
import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
|
||||||
@FactoryDescription( //
|
@FactoryDescription(
|
||||||
brief = "GADP connection over TCP", //
|
brief = "Ghidra debug agent (GADP)",
|
||||||
htmlDetails = "Connect to an optionally remote agent via GADP/TCP." //
|
htmlDetails = """
|
||||||
)
|
Connect to a Ghidra debug agent using GADP.
|
||||||
|
This is Ghidra's debugging protocol.
|
||||||
|
Use this if launching a (usually remote) agent manually.""")
|
||||||
public class GadpTcpDebuggerModelFactory implements DebuggerModelFactory {
|
public class GadpTcpDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
private String host = "localhost";
|
private String host = "localhost";
|
||||||
|
|
|
@ -22,10 +22,12 @@ import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.jdi.model.JdiModelImpl;
|
import ghidra.dbg.jdi.model.JdiModelImpl;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
|
|
||||||
@FactoryDescription( //
|
@FactoryDescription(
|
||||||
brief = "JDI debugger", //
|
brief = "PROTOTYPE: JDWP (Java or Dalvik)",
|
||||||
htmlDetails = "Debug a Java or Dalvik VM (supports JDWP)" //
|
htmlDetails = """
|
||||||
)
|
Connect to a Java or Dalvik VM via JDWP.
|
||||||
|
This is the same debugging protocol used by most Java IDEs.
|
||||||
|
Support for debugging Java and Dalvik is still experimental.""")
|
||||||
public class JdiDebuggerModelFactory implements DebuggerModelFactory {
|
public class JdiDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,10 +34,12 @@ import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||||
import ghidra.app.services.DebuggerModelService;
|
import ghidra.app.services.DebuggerModelService;
|
||||||
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.framework.plugintool.AutoService;
|
import ghidra.framework.plugintool.AutoService;
|
||||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
|
||||||
public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
||||||
|
|
||||||
|
@ -107,7 +109,9 @@ public class DebuggerTargetsProvider extends ComponentProviderAdapter {
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
// NB. Drop the future on the floor, because the UI will report issues.
|
// NB. Drop the future on the floor, because the UI will report issues.
|
||||||
// Cancellation should be ignored.
|
// Cancellation should be ignored.
|
||||||
modelService.showConnectDialog();
|
ProgramManager programManager = tool.getService(ProgramManager.class);
|
||||||
|
Program program = programManager == null ? null : programManager.getCurrentProgram();
|
||||||
|
modelService.showConnectDialog(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,7 +37,6 @@ public class DbgDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpi
|
||||||
protected List<String> getLauncherPath() {
|
protected List<String> getLauncherPath() {
|
||||||
return PathUtils.parse("");
|
return PathUtils.parse("");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class InVmDbgengDebuggerProgramLaunchOffer
|
protected class InVmDbgengDebuggerProgramLaunchOffer
|
||||||
|
@ -133,7 +132,7 @@ public class DbgDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpi
|
||||||
}
|
}
|
||||||
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
||||||
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
||||||
if (!factory.isCompatible()) {
|
if (!factory.isCompatible(program)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String clsName = factory.getClass().getName();
|
String clsName = factory.getClass().getName();
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class FridaDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchO
|
||||||
}
|
}
|
||||||
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
||||||
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
||||||
if (!factory.isCompatible()) {
|
if (!factory.isCompatible(program)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String clsName = factory.getClass().getName();
|
String clsName = factory.getClass().getName();
|
||||||
|
|
|
@ -139,7 +139,7 @@ public class GdbDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpi
|
||||||
}
|
}
|
||||||
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
||||||
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
||||||
if (!factory.isCompatible()) {
|
if (!factory.isCompatible(program)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String clsName = factory.getClass().getName();
|
String clsName = factory.getClass().getName();
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class LldbDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOp
|
||||||
}
|
}
|
||||||
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
List<DebuggerProgramLaunchOffer> offers = new ArrayList<>();
|
||||||
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
for (DebuggerModelFactory factory : service.getModelFactories()) {
|
||||||
if (!factory.isCompatible()) {
|
if (!factory.isCompatible(program)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String clsName = factory.getClass().getName();
|
String clsName = factory.getClass().getName();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
|
||||||
import org.apache.commons.collections4.BidiMap;
|
import org.apache.commons.collections4.BidiMap;
|
||||||
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
||||||
|
@ -40,13 +41,14 @@ import ghidra.dbg.DebuggerModelFactory;
|
||||||
import ghidra.dbg.DebuggerObjectModel;
|
import ghidra.dbg.DebuggerObjectModel;
|
||||||
import ghidra.dbg.util.ConfigurableFactory.Property;
|
import ghidra.dbg.util.ConfigurableFactory.Property;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.datastruct.CollectionChangeListener;
|
import ghidra.util.datastruct.CollectionChangeListener;
|
||||||
import ghidra.util.layout.PairLayout;
|
|
||||||
|
|
||||||
public class DebuggerConnectDialog extends DialogComponentProvider
|
public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
implements PropertyChangeListener {
|
implements PropertyChangeListener {
|
||||||
private static final String KEY_FACTORY_CLASSNAME = "factoryClassname";
|
private static final String KEY_CURRENT_FACTORY_CLASSNAME = "currentFactoryCls";
|
||||||
|
private static final String KEY_SUCCESS_FACTORY_CLASSNAME = "successFactoryCls";
|
||||||
private static final String HTML_BOLD_DESCRIPTION = "<html><b>Description:</b> ";
|
private static final String HTML_BOLD_DESCRIPTION = "<html><b>Description:</b> ";
|
||||||
|
|
||||||
protected class FactoriesChangedListener
|
protected class FactoriesChangedListener
|
||||||
|
@ -67,9 +69,40 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected record FactoryEntry(DebuggerModelFactory factory) {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return factory.getBrief();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected record PrioritizedFactory(FactoryEntry entry, int priority) {
|
||||||
|
public PrioritizedFactory(FactoryEntry ent, Program program) {
|
||||||
|
this(ent, ent.factory.getPriority(program));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum NameComparator implements Comparator<String> {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(String o1, String o2) {
|
||||||
|
boolean p1 = o1.startsWith("PROTOTYPE:");
|
||||||
|
boolean p2 = o2.startsWith("PROTOTYPE:");
|
||||||
|
if (p1 && !p2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!p1 && p2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return o1.toLowerCase().compareTo(o2.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DebuggerModelService modelService;
|
private DebuggerModelService modelService;
|
||||||
|
|
||||||
private DebuggerModelFactory factory;
|
private DebuggerModelFactory currentFactory;
|
||||||
|
private DebuggerModelFactory successFactory;
|
||||||
private final Map<DebuggerModelFactory, FactoryEntry> factories = new HashMap<>();
|
private final Map<DebuggerModelFactory, FactoryEntry> factories = new HashMap<>();
|
||||||
private FactoriesChangedListener listener = new FactoriesChangedListener();
|
private FactoriesChangedListener listener = new FactoriesChangedListener();
|
||||||
|
|
||||||
|
@ -81,26 +114,12 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
private final Map<Property<?>, Component> components = new LinkedHashMap<>();
|
private final Map<Property<?>, Component> components = new LinkedHashMap<>();
|
||||||
|
|
||||||
protected JLabel description;
|
protected JLabel description;
|
||||||
protected JPanel pairPanel;
|
protected JPanel gridPanel;
|
||||||
private PairLayout layout;
|
|
||||||
|
|
||||||
protected JButton connectButton;
|
protected JButton connectButton;
|
||||||
protected CompletableFuture<? extends DebuggerObjectModel> futureConnect;
|
protected CompletableFuture<? extends DebuggerObjectModel> futureConnect;
|
||||||
protected CompletableFuture<DebuggerObjectModel> result;
|
protected CompletableFuture<DebuggerObjectModel> result;
|
||||||
|
|
||||||
protected static class FactoryEntry {
|
|
||||||
DebuggerModelFactory factory;
|
|
||||||
|
|
||||||
public FactoryEntry(DebuggerModelFactory factory) {
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return factory.getBrief();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebuggerConnectDialog() {
|
public DebuggerConnectDialog() {
|
||||||
super(AbstractConnectAction.NAME, true, true, true, false);
|
super(AbstractConnectAction.NAME, true, true, true, false);
|
||||||
|
|
||||||
|
@ -126,7 +145,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
toAdd.add(entry);
|
toAdd.add(entry);
|
||||||
}
|
}
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
toAdd.sort(Comparator.comparing(FactoryEntry::toString));
|
toAdd.sort(Comparator.comparing(FactoryEntry::toString, NameComparator.INSTANCE));
|
||||||
for (FactoryEntry entry : toAdd) {
|
for (FactoryEntry entry : toAdd) {
|
||||||
dropdownModel.addElement(entry);
|
dropdownModel.addElement(entry);
|
||||||
}
|
}
|
||||||
|
@ -190,20 +209,20 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
JPanel inner = new JPanel(new BorderLayout());
|
JPanel inner = new JPanel(new BorderLayout());
|
||||||
description = new JLabel(HTML_BOLD_DESCRIPTION + "</html>");
|
description = new JLabel(HTML_BOLD_DESCRIPTION + "</html>");
|
||||||
description.setBorder(new EmptyBorder(10, 0, 10, 0));
|
description.setBorder(new EmptyBorder(10, 0, 10, 0));
|
||||||
|
description.setPreferredSize(new Dimension(400, 150));
|
||||||
inner.add(description);
|
inner.add(description);
|
||||||
topBox.add(inner);
|
topBox.add(inner);
|
||||||
|
|
||||||
panel.add(topBox, BorderLayout.NORTH);
|
panel.add(topBox, BorderLayout.NORTH);
|
||||||
|
|
||||||
layout = new PairLayout(5, 5);
|
gridPanel = new JPanel(new GridBagLayout());
|
||||||
pairPanel = new JPanel(layout);
|
|
||||||
|
|
||||||
JPanel centering = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
JPanel centering = new JPanel(new FlowLayout(FlowLayout.CENTER));
|
||||||
JScrollPane scrolling = new JScrollPane(centering, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
JScrollPane scrolling = new JScrollPane(centering, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
scrolling.setPreferredSize(new Dimension(100, 130));
|
scrolling.setPreferredSize(new Dimension(100, 200));
|
||||||
panel.add(scrolling, BorderLayout.CENTER);
|
panel.add(scrolling, BorderLayout.CENTER);
|
||||||
centering.add(pairPanel);
|
centering.add(gridPanel);
|
||||||
|
|
||||||
addWorkPanel(panel);
|
addWorkPanel(panel);
|
||||||
|
|
||||||
|
@ -219,13 +238,15 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
|
|
||||||
private void itemSelected(ItemEvent evt) {
|
private void itemSelected(ItemEvent evt) {
|
||||||
if (evt.getStateChange() == ItemEvent.DESELECTED) {
|
if (evt.getStateChange() == ItemEvent.DESELECTED) {
|
||||||
pairPanel.removeAll();
|
gridPanel.removeAll();
|
||||||
}
|
}
|
||||||
else if (evt.getStateChange() == ItemEvent.SELECTED) {
|
else if (evt.getStateChange() == ItemEvent.SELECTED) {
|
||||||
FactoryEntry ent = (FactoryEntry) evt.getItem();
|
FactoryEntry ent = (FactoryEntry) evt.getItem();
|
||||||
factory = ent.factory;
|
currentFactory = ent.factory;
|
||||||
populateOptions();
|
populateOptions();
|
||||||
//repack();
|
/**
|
||||||
|
* Don't repack here. It can shrink the dialog, which may not be what the user wants.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +260,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
setStatusText("Connecting...");
|
setStatusText("Connecting...");
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
futureConnect = factory.build();
|
futureConnect = currentFactory.build();
|
||||||
}
|
}
|
||||||
futureConnect.thenCompose(m -> m.fetchModelRoot()).thenAcceptAsync(r -> {
|
futureConnect.thenCompose(m -> m.fetchModelRoot()).thenAcceptAsync(r -> {
|
||||||
DebuggerObjectModel m = r.getModel();
|
DebuggerObjectModel m = r.getModel();
|
||||||
|
@ -268,6 +289,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
futureConnect = null;
|
futureConnect = null;
|
||||||
}
|
}
|
||||||
|
successFactory = currentFactory;
|
||||||
connectButton.setEnabled(true);
|
connectButton.setEnabled(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -284,7 +306,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
protected synchronized CompletableFuture<DebuggerObjectModel> reset(
|
protected synchronized CompletableFuture<DebuggerObjectModel> reset(
|
||||||
DebuggerModelFactory factory) {
|
DebuggerModelFactory factory, Program program) {
|
||||||
if (factory != null) {
|
if (factory != null) {
|
||||||
synchronized (factories) {
|
synchronized (factories) {
|
||||||
dropdownModel.setSelectedItem(factories.get(factory));
|
dropdownModel.setSelectedItem(factories.get(factory));
|
||||||
|
@ -292,6 +314,7 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
dropdown.setEnabled(false);
|
dropdown.setEnabled(false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
selectCompatibleFactory(program);
|
||||||
dropdown.setEnabled(true);
|
dropdown.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,18 +334,42 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void populateOptions() {
|
protected void populateOptions() {
|
||||||
description.setText(
|
description.setText(HTML_BOLD_DESCRIPTION + currentFactory.getHtmlDetails());
|
||||||
HTML_BOLD_DESCRIPTION + HTMLUtilities.friendlyEncodeHTML(factory.getHtmlDetails()));
|
|
||||||
|
|
||||||
propertyEditors.clear();
|
propertyEditors.clear();
|
||||||
components.clear();
|
components.clear();
|
||||||
Map<String, Property<?>> optsMap = factory.getOptions();
|
Map<String, Property<?>> optsMap = currentFactory.getOptions();
|
||||||
//layout.setRows(Math.max(1, optsMap.size()));
|
gridPanel.removeAll();
|
||||||
pairPanel.removeAll();
|
GridBagConstraints constraints;
|
||||||
|
|
||||||
|
if (optsMap.isEmpty()) {
|
||||||
|
JLabel label =
|
||||||
|
new JLabel("<html>There are no configuration options for this connector.");
|
||||||
|
constraints = new GridBagConstraints();
|
||||||
|
gridPanel.add(label, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
for (Map.Entry<String, Property<?>> opt : optsMap.entrySet()) {
|
for (Map.Entry<String, Property<?>> opt : optsMap.entrySet()) {
|
||||||
Property<?> property = opt.getValue();
|
Property<?> property = opt.getValue();
|
||||||
JLabel label = new JLabel(opt.getKey());
|
JLabel label = new JLabel("<html>" + HTMLUtilities.escapeHTML(opt.getKey())) {
|
||||||
pairPanel.add(label);
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
View v = (View) getClientProperty("html");
|
||||||
|
if (v == null) {
|
||||||
|
return super.getPreferredSize();
|
||||||
|
}
|
||||||
|
v.setSize(200, 0);
|
||||||
|
float height = v.getPreferredSpan(View.Y_AXIS);
|
||||||
|
return new Dimension(200, (int) height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
constraints = new GridBagConstraints();
|
||||||
|
constraints.fill = GridBagConstraints.BOTH;
|
||||||
|
constraints.gridx = 0;
|
||||||
|
constraints.gridy = i;
|
||||||
|
constraints.insets = new Insets(i == 0 ? 0 : 5, 0, 0, 5);
|
||||||
|
gridPanel.add(label, constraints);
|
||||||
|
|
||||||
Class<?> type = property.getValueClass();
|
Class<?> type = property.getValueClass();
|
||||||
PropertyEditor editor = PropertyEditorManager.findEditor(type);
|
PropertyEditor editor = PropertyEditorManager.findEditor(type);
|
||||||
|
@ -331,11 +378,22 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
editor.setValue(property.getValue());
|
editor.setValue(property.getValue());
|
||||||
editor.addPropertyChangeListener(this);
|
editor.addPropertyChangeListener(this);
|
||||||
Component comp = MiscellaneousUtils.getEditorComponent(editor);
|
Component editorComponent = MiscellaneousUtils.getEditorComponent(editor);
|
||||||
pairPanel.add(comp);
|
if (editorComponent instanceof JTextField textField) {
|
||||||
|
textField.setColumns(13);
|
||||||
|
}
|
||||||
|
constraints = new GridBagConstraints();
|
||||||
|
constraints.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
constraints.anchor = GridBagConstraints.WEST;
|
||||||
|
constraints.gridx = 1;
|
||||||
|
constraints.gridy = i;
|
||||||
|
constraints.insets = new Insets(i == 0 ? 0 : 5, 0, 0, 0);
|
||||||
|
gridPanel.add(editorComponent, constraints);
|
||||||
|
|
||||||
propertyEditors.put(property, editor);
|
propertyEditors.put(property, editor);
|
||||||
components.put(property, comp);
|
components.put(property, editorComponent);
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,24 +408,75 @@ public class DebuggerConnectDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
if (factory != null) {
|
if (currentFactory != null) {
|
||||||
saveState.putString(KEY_FACTORY_CLASSNAME, factory.getClass().getName());
|
saveState.putString(KEY_CURRENT_FACTORY_CLASSNAME, currentFactory.getClass().getName());
|
||||||
|
}
|
||||||
|
if (successFactory != null) {
|
||||||
|
saveState.putString(KEY_SUCCESS_FACTORY_CLASSNAME, successFactory.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readConfigState(SaveState saveState) {
|
protected FactoryEntry getByName(String className) {
|
||||||
String factoryName = saveState.getString(KEY_FACTORY_CLASSNAME, null);
|
|
||||||
if (factoryName == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
synchronized (factories) {
|
synchronized (factories) {
|
||||||
for (Map.Entry<DebuggerModelFactory, FactoryEntry> ent : factories.entrySet()) {
|
for (FactoryEntry ent : factories.values()) {
|
||||||
String name = ent.getKey().getClass().getName();
|
String name = ent.factory.getClass().getName();
|
||||||
if (factoryName.equals(name)) {
|
if (className.equals(name)) {
|
||||||
factory = ent.getKey();
|
return ent;
|
||||||
dropdown.setSelectedItem(ent.getValue());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Collection<PrioritizedFactory> getByPriority(Program program) {
|
||||||
|
synchronized (factories) {
|
||||||
|
return factories.values()
|
||||||
|
.stream()
|
||||||
|
.map(e -> new PrioritizedFactory(e, program))
|
||||||
|
.sorted(Comparator.comparing(pf -> -pf.priority()))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PrioritizedFactory getFirstCompatibleByPriority(Program program) {
|
||||||
|
for (PrioritizedFactory pf : getByPriority(program)) {
|
||||||
|
if (pf.priority >= 0) {
|
||||||
|
return pf;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void selectCompatibleFactory(Program program) {
|
||||||
|
if (currentFactory != null && currentFactory.isCompatible(program)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (successFactory != null && successFactory.isCompatible(program)) {
|
||||||
|
currentFactory = successFactory;
|
||||||
|
synchronized (factories) {
|
||||||
|
dropdown.setSelectedItem(factories.get(successFactory));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PrioritizedFactory compat = getFirstCompatibleByPriority(program);
|
||||||
|
if (compat == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentFactory = compat.entry.factory;
|
||||||
|
dropdown.setSelectedItem(compat.entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readConfigState(SaveState saveState) {
|
||||||
|
String currentFactoryName = saveState.getString(KEY_CURRENT_FACTORY_CLASSNAME, null);
|
||||||
|
FactoryEntry restoreCurrent =
|
||||||
|
currentFactoryName == null ? null : getByName(currentFactoryName);
|
||||||
|
currentFactory = restoreCurrent == null ? null : restoreCurrent.factory;
|
||||||
|
dropdown.setSelectedItem(restoreCurrent);
|
||||||
|
|
||||||
|
String successFactoryName = saveState.getString(KEY_SUCCESS_FACTORY_CLASSNAME, null);
|
||||||
|
FactoryEntry restoreSuccess =
|
||||||
|
successFactoryName == null ? null : getByName(successFactoryName);
|
||||||
|
successFactory = restoreSuccess == null ? null : restoreSuccess.factory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,14 +641,24 @@ public class DebuggerModelServicePlugin extends Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CompletableFuture<DebuggerObjectModel> doShowConnectDialog(PluginTool tool,
|
protected CompletableFuture<DebuggerObjectModel> doShowConnectDialog(PluginTool tool,
|
||||||
DebuggerModelFactory factory) {
|
DebuggerModelFactory factory, Program program) {
|
||||||
CompletableFuture<DebuggerObjectModel> future = connectDialog.reset(factory);
|
CompletableFuture<DebuggerObjectModel> future = connectDialog.reset(factory, program);
|
||||||
tool.showDialog(connectDialog);
|
tool.showDialog(connectDialog);
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog() {
|
||||||
|
return doShowConnectDialog(tool, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(Program program) {
|
||||||
|
return doShowConnectDialog(tool, null, program);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
||||||
return doShowConnectDialog(tool, factory);
|
return doShowConnectDialog(tool, factory, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,9 +269,19 @@ public class DebuggerModelServiceProxyPlugin extends Plugin
|
||||||
closeAllModels();
|
closeAllModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog() {
|
||||||
|
return delegate.doShowConnectDialog(tool, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(Program program) {
|
||||||
|
return delegate.doShowConnectDialog(tool, null, program);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
public CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory) {
|
||||||
return delegate.doShowConnectDialog(tool, factory);
|
return delegate.doShowConnectDialog(tool, factory, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -353,14 +353,20 @@ public interface DebuggerModelService {
|
||||||
*
|
*
|
||||||
* @return a future which completes with the new connection, possibly cancelled
|
* @return a future which completes with the new connection, possibly cancelled
|
||||||
*/
|
*/
|
||||||
default CompletableFuture<DebuggerObjectModel> showConnectDialog() {
|
CompletableFuture<DebuggerObjectModel> showConnectDialog();
|
||||||
return showConnectDialog(null);
|
|
||||||
}
|
/**
|
||||||
|
* Prompt the user to create a new connection, hinting at the program to launch
|
||||||
|
*
|
||||||
|
* @param program the current program used to help select a default
|
||||||
|
* @return a future which completes with the new connection, possibly cancelled
|
||||||
|
*/
|
||||||
|
CompletableFuture<DebuggerObjectModel> showConnectDialog(Program program);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt the user to create a new connection, optionally fixing the factory
|
* Prompt the user to create a new connection, optionally fixing the factory
|
||||||
*
|
*
|
||||||
* @param factory the required factory, or null for user selection
|
* @param factory the required factory
|
||||||
* @return a future which completes with the new connection, possible cancelled
|
* @return a future which completes with the new connection, possible cancelled
|
||||||
*/
|
*/
|
||||||
CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory);
|
CompletableFuture<DebuggerObjectModel> showConnectDialog(DebuggerModelFactory factory);
|
||||||
|
|
|
@ -473,15 +473,15 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||||
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||||
|
|
||||||
FactoryEntry fe = (FactoryEntry) dialog.dropdownModel.getSelectedItem();
|
FactoryEntry fe = (FactoryEntry) dialog.dropdownModel.getSelectedItem();
|
||||||
assertEquals(mb.testFactory, fe.factory);
|
assertEquals(mb.testFactory, fe.factory());
|
||||||
|
|
||||||
assertEquals(TestDebuggerModelFactory.FAKE_DETAILS_HTML, dialog.description.getText());
|
assertEquals(TestDebuggerModelFactory.FAKE_DETAILS_HTML, dialog.description.getText());
|
||||||
|
|
||||||
Component[] components = dialog.pairPanel.getComponents();
|
Component[] components = dialog.gridPanel.getComponents();
|
||||||
|
|
||||||
assertTrue(components[0] instanceof JLabel);
|
assertTrue(components[0] instanceof JLabel);
|
||||||
JLabel label = (JLabel) components[0];
|
JLabel label = (JLabel) components[0];
|
||||||
assertEquals(TestDebuggerModelFactory.FAKE_OPTION_NAME, label.getText());
|
assertEquals("<html>" + TestDebuggerModelFactory.FAKE_OPTION_NAME, label.getText());
|
||||||
|
|
||||||
assertTrue(components[1] instanceof JTextField);
|
assertTrue(components[1] instanceof JTextField);
|
||||||
JTextField field = (JTextField) components[1];
|
JTextField field = (JTextField) components[1];
|
||||||
|
@ -518,7 +518,7 @@ public class DebuggerModelServiceTest extends AbstractGhidraHeadedDebuggerGUITes
|
||||||
DebuggerConnectDialog connectDialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
DebuggerConnectDialog connectDialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||||
|
|
||||||
FactoryEntry fe = (FactoryEntry) connectDialog.dropdownModel.getSelectedItem();
|
FactoryEntry fe = (FactoryEntry) connectDialog.dropdownModel.getSelectedItem();
|
||||||
assertEquals(mb.testFactory, fe.factory);
|
assertEquals(mb.testFactory, fe.factory());
|
||||||
|
|
||||||
pressButtonByText(connectDialog, AbstractConnectAction.NAME, true);
|
pressButtonByText(connectDialog, AbstractConnectAction.NAME, true);
|
||||||
// NOTE: testModel is null. Don't use #createTestModel(), which adds to service
|
// NOTE: testModel is null. Don't use #createTestModel(), which adds to service
|
||||||
|
|
|
@ -16,22 +16,52 @@
|
||||||
package ghidra.dbg;
|
package ghidra.dbg;
|
||||||
|
|
||||||
import ghidra.dbg.util.ConfigurableFactory;
|
import ghidra.dbg.util.ConfigurableFactory;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.classfinder.ExtensionPoint;
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A factory for a debugger model
|
* A factory for a debugger model
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This provides a discoverable means of creating a debug model.
|
* This provides a discoverable means of configuring and creating a debug model.
|
||||||
*/
|
*/
|
||||||
public interface DebuggerModelFactory
|
public interface DebuggerModelFactory
|
||||||
extends ExtensionPoint, ConfigurableFactory<DebuggerObjectModel> {
|
extends ExtensionPoint, ConfigurableFactory<DebuggerObjectModel> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this factory is compatible with the local system.
|
* Get the priority for selecting this factory by default for the given program
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* A default factory is selected when the current factory and the last successful factory are
|
||||||
|
* incompatible with the current program, or if this is the very first time connecting. Of those
|
||||||
|
* factories compatible with the current program, the one with the highest priority (larger
|
||||||
|
* numerical value) is selected. If none are compatible, then the current selection is left as
|
||||||
|
* is.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that negative priorities imply the factory is not compatible with the given program or
|
||||||
|
* local system.
|
||||||
|
*
|
||||||
|
* @param program the current program, or null
|
||||||
|
* @return the priority, higher values mean higher priority
|
||||||
|
*/
|
||||||
|
default int getPriority(Program program) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this factory is compatible with the local system and given program.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* <b>WARNING:</b> Implementations should not likely override this method. If one does, it must
|
||||||
|
* behave in the same manner as given in this default implementation: If
|
||||||
|
* {@link #getPriority(Program)} would return a non-negative result for the program, then this
|
||||||
|
* factory is compatible with that program. If negative, this factory is not compatible.
|
||||||
|
*
|
||||||
|
* @param program the current program, or null
|
||||||
* @return true if compatible
|
* @return true if compatible
|
||||||
*/
|
*/
|
||||||
default boolean isCompatible() {
|
default boolean isCompatible(Program program) {
|
||||||
return true;
|
return getPriority(program) >= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
|
||||||
public class TestDebuggerModelFactory implements DebuggerModelFactory {
|
public class TestDebuggerModelFactory implements DebuggerModelFactory {
|
||||||
public static final String FAKE_DETAILS = "A 'connection' to a fake debugger";
|
public static final String FAKE_DETAILS = "A 'connection' to a fake debugger";
|
||||||
public static final String FAKE_DETAILS_HTML =
|
public static final String FAKE_DETAILS_HTML =
|
||||||
"<html><b>Description:</b> A 'connection' to a fake debugger";
|
"<html><b>Description:</b> A 'connection' to a fake debugger";
|
||||||
public static final String FAKE_OPTION_NAME = "Test String";
|
public static final String FAKE_OPTION_NAME = "Test String";
|
||||||
public static final String FAKE_DEFAULT = "Default test string";
|
public static final String FAKE_DEFAULT = "Default test string";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue