GP-4609: Add FileChoosers to launcher dialog.
|
@ -9,12 +9,12 @@
|
||||||
::@menu-group local
|
::@menu-group local
|
||||||
::@icon icon.debugger
|
::@icon icon.debugger
|
||||||
::@help TraceRmiLauncherServicePlugin#dbgeng
|
::@help TraceRmiLauncherServicePlugin#dbgeng
|
||||||
::@env OPT_PYTHON_EXE:str="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
::@env OPT_PYTHON_EXE:file="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||||
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
||||||
::@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image"
|
::@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
|
||||||
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
||||||
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
||||||
::@env WINDBG_DIR:str="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
::@menu-group local
|
::@menu-group local
|
||||||
::@icon icon.debugger
|
::@icon icon.debugger
|
||||||
::@help TraceRmiLauncherServicePlugin#dbgeng_ttd
|
::@help TraceRmiLauncherServicePlugin#dbgeng_ttd
|
||||||
::@env OPT_PYTHON_EXE:str="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
::@env OPT_PYTHON_EXE:file="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||||
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
||||||
::@env OPT_TARGET_IMG:str="" "Trace (.run)" "A trace associated with the target binary executable"
|
::@env OPT_TARGET_IMG:file="" "Trace (.run)" "A trace associated with the target binary executable"
|
||||||
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
||||||
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
||||||
::@env OPT_DBGMODEL_PATH:str="" "Path to dbgeng.dll & \\ttd" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
::@env OPT_DBGMODEL_PATH:dir="" "Path to dbgeng.dll & \\ttd" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#gdb
|
#@help TraceRmiLauncherServicePlugin#gdb
|
||||||
#@enum StartCmd:str run start starti
|
#@enum StartCmd:str run start starti
|
||||||
#@arg :str "Image" "The target binary executable image"
|
#@arg :file "Image" "The target binary executable image"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."
|
#@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."
|
||||||
#@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
#@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
||||||
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||||
|
|
|
@ -26,12 +26,12 @@
|
||||||
#@menu-group cross
|
#@menu-group cross
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#gdb_qemu
|
#@help TraceRmiLauncherServicePlugin#gdb_qemu
|
||||||
#@arg :str "Image" "The target binary executable image"
|
#@arg :file "Image" "The target binary executable image"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
#@env GHIDRA_LANG_EXTTOOL_qemu:str="" "QEMU command" "The path to qemu for the target architecture."
|
#@env GHIDRA_LANG_EXTTOOL_qemu:file="" "QEMU command" "The path to qemu for the target architecture."
|
||||||
#@env QEMU_GDB:int=12345 "QEMU Port" "Port for gdb connection to qemu"
|
#@env QEMU_GDB:int=12345 "QEMU Port" "Port for gdb connection to qemu"
|
||||||
#@env OPT_EXTRA_QEMU_ARGS:str="" "Extra qemu arguments" "Extra arguments to pass to qemu. Use with care."
|
#@env OPT_EXTRA_QEMU_ARGS:str="" "Extra qemu arguments" "Extra arguments to pass to qemu. Use with care."
|
||||||
#@env OPT_GDB_PATH:str="gdb-multiarch" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb-multiarch" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_EXTRA_TTY:bool=false "QEMU TTY" "Provide a separate terminal emulator for the target."
|
#@env OPT_EXTRA_TTY:bool=false "QEMU TTY" "Provide a separate terminal emulator for the target."
|
||||||
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#@menu-group raw
|
#@menu-group raw
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#gdb_raw
|
#@help TraceRmiLauncherServicePlugin#gdb_raw
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_ARCH:str="i386:x86-64" "Architecture" "Target architecture"
|
#@env OPT_ARCH:str="i386:x86-64" "Architecture" "Target architecture"
|
||||||
|
|
||||||
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
|
#@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
|
||||||
#@env OPT_PORT:str="9999" "Port" "The host's listening port"
|
#@env OPT_PORT:str="9999" "Port" "The host's listening port"
|
||||||
#@env OPT_ARCH:str="" "Architecture (optional)" "Target architecture override"
|
#@env OPT_ARCH:str="" "Architecture (optional)" "Target architecture override"
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb on the local system. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb on the local system. Omit the full path to resolve using the system PATH."
|
||||||
|
|
||||||
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
||||||
then
|
then
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#@env OPT_REMOTE_PORT:int=12345 "Remote Trace RMI Port" "A free port on the remote end to receive and forward the Trace RMI connection."
|
#@env OPT_REMOTE_PORT:int=12345 "Remote Trace RMI Port" "A free port on the remote end to receive and forward the Trace RMI connection."
|
||||||
#@env OPT_EXTRA_SSH_ARGS:str="" "Extra ssh arguments" "Extra arguments to pass to ssh. Use with care."
|
#@env OPT_EXTRA_SSH_ARGS:str="" "Extra ssh arguments" "Extra arguments to pass to ssh. Use with care."
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb on the remote system. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb on the remote system. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_START_CMD:StartCmd="start" "Run command" "The gdb command to actually run the target."
|
#@env OPT_START_CMD:StartCmd="starti" "Run command" "The gdb command to actually run the target."
|
||||||
|
|
||||||
target_image="$1"
|
target_image="$1"
|
||||||
shift
|
shift
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#@env OPT_EXTRA_SSH_ARGS:str="" "Extra ssh arguments" "Extra arguments to pass to ssh. Use with care."
|
#@env OPT_EXTRA_SSH_ARGS:str="" "Extra ssh arguments" "Extra arguments to pass to ssh. Use with care."
|
||||||
#@env OPT_GDBSERVER_PATH:str="gdbserver" "gdbserver command (remote)" "The path to gdbserver on the remote system. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDBSERVER_PATH:str="gdbserver" "gdbserver command (remote)" "The path to gdbserver on the remote system. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_EXTRA_GDBSERVER_ARGS:str="" "Extra gdbserver arguments" "Extra arguments to pass to gdbserver. Use with care."
|
#@env OPT_EXTRA_GDBSERVER_ARGS:str="" "Extra gdbserver arguments" "Extra arguments to pass to gdbserver. Use with care."
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb on the local system. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb on the local system. Omit the full path to resolve using the system PATH."
|
||||||
|
|
||||||
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
||||||
then
|
then
|
||||||
|
|
|
@ -25,10 +25,10 @@
|
||||||
#@menu-group cross
|
#@menu-group cross
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#gdb_wine
|
#@help TraceRmiLauncherServicePlugin#gdb_wine
|
||||||
#@arg :str "Image" "The target binary executable image"
|
#@arg :file "Image" "The target binary executable image"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
#@env OPT_WINE_PATH:str="/usr/lib/wine/wine64" "Path to wine binary" "The path to the wine executable for your target architecture."
|
#@env OPT_WINE_PATH:file="/usr/lib/wine/wine64" "Path to wine binary" "The path to the wine executable for your target architecture."
|
||||||
#@env OPT_GDB_PATH:str="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
#@env OPT_GDB_PATH:file="gdb" "gdb command" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
#@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
||||||
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#lldb
|
#@help TraceRmiLauncherServicePlugin#lldb
|
||||||
#@enum StartCmd:str "process launch" "process launch --stop-at-entry"
|
#@enum StartCmd:str "process launch" "process launch --stop-at-entry"
|
||||||
#@arg :str "Image" "The target binary executable image"
|
#@arg :file "Image" "The target binary executable image"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
#@env OPT_LLDB_PATH:str="lldb" "lldb command" "The path to lldb. Omit the full path to resolve using the system PATH."
|
#@env OPT_LLDB_PATH:file="lldb" "lldb command" "The path to lldb. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_START_CMD:StartCmd="process launch" "Run command" "The lldb command to actually run the target."
|
#@env OPT_START_CMD:StartCmd="process launch" "Run command" "The lldb command to actually run the target."
|
||||||
#@env OPT_EXTRA_TTY:bool=false "Target TTY" "Provide a separate terminal emulator for the target."
|
#@env OPT_EXTRA_TTY:bool=false "Target TTY" "Provide a separate terminal emulator for the target."
|
||||||
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
|
#@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
|
||||||
#@env OPT_PORT:str="9999" "Port" "The host's listening port"
|
#@env OPT_PORT:str="9999" "Port" "The host's listening port"
|
||||||
#@env OPT_ARCH:str="" "Architecture" "Target architecture override"
|
#@env OPT_ARCH:str="" "Architecture" "Target architecture override"
|
||||||
#@env OPT_LLDB_PATH:str="lldb" "lldb command" "The path to lldb on the local system. Omit the full path to resolve using the system PATH."
|
#@env OPT_LLDB_PATH:file="lldb" "lldb command" "The path to lldb on the local system. Omit the full path to resolve using the system PATH."
|
||||||
|
|
||||||
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
||||||
then
|
then
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#@menu-group raw
|
#@menu-group raw
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#python_raw
|
#@help TraceRmiLauncherServicePlugin#python_raw
|
||||||
#@env OPT_PYTHON_EXE:str="python" "python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
#@env OPT_PYTHON_EXE:file="python" "python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_LANG:str="DATA:LE:64:default" "Ghidra Language" "The Ghidra LanguageID for the trace"
|
#@env OPT_LANG:str="DATA:LE:64:default" "Ghidra Language" "The Ghidra LanguageID for the trace"
|
||||||
#@env OPT_COMP:str="pointer64" "Ghidra Compiler" "The Ghidra CompilerSpecID for the trace"
|
#@env OPT_COMP:str="pointer64" "Ghidra Compiler" "The Ghidra CompilerSpecID for the trace"
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -42,6 +43,7 @@ import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
|
||||||
import ghidra.debug.api.tracermi.*;
|
import ghidra.debug.api.tracermi.*;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
|
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
|
||||||
|
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.InstructionIterator;
|
import ghidra.program.model.listing.InstructionIterator;
|
||||||
|
@ -263,6 +265,54 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
|
||||||
saveState(saveLauncherArgsToState(args, params));
|
saveState(saveLauncherArgsToState(args, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ImageParamSetter {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static ImageParamSetter get(ParameterDescription<?> param) {
|
||||||
|
if (param.type == String.class) {
|
||||||
|
return new StringImageParamSetter((ParameterDescription<String>) param);
|
||||||
|
}
|
||||||
|
if (param.type == PathIsFile.class) {
|
||||||
|
return new FileImageParamSetter((ParameterDescription<PathIsFile>) param);
|
||||||
|
}
|
||||||
|
Msg.warn(ImageParamSetter.class,
|
||||||
|
"'Image' parameter has unsupported type: " + param.type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setImage(Map<String, Object> map, Program program);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class StringImageParamSetter implements ImageParamSetter {
|
||||||
|
private final ParameterDescription<String> param;
|
||||||
|
|
||||||
|
public StringImageParamSetter(ParameterDescription<String> param) {
|
||||||
|
this.param = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(Map<String, Object> map, Program program) {
|
||||||
|
// str-type Image is a hint that the launcher is remote
|
||||||
|
String value = TraceRmiLauncherServicePlugin.getProgramPath(program, false);
|
||||||
|
param.set(map, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileImageParamSetter implements ImageParamSetter {
|
||||||
|
private final ParameterDescription<PathIsFile> param;
|
||||||
|
|
||||||
|
public FileImageParamSetter(ParameterDescription<PathIsFile> param) {
|
||||||
|
this.param = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(Map<String, Object> map, Program program) {
|
||||||
|
// file-type Image is a hint that the launcher is local
|
||||||
|
String str = TraceRmiLauncherServicePlugin.getProgramPath(program, true);
|
||||||
|
PathIsFile value = str == null ? null : new PathIsFile(Paths.get(str));
|
||||||
|
param.set(map, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the default launcher arguments
|
* Generate the default launcher arguments
|
||||||
*
|
*
|
||||||
|
@ -277,15 +327,13 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
|
||||||
protected Map<String, ?> generateDefaultLauncherArgs(
|
protected Map<String, ?> generateDefaultLauncherArgs(
|
||||||
Map<String, ParameterDescription<?>> params) {
|
Map<String, ParameterDescription<?>> params) {
|
||||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||||
ParameterDescription<String> paramImage = null;
|
ImageParamSetter imageSetter = null;
|
||||||
for (Entry<String, ParameterDescription<?>> entry : params.entrySet()) {
|
for (Entry<String, ParameterDescription<?>> entry : params.entrySet()) {
|
||||||
ParameterDescription<?> param = entry.getValue();
|
ParameterDescription<?> param = entry.getValue();
|
||||||
map.put(entry.getKey(), param.defaultValue);
|
map.put(entry.getKey(), param.defaultValue);
|
||||||
if (PARAM_DISPLAY_IMAGE.equals(param.display)) {
|
if (PARAM_DISPLAY_IMAGE.equals(param.display)) {
|
||||||
if (param.type != String.class) {
|
imageSetter = ImageParamSetter.get(param);
|
||||||
Msg.warn(this, "'Image' parameter has unexpected type: " + paramImage.type);
|
// May still be null if type is not supported
|
||||||
}
|
|
||||||
paramImage = (ParameterDescription<String>) param;
|
|
||||||
}
|
}
|
||||||
else if (param.name.startsWith(PREFIX_PARAM_EXTTOOL)) {
|
else if (param.name.startsWith(PREFIX_PARAM_EXTTOOL)) {
|
||||||
String tool = param.name.substring(PREFIX_PARAM_EXTTOOL.length());
|
String tool = param.name.substring(PREFIX_PARAM_EXTTOOL.length());
|
||||||
|
@ -297,11 +345,8 @@ public abstract class AbstractTraceRmiLaunchOffer implements TraceRmiLaunchOffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (paramImage != null && program != null) {
|
if (imageSetter != null && program != null) {
|
||||||
File imageFile = TraceRmiLauncherServicePlugin.getProgramPath(program);
|
imageSetter.setImage(map, program);
|
||||||
if (imageFile != null) {
|
|
||||||
paramImage.set(map, imageFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
@ -28,6 +30,8 @@ import generic.theme.Gui;
|
||||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||||
import ghidra.dbg.util.ShellUtils;
|
import ghidra.dbg.util.ShellUtils;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
|
import ghidra.framework.plugintool.AutoConfigState.PathIsDir;
|
||||||
|
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
@ -79,13 +83,11 @@ public abstract class ScriptAttributesParser {
|
||||||
protected interface OptType<T> {
|
protected interface OptType<T> {
|
||||||
static OptType<?> parse(Location loc, String typeName,
|
static OptType<?> parse(Location loc, String typeName,
|
||||||
Map<String, UserType<?>> userEnums) {
|
Map<String, UserType<?>> userEnums) {
|
||||||
OptType<?> type = switch (typeName) {
|
OptType<?> type = BaseType.parseNoErr(typeName);
|
||||||
case "str" -> BaseType.STRING;
|
|
||||||
case "int" -> BaseType.INT;
|
|
||||||
case "bool" -> BaseType.BOOL;
|
|
||||||
default -> userEnums.get(typeName);
|
|
||||||
};
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
|
type = userEnums.get(typeName);
|
||||||
|
}
|
||||||
|
if (type == null) { // still
|
||||||
Msg.error(ScriptAttributesParser.class,
|
Msg.error(ScriptAttributesParser.class,
|
||||||
"%s: Invalid type %s".formatted(loc, typeName));
|
"%s: Invalid type %s".formatted(loc, typeName));
|
||||||
return null;
|
return null;
|
||||||
|
@ -106,13 +108,20 @@ public abstract class ScriptAttributesParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface BaseType<T> extends OptType<T> {
|
protected interface BaseType<T> extends OptType<T> {
|
||||||
public static BaseType<?> parse(Location loc, String typeName) {
|
public static BaseType<?> parseNoErr(String typeName) {
|
||||||
BaseType<?> type = switch (typeName) {
|
return switch (typeName) {
|
||||||
case "str" -> BaseType.STRING;
|
case "str" -> BaseType.STRING;
|
||||||
case "int" -> BaseType.INT;
|
case "int" -> BaseType.INT;
|
||||||
case "bool" -> BaseType.BOOL;
|
case "bool" -> BaseType.BOOL;
|
||||||
|
case "path" -> BaseType.PATH;
|
||||||
|
case "dir" -> BaseType.DIR;
|
||||||
|
case "file" -> BaseType.FILE;
|
||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseType<?> parse(Location loc, String typeName) {
|
||||||
|
BaseType<?> type = parseNoErr(typeName);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
Msg.error(ScriptAttributesParser.class,
|
Msg.error(ScriptAttributesParser.class,
|
||||||
"%s: Invalid base type %s".formatted(loc, typeName));
|
"%s: Invalid base type %s".formatted(loc, typeName));
|
||||||
|
@ -179,6 +188,42 @@ public abstract class ScriptAttributesParser {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final BaseType<Path> PATH = new BaseType<>() {
|
||||||
|
@Override
|
||||||
|
public Class<Path> cls() {
|
||||||
|
return Path.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path decode(Location loc, String str) {
|
||||||
|
return Paths.get(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final BaseType<PathIsDir> DIR = new BaseType<>() {
|
||||||
|
@Override
|
||||||
|
public Class<PathIsDir> cls() {
|
||||||
|
return PathIsDir.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIsDir decode(Location loc, String str) {
|
||||||
|
return new PathIsDir(Paths.get(str));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final BaseType<PathIsFile> FILE = new BaseType<>() {
|
||||||
|
@Override
|
||||||
|
public Class<PathIsFile> cls() {
|
||||||
|
return PathIsFile.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIsFile decode(Location loc, String str) {
|
||||||
|
return new PathIsFile(Paths.get(str));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
default UserType<T> withCastChoices(List<?> choices) {
|
default UserType<T> withCastChoices(List<?> choices) {
|
||||||
return new UserType<>(this, choices.stream().map(cls()::cast).toList());
|
return new UserType<>(this, choices.stream().map(cls()::cast).toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,15 +150,23 @@ public class TraceRmiLauncherServicePlugin extends Plugin
|
||||||
return first.getPath();
|
return first.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getProgramPath(Program program) {
|
public static String getProgramPath(Program program, boolean isLocal) {
|
||||||
if (program == null) {
|
if (program == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
File exec = tryProgramPath(program.getExecutablePath());
|
File exec = tryProgramPath(program.getExecutablePath());
|
||||||
if (exec != null) {
|
if (exec != null) {
|
||||||
return exec;
|
return exec.getAbsolutePath();
|
||||||
}
|
}
|
||||||
return tryProgramPath(extractFirstFsrl(program));
|
String first = extractFirstFsrl(program);
|
||||||
|
if (!isLocal) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
exec = tryProgramPath(first);
|
||||||
|
if (exec != null) {
|
||||||
|
return exec.getAbsolutePath();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final ToolOptions options;
|
protected final ToolOptions options;
|
||||||
|
|
|
@ -18,12 +18,17 @@ package ghidra.app.plugin.core.debug.gui.objects.components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.*;
|
import java.beans.*;
|
||||||
|
import java.io.File;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -33,12 +38,16 @@ import org.apache.commons.text.StringEscapeUtils;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
|
import docking.options.editor.FileChooserEditor;
|
||||||
|
import docking.widgets.button.BrowseButton;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooser;
|
||||||
|
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||||
import ghidra.dbg.target.TargetMethod;
|
import ghidra.dbg.target.TargetMethod;
|
||||||
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
import ghidra.dbg.target.TargetMethod.ParameterDescription;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.AutoConfigState.ConfigStateField;
|
import ghidra.framework.plugintool.AutoConfigState.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
|
@ -63,8 +72,240 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FileChooserPanel extends JPanel {
|
||||||
|
private final static int NUMBER_OF_COLUMNS = 20;
|
||||||
|
|
||||||
|
private final JTextField textField = new JTextField(NUMBER_OF_COLUMNS);
|
||||||
|
private final JButton browseButton = new BrowseButton();
|
||||||
|
private final Runnable propertyChange;
|
||||||
|
|
||||||
|
private GhidraFileChooser fileChooser; // lazy
|
||||||
|
|
||||||
|
public FileChooserPanel(Runnable propertyChange) {
|
||||||
|
this.propertyChange = propertyChange;
|
||||||
|
|
||||||
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||||
|
add(textField);
|
||||||
|
add(Box.createHorizontalStrut(5));
|
||||||
|
add(browseButton);
|
||||||
|
setBorder(BorderFactory.createEmptyBorder());
|
||||||
|
|
||||||
|
textField.addActionListener(e -> propertyChange.run());
|
||||||
|
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
propertyChange.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
propertyChange.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
propertyChange.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browseButton.addActionListener(e -> displayFileChooser());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(File file) {
|
||||||
|
textField.setText(file == null ? "" : file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayFileChooser() {
|
||||||
|
if (fileChooser == null) {
|
||||||
|
fileChooser = createFileChooser();
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = textField.getText().trim();
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
File f = new File(path);
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
fileChooser.setCurrentDirectory(f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
File pf = f.getParentFile();
|
||||||
|
if (pf != null && pf.isDirectory()) {
|
||||||
|
fileChooser.setSelectedFile(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File chosen = fileChooser.getSelectedFile(true);
|
||||||
|
if (chosen != null) {
|
||||||
|
textField.setText(chosen.getAbsolutePath());
|
||||||
|
propertyChange.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getTitle() {
|
||||||
|
return "Choose Path";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GhidraFileChooserMode getSelectionMode() {
|
||||||
|
return GhidraFileChooserMode.FILES_AND_DIRECTORIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GhidraFileChooser createFileChooser() {
|
||||||
|
GhidraFileChooser chooser = new GhidraFileChooser(browseButton);
|
||||||
|
chooser.setTitle(getTitle());
|
||||||
|
chooser.setApproveButtonText(getTitle());
|
||||||
|
chooser.setFileSelectionMode(getSelectionMode());
|
||||||
|
// No way for script to specify filter....
|
||||||
|
|
||||||
|
return chooser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compared to {@link FileChooserEditor}, this does not require the user to enter a full path.
|
||||||
|
* Nor will it resolve file names against the working directory. It's just a text box with a
|
||||||
|
* file browser assist.
|
||||||
|
*/
|
||||||
|
public static class PathEditor extends PropertyEditorSupport {
|
||||||
|
private final FileChooserPanel panel = newChooserPanel();
|
||||||
|
|
||||||
|
protected FileChooserPanel newChooserPanel() {
|
||||||
|
return new FileChooserPanel(this::firePropertyChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsText() {
|
||||||
|
return panel.textField.getText().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
String text = panel.textField.getText().trim();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Paths.get(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAsText(String text) throws IllegalArgumentException {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
panel.textField.setText("");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panel.textField.setText(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
panel.textField.setText("");
|
||||||
|
}
|
||||||
|
else if (value instanceof String s) {
|
||||||
|
panel.textField.setText(s);
|
||||||
|
}
|
||||||
|
else if (value instanceof Path p) {
|
||||||
|
panel.textField.setText(p.toString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("value=" + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCustomEditor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getCustomEditor() {
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PathIsDirEditor extends PathEditor {
|
||||||
|
@Override
|
||||||
|
protected FileChooserPanel newChooserPanel() {
|
||||||
|
return new FileChooserPanel(this::firePropertyChange) {
|
||||||
|
@Override
|
||||||
|
protected String getTitle() {
|
||||||
|
return "Choose Directory";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GhidraFileChooserMode getSelectionMode() {
|
||||||
|
return GhidraFileChooserMode.DIRECTORIES_ONLY;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
Object value = super.getValue();
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Path p) {
|
||||||
|
return new PathIsDir(p);
|
||||||
|
}
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Object value) {
|
||||||
|
if (value instanceof PathIsDir dir) {
|
||||||
|
super.setValue(dir.path());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PathIsFileEditor extends PathEditor {
|
||||||
|
@Override
|
||||||
|
protected FileChooserPanel newChooserPanel() {
|
||||||
|
return new FileChooserPanel(this::firePropertyChange) {
|
||||||
|
@Override
|
||||||
|
protected String getTitle() {
|
||||||
|
return "Choose File";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GhidraFileChooserMode getSelectionMode() {
|
||||||
|
return GhidraFileChooserMode.FILES_ONLY;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
Object value = super.getValue();
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Path p) {
|
||||||
|
return new PathIsFile(p);
|
||||||
|
}
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Object value) {
|
||||||
|
if (value instanceof PathIsFile file) {
|
||||||
|
super.setValue(file.path());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PropertyEditorManager.registerEditor(BigInteger.class, BigIntEditor.class);
|
PropertyEditorManager.registerEditor(BigInteger.class, BigIntEditor.class);
|
||||||
|
PropertyEditorManager.registerEditor(Path.class, PathEditor.class);
|
||||||
|
PropertyEditorManager.registerEditor(PathIsDir.class, PathIsDirEditor.class);
|
||||||
|
PropertyEditorManager.registerEditor(PathIsFile.class, PathIsFileEditor.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
|
private static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
|
||||||
|
@ -335,7 +576,8 @@ public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
Object val = computeMemorizedValue(param);
|
Object val = computeMemorizedValue(param);
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
editor.setValue("");
|
editor.setValue("");
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
editor.setValue(val);
|
editor.setValue(val);
|
||||||
}
|
}
|
||||||
editor.addPropertyChangeListener(this);
|
editor.addPropertyChangeListener(this);
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.framework.plugintool;
|
package ghidra.framework.plugintool;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles.Lookup;
|
import java.lang.invoke.MethodHandles.Lookup;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
|
@ -273,6 +276,80 @@ public interface AutoConfigState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class FileConfigFieldCodec implements ConfigFieldCodec<File> {
|
||||||
|
public static final FileConfigFieldCodec INSTANCE = new FileConfigFieldCodec();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File read(SaveState state, String name, File current) {
|
||||||
|
return state.getFile(name, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(SaveState state, String name, File value) {
|
||||||
|
state.putFile(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PathConfigFieldCodec implements ConfigFieldCodec<Path> {
|
||||||
|
public static final PathConfigFieldCodec INSTANCE = new PathConfigFieldCodec();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path read(SaveState state, String name, Path current) {
|
||||||
|
return Paths.get(state.getString(name, current == null ? null : current.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(SaveState state, String name, Path value) {
|
||||||
|
state.putString(name, value == null ? null : value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record PathIsDir(Path path) {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return path.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PathIsDirConfigFieldCodec implements ConfigFieldCodec<PathIsDir> {
|
||||||
|
public static final PathIsDirConfigFieldCodec INSTANCE = new PathIsDirConfigFieldCodec();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIsDir read(SaveState state, String name, PathIsDir current) {
|
||||||
|
Path path = PathConfigFieldCodec.INSTANCE.read(state, name,
|
||||||
|
current == null ? null : current.path);
|
||||||
|
return path == null ? null : new PathIsDir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(SaveState state, String name, PathIsDir value) {
|
||||||
|
PathConfigFieldCodec.INSTANCE.write(state, name, value == null ? null : value.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record PathIsFile(Path path) {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return path.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PathIsFileConfigFieldCodec implements ConfigFieldCodec<PathIsFile> {
|
||||||
|
public static final PathIsFileConfigFieldCodec INSTANCE = new PathIsFileConfigFieldCodec();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PathIsFile read(SaveState state, String name, PathIsFile current) {
|
||||||
|
Path path = PathConfigFieldCodec.INSTANCE.read(state, name,
|
||||||
|
current == null ? null : current.path);
|
||||||
|
return path == null ? null : new PathIsFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(SaveState state, String name, PathIsFile value) {
|
||||||
|
PathConfigFieldCodec.INSTANCE.write(state, name, value == null ? null : value.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class EnumConfigFieldCodec implements ConfigFieldCodec<Enum<?>> {
|
static class EnumConfigFieldCodec implements ConfigFieldCodec<Enum<?>> {
|
||||||
public static final EnumConfigFieldCodec INSTANCE = new EnumConfigFieldCodec();
|
public static final EnumConfigFieldCodec INSTANCE = new EnumConfigFieldCodec();
|
||||||
|
|
||||||
|
@ -318,6 +395,10 @@ public interface AutoConfigState {
|
||||||
addCodec(String[].class, StringArrayConfigFieldCodec.INSTANCE);
|
addCodec(String[].class, StringArrayConfigFieldCodec.INSTANCE);
|
||||||
|
|
||||||
addCodec(BigInteger.class, BigIntegerConfigFieldCodec.INSTANCE);
|
addCodec(BigInteger.class, BigIntegerConfigFieldCodec.INSTANCE);
|
||||||
|
addCodec(File.class, FileConfigFieldCodec.INSTANCE);
|
||||||
|
addCodec(Path.class, PathConfigFieldCodec.INSTANCE);
|
||||||
|
addCodec(PathIsDir.class, PathIsDirConfigFieldCodec.INSTANCE);
|
||||||
|
addCodec(PathIsFile.class, PathIsFileConfigFieldCodec.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> void addCodec(Class<T> cls, ConfigFieldCodec<T> codec) {
|
private static <T> void addCodec(Class<T> cls, ConfigFieldCodec<T> codec) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -22,6 +23,7 @@ import org.junit.Test;
|
||||||
import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerMethodInvocationDialog;
|
import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerMethodInvocationDialog;
|
||||||
import ghidra.app.plugin.core.terminal.TerminalProvider;
|
import ghidra.app.plugin.core.terminal.TerminalProvider;
|
||||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||||
|
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||||
import ghidra.test.ToyProgramBuilder;
|
import ghidra.test.ToyProgramBuilder;
|
||||||
import help.screenshot.GhidraScreenShotGenerator;
|
import help.screenshot.GhidraScreenShotGenerator;
|
||||||
|
|
||||||
|
@ -49,7 +51,8 @@ public class TraceRmiLauncherServicePluginScreenShots extends GhidraScreenShotGe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCaptureGdbLauncher() throws Throwable {
|
public void testCaptureGdbLauncher() throws Throwable {
|
||||||
captureLauncherByTitle("gdb", Map.of("arg:1", "/home/user/demo"));
|
captureLauncherByTitle("gdb",
|
||||||
|
Map.of("arg:1", new PathIsFile(Paths.get("/home/user/demo"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -30,6 +31,7 @@ import ghidra.app.services.TraceRmiLauncherService;
|
||||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
|
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
|
||||||
import ghidra.framework.OperatingSystem;
|
import ghidra.framework.OperatingSystem;
|
||||||
|
import ghidra.framework.plugintool.AutoConfigState.PathIsFile;
|
||||||
import ghidra.util.task.ConsoleTaskMonitor;
|
import ghidra.util.task.ConsoleTaskMonitor;
|
||||||
|
|
||||||
public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebuggerTest {
|
public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebuggerTest {
|
||||||
|
@ -58,7 +60,7 @@ public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebug
|
||||||
public Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
|
public Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
|
||||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||||
Map<String, Object> args = new HashMap<>(arguments);
|
Map<String, Object> args = new HashMap<>(arguments);
|
||||||
args.put("arg:1", file);
|
args.put("arg:1", new PathIsFile(Paths.get(file)));
|
||||||
args.put("env:OPT_START_CMD", "starti");
|
args.put("env:OPT_START_CMD", "starti");
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +96,7 @@ public class TraceRmiLauncherServicePluginTest extends AbstractGhidraHeadedDebug
|
||||||
public Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
|
public Map<String, ?> configureLauncher(TraceRmiLaunchOffer offer,
|
||||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||||
Map<String, Object> args = new HashMap<>(arguments);
|
Map<String, Object> args = new HashMap<>(arguments);
|
||||||
args.put("env:OPT_TARGET_IMG", file);
|
args.put("env:OPT_TARGET_IMG", new PathIsFile(Paths.get(file)));
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 19 KiB |