mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
64e9b2bff5
@ -2,6 +2,11 @@
|
|||||||
##MODULE IP: JSch License
|
##MODULE IP: JSch License
|
||||||
Module.manifest||GHIDRA||||END|
|
Module.manifest||GHIDRA||||END|
|
||||||
data/debugger-launchers/local-gdb.bat||GHIDRA||||END|
|
data/debugger-launchers/local-gdb.bat||GHIDRA||||END|
|
||||||
|
data/debugger-launchers/qemu-gdb.bat||GHIDRA||||END|
|
||||||
|
data/debugger-launchers/raw-gdb.bat||GHIDRA||||END|
|
||||||
|
data/debugger-launchers/remote-gdb.bat||GHIDRA||||END|
|
||||||
|
data/debugger-launchers/ssh-gdb.bat||GHIDRA||||END|
|
||||||
|
data/debugger-launchers/ssh-gdbserver.bat||GHIDRA||||END|
|
||||||
data/scripts/fallback_info_proc_mappings.gdb||GHIDRA||||END|
|
data/scripts/fallback_info_proc_mappings.gdb||GHIDRA||||END|
|
||||||
data/scripts/fallback_maintenance_info_sections.gdb||GHIDRA||||END|
|
data/scripts/fallback_maintenance_info_sections.gdb||GHIDRA||||END|
|
||||||
data/scripts/getpid-linux-i386.gdb||GHIDRA||||END|
|
data/scripts/getpid-linux-i386.gdb||GHIDRA||||END|
|
||||||
|
@ -10,12 +10,10 @@
|
|||||||
::@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 :file "Image" "The target binary executable image"
|
::@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
|
||||||
::@args "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_GDB_PATH:file="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."
|
|
||||||
::@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||||
@ -30,24 +28,18 @@ IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
|||||||
)
|
)
|
||||||
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||||
|
|
||||||
set target_image=%1
|
|
||||||
shift
|
|
||||||
set target_args=%*
|
|
||||||
|
|
||||||
"%OPT_GDB_PATH%" ^
|
"%OPT_GDB_PATH%" ^
|
||||||
-q ^
|
-q ^
|
||||||
-ex "set pagination off" ^
|
-ex "set pagination off" ^
|
||||||
-ex "set confirm off" ^
|
-ex "set confirm off" ^
|
||||||
-ex "show version" ^
|
-ex "show version" ^
|
||||||
-ex "python import ghidragdb" ^
|
-ex "python import ghidragdb" ^
|
||||||
-ex "target exec %target_image%" ^
|
-ex "target exec %OPT_TARGET_IMG%" ^
|
||||||
-ex "set args %target_args%" ^
|
-ex "set args %OPT_TARGET_ARGS%" ^
|
||||||
-ex "set inferior-tty %TTY_TARGET%" ^
|
-ex "set inferior-tty %TTY_TARGET%" ^
|
||||||
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||||
-ex "ghidra trace start" ^
|
-ex "ghidra trace start" ^
|
||||||
-ex "ghidra trace sync-enable" ^
|
-ex "ghidra trace sync-enable" ^
|
||||||
-ex "%OPT_START_CMD%" ^
|
-ex "%OPT_START_CMD%" ^
|
||||||
-ex "set confirm on" ^
|
-ex "set confirm on" ^
|
||||||
-ex "set pagination on" ^
|
-ex "set pagination on"
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
::@title qemu + gdb
|
||||||
|
::@desc <html><body width="300px">
|
||||||
|
::@desc <h3>Launch with <tt>qemu</tt> and connect with <tt>gdb</tt></h3>
|
||||||
|
::@desc <p>
|
||||||
|
::@desc This will launch the target on the local machine using <tt>qemu</tt>.
|
||||||
|
::@desc Then in a second terminal, it will connect <tt>gdb</tt> to QEMU's GDBstub.
|
||||||
|
::@desc For setup instructions, press <b>F1</b>.
|
||||||
|
::@desc </p>
|
||||||
|
::@desc </body></html>
|
||||||
|
::@menu-group cross
|
||||||
|
::@icon icon.debugger
|
||||||
|
::@help TraceRmiLauncherServicePlugin#gdb_qemu
|
||||||
|
::@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 GHIDRA_LANG_EXTTOOL_qemu:file="" "QEMU command" "The path to qemu for the target architecture."
|
||||||
|
::@env QEMU_GDB:int=1234 "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_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."
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||||
|
IF EXIST %GHIDRA_HOME%\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||||
|
|
||||||
|
IF "%OPT_EXTRA_TTY%"=="true" (
|
||||||
|
start "qemu" "%GHIDRA_LANG_EXTTOOL_qemu%" %OPT_EXTRA_QEMU_ARGS% -gdb tcp::%QEMU_GDB% -S "%OPT_TARGET_IMG%" %OPT_TARGET_ARGS%
|
||||||
|
) ELSE (
|
||||||
|
start /B "qemu" "%GHIDRA_LANG_EXTTOOL_qemu%" %OPT_EXTRA_QEMU_ARGS% -gdb tcp::%QEMU_GDB% -S "%OPT_TARGET_IMG%" %OPT_TARGET_ARGS%
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Give QEMU a moment to open the socket
|
||||||
|
powershell -nop -c "& {sleep -m 100}"
|
||||||
|
|
||||||
|
"%OPT_GDB_PATH%" ^
|
||||||
|
-q ^
|
||||||
|
-ex "set pagination off" ^
|
||||||
|
-ex "set confirm off" ^
|
||||||
|
-ex "show version" ^
|
||||||
|
-ex "python import ghidragdb" ^
|
||||||
|
-ex "target exec '%OPT_TARGET_IMG%'" ^
|
||||||
|
-ex "set args %OPT_TARGET_ARGS%" ^
|
||||||
|
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||||
|
-ex "ghidra trace start" ^
|
||||||
|
-ex "ghidra trace sync-enable" ^
|
||||||
|
-ex "target remote localhost:%QEMU_GDB%" ^
|
||||||
|
-ex "set confirm on" ^
|
||||||
|
-ex "set pagination on"
|
@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
## ###
|
## ###
|
||||||
# IP: GHIDRA
|
# IP: GHIDRA
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
##
|
##
|
||||||
#@title qemu + gdb
|
#@title qemu + gdb
|
||||||
#@desc <html><body width="300px">
|
#@desc <html><body width="300px">
|
||||||
@ -29,7 +29,7 @@
|
|||||||
#@arg :file "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:file="" "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=1234 "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:file="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."
|
||||||
@ -52,9 +52,9 @@ target_image="$1"
|
|||||||
|
|
||||||
if [ -z "$TTY_TARGET" ]
|
if [ -z "$TTY_TARGET" ]
|
||||||
then
|
then
|
||||||
"$GHIDRA_LANG_EXTTOOL_qemu" $OPT_EXTRA_QEMU_ARGS $@ &
|
"$GHIDRA_LANG_EXTTOOL_qemu" -gdb tcp::$QEMU_GDB -S $OPT_EXTRA_QEMU_ARGS $@ &
|
||||||
else
|
else
|
||||||
"$GHIDRA_LANG_EXTTOOL_qemu" $OPT_EXTRA_QEMU_ARGS $@ <$TTY_TARGET >$TTY_TARGET 2>&1 &
|
"$GHIDRA_LANG_EXTTOOL_qemu" -gdb tcp::$QEMU_GDB -S $OPT_EXTRA_QEMU_ARGS $@ <$TTY_TARGET >$TTY_TARGET 2>&1 &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Give QEMU a moment to open the socket
|
# Give QEMU a moment to open the socket
|
||||||
@ -68,7 +68,6 @@ sleep 0.1
|
|||||||
-ex "python import ghidragdb" \
|
-ex "python import ghidragdb" \
|
||||||
-ex "file \"$target_image\"" \
|
-ex "file \"$target_image\"" \
|
||||||
-ex "set args $target_args" \
|
-ex "set args $target_args" \
|
||||||
-ex "set inferior-tty $TTY_TARGET" \
|
|
||||||
-ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
|
-ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
|
||||||
-ex "ghidra trace start" \
|
-ex "ghidra trace start" \
|
||||||
-ex "ghidra trace sync-enable" \
|
-ex "ghidra trace sync-enable" \
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
::@title raw gdb
|
||||||
|
::@no-image
|
||||||
|
::@desc <html><body width="300px">
|
||||||
|
::@desc <h3>Start <tt>gdb</tt></h3>
|
||||||
|
::@desc <p>
|
||||||
|
::@desc This will start <tt>gdb</tt> and connect to it.
|
||||||
|
::@desc It will not launch a target, so you can (must) set up your target manually.
|
||||||
|
::@desc For setup instructions, press <b>F1</b>.
|
||||||
|
::@desc </p>
|
||||||
|
::@desc </body></html>
|
||||||
|
::@menu-group raw
|
||||||
|
::@icon icon.debugger
|
||||||
|
::@help TraceRmiLauncherServicePlugin#gdb_raw
|
||||||
|
::@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"
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||||
|
IF EXIST %GHIDRA_HOME%\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||||
|
|
||||||
|
"%OPT_GDB_PATH%" ^
|
||||||
|
-q ^
|
||||||
|
-ex "set pagination off" ^
|
||||||
|
-ex "set confirm off" ^
|
||||||
|
-ex "show version" ^
|
||||||
|
-ex "python import ghidragdb" ^
|
||||||
|
-ex "set architecture %OPT_ARCH%" ^
|
||||||
|
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||||
|
-ex "ghidra trace start" ^
|
||||||
|
-ex "ghidra trace sync-enable" ^
|
||||||
|
-ex "set confirm on" ^
|
||||||
|
-ex "set pagination on"
|
@ -0,0 +1,53 @@
|
|||||||
|
::@title remote gdb
|
||||||
|
::@no-image
|
||||||
|
::@desc <html><body width="300px">
|
||||||
|
::@desc <h3>Launch with local <tt>gdb</tt> and connect to a stub (e.g., <tt>gdbserver</tt>)</h3>
|
||||||
|
::@desc <p>
|
||||||
|
::@desc This will start <tt>gdb</tt> on the local system and then use it to connect to the remote system.
|
||||||
|
::@desc For setup instructions, press <b>F1</b>.
|
||||||
|
::@desc </p>
|
||||||
|
::@desc </body></html>
|
||||||
|
::@menu-group remote
|
||||||
|
::@icon icon.debugger
|
||||||
|
::@help TraceRmiLauncherServicePlugin#gdb_remote
|
||||||
|
::@enum TargetType:str remote extended-remote
|
||||||
|
::@env OPT_TARGET_TYPE:TargetType="remote" "Target" "The type of remote target"
|
||||||
|
::@env OPT_HOST:str="localhost" "Host" "The hostname of the target"
|
||||||
|
::@env OPT_PORT:int=9999 "Port" "The host's listening port"
|
||||||
|
::@env OPT_ARCH:str="" "Architecture (optional)" "Target architecture override"
|
||||||
|
::@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."
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||||
|
IF EXIST %GHIDRA_HOME%\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||||
|
|
||||||
|
IF "%OPT_ARCH%"=="" (
|
||||||
|
set archcmd=
|
||||||
|
) ELSE (
|
||||||
|
set archcmd=-ex "set arch %OPT_ARCH%"
|
||||||
|
)
|
||||||
|
|
||||||
|
"%OPT_GDB_PATH%" ^
|
||||||
|
-q ^
|
||||||
|
-ex "set pagination off" ^
|
||||||
|
-ex "set confirm off" ^
|
||||||
|
-ex "show version" ^
|
||||||
|
-ex "python import ghidragdb" ^
|
||||||
|
%archcmd% ^
|
||||||
|
-ex "echo Connecting to %OPT_HOST%:%OPT_PORT%... " ^
|
||||||
|
-ex "target %OPT_TARGET_TYPE% %OPT_HOST%:%OPT_PORT%" ^
|
||||||
|
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||||
|
-ex "ghidra trace start" ^
|
||||||
|
-ex "ghidra trace sync-enable" ^
|
||||||
|
-ex "ghidra trace sync-synth-stopped" ^
|
||||||
|
-ex "set confirm on" ^
|
||||||
|
-ex "set pagination on"
|
@ -0,0 +1,39 @@
|
|||||||
|
::@timeout 60000
|
||||||
|
::@title gdb via ssh
|
||||||
|
::@desc <html><body width="300px">
|
||||||
|
::@desc <h3>Launch with <tt>gdb</tt> via <tt>ssh</tt></h3>
|
||||||
|
::@desc <p>
|
||||||
|
::@desc This will launch the target on a remote machine using <tt>gdb</tt> via <tt>ssh</tt>.
|
||||||
|
::@desc For setup instructions, press <b>F1</b>.
|
||||||
|
::@desc </p>
|
||||||
|
::@desc </body></html>
|
||||||
|
::@menu-group remote
|
||||||
|
::@icon icon.debugger
|
||||||
|
::@help TraceRmiLauncherServicePlugin#gdb_ssh
|
||||||
|
::@enum StartCmd:str run start starti
|
||||||
|
::@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image on the remote system"
|
||||||
|
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
||||||
|
::@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH."
|
||||||
|
::@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
||||||
|
::@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_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="starti" "Run command" "The gdb command to actually run the target."
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
set cmd=TERM='%TERM%' '%OPT_GDB_PATH%' ^
|
||||||
|
-q ^
|
||||||
|
-ex 'set pagination off' ^
|
||||||
|
-ex 'set confirm off' ^
|
||||||
|
-ex 'show version' ^
|
||||||
|
-ex 'python import ghidragdb' ^
|
||||||
|
-ex 'file \"%OPT_TARGET_IMG%\"' ^
|
||||||
|
-ex 'set args %OPT_TARGET_ARGS%' ^
|
||||||
|
-ex 'ghidra trace connect \"localhost:%OPT_REMOTE_PORT%\"' ^
|
||||||
|
-ex 'ghidra trace start' ^
|
||||||
|
-ex 'ghidra trace sync-enable' ^
|
||||||
|
-ex '%OPT_START_CMD%' ^
|
||||||
|
-ex 'set confirm on' ^
|
||||||
|
-ex 'set pagination on'
|
||||||
|
|
||||||
|
"%OPT_SSH_PATH%" "-R%OPT_REMOTE_PORT%:%GHIDRA_TRACE_RMI_ADDR%" -t %OPT_EXTRA_SSH_ARGS% "%OPT_HOST%" "%cmd%"
|
@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
## ###
|
## ###
|
||||||
# IP: GHIDRA
|
# IP: GHIDRA
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
##
|
##
|
||||||
#@timeout 60000
|
#@timeout 60000
|
||||||
#@title gdb via ssh
|
#@title gdb via ssh
|
||||||
@ -29,6 +29,7 @@
|
|||||||
#@enum StartCmd:str run start starti
|
#@enum StartCmd:str run start starti
|
||||||
#@arg :str "Image" "The target binary executable image on the remote system"
|
#@arg :str "Image" "The target binary executable image on the remote system"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
|
#@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
#@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
||||||
#@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."
|
||||||
@ -39,7 +40,7 @@ target_image="$1"
|
|||||||
shift
|
shift
|
||||||
target_args="$@"
|
target_args="$@"
|
||||||
|
|
||||||
ssh "-R$OPT_REMOTE_PORT:$GHIDRA_TRACE_RMI_ADDR" -t $OPT_EXTRA_SSH_ARGS "$OPT_HOST" "TERM='$TERM' '$OPT_GDB_PATH' \
|
"$OPT_SSH_PATH" "-R$OPT_REMOTE_PORT:$GHIDRA_TRACE_RMI_ADDR" -t $OPT_EXTRA_SSH_ARGS "$OPT_HOST" "TERM='$TERM' '$OPT_GDB_PATH' \
|
||||||
-q \
|
-q \
|
||||||
-ex 'set pagination off' \
|
-ex 'set pagination off' \
|
||||||
-ex 'set confirm off' \
|
-ex 'set confirm off' \
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
::@timeout 60000
|
||||||
|
::@title gdb + gdbserver via ssh
|
||||||
|
::@desc <html><body width="300px">
|
||||||
|
::@desc <h3>Launch with local <tt>gdb</tt> and <tt>gdbserver</tt> via <tt>ssh</tt></h3>
|
||||||
|
::@desc <p>
|
||||||
|
::@desc This will start <tt>gdb</tt> on the local system and then use it to connect and launch the target in <tt>gdbserver</tt> on the remote system via <tt>ssh</tt>.
|
||||||
|
::@desc For setup instructions, press <b>F1</b>.
|
||||||
|
::@desc </p>
|
||||||
|
::@desc </body></html>
|
||||||
|
::@menu-group remote
|
||||||
|
::@icon icon.debugger
|
||||||
|
::@help TraceRmiLauncherServicePlugin#gdb_gdbserver_ssh
|
||||||
|
::@env OPT_TARGET_IMG:str="" "Image" "The target binary executable image on the remote system"
|
||||||
|
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
||||||
|
::@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH."
|
||||||
|
::@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
||||||
|
::@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_EXTRA_GDBSERVER_ARGS:str="" "Extra gdbserver arguments" "Extra arguments to pass to gdbserver. Use with care."
|
||||||
|
::@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."
|
||||||
|
|
||||||
|
@echo off
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\pypkg\src
|
||||||
|
IF EXIST %GHIDRA_HOME%\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
IF EXIST %GHIDRA_HOME%\ghidra\.git (
|
||||||
|
set PYTHONPATH0=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-agent-gdb\build\pypkg\src
|
||||||
|
set PYTHONPATH1=%GHIDRA_HOME%\ghidra\Ghidra\Debug\Debugger-rmi-trace\build\pypkg\src
|
||||||
|
)
|
||||||
|
set PYTHONPATH=%PYTHONPATH1%;%PYTHONPATH0%;%PYTHONPATH%
|
||||||
|
|
||||||
|
"%OPT_GDB_PATH%" ^
|
||||||
|
-q ^
|
||||||
|
-ex "set pagination off" ^
|
||||||
|
-ex "set confirm off" ^
|
||||||
|
-ex "show version" ^
|
||||||
|
-ex "python import ghidragdb" ^
|
||||||
|
-ex "target remote | '%OPT_SSH_PATH%' %OPT_EXTRA_SSH_ARGS% '%OPT_HOST%' '%OPT_GDBSERVER_PATH%' %OPT_EXTRA_GDBSERVER_ARGS% - '%OPT_TARGET_IMG%' %OPT_TARGET_ARGS%" ^
|
||||||
|
-ex "ghidra trace connect '%GHIDRA_TRACE_RMI_ADDR%'" ^
|
||||||
|
-ex "ghidra trace start" ^
|
||||||
|
-ex "ghidra trace sync-enable" ^
|
||||||
|
-ex "ghidra trace sync-synth-stopped" ^
|
||||||
|
-ex "set confirm on" ^
|
||||||
|
-ex "set pagination on"
|
@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
## ###
|
## ###
|
||||||
# IP: GHIDRA
|
# IP: GHIDRA
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
##
|
##
|
||||||
#@timeout 60000
|
#@timeout 60000
|
||||||
#@title gdb + gdbserver via ssh
|
#@title gdb + gdbserver via ssh
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#@help TraceRmiLauncherServicePlugin#gdb_gdbserver_ssh
|
#@help TraceRmiLauncherServicePlugin#gdb_gdbserver_ssh
|
||||||
#@arg :str "Image" "The target binary executable image on the remote system"
|
#@arg :str "Image" "The target binary executable image on the remote system"
|
||||||
#@args "Arguments" "Command-line arguments to pass to the target"
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
|
#@env OPT_SSH_PATH:file="ssh" "ssh command" "The path to ssh on the local system. Omit the full path to resolve using the system PATH."
|
||||||
#@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
#@env OPT_HOST:str="localhost" "[User@]Host" "The hostname or user@host"
|
||||||
#@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."
|
||||||
@ -53,8 +54,7 @@ fi
|
|||||||
-ex "set confirm off" \
|
-ex "set confirm off" \
|
||||||
-ex "show version" \
|
-ex "show version" \
|
||||||
-ex "python import ghidragdb" \
|
-ex "python import ghidragdb" \
|
||||||
-ex "set inferior-tty $TTY_TARGET" \
|
-ex "target remote | '$OPT_SSH_PATH' $OPT_EXTRA_SSH_ARGS '$OPT_HOST' '$OPT_GDBSERVER_PATH' $OPT_EXTRA_GDBSERVER_ARGS - $@" \
|
||||||
-ex "target remote | ssh $OPT_EXTRA_SSH_ARGS '$OPT_HOST' '$OPT_GDBSERVER_PATH' $OPT_EXTRA_GDBSERVER_ARGS - $@" \
|
|
||||||
-ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
|
-ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
|
||||||
-ex "ghidra trace start" \
|
-ex "ghidra trace start" \
|
||||||
-ex "ghidra trace sync-enable" \
|
-ex "ghidra trace sync-enable" \
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.mapping;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
|
import ghidra.app.plugin.core.debug.disassemble.DisassemblyInject;
|
||||||
@ -27,7 +28,11 @@ import ghidra.framework.plugintool.PluginTool;
|
|||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Endian;
|
import ghidra.program.model.lang.Endian;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
|
import ghidra.trace.model.TraceAddressSnapRange;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
|
import ghidra.trace.model.listing.TraceInstruction;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryState;
|
||||||
import ghidra.trace.model.target.TraceObject;
|
import ghidra.trace.model.target.TraceObject;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
@ -61,7 +66,13 @@ public abstract class AbstractDebuggerPlatformMapper implements DebuggerPlatform
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isCancelSilently(Address start, long snap) {
|
protected boolean isCancelSilently(Address start, long snap) {
|
||||||
return trace.getCodeManager().instructions().getAt(snap, start) != null;
|
TraceInstruction exists = trace.getCodeManager().instructions().getAt(snap, start);
|
||||||
|
if (exists == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var states = trace.getMemoryManager().getStates(snap, exists.getRange());
|
||||||
|
return TraceMemoryOperations.isStateEntirely(exists.getRange(), states,
|
||||||
|
TraceMemoryState.KNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Collection<DisassemblyInject> getDisassemblyInjections(TracePlatform platform) {
|
protected Collection<DisassemblyInject> getDisassemblyInjections(TracePlatform platform) {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -43,7 +43,8 @@ import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
|||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.async.AsyncLazyMap;
|
import ghidra.async.AsyncLazyMap;
|
||||||
import ghidra.debug.api.control.ControlMode;
|
import ghidra.debug.api.control.ControlMode;
|
||||||
import ghidra.debug.api.emulation.*;
|
import ghidra.debug.api.emulation.DebuggerPcodeEmulatorFactory;
|
||||||
|
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||||
@ -781,7 +782,12 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
|||||||
TraceThread eventThread = key.time.getEventThread(key.trace);
|
TraceThread eventThread = key.time.getEventThread(key.trace);
|
||||||
be.ce.emulator().setSoftwareInterruptMode(SwiMode.IGNORE_STEP);
|
be.ce.emulator().setSoftwareInterruptMode(SwiMode.IGNORE_STEP);
|
||||||
RunResult result = scheduler.run(key.trace, eventThread, be.ce.emulator(), monitor);
|
RunResult result = scheduler.run(key.trace, eventThread, be.ce.emulator(), monitor);
|
||||||
key = new CacheKey(key.platform, key.time.advanced(result.schedule()));
|
if (result.schedule().hasSteps()) {
|
||||||
|
key = new CacheKey(key.platform, key.time.dropPSteps().advanced(result.schedule()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
key = new CacheKey(key.platform, key.time.advanced(result.schedule()));
|
||||||
|
}
|
||||||
Msg.info(this, "Stopped emulation at " + key.time);
|
Msg.info(this, "Stopped emulation at " + key.time);
|
||||||
TraceSnapshot destSnap = writeToScratch(key, be.ce);
|
TraceSnapshot destSnap = writeToScratch(key, be.ce);
|
||||||
cacheEmulator(key, be.ce);
|
cacheEmulator(key, be.ce);
|
||||||
|
@ -38,11 +38,11 @@ import ghidra.app.plugin.core.debug.service.platform.DebuggerPlatformServicePlug
|
|||||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
import ghidra.app.services.DebuggerTraceManagerService.ActivationCause;
|
||||||
|
import ghidra.debug.api.emulation.DebuggerPcodeMachine;
|
||||||
import ghidra.debug.api.platform.DebuggerPlatformMapper;
|
import ghidra.debug.api.platform.DebuggerPlatformMapper;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.pcode.emu.PcodeThread;
|
import ghidra.pcode.emu.PcodeThread;
|
||||||
import ghidra.pcode.exec.DecodePcodeExecutionException;
|
import ghidra.pcode.exec.*;
|
||||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
|
||||||
import ghidra.pcode.utils.Utils;
|
import ghidra.pcode.utils.Utils;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSpace;
|
import ghidra.program.model.address.AddressSpace;
|
||||||
@ -489,25 +489,180 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
|
|||||||
trace.getBreakpointManager()
|
trace.getBreakpointManager()
|
||||||
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrText, Set.of(thread),
|
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrText, Set.of(thread),
|
||||||
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
trace.getBreakpointManager()
|
TraceBreakpoint tb = trace.getBreakpointManager()
|
||||||
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
|
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
|
||||||
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
|
// Force "partial instruction"
|
||||||
|
tb.setEmuSleigh("""
|
||||||
|
r1 = 0xbeef;
|
||||||
|
emu_swi();
|
||||||
|
emu_exec_decoded();
|
||||||
|
""");
|
||||||
trace.getBreakpointManager()
|
trace.getBreakpointManager()
|
||||||
.addBreakpoint("Breakpoints[2]", Lifespan.nowOn(0), addrI2, Set.of(thread),
|
.addBreakpoint("Breakpoints[2]", Lifespan.nowOn(0), addrI2, Set.of(thread),
|
||||||
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertEquals(0, emulationPlugin.cache.size());
|
||||||
|
|
||||||
// This is already testing if the one set at the entry is ignored
|
// This is already testing if the one set at the entry is ignored
|
||||||
EmulationResult result1 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
EmulationResult result1 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
TraceSchedule.snap(0), monitor, Scheduler.oneThread(thread));
|
TraceSchedule.snap(0), monitor, Scheduler.oneThread(thread));
|
||||||
assertEquals(TraceSchedule.snap(0).steppedForward(thread, 1), result1.schedule());
|
assertEquals(TraceSchedule.snap(0)
|
||||||
|
.steppedForward(thread, 1)
|
||||||
|
.steppedPcodeForward(thread, 2),
|
||||||
|
result1.schedule());
|
||||||
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
|
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
|
||||||
|
|
||||||
|
// Save this for comparison later
|
||||||
|
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
|
||||||
|
|
||||||
// This will test if the one just hit gets ignored
|
// This will test if the one just hit gets ignored
|
||||||
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
result1.schedule(), monitor, Scheduler.oneThread(thread));
|
result1.schedule(), monitor, Scheduler.oneThread(thread));
|
||||||
assertEquals(TraceSchedule.snap(0).steppedForward(thread, 2), result2.schedule());
|
assertEquals(TraceSchedule.snap(0).steppedForward(thread, 2), result2.schedule());
|
||||||
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
|
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
|
||||||
|
|
||||||
|
// For efficiency, esp. after a long run, make sure we used the same emulator
|
||||||
|
assertSame(emu, Unique.assertOne(emulationPlugin.cache.values()).emulator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStepAfterExecutionBreakpoint() throws Exception {
|
||||||
|
createProgram();
|
||||||
|
intoProject(program);
|
||||||
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
|
Memory memory = program.getMemory();
|
||||||
|
Address addrText = addr(program, 0x00400000);
|
||||||
|
Address addrI1;
|
||||||
|
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||||
|
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
|
||||||
|
(byte) 0, TaskMonitor.DUMMY, false);
|
||||||
|
blockText.setExecute(true);
|
||||||
|
InstructionIterator ii = asm.assemble(addrText,
|
||||||
|
"mov r0, r0",
|
||||||
|
"mov r0, r1",
|
||||||
|
"mov r2, r0");
|
||||||
|
ii.next(); // addrText
|
||||||
|
addrI1 = ii.next().getMinAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
programManager.openProgram(program);
|
||||||
|
waitForSwing();
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addrText));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionEmulateProgram, true);
|
||||||
|
|
||||||
|
Trace trace = traceManager.getCurrentTrace();
|
||||||
|
assertNotNull(trace);
|
||||||
|
|
||||||
|
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||||
|
|
||||||
|
try (Transaction tx = trace.openTransaction("Add breakpoint")) {
|
||||||
|
trace.getBreakpointManager()
|
||||||
|
.addBreakpoint("Breakpoints[0]", Lifespan.nowOn(0), addrText, Set.of(thread),
|
||||||
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
|
TraceBreakpoint tb = trace.getBreakpointManager()
|
||||||
|
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
|
||||||
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
|
// Force "partial instruction"
|
||||||
|
tb.setEmuSleigh("""
|
||||||
|
r1 = 0xbeef;
|
||||||
|
emu_swi();
|
||||||
|
emu_exec_decoded();
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, emulationPlugin.cache.size());
|
||||||
|
|
||||||
|
// This is already testing if the one set at the entry is ignored
|
||||||
|
EmulationResult result1 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
|
TraceSchedule.snap(0), monitor, Scheduler.oneThread(thread));
|
||||||
|
assertEquals(TraceSchedule.snap(0)
|
||||||
|
.steppedForward(thread, 1)
|
||||||
|
.steppedPcodeForward(thread, 2),
|
||||||
|
result1.schedule());
|
||||||
|
assertTrue(result1.error() instanceof InterruptPcodeExecutionException);
|
||||||
|
|
||||||
|
// Save this for comparison later
|
||||||
|
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
|
||||||
|
|
||||||
|
// Now, step it forward to complete the instruction
|
||||||
|
emulationPlugin.emulate(trace.getPlatformManager().getHostPlatform(),
|
||||||
|
TraceSchedule.snap(0).steppedForward(thread, 2), monitor);
|
||||||
|
|
||||||
|
// For efficiency, esp. after a long run, make sure we used the same emulator
|
||||||
|
assertSame(emu, Unique.assertOne(emulationPlugin.cache.values()).emulator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStuckAtUserop() throws Exception {
|
||||||
|
createProgram();
|
||||||
|
intoProject(program);
|
||||||
|
Assembler asm = Assemblers.getAssembler(program);
|
||||||
|
Memory memory = program.getMemory();
|
||||||
|
Address addrText = addr(program, 0x00400000);
|
||||||
|
Address addrI1;
|
||||||
|
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||||
|
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
|
||||||
|
(byte) 0, TaskMonitor.DUMMY, false);
|
||||||
|
blockText.setExecute(true);
|
||||||
|
InstructionIterator ii = asm.assemble(addrText,
|
||||||
|
"mov r0, r0",
|
||||||
|
"mov r0, r1",
|
||||||
|
"mov r2, r0");
|
||||||
|
ii.next(); // addrText
|
||||||
|
addrI1 = ii.next().getMinAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
programManager.openProgram(program);
|
||||||
|
waitForSwing();
|
||||||
|
codeBrowser.goTo(new ProgramLocation(program, addrText));
|
||||||
|
waitForSwing();
|
||||||
|
|
||||||
|
performEnabledAction(codeBrowser.getProvider(), emulationPlugin.actionEmulateProgram, true);
|
||||||
|
|
||||||
|
Trace trace = traceManager.getCurrentTrace();
|
||||||
|
assertNotNull(trace);
|
||||||
|
|
||||||
|
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||||
|
|
||||||
|
try (Transaction tx = trace.openTransaction("Add breakpoint")) {
|
||||||
|
TraceBreakpoint tb = trace.getBreakpointManager()
|
||||||
|
.addBreakpoint("Breakpoints[1]", Lifespan.nowOn(0), addrI1, Set.of(thread),
|
||||||
|
Set.of(TraceBreakpointKind.SW_EXECUTE), true, "test");
|
||||||
|
// Force "partial instruction"
|
||||||
|
tb.setEmuSleigh("""
|
||||||
|
r1 = 0xbeef;
|
||||||
|
pcodeop_one(r1);
|
||||||
|
emu_exec_decoded();
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceSchedule stuck = TraceSchedule.snap(0)
|
||||||
|
.steppedForward(thread, 1)
|
||||||
|
.steppedPcodeForward(thread, 2);
|
||||||
|
|
||||||
|
assertEquals(0, emulationPlugin.cache.size());
|
||||||
|
|
||||||
|
// This is already testing if the one set at the entry is ignored
|
||||||
|
EmulationResult result1 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
|
TraceSchedule.snap(0), monitor, Scheduler.oneThread(thread));
|
||||||
|
assertEquals(stuck, result1.schedule());
|
||||||
|
assertTrue(result1.error() instanceof PcodeExecutionException);
|
||||||
|
|
||||||
|
// Save this for comparison later
|
||||||
|
DebuggerPcodeMachine<?> emu = Unique.assertOne(emulationPlugin.cache.values()).emulator();
|
||||||
|
|
||||||
|
// We shouldn't get any further
|
||||||
|
EmulationResult result2 = emulationPlugin.run(trace.getPlatformManager().getHostPlatform(),
|
||||||
|
result1.schedule(), monitor, Scheduler.oneThread(thread));
|
||||||
|
assertEquals(stuck, result2.schedule());
|
||||||
|
assertTrue(result1.error() instanceof PcodeExecutionException);
|
||||||
|
|
||||||
|
// For efficiency, esp. after a long run, make sure we used the same emulator
|
||||||
|
assertSame(emu, Unique.assertOne(emulationPlugin.cache.values()).emulator());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -817,6 +972,7 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerTe
|
|||||||
newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000));
|
newSnap, program, tb.addr(0x00400000), addr(program, 0x00400000));
|
||||||
newTraceThread.setName("MyThread");
|
newTraceThread.setName("MyThread");
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath());
|
PcodeThread<byte[]> newEmuThread = emulator.newThread(newTraceThread.getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -16,6 +16,7 @@
|
|||||||
package ghidra.trace.database.listing;
|
package ghidra.trace.database.listing;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@ -29,9 +30,13 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
|||||||
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
import ghidra.trace.database.context.DBTraceRegisterContextManager;
|
||||||
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
import ghidra.trace.database.context.DBTraceRegisterContextSpace;
|
||||||
import ghidra.trace.database.guest.InternalTracePlatform;
|
import ghidra.trace.database.guest.InternalTracePlatform;
|
||||||
|
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||||
|
import ghidra.trace.database.memory.DBTraceMemorySpace;
|
||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
import ghidra.trace.model.listing.*;
|
import ghidra.trace.model.listing.*;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryState;
|
||||||
import ghidra.trace.util.*;
|
import ghidra.trace.util.*;
|
||||||
import ghidra.util.LockHold;
|
import ghidra.util.LockHold;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
@ -92,7 +97,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
|
|
||||||
protected void truncateOrDelete(TraceInstruction exists) {
|
protected void truncateOrDelete(TraceInstruction exists) {
|
||||||
if (exists.getStartSnap() < lifespan.lmin()) {
|
if (exists.getStartSnap() < lifespan.lmin()) {
|
||||||
exists.setEndSnap(lifespan.lmin());
|
exists.setEndSnap(lifespan.lmin() - 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
exists.delete();
|
exists.delete();
|
||||||
@ -471,6 +476,15 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
conflict, conflictCodeUnit, overwrite);
|
conflict, conflictCodeUnit, overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isKnown(DBTraceMemorySpace ms, long snap, CodeUnit cu) {
|
||||||
|
if (ms == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AddressRange range = new AddressRangeImpl(cu.getMinAddress(), cu.getMaxAddress());
|
||||||
|
var states = ms.getStates(snap, range);
|
||||||
|
return TraceMemoryOperations.isStateEntirely(range, states, TraceMemoryState.KNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the intended locations for conflicts with existing units.
|
* Checks the intended locations for conflicts with existing units.
|
||||||
*
|
*
|
||||||
@ -486,6 +500,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
Set<Address> skipDelaySlots) {
|
Set<Address> skipDelaySlots) {
|
||||||
// NOTE: Partly derived from CodeManager#checkInstructionSet()
|
// NOTE: Partly derived from CodeManager#checkInstructionSet()
|
||||||
// Attempted to factor more fluently
|
// Attempted to factor more fluently
|
||||||
|
DBTraceMemoryManager mm = space.trace.getMemoryManager();
|
||||||
for (InstructionBlock block : instructionSet) {
|
for (InstructionBlock block : instructionSet) {
|
||||||
// If block contains a known error, record its address, and do not proceed beyond it
|
// If block contains a known error, record its address, and do not proceed beyond it
|
||||||
Address errorAddress = null;
|
Address errorAddress = null;
|
||||||
@ -519,6 +534,12 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
lastProtoInstr = protoInstr;
|
lastProtoInstr = protoInstr;
|
||||||
}
|
}
|
||||||
CodeUnit existsCu = overlap.getRight();
|
CodeUnit existsCu = overlap.getRight();
|
||||||
|
DBTraceMemorySpace ms =
|
||||||
|
mm.getMemorySpace(existsCu.getAddress().getAddressSpace(), false);
|
||||||
|
if (!isKnown(ms, startSnap, existsCu) && existsCu instanceof TraceCodeUnit tcu) {
|
||||||
|
tcu.delete();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int cmp = existsCu.getMinAddress().compareTo(protoInstr.getMinAddress());
|
int cmp = existsCu.getMinAddress().compareTo(protoInstr.getMinAddress());
|
||||||
boolean existsIsInstruction = (existsCu instanceof TraceInstruction);
|
boolean existsIsInstruction = (existsCu instanceof TraceInstruction);
|
||||||
if (cmp == 0 && existsIsInstruction) {
|
if (cmp == 0 && existsIsInstruction) {
|
||||||
@ -552,7 +573,7 @@ public class DBTraceInstructionsView extends AbstractBaseDBTraceDefinedUnitsView
|
|||||||
}
|
}
|
||||||
// NOTE: existsIsInstruction implies cmp != 0, so record as off-cut conflict
|
// NOTE: existsIsInstruction implies cmp != 0, so record as off-cut conflict
|
||||||
block.setCodeUnitConflict(existsCu.getAddress(), protoInstr.getAddress(),
|
block.setCodeUnitConflict(existsCu.getAddress(), protoInstr.getAddress(),
|
||||||
flowFromAddress, existsIsInstruction, existsIsInstruction);
|
flowFromAddress, existsIsInstruction, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -39,13 +39,17 @@ public class DBTraceAddressSnapRangePropertyMapAddressSetView<T> extends Abstrac
|
|||||||
private final Predicate<? super T> predicate;
|
private final Predicate<? super T> predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO Document me
|
* Construct an {@link AddressSetView} based on the given map of entries and predicate.
|
||||||
*
|
*
|
||||||
* The caller must reduce the map if only a certain range is desired.
|
* <p>
|
||||||
|
* The spatial map is a 2-dimensional collection of entries, but only the address dimension is
|
||||||
|
* considered. This set behaves as the union of address ranges for all entries whose values pass
|
||||||
|
* the predicate. Typically, the caller reduces the map first.
|
||||||
*
|
*
|
||||||
* @param lock
|
* @param space the address space of the given map
|
||||||
* @param map
|
* @param lock a lock to ensure access to the underlying database is synchronized
|
||||||
* @param predicate
|
* @param map the map whose entries to test
|
||||||
|
* @param predicate the predicate for testing entry values
|
||||||
*/
|
*/
|
||||||
public DBTraceAddressSnapRangePropertyMapAddressSetView(AddressSpace space, ReadWriteLock lock,
|
public DBTraceAddressSnapRangePropertyMapAddressSetView(AddressSpace space, ReadWriteLock lock,
|
||||||
SpatialMap<TraceAddressSnapRange, T, TraceAddressSnapRangeQuery> map,
|
SpatialMap<TraceAddressSnapRange, T, TraceAddressSnapRangeQuery> map,
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -515,6 +515,12 @@ public class DBTraceAddressSnapRangePropertyMapTree<T, DR extends AbstractDBTrac
|
|||||||
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
|
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TraceAddressSnapRangeQuery mostRecent(AddressRange range, Lifespan span) {
|
||||||
|
return intersecting(
|
||||||
|
new ImmutableTraceAddressSnapRange(range, span),
|
||||||
|
Rectangle2DDirection.TOPMOST, TraceAddressSnapRangeQuery::new);
|
||||||
|
}
|
||||||
|
|
||||||
public static TraceAddressSnapRangeQuery equalTo(TraceAddressSnapRange shape) {
|
public static TraceAddressSnapRangeQuery equalTo(TraceAddressSnapRange shape) {
|
||||||
return equalTo(shape, null, TraceAddressSnapRangeQuery::new);
|
return equalTo(shape, null, TraceAddressSnapRangeQuery::new);
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -279,6 +279,13 @@ public class DBTraceMemoryManager extends AbstractDBTraceSpaceBasedManager<DBTra
|
|||||||
m -> m.getMostRecentStateEntry(snap, address));
|
m -> m.getMostRecentStateEntry(snap, address));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
|
AddressRange range, Predicate<TraceMemoryState> predicate) {
|
||||||
|
return delegateRead(range.getAddressSpace(),
|
||||||
|
m -> m.getViewMostRecentStateEntry(snap, range, predicate));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
Address address) {
|
Address address) {
|
||||||
|
@ -406,11 +406,17 @@ public class DBTraceMemorySpace
|
|||||||
@Override
|
@Override
|
||||||
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
Address address) {
|
Address address) {
|
||||||
|
return getViewMostRecentStateEntry(snap, new AddressRangeImpl(address, address), s -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
|
AddressRange range, Predicate<TraceMemoryState> predicate) {
|
||||||
|
assertInSpace(range);
|
||||||
for (Lifespan span : viewport.getOrderedSpans(snap)) {
|
for (Lifespan span : viewport.getOrderedSpans(snap)) {
|
||||||
Entry<TraceAddressSnapRange, TraceMemoryState> entry =
|
var entry = stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(range, span))
|
||||||
stateMapSpace.reduce(TraceAddressSnapRangeQuery.mostRecent(address, span))
|
.firstEntry();
|
||||||
.firstEntry();
|
if (entry != null && predicate.test(entry.getValue())) {
|
||||||
if (entry != null) {
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.apache.commons.collections4.IteratorUtils;
|
|||||||
|
|
||||||
import generic.NestedIterator;
|
import generic.NestedIterator;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
|
import ghidra.program.database.code.InstructionDB;
|
||||||
import ghidra.program.database.function.OverlappingFunctionException;
|
import ghidra.program.database.function.OverlappingFunctionException;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.DataType;
|
import ghidra.program.model.data.DataType;
|
||||||
@ -41,6 +42,7 @@ import ghidra.trace.database.thread.DBTraceThread;
|
|||||||
import ghidra.trace.model.*;
|
import ghidra.trace.model.*;
|
||||||
import ghidra.trace.model.listing.*;
|
import ghidra.trace.model.listing.*;
|
||||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryState;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
import ghidra.trace.model.program.TraceProgramViewListing;
|
import ghidra.trace.model.program.TraceProgramViewListing;
|
||||||
import ghidra.trace.model.property.TracePropertyMapOperations;
|
import ghidra.trace.model.property.TracePropertyMapOperations;
|
||||||
@ -65,8 +67,8 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||||||
public int getBytes(ByteBuffer buffer, int addressOffset) {
|
public int getBytes(ByteBuffer buffer, int addressOffset) {
|
||||||
DBTraceMemorySpace mem = trace.getMemoryManager().get(this, false);
|
DBTraceMemorySpace mem = trace.getMemoryManager().get(this, false);
|
||||||
if (mem == null) {
|
if (mem == null) {
|
||||||
// TODO: 0-fill instead? Will need to check memory space bounds.
|
buffer.put((byte) 0);
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
return mem.getViewBytes(program.snap, address.add(addressOffset), buffer);
|
return mem.getViewBytes(program.snap, address.add(addressOffset), buffer);
|
||||||
}
|
}
|
||||||
@ -717,9 +719,21 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
|
|||||||
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
public Instruction createInstruction(Address addr, InstructionPrototype prototype,
|
||||||
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
|
MemBuffer memBuf, ProcessorContextView context, int forcedLengthOverride)
|
||||||
throws CodeUnitInsertionException {
|
throws CodeUnitInsertionException {
|
||||||
// TODO: Why memBuf? Can it vary from program memory?
|
int checkLengthOverride =
|
||||||
|
InstructionDB.checkLengthOverride(forcedLengthOverride, prototype);
|
||||||
|
int length = checkLengthOverride != 0 ? checkLengthOverride : prototype.getLength();
|
||||||
|
AddressRange range;
|
||||||
|
try {
|
||||||
|
range = new AddressRangeImpl(addr, length);
|
||||||
|
}
|
||||||
|
catch (AddressOverflowException e) {
|
||||||
|
throw new CodeUnitInsertionException("Code unit would extend beyond address space");
|
||||||
|
}
|
||||||
|
var mostRecent = program.memory.memoryManager.getViewMostRecentStateEntry(program.snap,
|
||||||
|
range, s -> s == TraceMemoryState.KNOWN);
|
||||||
|
long snap = mostRecent == null ? program.snap : mostRecent.getKey().getY2();
|
||||||
return codeOperations.instructions()
|
return codeOperations.instructions()
|
||||||
.create(Lifespan.nowOn(program.snap), addr, platform, prototype, context,
|
.create(Lifespan.nowOn(snap), addr, platform, prototype, context,
|
||||||
forcedLengthOverride);
|
forcedLengthOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,6 +1635,17 @@ public class DBTraceProgramView implements TraceProgramView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isCodeVisible(TraceCodeUnit cu, Lifespan lifespan) {
|
protected boolean isCodeVisible(TraceCodeUnit cu, Lifespan lifespan) {
|
||||||
|
try {
|
||||||
|
byte[] cubytes = cu.getBytes();
|
||||||
|
byte[] mmbytes = new byte[cubytes.length];
|
||||||
|
memory.getBytes(cu.getAddress(), mmbytes);
|
||||||
|
if (!Arrays.equals(cubytes, mmbytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
return viewport.isCompletelyVisible(cu.getRange(), lifespan, cu,
|
return viewport.isCompletelyVisible(cu.getRange(), lifespan, cu,
|
||||||
getCodeOcclusion(cu.getTraceSpace()));
|
getCodeOcclusion(cu.getTraceSpace()));
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -48,7 +48,13 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||||||
/**
|
/**
|
||||||
* Create an instruction for the host platform
|
* Create an instruction for the host platform
|
||||||
*
|
*
|
||||||
* @see #create(Lifespan, Address, TracePlatform, InstructionPrototype, ProcessorContextView)
|
* @param lifespan the lifespan for the instruction unit
|
||||||
|
* @param address the starting address of the instruction
|
||||||
|
* @param prototype the instruction prototype
|
||||||
|
* @param context the input disassembly context for the instruction
|
||||||
|
* @param forcedLengthOverride reduced instruction byte-length (1..7) or 0 to use default length
|
||||||
|
* @return the new instruction
|
||||||
|
* @throws CodeUnitInsertionException if the instruction cannot be created
|
||||||
*/
|
*/
|
||||||
default TraceInstruction create(Lifespan lifespan, Address address,
|
default TraceInstruction create(Lifespan lifespan, Address address,
|
||||||
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
InstructionPrototype prototype, ProcessorContextView context, int forcedLengthOverride)
|
||||||
@ -76,7 +82,14 @@ public interface TraceInstructionsView extends TraceBaseDefinedUnitsView<TraceIn
|
|||||||
/**
|
/**
|
||||||
* Create several instructions for the host platform
|
* Create several instructions for the host platform
|
||||||
*
|
*
|
||||||
* @see #addInstructionSet(Lifespan, TracePlatform, InstructionSet, boolean)
|
* <p>
|
||||||
|
* <b>NOTE:</b> This does not throw {@link CodeUnitInsertionException}. Conflicts are instead
|
||||||
|
* recorded in the {@code instructionSet}.
|
||||||
|
*
|
||||||
|
* @param lifespan the lifespan for all instruction units
|
||||||
|
* @param instructionSet the set of instructions to add
|
||||||
|
* @param overwrite true to replace conflicting instructions
|
||||||
|
* @return the (host) address set of instructions actually added
|
||||||
*/
|
*/
|
||||||
default AddressSetView addInstructionSet(Lifespan lifespan, InstructionSet instructionSet,
|
default AddressSetView addInstructionSet(Lifespan lifespan, InstructionSet instructionSet,
|
||||||
boolean overwrite) {
|
boolean overwrite) {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -62,6 +62,38 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
* accidentally rely on implied temporal relationships in scratch space.
|
* accidentally rely on implied temporal relationships in scratch space.
|
||||||
*/
|
*/
|
||||||
public interface TraceMemoryOperations {
|
public interface TraceMemoryOperations {
|
||||||
|
/**
|
||||||
|
* Check if the return value of {@link #getStates(long, AddressRange)} or similar represents a
|
||||||
|
* single entry of the given state.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method returns false if there is not exactly one entry of the given state whose range
|
||||||
|
* covers the given range. As a special case, an empty collection will cause this method to
|
||||||
|
* return true iff state is {@link TraceMemoryState#UNKNOWN}.
|
||||||
|
*
|
||||||
|
* @param range the range to check, usually that passed to
|
||||||
|
* {@link #getStates(long, AddressRange)}.
|
||||||
|
* @param stateEntries the collection returned by {@link #getStates(long, AddressRange)}.
|
||||||
|
* @param state the expected state
|
||||||
|
* @return true if the state matches
|
||||||
|
*/
|
||||||
|
static boolean isStateEntirely(AddressRange range,
|
||||||
|
Collection<Entry<TraceAddressSnapRange, TraceMemoryState>> stateEntries,
|
||||||
|
TraceMemoryState state) {
|
||||||
|
if (stateEntries.isEmpty()) {
|
||||||
|
return state == TraceMemoryState.UNKNOWN;
|
||||||
|
}
|
||||||
|
if (stateEntries.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Entry<TraceAddressSnapRange, TraceMemoryState> ent = stateEntries.iterator().next();
|
||||||
|
if (ent.getValue() != state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AddressRange entRange = ent.getKey().getRange();
|
||||||
|
return entRange.contains(range.getMinAddress()) && entRange.contains(range.getMaxAddress());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the trace to which the memory manager belongs
|
* Get the trace to which the memory manager belongs
|
||||||
*
|
*
|
||||||
@ -261,13 +293,25 @@ public interface TraceMemoryOperations {
|
|||||||
* Get the entry recording the most recent state at the given snap and address, following
|
* Get the entry recording the most recent state at the given snap and address, following
|
||||||
* schedule forks
|
* schedule forks
|
||||||
*
|
*
|
||||||
* @param snap the time
|
* @param snap the latest time to consider
|
||||||
* @param address the location
|
* @param address the address
|
||||||
* @return the state
|
* @return the most-recent entry
|
||||||
*/
|
*/
|
||||||
Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
Address address);
|
Address address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the entry recording the most recent state since the given snap within the given range
|
||||||
|
* that satisfies a given predicate, following schedule forks
|
||||||
|
*
|
||||||
|
* @param snap the latest time to consider
|
||||||
|
* @param range the range of addresses
|
||||||
|
* @param predicate a predicate on the state
|
||||||
|
* @return the most-recent entry
|
||||||
|
*/
|
||||||
|
Entry<TraceAddressSnapRange, TraceMemoryState> getViewMostRecentStateEntry(long snap,
|
||||||
|
AddressRange range, Predicate<TraceMemoryState> predicate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get at least the subset of addresses having state satisfying the given predicate
|
* Get at least the subset of addresses having state satisfying the given predicate
|
||||||
*
|
*
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -82,6 +82,10 @@ public class PatchStep implements Step {
|
|||||||
/**
|
/**
|
||||||
* Generate a single line of Sleigh
|
* Generate a single line of Sleigh
|
||||||
*
|
*
|
||||||
|
* @param language the target language
|
||||||
|
* @param address the (start) address of the variable
|
||||||
|
* @param data the bytes to write to the variable
|
||||||
|
* @return the Sleigh code
|
||||||
* @see #generateSleighLine(Language, Address, byte[], int)
|
* @see #generateSleighLine(Language, Address, byte[], int)
|
||||||
*/
|
*/
|
||||||
public static String generateSleighLine(Language language, Address address, byte[] data) {
|
public static String generateSleighLine(Language language, Address address, byte[] data) {
|
||||||
@ -169,10 +173,18 @@ public class PatchStep implements Step {
|
|||||||
if (register == null) {
|
if (register == null) {
|
||||||
throw new IllegalArgumentException("Could not find a register for " + min);
|
throw new IllegalArgumentException("Could not find a register for " + min);
|
||||||
}
|
}
|
||||||
|
if (register.getBaseRegister().isProgramCounter()) {
|
||||||
|
register = register.getBaseRegister();
|
||||||
|
}
|
||||||
int length = register.getNumBytes();
|
int length = register.getNumBytes();
|
||||||
array.getData(min.getOffset(), data, 0, length);
|
array.getData(register.getOffset(), data, 0, length);
|
||||||
BigInteger value = Utils.bytesToBigInteger(data, length, language.isBigEndian(), false);
|
BigInteger value = Utils.bytesToBigInteger(data, length, language.isBigEndian(), false);
|
||||||
result.add(String.format("%s=0x%s", register, value.toString(16)));
|
if (register.isProgramCounter()) {
|
||||||
|
result.add(String.format("goto 0x%s", value.toString(16)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.add(String.format("%s=0x%s", register, value.toString(16)));
|
||||||
|
}
|
||||||
remains.remove(spanOfRegister(register));
|
remains.remove(spanOfRegister(register));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,6 +244,11 @@ public class PatchStep implements Step {
|
|||||||
return StepType.PATCH;
|
return StepType.PATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSkipCount() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNop() {
|
public boolean isNop() {
|
||||||
// TODO: If parsing beforehand, base on number of ops
|
// TODO: If parsing beforehand, base on number of ops
|
||||||
@ -307,7 +324,7 @@ public class PatchStep implements Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
public void execute(PcodeThread<?> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
emuThread.stepPatch(sleigh);
|
emuThread.stepPatch(sleigh);
|
||||||
}
|
}
|
||||||
@ -366,20 +383,26 @@ public class PatchStep implements Step {
|
|||||||
// SemisparseArray is a bit overkill, no?
|
// SemisparseArray is a bit overkill, no?
|
||||||
Map<AddressSpace, SemisparseByteArray> result = new TreeMap<>();
|
Map<AddressSpace, SemisparseByteArray> result = new TreeMap<>();
|
||||||
for (PcodeOp op : prog.getCode()) {
|
for (PcodeOp op : prog.getCode()) {
|
||||||
// Only accept patches in form [mem/reg] = [constant]
|
// Only accept patches in form [mem/reg] = [constant], or goto [constant]
|
||||||
switch (op.getOpcode()) {
|
switch (op.getOpcode()) {
|
||||||
case PcodeOp.COPY:
|
case PcodeOp.COPY -> {
|
||||||
if (!getPatchCopyOp(language, result, op)) {
|
if (!getPatchCopyOp(language, result, op)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case PcodeOp.STORE:
|
case PcodeOp.STORE -> {
|
||||||
if (!getPatchStoreOp(language, result, op)) {
|
if (!getPatchStoreOp(language, result, op)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
case PcodeOp.BRANCH -> {
|
||||||
|
if (!getPatchBranchOp(language, result, op)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -405,8 +428,7 @@ public class PatchStep implements Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean getPatchStoreOp(Language language,
|
protected boolean getPatchStoreOp(Language language,
|
||||||
Map<AddressSpace, SemisparseByteArray> result,
|
Map<AddressSpace, SemisparseByteArray> result, PcodeOp op) {
|
||||||
PcodeOp op) {
|
|
||||||
Varnode vnSpace = op.getInput(0);
|
Varnode vnSpace = op.getInput(0);
|
||||||
if (!vnSpace.isConstant()) {
|
if (!vnSpace.isConstant()) {
|
||||||
return false;
|
return false;
|
||||||
@ -427,4 +449,17 @@ public class PatchStep implements Step {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean getPatchBranchOp(Language language,
|
||||||
|
Map<AddressSpace, SemisparseByteArray> result, PcodeOp op) {
|
||||||
|
Address target = op.getInput(0).getAddress();
|
||||||
|
if (target.getAddressSpace() != language.getDefaultSpace()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Register pc = language.getProgramCounter();
|
||||||
|
SemisparseByteArray array =
|
||||||
|
result.computeIfAbsent(pc.getAddressSpace(), as -> new SemisparseByteArray());
|
||||||
|
array.putData(pc.getOffset(),
|
||||||
|
Utils.longToBytes(target.getOffset(), pc.getNumBytes(), language.isBigEndian()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -81,14 +81,17 @@ public interface Scheduler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The result of running a machine
|
* The result of running a machine
|
||||||
|
*
|
||||||
|
* @param schedule the actual schedule executed
|
||||||
|
* @param error if applicable, the error that interrupted execution
|
||||||
*/
|
*/
|
||||||
record RecordRunResult(TraceSchedule schedule, Throwable error) implements RunResult {
|
record RecordRunResult(TraceSchedule schedule, Throwable error) implements RunResult {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next step to schedule
|
* Get the next step to schedule
|
||||||
*
|
*
|
||||||
* @return the (instruction-level) thread and tick count
|
* @param trace the trace being emulated
|
||||||
|
* @return the thread and (instruction-level) tick count
|
||||||
*/
|
*/
|
||||||
TickStep nextSlice(Trace trace);
|
TickStep nextSlice(Trace trace);
|
||||||
|
|
||||||
@ -118,12 +121,17 @@ public interface Scheduler {
|
|||||||
TickStep slice = nextSlice(trace);
|
TickStep slice = nextSlice(trace);
|
||||||
eventThread = slice.getThread(tm, eventThread);
|
eventThread = slice.getThread(tm, eventThread);
|
||||||
emuThread = machine.getThread(eventThread.getPath(), true);
|
emuThread = machine.getThread(eventThread.getPath(), true);
|
||||||
if (emuThread.getFrame() != null) {
|
long ticksLeft = slice.tickCount;
|
||||||
|
if (ticksLeft > 0 && emuThread.getFrame() != null) {
|
||||||
|
monitor.checkCancelled();
|
||||||
emuThread.finishInstruction();
|
emuThread.finishInstruction();
|
||||||
|
ticksLeft--;
|
||||||
|
completedTicks++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < slice.tickCount; i++) {
|
while (ticksLeft > 0) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
emuThread.stepInstruction();
|
emuThread.stepInstruction();
|
||||||
|
ticksLeft--;
|
||||||
completedTicks++;
|
completedTicks++;
|
||||||
}
|
}
|
||||||
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
||||||
@ -134,11 +142,11 @@ public interface Scheduler {
|
|||||||
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
completedSteps = completedSteps.steppedForward(eventThread, completedTicks);
|
||||||
PcodeFrame frame = emuThread.getFrame();
|
PcodeFrame frame = emuThread.getFrame();
|
||||||
if (frame == null) {
|
if (frame == null) {
|
||||||
return new RecordRunResult(completedSteps, e);
|
return new RecordRunResult(completedSteps.assumeRecorded(), e);
|
||||||
}
|
}
|
||||||
// Rewind one so stepping retries the op causing the error
|
// Rewind one so stepping retries the op causing the error
|
||||||
frame.stepBack();
|
frame.stepBack();
|
||||||
int count = frame.count();
|
int count = frame.resetCount();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
// If we've decoded, but could not execute the first op, just drop the p-code steps
|
// If we've decoded, but could not execute the first op, just drop the p-code steps
|
||||||
emuThread.dropInstruction();
|
emuThread.dropInstruction();
|
||||||
@ -146,11 +154,11 @@ public interface Scheduler {
|
|||||||
}
|
}
|
||||||
// The +1 accounts for the decode step
|
// The +1 accounts for the decode step
|
||||||
return new RecordRunResult(
|
return new RecordRunResult(
|
||||||
completedSteps.steppedPcodeForward(eventThread, count + 1), e);
|
completedSteps.steppedPcodeForward(eventThread, count + 1).assumeRecorded(), e);
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
return new RecordRunResult(
|
return new RecordRunResult(
|
||||||
completedSteps.steppedForward(eventThread, completedTicks), e);
|
completedSteps.steppedForward(eventThread, completedTicks).assumeRecorded(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -21,6 +21,7 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ghidra.pcode.emu.PcodeMachine;
|
import ghidra.pcode.emu.PcodeMachine;
|
||||||
|
import ghidra.pcode.emu.PcodeThread;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.Language;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
@ -240,7 +241,7 @@ public class Sequence implements Comparable<Sequence> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Richly compare to sequences
|
* Richly compare two sequences
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The result indicates not only which is "less" or "greater" than the other, but also indicates
|
* The result indicates not only which is "less" or "greater" than the other, but also indicates
|
||||||
@ -355,6 +356,14 @@ public class Sequence implements Comparable<Sequence> {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long totalSkipCount() {
|
||||||
|
long count = 0;
|
||||||
|
for (Step step : steps) {
|
||||||
|
count += step.getSkipCount();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute to total number of patches specified
|
* Compute to total number of patches specified
|
||||||
*
|
*
|
||||||
@ -440,4 +449,25 @@ public class Sequence implements Comparable<Sequence> {
|
|||||||
}
|
}
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the first instruction step is actually to finish an incomplete instruction.
|
||||||
|
*
|
||||||
|
* @param thread the thread whose instruction to potentially finish
|
||||||
|
* @param machine a machine bound to the trace whose current state reflects the given position
|
||||||
|
* @return if a finish was performed, this sequence with one initial step removed, i.e., a
|
||||||
|
* sequence representing the steps remaining
|
||||||
|
*/
|
||||||
|
Sequence checkFinish(TraceThread thread, PcodeMachine<?> machine) {
|
||||||
|
PcodeThread<?> emuThread = machine.getThread(thread.getPath(), true);
|
||||||
|
if (emuThread.getFrame() == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Sequence result = new Sequence(new ArrayList<>(steps));
|
||||||
|
emuThread.finishInstruction();
|
||||||
|
if (result.steps.get(0).rewind(1) == 0) {
|
||||||
|
result.steps.remove(0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -48,6 +48,11 @@ public class SkipStep extends AbstractStep {
|
|||||||
return StepType.SKIP;
|
return StepType.SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSkipCount() {
|
||||||
|
return tickCount;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String toStringStepPart() {
|
protected String toStringStepPart() {
|
||||||
return String.format("s%d", tickCount);
|
return String.format("s%d", tickCount);
|
||||||
@ -66,7 +71,7 @@ public class SkipStep extends AbstractStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
public void execute(PcodeThread<?> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
for (int i = 0; i < tickCount; i++) {
|
for (int i = 0; i < tickCount; i++) {
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -116,6 +116,8 @@ public interface Step extends Comparable<Step> {
|
|||||||
|
|
||||||
long getTickCount();
|
long getTickCount();
|
||||||
|
|
||||||
|
long getSkipCount();
|
||||||
|
|
||||||
long getPatchCount();
|
long getPatchCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +149,7 @@ public interface Step extends Comparable<Step> {
|
|||||||
* method sets the count to 0 and returns the (positive) difference, indicating this step should
|
* method sets the count to 0 and returns the (positive) difference, indicating this step should
|
||||||
* be removed from the sequence, and the remaining steps rewound from the preceding step.
|
* be removed from the sequence, and the remaining steps rewound from the preceding step.
|
||||||
*
|
*
|
||||||
* @param steps the count to rewind
|
* @param count the count to rewind
|
||||||
* @return the number of steps remaining
|
* @return the number of steps remaining
|
||||||
*/
|
*/
|
||||||
long rewind(long count);
|
long rewind(long count);
|
||||||
@ -155,7 +157,7 @@ public interface Step extends Comparable<Step> {
|
|||||||
/**
|
/**
|
||||||
* Richly compare this step to another
|
* Richly compare this step to another
|
||||||
*
|
*
|
||||||
* @param step the object of comparison (this being the subject)
|
* @param that the object of comparison (this being the subject)
|
||||||
* @return a result describing the relationship from subject to object
|
* @return a result describing the relationship from subject to object
|
||||||
*/
|
*/
|
||||||
CompareResult compareStep(Step that);
|
CompareResult compareStep(Step that);
|
||||||
@ -183,7 +185,7 @@ public interface Step extends Comparable<Step> {
|
|||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
void execute(PcodeThread<?> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||||
throws CancelledException;
|
throws CancelledException;
|
||||||
|
|
||||||
long coalescePatches(Language language, List<Step> steps);
|
long coalescePatches(Language language, List<Step> steps);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -48,6 +48,11 @@ public class TickStep extends AbstractStep {
|
|||||||
return StepType.TICK;
|
return StepType.TICK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSkipCount() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String toStringStepPart() {
|
protected String toStringStepPart() {
|
||||||
return Long.toString(tickCount);
|
return Long.toString(tickCount);
|
||||||
@ -66,7 +71,7 @@ public class TickStep extends AbstractStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void execute(PcodeThread<T> emuThread, Stepper stepper, TaskMonitor monitor)
|
public void execute(PcodeThread<?> emuThread, Stepper stepper, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
for (int i = 0; i < tickCount; i++) {
|
for (int i = 0; i < tickCount; i++) {
|
||||||
monitor.incrementProgress(1);
|
monitor.incrementProgress(1);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -39,7 +39,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
* @return the schedule
|
* @return the schedule
|
||||||
*/
|
*/
|
||||||
public static final TraceSchedule snap(long snap) {
|
public static final TraceSchedule snap(long snap) {
|
||||||
return new TraceSchedule(snap, new Sequence(), new Sequence());
|
return new TraceSchedule(snap, new Sequence(), new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String PARSE_ERR_MSG =
|
private static final String PARSE_ERR_MSG =
|
||||||
@ -55,9 +55,10 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
* threads forward, and/or patching machine state.
|
* threads forward, and/or patching machine state.
|
||||||
*
|
*
|
||||||
* @param spec the string specification
|
* @param spec the string specification
|
||||||
|
* @param source the presumed source of the schedule
|
||||||
* @return the parsed schedule
|
* @return the parsed schedule
|
||||||
*/
|
*/
|
||||||
public static TraceSchedule parse(String spec) {
|
public static TraceSchedule parse(String spec, Source source) {
|
||||||
String[] parts = spec.split(":", 2);
|
String[] parts = spec.split(":", 2);
|
||||||
if (parts.length > 2) {
|
if (parts.length > 2) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
@ -98,24 +99,76 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
ticks = new Sequence();
|
ticks = new Sequence();
|
||||||
pTicks = new Sequence();
|
pTicks = new Sequence();
|
||||||
}
|
}
|
||||||
return new TraceSchedule(snap, ticks, pTicks);
|
return new TraceSchedule(snap, ticks, pTicks, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As in {@link #parse(String, Source)}, but assumed abnormal
|
||||||
|
*
|
||||||
|
* @param spec the string specification
|
||||||
|
* @return the parsed schedule
|
||||||
|
*/
|
||||||
|
public static TraceSchedule parse(String spec) {
|
||||||
|
return parse(spec, Source.INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Source {
|
||||||
|
/**
|
||||||
|
* The schedule comes from the user or some source other than a recorded emulation schedule.
|
||||||
|
*/
|
||||||
|
INPUT {
|
||||||
|
@Override
|
||||||
|
Source adjust(long pTickCount, long pPatchCount, long pSkipCount) {
|
||||||
|
// The first tick is decode, so <= 1 tick is definitely not a full instruction
|
||||||
|
return pTickCount <= 1 && pPatchCount == 0 && pSkipCount == 0 ? RECORD : INPUT;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* The schedule comes from recording actual emulation.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Specifically, the p-code steps must be known not to exceed one instruction.
|
||||||
|
*/
|
||||||
|
RECORD {
|
||||||
|
@Override
|
||||||
|
Source adjust(long pTickCount, long pPatchCount, long pSkipCount) {
|
||||||
|
return pPatchCount == 0 && pSkipCount == 0 ? RECORD : INPUT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
abstract Source adjust(long tickCount, long patchCount, long skipCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final long snap;
|
private final long snap;
|
||||||
private final Sequence steps;
|
private final Sequence steps;
|
||||||
private final Sequence pSteps;
|
private final Sequence pSteps;
|
||||||
|
private final Source source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the given schedule
|
* Construct the given schedule
|
||||||
*
|
*
|
||||||
* @param snap the initial trace snapshot
|
* @param snap the initial trace snapshot
|
||||||
* @param steps the step sequence
|
* @param steps the step sequence
|
||||||
* @param pSteps the of p-code step sequence
|
* @param pSteps the p-code step sequence
|
||||||
|
* @param source if the p-code steps are known not to exceed one instruction
|
||||||
*/
|
*/
|
||||||
public TraceSchedule(long snap, Sequence steps, Sequence pSteps) {
|
public TraceSchedule(long snap, Sequence steps, Sequence pSteps, Source source) {
|
||||||
this.snap = snap;
|
this.snap = snap;
|
||||||
this.steps = steps;
|
this.steps = steps;
|
||||||
this.pSteps = pSteps;
|
this.pSteps = pSteps;
|
||||||
|
this.source = source.adjust(pSteps.totalTickCount(), pSteps.totalPatchCount(),
|
||||||
|
pSteps.totalSkipCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the given schedule, but assumed abnormal
|
||||||
|
*
|
||||||
|
* @param snap the initial trace snapshot
|
||||||
|
* @param steps the step sequence
|
||||||
|
* @param pSteps the p-code step sequence
|
||||||
|
*/
|
||||||
|
public TraceSchedule(long snap, Sequence steps, Sequence pSteps) {
|
||||||
|
this(snap, steps, pSteps, Source.INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -152,33 +205,17 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = this.steps.compareSeq(that.steps);
|
result = this.steps.compareSeq(that.steps);
|
||||||
switch (result) {
|
|
||||||
case UNREL_LT:
|
|
||||||
case UNREL_GT:
|
|
||||||
return result;
|
|
||||||
case REL_LT:
|
|
||||||
if (this.pSteps.isNop()) {
|
|
||||||
return CompareResult.REL_LT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return CompareResult.UNREL_LT;
|
|
||||||
}
|
|
||||||
case REL_GT:
|
|
||||||
if (that.pSteps.isNop()) {
|
|
||||||
return CompareResult.REL_GT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return CompareResult.UNREL_GT;
|
|
||||||
}
|
|
||||||
default: // EQUALS, compare pSteps
|
|
||||||
}
|
|
||||||
|
|
||||||
result = this.pSteps.compareSeq(that.pSteps);
|
return switch (result) {
|
||||||
if (result != CompareResult.EQUALS) {
|
case UNREL_LT, UNREL_GT -> result;
|
||||||
return result;
|
case REL_LT -> (this.pSteps.isNop() || this.source == Source.RECORD)
|
||||||
}
|
? CompareResult.REL_LT
|
||||||
|
: CompareResult.UNREL_LT;
|
||||||
return CompareResult.EQUALS;
|
case REL_GT -> (that.pSteps.isNop() || that.source == Source.RECORD)
|
||||||
|
? CompareResult.REL_GT
|
||||||
|
: CompareResult.UNREL_GT;
|
||||||
|
default -> this.pSteps.compareSeq(that.pSteps);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -222,10 +259,19 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
return steps.isNop() && pSteps.isNop();
|
return steps.isNop() && pSteps.isNop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this schedule has instruction steps
|
||||||
|
*
|
||||||
|
* @return true if this indicates at least one instruction step
|
||||||
|
*/
|
||||||
|
public boolean hasSteps() {
|
||||||
|
return !steps.isNop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the source snapshot
|
* Get the source snapshot
|
||||||
*
|
*
|
||||||
* @return
|
* @return the snapshot key
|
||||||
*/
|
*/
|
||||||
public long getSnap() {
|
public long getSnap() {
|
||||||
return snap;
|
return snap;
|
||||||
@ -234,7 +280,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
/**
|
/**
|
||||||
* Get the last thread key stepped by this schedule
|
* Get the last thread key stepped by this schedule
|
||||||
*
|
*
|
||||||
* @return
|
* @return the thread key
|
||||||
*/
|
*/
|
||||||
public long getLastThreadKey() {
|
public long getLastThreadKey() {
|
||||||
long last = pSteps.getLastThreadKey();
|
long last = pSteps.getLastThreadKey();
|
||||||
@ -396,6 +442,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
pRemains.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
|
pRemains.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
remains = remains.checkFinish(lastThread, machine);
|
||||||
lastThread =
|
lastThread =
|
||||||
remains.execute(trace, lastThread, machine, Stepper.instruction(), monitor);
|
remains.execute(trace, lastThread, machine, Stepper.instruction(), monitor);
|
||||||
lastThread = pSteps.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
|
lastThread = pSteps.execute(trace, lastThread, machine, Stepper.pcode(), monitor);
|
||||||
@ -417,7 +464,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
public TraceSchedule steppedForward(TraceThread thread, long tickCount) {
|
public TraceSchedule steppedForward(TraceThread thread, long tickCount) {
|
||||||
Sequence steps = this.steps.clone();
|
Sequence steps = this.steps.clone();
|
||||||
steps.advance(new TickStep(thread == null ? -1 : thread.getKey(), tickCount));
|
steps.advance(new TickStep(thread == null ? -1 : thread.getKey(), tickCount));
|
||||||
return new TraceSchedule(snap, steps, new Sequence());
|
return new TraceSchedule(snap, steps, new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -430,7 +477,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
public TraceSchedule skippedForward(TraceThread thread, long tickCount) {
|
public TraceSchedule skippedForward(TraceThread thread, long tickCount) {
|
||||||
Sequence steps = this.steps.clone();
|
Sequence steps = this.steps.clone();
|
||||||
steps.advance(new SkipStep(thread == null ? -1 : thread.getKey(), tickCount));
|
steps.advance(new SkipStep(thread == null ? -1 : thread.getKey(), tickCount));
|
||||||
return new TraceSchedule(snap, steps, new Sequence());
|
return new TraceSchedule(snap, steps, new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TraceSchedule doSteppedBackward(Trace trace, long tickCount, Set<Long> visited) {
|
protected TraceSchedule doSteppedBackward(Trace trace, long tickCount, Set<Long> visited) {
|
||||||
@ -454,7 +501,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
}
|
}
|
||||||
Sequence steps = this.steps.clone();
|
Sequence steps = this.steps.clone();
|
||||||
steps.rewind(tickCount);
|
steps.rewind(tickCount);
|
||||||
return new TraceSchedule(snap, steps, new Sequence());
|
return new TraceSchedule(snap, steps, new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -486,7 +533,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
public TraceSchedule steppedPcodeForward(TraceThread thread, int pTickCount) {
|
public TraceSchedule steppedPcodeForward(TraceThread thread, int pTickCount) {
|
||||||
Sequence pTicks = this.pSteps.clone();
|
Sequence pTicks = this.pSteps.clone();
|
||||||
pTicks.advance(new TickStep(thread == null ? -1 : thread.getKey(), pTickCount));
|
pTicks.advance(new TickStep(thread == null ? -1 : thread.getKey(), pTickCount));
|
||||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
return new TraceSchedule(snap, steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -499,7 +546,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
public TraceSchedule skippedPcodeForward(TraceThread thread, int pTickCount) {
|
public TraceSchedule skippedPcodeForward(TraceThread thread, int pTickCount) {
|
||||||
Sequence pTicks = this.pSteps.clone();
|
Sequence pTicks = this.pSteps.clone();
|
||||||
pTicks.advance(new SkipStep(thread == null ? -1 : thread.getKey(), pTickCount));
|
pTicks.advance(new SkipStep(thread == null ? -1 : thread.getKey(), pTickCount));
|
||||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
return new TraceSchedule(snap, steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -519,7 +566,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
}
|
}
|
||||||
Sequence pTicks = this.pSteps.clone();
|
Sequence pTicks = this.pSteps.clone();
|
||||||
pTicks.rewind(pStepCount);
|
pTicks.rewind(pStepCount);
|
||||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
return new TraceSchedule(snap, steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long keyOf(TraceThread thread) {
|
private long keyOf(TraceThread thread) {
|
||||||
@ -530,6 +577,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
* Returns the equivalent of executing this schedule then performing a given patch
|
* Returns the equivalent of executing this schedule then performing a given patch
|
||||||
*
|
*
|
||||||
* @param thread the thread context for the patch; cannot be null
|
* @param thread the thread context for the patch; cannot be null
|
||||||
|
* @param language the sleigh language for the patch
|
||||||
* @param sleigh a single line of sleigh, excluding the terminating semicolon.
|
* @param sleigh a single line of sleigh, excluding the terminating semicolon.
|
||||||
* @return the resulting schedule
|
* @return the resulting schedule
|
||||||
*/
|
*/
|
||||||
@ -538,19 +586,20 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
Sequence pTicks = this.pSteps.clone();
|
Sequence pTicks = this.pSteps.clone();
|
||||||
pTicks.advance(new PatchStep(thread.getKey(), sleigh));
|
pTicks.advance(new PatchStep(thread.getKey(), sleigh));
|
||||||
pTicks.coalescePatches(language);
|
pTicks.coalescePatches(language);
|
||||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
return new TraceSchedule(snap, steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
Sequence ticks = this.steps.clone();
|
Sequence ticks = this.steps.clone();
|
||||||
ticks.advance(new PatchStep(keyOf(thread), sleigh));
|
ticks.advance(new PatchStep(keyOf(thread), sleigh));
|
||||||
ticks.coalescePatches(language);
|
ticks.coalescePatches(language);
|
||||||
return new TraceSchedule(snap, ticks, new Sequence());
|
return new TraceSchedule(snap, ticks, new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the equivalent of executing this schedule then performing the given patches
|
* Returns the equivalent of executing this schedule then performing the given patches
|
||||||
*
|
*
|
||||||
* @param thread the thread context for the patch; cannot be null
|
* @param thread the thread context for the patch; cannot be null
|
||||||
* @param sleigh the lines of sleigh, excluding the terminating semicolons.
|
* @param language the sleigh language for the patch
|
||||||
|
* @param sleigh the lines of sleigh, excluding the terminating semicolons
|
||||||
* @return the resulting schedule
|
* @return the resulting schedule
|
||||||
*/
|
*/
|
||||||
public TraceSchedule patched(TraceThread thread, Language language, List<String> sleigh) {
|
public TraceSchedule patched(TraceThread thread, Language language, List<String> sleigh) {
|
||||||
@ -560,14 +609,14 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
pTicks.advance(new PatchStep(thread.getKey(), line));
|
pTicks.advance(new PatchStep(thread.getKey(), line));
|
||||||
}
|
}
|
||||||
pTicks.coalescePatches(language);
|
pTicks.coalescePatches(language);
|
||||||
return new TraceSchedule(snap, steps.clone(), pTicks);
|
return new TraceSchedule(snap, steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
Sequence ticks = this.steps.clone();
|
Sequence ticks = this.steps.clone();
|
||||||
for (String line : sleigh) {
|
for (String line : sleigh) {
|
||||||
ticks.advance(new PatchStep(thread.getKey(), line));
|
ticks.advance(new PatchStep(thread.getKey(), line));
|
||||||
}
|
}
|
||||||
ticks.coalescePatches(language);
|
ticks.coalescePatches(language);
|
||||||
return new TraceSchedule(snap, ticks, new Sequence());
|
return new TraceSchedule(snap, ticks, new Sequence(), Source.RECORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -575,7 +624,7 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This operation cannot be used to append instruction steps after p-code steps. Thus, if this
|
* This operation cannot be used to append instruction steps after p-code steps. Thus, if this
|
||||||
* schedule contains any p-code steps and {@code} next has instruction steps, an error will be
|
* schedule contains any p-code steps and {@code next} has instruction steps, an error will be
|
||||||
*
|
*
|
||||||
* @param next the schedule to append. Its snap is ignored.
|
* @param next the schedule to append. Its snap is ignored.
|
||||||
* @return the complete schedule
|
* @return the complete schedule
|
||||||
@ -586,16 +635,25 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
if (this.pSteps.isNop()) {
|
if (this.pSteps.isNop()) {
|
||||||
Sequence ticks = this.steps.clone();
|
Sequence ticks = this.steps.clone();
|
||||||
ticks.advance(next.steps);
|
ticks.advance(next.steps);
|
||||||
return new TraceSchedule(this.snap, ticks, next.pSteps.clone());
|
return new TraceSchedule(this.snap, ticks, next.pSteps.clone(), next.source);
|
||||||
}
|
}
|
||||||
else if (next.steps.isNop()) {
|
else if (next.steps.isNop()) {
|
||||||
Sequence pTicks = this.steps.clone();
|
Sequence pTicks = this.pSteps.clone();
|
||||||
pTicks.advance(next.pSteps);
|
pTicks.advance(next.pSteps);
|
||||||
return new TraceSchedule(this.snap, this.steps.clone(), pTicks);
|
return new TraceSchedule(this.snap, this.steps.clone(), pTicks, Source.INPUT);
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Cannot have instructions steps following p-code steps");
|
throw new IllegalArgumentException("Cannot have instructions steps following p-code steps");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop the p-code steps
|
||||||
|
*
|
||||||
|
* @return the schedule without ops
|
||||||
|
*/
|
||||||
|
public TraceSchedule dropPSteps() {
|
||||||
|
return new TraceSchedule(this.snap, this.steps, new Sequence());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the threads involved in the schedule
|
* Get the threads involved in the schedule
|
||||||
*
|
*
|
||||||
@ -611,4 +669,8 @@ public class TraceSchedule implements Comparable<TraceSchedule> {
|
|||||||
result.remove(null);
|
result.remove(null);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TraceSchedule assumeRecorded() {
|
||||||
|
return new TraceSchedule(snap, steps, pSteps, Source.RECORD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -19,13 +19,16 @@ import static ghidra.lifecycle.Unfinished.TODO;
|
|||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import db.Transaction;
|
import db.Transaction;
|
||||||
|
import ghidra.app.plugin.assembler.*;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.model.address.AddressSet;
|
import ghidra.program.disassemble.Disassembler;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
@ -35,6 +38,13 @@ import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
|||||||
import ghidra.trace.database.ToyDBTraceBuilder;
|
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||||
import ghidra.trace.database.listing.DBTraceCodeManager;
|
import ghidra.trace.database.listing.DBTraceCodeManager;
|
||||||
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
import ghidra.trace.database.memory.DBTraceMemoryManager;
|
||||||
|
import ghidra.trace.model.Lifespan;
|
||||||
|
import ghidra.trace.model.memory.TraceMemoryFlag;
|
||||||
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
|
import ghidra.trace.model.time.TraceSnapshot;
|
||||||
|
import ghidra.trace.model.time.TraceTimeManager;
|
||||||
|
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||||
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
@ -48,9 +58,11 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||||||
DBTraceCodeManager code;
|
DBTraceCodeManager code;
|
||||||
|
|
||||||
protected static void assertUndefined(CodeUnit cu) {
|
protected static void assertUndefined(CodeUnit cu) {
|
||||||
Data data = (Data) cu;
|
if (cu instanceof Data data && DataType.DEFAULT.equals(data.getDataType()) &&
|
||||||
assertEquals(DataType.DEFAULT, data.getDataType());
|
!data.isDefined()) {
|
||||||
assertFalse(data.isDefined());
|
return;
|
||||||
|
}
|
||||||
|
fail("Expected undefined unit, but was '%s'".formatted(cu));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> List<T> takeN(int n, Iterator<T> it) {
|
protected <T> List<T> takeN(int n, Iterator<T> it) {
|
||||||
@ -896,4 +908,81 @@ public class DBTraceProgramViewListingTest extends AbstractGhidraHeadlessIntegra
|
|||||||
assertArrayEquals(b.arr(1), cu0.getBytes());
|
assertArrayEquals(b.arr(1), cu0.getBytes());
|
||||||
assertArrayEquals(b.arr(8), cu1.getBytes());
|
assertArrayEquals(b.arr(8), cu1.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetCodeUnitsInScratchView() throws Throwable {
|
||||||
|
TraceTimeManager tm = b.trace.getTimeManager();
|
||||||
|
Address entry = b.addr(0x00400000);
|
||||||
|
AddressSetView set = b.set(b.range(0x00400000, 0x00400003));
|
||||||
|
Assembler asm = Assemblers.getAssembler(b.language);
|
||||||
|
|
||||||
|
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||||
|
buf.assemble("imm r1, #234");
|
||||||
|
buf.assemble("add r1, r1");
|
||||||
|
|
||||||
|
final long snap;
|
||||||
|
try (Transaction tx = b.startTransaction()) {
|
||||||
|
TraceThread thread = b.getOrAddThread("Threads[1]", 0);
|
||||||
|
tm.getSnapshot(0, true);
|
||||||
|
memory.addRegion("Memory[test]", Lifespan.nowOn(0), b.range(0x00400000, 0x00400fff),
|
||||||
|
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
|
||||||
|
|
||||||
|
TraceSnapshot scratch = tm.getSnapshot(Long.MIN_VALUE, true);
|
||||||
|
snap = scratch.getKey();
|
||||||
|
scratch.setSchedule(TraceSchedule.ZERO.steppedForward(thread, 1));
|
||||||
|
|
||||||
|
view.setSnap(snap);
|
||||||
|
Disassembler dis =
|
||||||
|
Disassembler.getDisassembler(view, TaskMonitor.DUMMY, msg -> Msg.error(this, msg));
|
||||||
|
AddressSetView result = dis.disassemble(entry, set);
|
||||||
|
assertEquals(set, result);
|
||||||
|
|
||||||
|
assertEquals(4, memory.putBytes(0, entry, ByteBuffer.wrap(buf.getBytes())));
|
||||||
|
// No disassembly at snap 0
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] arr = new byte[4];
|
||||||
|
view.getMemory().getBytes(entry, arr);
|
||||||
|
assertArrayEquals(buf.getBytes(), arr);
|
||||||
|
assertUndefined(listing.getCodeUnitAt(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateCodeUnitsInScratchViewAfterBytesChanged() throws Throwable {
|
||||||
|
TraceTimeManager tm = b.trace.getTimeManager();
|
||||||
|
Address entry = b.addr(0x00400000);
|
||||||
|
AddressSetView set = b.set(b.range(0x00400000, 0x00400003));
|
||||||
|
Assembler asm = Assemblers.getAssembler(b.language);
|
||||||
|
|
||||||
|
AssemblyBuffer buf = new AssemblyBuffer(asm, entry);
|
||||||
|
buf.assemble("imm r1, #234");
|
||||||
|
buf.assemble("add r1, r1");
|
||||||
|
|
||||||
|
final long snap;
|
||||||
|
try (Transaction tx = b.startTransaction()) {
|
||||||
|
TraceThread thread = b.getOrAddThread("Threads[1]", 0);
|
||||||
|
tm.getSnapshot(0, true);
|
||||||
|
memory.addRegion("Memory[test]", Lifespan.nowOn(0), b.range(0x00400000, 0x00400fff),
|
||||||
|
TraceMemoryFlag.READ, TraceMemoryFlag.EXECUTE);
|
||||||
|
|
||||||
|
TraceSnapshot scratch = tm.getSnapshot(Long.MIN_VALUE, true);
|
||||||
|
snap = scratch.getKey();
|
||||||
|
scratch.setSchedule(TraceSchedule.ZERO.steppedForward(thread, 1));
|
||||||
|
|
||||||
|
view.setSnap(snap);
|
||||||
|
Disassembler dis =
|
||||||
|
Disassembler.getDisassembler(view, TaskMonitor.DUMMY, msg -> Msg.error(this, msg));
|
||||||
|
AddressSetView result = dis.disassemble(entry, set);
|
||||||
|
assertEquals(set, result);
|
||||||
|
|
||||||
|
assertEquals(4, memory.putBytes(0, entry, ByteBuffer.wrap(buf.getBytes())));
|
||||||
|
// No disassembly at snap 0
|
||||||
|
|
||||||
|
// Attempt re-disassembly at scratch snap
|
||||||
|
result = dis.disassemble(entry, set);
|
||||||
|
assertEquals(set, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("imm r1,#0xea", listing.getCodeUnitAt(entry).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -224,19 +224,30 @@ public class TraceScheduleTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
expectU("0:10", "1:10");
|
expectU("0:10", "1:10");
|
||||||
expectU("0:t0-10", "0:t1-10");
|
expectU("0:t0-10", "0:t1-10");
|
||||||
// We don't know how many p-code steps complete an instruction step
|
// We don't know how many p-code steps complete an instruction step
|
||||||
expectU("0:t0-10.1", "0:t0-11");
|
// But we need at least 2 to actually enter the instruction
|
||||||
|
expectU("0:t0-10.2", "0:t0-11");
|
||||||
expectU("0:t0-10;t1-5", "0:t0-11;t1-5");
|
expectU("0:t0-10;t1-5", "0:t0-11;t1-5");
|
||||||
|
|
||||||
expectR("0:t0-10", "0:t0-11");
|
expectR("0:t0-10", "0:t0-11");
|
||||||
expectR("0:t0-10", "0:t0-10;t1-5");
|
expectR("0:t0-10", "0:t0-10;t1-5");
|
||||||
expectR("0:t0-10", "0:t0-11;t1-5");
|
expectR("0:t0-10", "0:t0-11;t1-5");
|
||||||
expectR("0:t0-10", "0:t0-10.1");
|
expectR("0:t0-10", "0:t0-10.2");
|
||||||
expectR("0:t0-10", "0:t0-11.1");
|
expectR("0:t0-10", "0:t0-11.2");
|
||||||
expectR("0:t0-10", "0:t0-10;t1-5.1");
|
expectR("0:t0-10", "0:t0-10;t1-5.2");
|
||||||
expectR("0:t0-10", "0:t0-11;t1-5.1");
|
expectR("0:t0-10", "0:t0-11;t1-5.2");
|
||||||
|
|
||||||
expectE("0:t0-10", "0:t0-10");
|
expectE("0:t0-10", "0:t0-10");
|
||||||
expectE("0:t0-10.1", "0:t0-10.1");
|
expectE("0:t0-10.2", "0:t0-10.2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompare2() {
|
||||||
|
TraceSchedule timeL = TraceSchedule.parse("0:t0-2.5");
|
||||||
|
TraceSchedule timeR = TraceSchedule.parse("0:t0-3");
|
||||||
|
|
||||||
|
assertEquals(CompareResult.UNREL_LT, timeL.compareSchedule(timeR));
|
||||||
|
assertEquals(CompareResult.REL_LT, timeL.assumeRecorded().compareSchedule(timeR));
|
||||||
|
assertEquals(CompareResult.UNREL_LT, timeL.compareSchedule(timeR.assumeRecorded()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String strRelativize(String fromSpec, String toSpec) {
|
public String strRelativize(String fromSpec, String toSpec) {
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -400,6 +400,7 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stepInstruction() {
|
public void stepInstruction() {
|
||||||
|
assertCompletedInstruction();
|
||||||
PcodeProgram inj = getInject(counter);
|
PcodeProgram inj = getInject(counter);
|
||||||
if (inj != null) {
|
if (inj != null) {
|
||||||
instruction = null;
|
instruction = null;
|
||||||
@ -576,7 +577,6 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void executeInstruction() {
|
public void executeInstruction() {
|
||||||
assertCompletedInstruction();
|
|
||||||
instruction = decoder.decodeInstruction(counter, context);
|
instruction = decoder.decodeInstruction(counter, context);
|
||||||
PcodeProgram insProg = PcodeProgram.fromInstruction(instruction);
|
PcodeProgram insProg = PcodeProgram.fromInstruction(instruction);
|
||||||
preExecuteInstruction();
|
preExecuteInstruction();
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -159,7 +159,7 @@ public class PcodeFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of p-code ops executed
|
* Get and reset the number of p-code ops executed
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Contrast this to {@link #index()}, which marks the next op to be executed. This counts the
|
* Contrast this to {@link #index()}, which marks the next op to be executed. This counts the
|
||||||
@ -167,7 +167,9 @@ public class PcodeFrame {
|
|||||||
*
|
*
|
||||||
* @return the count
|
* @return the count
|
||||||
*/
|
*/
|
||||||
public int count() {
|
public int resetCount() {
|
||||||
|
int count = this.count;
|
||||||
|
this.count = 0;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user