GP-4546: Add 'Set breakpoint' multi-action to Breakpoints window.
|
@ -144,8 +144,8 @@ def find_thread_by_regs_obj(object):
|
|||
def find_frame_by_level(level):
|
||||
for f in util.dbg._base.backtrace_list():
|
||||
if f.FrameNumber == level:
|
||||
return f
|
||||
#return dbg().backtrace_list()[level]
|
||||
return f
|
||||
# return dbg().backtrace_list()[level]
|
||||
|
||||
|
||||
def find_frame_by_pattern(pattern, object, err_msg):
|
||||
|
@ -206,8 +206,8 @@ def execute(cmd: str, to_string: bool=False):
|
|||
@REGISTRY.method(action='evaluate', display='Evaluate')
|
||||
# @util.dbg.eng_thread
|
||||
def evaluate(
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
"""Evaluate a Python3 expression."""
|
||||
return str(eval(expr, shared_globals))
|
||||
|
||||
|
@ -328,8 +328,8 @@ def remove_process(process: sch.Schema('Process')):
|
|||
@REGISTRY.method(action='connect', display='Connect')
|
||||
@util.dbg.eng_thread
|
||||
def target(
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
"""Connect to a target machine or process."""
|
||||
dbg().attach_kernel(cmd)
|
||||
|
||||
|
@ -345,8 +345,8 @@ def attach_obj(target: sch.Schema('Attachable')):
|
|||
@REGISTRY.method(action='attach', display='Attach by pid')
|
||||
@util.dbg.eng_thread
|
||||
def attach_pid(
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(int(pid))
|
||||
|
||||
|
@ -354,8 +354,8 @@ def attach_pid(
|
|||
@REGISTRY.method(action='attach', display='Attach by name')
|
||||
@util.dbg.eng_thread
|
||||
def attach_name(
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(name)
|
||||
|
||||
|
@ -369,7 +369,7 @@ def detach(process: sch.Schema('Process')):
|
|||
|
||||
@REGISTRY.method(action='launch', display='Launch')
|
||||
def launch_loader(
|
||||
session: sch.Schema('Session'),
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
|
@ -383,7 +383,7 @@ def launch_loader(
|
|||
|
||||
@REGISTRY.method(action='launch', display='LaunchEx')
|
||||
def launch(
|
||||
session: sch.Schema('Session'),
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')='',
|
||||
initial_break: ParamDesc(bool, display='Initial Break')=True,
|
||||
|
@ -405,7 +405,7 @@ def kill(process: sch.Schema('Process')):
|
|||
commands.ghidra_trace_kill()
|
||||
|
||||
|
||||
@REGISTRY.method(action='resume')
|
||||
@REGISTRY.method(action='resume', display="Go")
|
||||
def go(process: sch.Schema('Process')):
|
||||
"""Continue execution of the process."""
|
||||
util.dbg.run_async(lambda: dbg().go())
|
||||
|
@ -456,7 +456,7 @@ def break_address(process: sch.Schema('Process'), address: Address):
|
|||
dbg().bp(expr=address.offset)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_expression(expression: str):
|
||||
"""Set a breakpoint."""
|
||||
|
@ -472,7 +472,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
|
|||
dbg().ba(expr=address.offset)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_hw_expression(expression: str):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
|
@ -482,50 +482,50 @@ def break_hw_expression(expression: str):
|
|||
@REGISTRY.method(action='break_read')
|
||||
@util.dbg.eng_thread
|
||||
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a read watchpoint."""
|
||||
"""Set a read breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
@REGISTRY.method(action='break_ext', display='Set Read Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_read_expression(expression: str):
|
||||
"""Set a read watchpoint."""
|
||||
"""Set a read breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
@util.dbg.eng_thread
|
||||
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a watchpoint."""
|
||||
"""Set a write breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
@REGISTRY.method(action='break_ext', display='Set Write Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_write_expression(expression: str):
|
||||
"""Set a watchpoint."""
|
||||
"""Set a write breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
@util.dbg.eng_thread
|
||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set an access watchpoint."""
|
||||
"""Set an access breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(),
|
||||
access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
@REGISTRY.method(action='break_ext', display='Set Access Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def break_access_expression(expression: str):
|
||||
"""Set an access watchpoint."""
|
||||
"""Set an access breakpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
"""Toggle a breakpoint."""
|
||||
|
@ -536,7 +536,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
|||
dbg().bd(bpt.GetId())
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display='Delete Breakpoint')
|
||||
@util.dbg.eng_thread
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
"""Delete a breakpoint."""
|
||||
|
|
|
@ -391,19 +391,19 @@ def refresh_sections(node: sch.Schema('Module')):
|
|||
gdb.execute(f'ghidra trace put-sections "{modname}"')
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display="Activate Inferior")
|
||||
def activate_inferior(inferior: sch.Schema('Inferior')):
|
||||
"""Switch to the inferior."""
|
||||
switch_inferior(find_inf_by_obj(inferior))
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display="Activate Thread")
|
||||
def activate_thread(thread: sch.Schema('Thread')):
|
||||
"""Switch to the thread."""
|
||||
find_thread_by_obj(thread).switch()
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display="Activate Frame")
|
||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||
"""Select the frame."""
|
||||
find_frame_by_obj(frame).select()
|
||||
|
@ -415,7 +415,7 @@ def add_inferior(container: sch.Schema('InferiorContainer')):
|
|||
gdb.execute('add-inferior')
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display="Delete Inferior")
|
||||
def delete_inferior(inferior: sch.Schema('Inferior')):
|
||||
"""Remove the inferior."""
|
||||
inf = find_inf_by_obj(inferior)
|
||||
|
@ -433,7 +433,7 @@ def connect(inferior: sch.Schema('Inferior'), spec: str):
|
|||
@REGISTRY.method(action='attach', display='Attach')
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
"""Attach the inferior to the given target."""
|
||||
#switch_inferior(find_inf_by_obj(inferior))
|
||||
# switch_inferior(find_inf_by_obj(inferior))
|
||||
pid = find_availpid_by_obj(target)
|
||||
gdb.execute(f'attach {pid}')
|
||||
|
||||
|
@ -577,7 +577,7 @@ def break_sw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
|
|||
gdb.execute(f'break *0x{offset:x}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
@REGISTRY.method(action='break_ext', display="Set Breakpoint")
|
||||
def break_sw_execute_expression(expression: str):
|
||||
"""Set a breakpoint (break)."""
|
||||
# TODO: Escape?
|
||||
|
@ -592,7 +592,7 @@ def break_hw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
|
|||
gdb.execute(f'hbreak *0x{offset:x}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
@REGISTRY.method(action='break_ext', display="Set Hardware Breakpoint")
|
||||
def break_hw_execute_expression(expression: str):
|
||||
"""Set a hardware-assisted breakpoint (hbreak)."""
|
||||
# TODO: Escape?
|
||||
|
@ -609,7 +609,7 @@ def break_read_range(inferior: sch.Schema('Inferior'), range: AddressRange):
|
|||
f'rwatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
@REGISTRY.method(action='break_ext', display="Set Read Watchpoint")
|
||||
def break_read_expression(expression: str):
|
||||
"""Set a read watchpoint (rwatch)."""
|
||||
gdb.execute(f'rwatch {expression}')
|
||||
|
@ -625,7 +625,7 @@ def break_write_range(inferior: sch.Schema('Inferior'), range: AddressRange):
|
|||
f'watch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
@REGISTRY.method(action='break_ext', display="Set Watchpoint")
|
||||
def break_write_expression(expression: str):
|
||||
"""Set a watchpoint (watch)."""
|
||||
gdb.execute(f'watch {expression}')
|
||||
|
@ -641,7 +641,7 @@ def break_access_range(inferior: sch.Schema('Inferior'), range: AddressRange):
|
|||
f'awatch -location *((char(*)[{range.length()}]) 0x{offset_start:x})')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
@REGISTRY.method(action='break_ext', display="Set Access Watchpoint")
|
||||
def break_access_expression(expression: str):
|
||||
"""Set an access watchpoint (awatch)."""
|
||||
gdb.execute(f'awatch {expression}')
|
||||
|
@ -653,21 +653,23 @@ def break_event(inferior: sch.Schema('Inferior'), spec: str):
|
|||
gdb.execute(f'catch {spec}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
@REGISTRY.method(action='toggle', display="Toggle Breakpoint")
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
"""Toggle a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
bpt.enabled = enabled
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle', condition=util.GDB_VERSION.major >= 13)
|
||||
@REGISTRY.method(action='toggle', display="Toggle Breakpoint Location",
|
||||
condition=util.GDB_VERSION.major >= 13)
|
||||
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||
"""Toggle a breakpoint location."""
|
||||
loc = find_bpt_loc_by_obj(location)
|
||||
loc.enabled = enabled
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle', condition=util.GDB_VERSION.major < 13)
|
||||
@REGISTRY.method(action='toggle', display="Toggle Breakpoint Location",
|
||||
condition=util.GDB_VERSION.major < 13)
|
||||
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||
"""Toggle a breakpoint location."""
|
||||
bptnum, locnum = find_bptlocnum_by_obj(location)
|
||||
|
@ -675,7 +677,7 @@ def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabl
|
|||
gdb.execute(f'{cmd} {bptnum}.{locnum}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display="Delete Breakpoint")
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
"""Delete a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
|
|
|
@ -17,11 +17,11 @@ from concurrent.futures import Future, ThreadPoolExecutor
|
|||
import re
|
||||
import sys
|
||||
|
||||
import lldb
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||
|
||||
import lldb
|
||||
|
||||
from . import commands, util
|
||||
|
||||
|
||||
|
@ -248,7 +248,7 @@ def execute(cmd: str, to_string: bool=False):
|
|||
return exec_convert_errors(cmd, to_string)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method(display='Evaluate')
|
||||
def evaluate(expr: str):
|
||||
"""Evaluate an expression."""
|
||||
value = util.get_target().EvaluateExpression(expr)
|
||||
|
@ -257,7 +257,7 @@ def evaluate(expr: str):
|
|||
return commands.convert_value(value)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
@REGISTRY.method(display="Python Evaluate")
|
||||
def pyeval(expr: str):
|
||||
return eval(expr)
|
||||
|
||||
|
@ -345,28 +345,28 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
|
|||
exec_convert_errors('ghidra trace put-modules')
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display='Activate Process')
|
||||
def activate_process(process: sch.Schema('Process')):
|
||||
"""Switch to the process."""
|
||||
# TODO
|
||||
return
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display='Activate Thread')
|
||||
def activate_thread(thread: sch.Schema('Thread')):
|
||||
"""Switch to the thread."""
|
||||
t = find_thread_by_obj(thread)
|
||||
t.process.SetSelectedThread(t)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
@REGISTRY.method(action='activate', display='Activate Frame')
|
||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||
"""Select the frame."""
|
||||
f = find_frame_by_obj(frame)
|
||||
f.thread.SetSelectedFrame(f.GetFrameID())
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display='Remove Process')
|
||||
def remove_process(process: sch.Schema('Process')):
|
||||
"""Remove the process."""
|
||||
proc = find_proc_by_obj(process)
|
||||
|
@ -442,7 +442,7 @@ def kill(process: sch.Schema('Process')):
|
|||
exec_convert_errors('process kill')
|
||||
|
||||
|
||||
@REGISTRY.method(name='continue', action='resume')
|
||||
@REGISTRY.method(name='continue', action='resume', display="Continue")
|
||||
def _continue(process: sch.Schema('Process')):
|
||||
"""Continue execution of the process."""
|
||||
exec_convert_errors('process continue')
|
||||
|
@ -510,7 +510,7 @@ def break_address(process: sch.Schema('Process'), address: Address):
|
|||
exec_convert_errors(f'breakpoint set -a 0x{offset:x}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
|
||||
def break_expression(expression: str):
|
||||
"""Set a breakpoint."""
|
||||
# TODO: Escape?
|
||||
|
@ -525,7 +525,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
|
|||
exec_convert_errors(f'breakpoint set -H -a 0x{offset:x}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
|
||||
def break_hw_expression(expression: str):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
# TODO: Escape?
|
||||
|
@ -543,7 +543,7 @@ def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
|||
f'watchpoint set expression -s {sz} -w read -- {offset_start}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
@REGISTRY.method(action='break_ext', display='Set Read Watchpoint')
|
||||
def break_read_expression(expression: str, size=None):
|
||||
"""Set a read watchpoint."""
|
||||
size_part = '' if size is None else f'-s {size}'
|
||||
|
@ -562,7 +562,7 @@ def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
|||
f'watchpoint set expression -s {sz} -- {offset_start}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
@REGISTRY.method(action='break_ext', display='Set Watchpoint')
|
||||
def break_write_expression(expression: str, size=None):
|
||||
"""Set a watchpoint."""
|
||||
size_part = '' if size is None else f'-s {size}'
|
||||
|
@ -572,7 +572,7 @@ def break_write_expression(expression: str, size=None):
|
|||
|
||||
@REGISTRY.method(action='break_access')
|
||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set an access watchpoint."""
|
||||
"""Set a read/write watchpoint."""
|
||||
proc = find_proc_by_obj(process)
|
||||
offset_start = process.trace.memory_mapper.map_back(
|
||||
proc, Address(range.space, range.min))
|
||||
|
@ -581,9 +581,9 @@ def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
|||
f'watchpoint set expression -s {sz} -w read_write -- {offset_start}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
@REGISTRY.method(action='break_ext', display='Set Read/Write Watchpoint')
|
||||
def break_access_expression(expression: str, size=None):
|
||||
"""Set an access watchpoint."""
|
||||
"""Set a read/write watchpoint."""
|
||||
size_part = '' if size is None else f'-s {size}'
|
||||
exec_convert_errors(
|
||||
f'watchpoint set expression {size_part} -w read_write -- {expression}')
|
||||
|
@ -595,7 +595,7 @@ def break_exception(lang: str):
|
|||
exec_convert_errors(f'breakpoint set -E {lang}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
@REGISTRY.method(action='toggle', display='Toggle Watchpoint')
|
||||
def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
|
||||
"""Toggle a watchpoint."""
|
||||
wpt = find_wpt_by_obj(watchpoint)
|
||||
|
@ -604,7 +604,7 @@ def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
|
|||
exec_convert_errors(f'watchpoint {cmd} {wpt.GetID()}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
@REGISTRY.method(action='toggle', display='Toggle Breakpoint')
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
"""Toggle a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
|
@ -612,7 +612,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
|||
exec_convert_errors(f'breakpoint {cmd} {bpt.GetID()}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
@REGISTRY.method(action='toggle', display='Toggle Breakpoint Location')
|
||||
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||
"""Toggle a breakpoint location."""
|
||||
bptnum, locnum = find_bptlocnum_by_obj(location)
|
||||
|
@ -620,7 +620,7 @@ def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabl
|
|||
exec_convert_errors(f'breakpoint {cmd} {bptnum}.{locnum}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display='Delete Watchpoint')
|
||||
def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
|
||||
"""Delete a watchpoint."""
|
||||
wpt = find_wpt_by_obj(watchpoint)
|
||||
|
@ -628,7 +628,7 @@ def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
|
|||
exec_convert_errors(f'watchpoint delete {wptnum}')
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
@REGISTRY.method(action='delete', display='Delete Breakpoint')
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
"""Delete a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,8 +15,16 @@
|
|||
*/
|
||||
package ghidra.debug.api.target;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.trace.model.TraceExecutionState;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
|
||||
/**
|
||||
* A name for a commonly-recognized target action.
|
||||
|
@ -35,21 +43,137 @@ import java.util.Map;
|
|||
* the UI to decide what is presented where.
|
||||
*
|
||||
* @param name the name of the action (given as the action attribute on method annotations)
|
||||
* @param builtIn true if the action should <em>not</em> be presented in generic contexts, but
|
||||
* reserved for built-in, purpose-specific actions
|
||||
* @param show when to show the general UI action for this debugger action
|
||||
* @param enabler determines when the action is enabled, based on the object
|
||||
* @param display the default text to display
|
||||
* @param icon the default icon for menus and dialogs
|
||||
* @param okText the default text for confirm buttons in dialogs
|
||||
*/
|
||||
public record ActionName(String name, boolean builtIn) {
|
||||
public record ActionName(String name, Show show, Enabler enabler, String display, Icon icon,
|
||||
String okText) {
|
||||
|
||||
private static final Icon ICON_ATTACH = new GIcon("icon.debugger.attach");
|
||||
private static final Icon ICON_CONNECT = new GIcon("icon.debugger.connect");
|
||||
private static final Icon ICON_DETACH = new GIcon("icon.debugger.detach");
|
||||
private static final Icon ICON_INTERRUPT = new GIcon("icon.debugger.interrupt");
|
||||
private static final Icon ICON_KILL = new GIcon("icon.debugger.kill");
|
||||
private static final Icon ICON_LAUNCH = new GIcon("icon.debugger.launch");
|
||||
private static final Icon ICON_REFRESH = new GIcon("icon.debugger.refresh");
|
||||
private static final Icon ICON_RESUME = new GIcon("icon.debugger.resume");
|
||||
private static final Icon ICON_STEP_BACK = new GIcon("icon.debugger.step.back");
|
||||
private static final Icon ICON_STEP_INTO = new GIcon("icon.debugger.step.into");
|
||||
private static final Icon ICON_STEP_LAST = new GIcon("icon.debugger.step.last");
|
||||
private static final Icon ICON_STEP_OUT = new GIcon("icon.debugger.step.finish");
|
||||
private static final Icon ICON_STEP_OVER = new GIcon("icon.debugger.step.over");
|
||||
private static final Icon ICON_SKIP_OVER = new GIcon("icon.debugger.skip.over");
|
||||
private static final Icon ICON_SET_BREAKPOINT = new GIcon("icon.debugger.breakpoint.set");
|
||||
|
||||
private static final Map<String, ActionName> NAMES = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Specifies when an action should appear in the menus. For diagnostics, a user may override
|
||||
* this by holding SHIFT when right-clicking, causing all applicable general actions to appear.
|
||||
*/
|
||||
public enum Show {
|
||||
/**
|
||||
* Don't show general actions. The tool has built-in actions that already know how to invoke
|
||||
* this.
|
||||
*/
|
||||
BUILTIN {
|
||||
@Override
|
||||
boolean doIsShowing(ActionContext context) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Only show general actions in address-based context, e.g., when right-clicking in the
|
||||
* listing.
|
||||
*/
|
||||
ADDRESS {
|
||||
@Override
|
||||
boolean doIsShowing(ActionContext context) {
|
||||
return context instanceof ProgramLocationActionContext;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Show in all contexts. This is the default.
|
||||
*/
|
||||
EXTENDED {
|
||||
@Override
|
||||
boolean doIsShowing(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public boolean isShowing(ActionContext context) {
|
||||
if (isOverriden(context)) {
|
||||
return true;
|
||||
}
|
||||
return doIsShowing(context);
|
||||
}
|
||||
|
||||
abstract boolean doIsShowing(ActionContext context);
|
||||
|
||||
private boolean isOverriden(ActionContext context) {
|
||||
return (context.getEventClickModifiers() & InputEvent.SHIFT_DOWN_MASK) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Enabler {
|
||||
ALWAYS {
|
||||
@Override
|
||||
public boolean isEnabled(TraceObject obj, long snap) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
NOT_RUNNING {
|
||||
@Override
|
||||
boolean doIsEnabled(TraceExecutionState state) {
|
||||
return state != null && state != TraceExecutionState.RUNNING;
|
||||
}
|
||||
},
|
||||
NOT_STOPPED {
|
||||
@Override
|
||||
boolean doIsEnabled(TraceExecutionState state) {
|
||||
return state != TraceExecutionState.STOPPED;
|
||||
}
|
||||
},
|
||||
NOT_DEAD {
|
||||
@Override
|
||||
boolean doIsEnabled(TraceExecutionState state) {
|
||||
return state != TraceExecutionState.TERMINATED;
|
||||
}
|
||||
};
|
||||
|
||||
private TraceExecutionState getState(TraceObject obj, long snap) {
|
||||
try {
|
||||
return obj.getExecutionState(snap);
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
return TraceExecutionState.TERMINATED;
|
||||
}
|
||||
}
|
||||
|
||||
boolean doIsEnabled(TraceExecutionState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEnabled(TraceObject obj, long snap) {
|
||||
return doIsEnabled(getState(obj, snap));
|
||||
}
|
||||
}
|
||||
|
||||
public static ActionName name(String name) {
|
||||
synchronized (NAMES) {
|
||||
return NAMES.computeIfAbsent(name, n -> new ActionName(n, false));
|
||||
return NAMES.computeIfAbsent(name,
|
||||
n -> new ActionName(n, Show.EXTENDED, Enabler.ALWAYS, n, null, "OK"));
|
||||
}
|
||||
}
|
||||
|
||||
private static ActionName builtIn(String name) {
|
||||
private static ActionName create(String name, Show show, Enabler enabler, String display,
|
||||
Icon icon, String okText) {
|
||||
synchronized (NAMES) {
|
||||
ActionName action = new ActionName(name, true);
|
||||
ActionName action = new ActionName(name, show, enabler, display, icon, okText);
|
||||
if (NAMES.put(name, action) != null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
@ -57,24 +181,16 @@ public record ActionName(String name, boolean builtIn) {
|
|||
}
|
||||
}
|
||||
|
||||
private static ActionName extended(String name) {
|
||||
synchronized (NAMES) {
|
||||
ActionName action = new ActionName(name, false);
|
||||
if (NAMES.put(name, action) != null) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
public static final ActionName REFRESH = extended("refresh");
|
||||
public static final ActionName REFRESH =
|
||||
create("refresh", Show.EXTENDED, Enabler.ALWAYS, "Refresh", ICON_REFRESH, "Refresh");
|
||||
/**
|
||||
* Activate a given object and optionally a time
|
||||
*
|
||||
* <p>
|
||||
* Forms: (focus:Object), (focus:Object, snap:LONG), (focus:Object, time:STR)
|
||||
*/
|
||||
public static final ActionName ACTIVATE = builtIn("activate");
|
||||
public static final ActionName ACTIVATE =
|
||||
create("activate", Show.BUILTIN, Enabler.ALWAYS, "Activate", null, "Activate");
|
||||
/**
|
||||
* A weaker form of activate.
|
||||
*
|
||||
|
@ -83,9 +199,12 @@ public record ActionName(String name, boolean builtIn) {
|
|||
* used to communicate selection (i.e., highlight) of the object. Whereas, double-clicking or
|
||||
* pressing enter would more likely invoke 'activate.'
|
||||
*/
|
||||
public static final ActionName FOCUS = builtIn("focus");
|
||||
public static final ActionName TOGGLE = builtIn("toggle");
|
||||
public static final ActionName DELETE = builtIn("delete");
|
||||
public static final ActionName FOCUS =
|
||||
create("focus", Show.BUILTIN, Enabler.ALWAYS, "Focus", null, "Focus");
|
||||
public static final ActionName TOGGLE =
|
||||
create("toggle", Show.BUILTIN, Enabler.ALWAYS, "Toggle", null, "Toggle");
|
||||
public static final ActionName DELETE =
|
||||
create("delete", Show.BUILTIN, Enabler.ALWAYS, "Delete", null, "Delete");
|
||||
|
||||
/**
|
||||
* Execute a CLI command
|
||||
|
@ -93,7 +212,8 @@ public record ActionName(String name, boolean builtIn) {
|
|||
* <p>
|
||||
* Forms: (cmd:STRING):STRING; Optional arguments: capture:BOOL
|
||||
*/
|
||||
public static final ActionName EXECUTE = builtIn("execute");
|
||||
public static final ActionName EXECUTE =
|
||||
create("execute", Show.BUILTIN, Enabler.ALWAYS, "Execute", null, "Execute");
|
||||
|
||||
/**
|
||||
* Connect the back-end to a (usually remote) target
|
||||
|
@ -101,23 +221,31 @@ public record ActionName(String name, boolean builtIn) {
|
|||
* <p>
|
||||
* Forms: (spec:STRING)
|
||||
*/
|
||||
public static final ActionName CONNECT = extended("connect");
|
||||
public static final ActionName CONNECT =
|
||||
create("connect", Show.EXTENDED, Enabler.ALWAYS, "Connect", ICON_CONNECT, "Connect");
|
||||
|
||||
/**
|
||||
* Forms: (target:Attachable), (pid:INT), (spec:STRING)
|
||||
*/
|
||||
public static final ActionName ATTACH = extended("attach");
|
||||
public static final ActionName DETACH = extended("detach");
|
||||
public static final ActionName ATTACH =
|
||||
create("attach", Show.EXTENDED, Enabler.ALWAYS, "Attach", ICON_ATTACH, "Attach");
|
||||
public static final ActionName DETACH =
|
||||
create("detach", Show.EXTENDED, Enabler.ALWAYS, "Detach", ICON_DETACH, "Detach");
|
||||
|
||||
/**
|
||||
* Forms: (command_line:STRING), (file:STRING,args:STRING), (file:STRING,args:STRING_ARRAY),
|
||||
* (ANY*)
|
||||
*/
|
||||
public static final ActionName LAUNCH = extended("launch");
|
||||
public static final ActionName KILL = builtIn("kill");
|
||||
public static final ActionName LAUNCH =
|
||||
create("launch", Show.EXTENDED, Enabler.ALWAYS, "Launch", ICON_LAUNCH, "Launch");
|
||||
public static final ActionName KILL =
|
||||
create("kill", Show.BUILTIN, Enabler.NOT_DEAD, "Kill", ICON_KILL, "Kill");
|
||||
|
||||
public static final ActionName RESUME = builtIn("resume");
|
||||
public static final ActionName INTERRUPT = builtIn("interrupt");
|
||||
public static final ActionName RESUME =
|
||||
create("resume", Show.BUILTIN, Enabler.NOT_RUNNING, "Resume", ICON_RESUME, "Resume");
|
||||
public static final ActionName INTERRUPT =
|
||||
create("interrupt", Show.BUILTIN, Enabler.NOT_STOPPED, "Interrupt", ICON_INTERRUPT,
|
||||
"Interrupt");
|
||||
|
||||
/**
|
||||
* All of these will show in the "step" portion of the control toolbar, if present. The
|
||||
|
@ -128,25 +256,31 @@ public record ActionName(String name, boolean builtIn) {
|
|||
* context. (Multiple will appear, but may confuse the user.) You can have as many extended step
|
||||
* actions as you like. They will be ordered lexicographically by name.
|
||||
*/
|
||||
public static final ActionName STEP_INTO = builtIn("step_into");
|
||||
public static final ActionName STEP_OVER = builtIn("step_over");
|
||||
public static final ActionName STEP_OUT = builtIn("step_out");
|
||||
public static final ActionName STEP_INTO =
|
||||
create("step_into", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Into", ICON_STEP_INTO, "Step");
|
||||
public static final ActionName STEP_OVER =
|
||||
create("step_over", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Over", ICON_STEP_OVER, "Step");
|
||||
public static final ActionName STEP_OUT =
|
||||
create("step_out", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Out", ICON_STEP_OUT, "Step");
|
||||
/**
|
||||
* Skip is not typically available, except in emulators. If the back-end debugger does not have
|
||||
* a command for this action out-of-the-box, we do not recommend trying to implement it
|
||||
* yourself. The purpose of these actions just to expose/map each command to the UI, not to
|
||||
* invent new features for the back-end debugger.
|
||||
*/
|
||||
public static final ActionName STEP_SKIP = builtIn("step_skip");
|
||||
public static final ActionName STEP_SKIP =
|
||||
create("step_skip", Show.BUILTIN, Enabler.NOT_RUNNING, "Skip Over", ICON_SKIP_OVER, "Skip");
|
||||
/**
|
||||
* Step back is not typically available, except in emulators and timeless (or time-travel)
|
||||
* debuggers.
|
||||
*/
|
||||
public static final ActionName STEP_BACK = builtIn("step_back");
|
||||
public static final ActionName STEP_BACK =
|
||||
create("step_back", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Back", ICON_STEP_BACK, "Back");
|
||||
/**
|
||||
* The action for steps that don't fit one of the common stepping actions.
|
||||
*/
|
||||
public static final ActionName STEP_EXT = extended("step_ext");
|
||||
public static final ActionName STEP_EXT =
|
||||
create("step_ext", Show.ADDRESS, Enabler.NOT_RUNNING, null, ICON_STEP_LAST, "Step");
|
||||
|
||||
/**
|
||||
* Forms: (addr:ADDRESS), R/W(rng:RANGE), (expr:STRING)
|
||||
|
@ -158,25 +292,39 @@ public record ActionName(String name, boolean builtIn) {
|
|||
* The client may pass either null or "" for condition and/or commands to indicate omissions of
|
||||
* those arguments.
|
||||
*/
|
||||
public static final ActionName BREAK_SW_EXECUTE = builtIn("break_sw_execute");
|
||||
public static final ActionName BREAK_HW_EXECUTE = builtIn("break_hw_execute");
|
||||
public static final ActionName BREAK_READ = builtIn("break_read");
|
||||
public static final ActionName BREAK_WRITE = builtIn("break_write");
|
||||
public static final ActionName BREAK_ACCESS = builtIn("break_access");
|
||||
public static final ActionName BREAK_EXT = extended("break_ext");
|
||||
public static final ActionName BREAK_SW_EXECUTE =
|
||||
create("break_sw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Software Breakpoint",
|
||||
ICON_SET_BREAKPOINT, "Set");
|
||||
public static final ActionName BREAK_HW_EXECUTE =
|
||||
create("break_hw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Hardware Breakpoint",
|
||||
ICON_SET_BREAKPOINT, "Set");
|
||||
public static final ActionName BREAK_READ =
|
||||
create("break_read", Show.BUILTIN, Enabler.ALWAYS, "Set Read Breakpoint",
|
||||
ICON_SET_BREAKPOINT, "Set");
|
||||
public static final ActionName BREAK_WRITE =
|
||||
create("break_write", Show.BUILTIN, Enabler.ALWAYS, "Set Write Breakpoint",
|
||||
ICON_SET_BREAKPOINT, "Set");
|
||||
public static final ActionName BREAK_ACCESS =
|
||||
create("break_access", Show.BUILTIN, Enabler.ALWAYS, "Set Access Breakpont",
|
||||
ICON_SET_BREAKPOINT, "Set");
|
||||
public static final ActionName BREAK_EXT =
|
||||
create("break_ext", Show.BUILTIN, Enabler.ALWAYS, null, ICON_SET_BREAKPOINT, "Set");
|
||||
|
||||
/**
|
||||
* Forms: (rng:RANGE)
|
||||
*/
|
||||
public static final ActionName READ_MEM = builtIn("read_mem");
|
||||
public static final ActionName READ_MEM =
|
||||
create("read_mem", Show.BUILTIN, Enabler.ALWAYS, "Read Memory", null, "Read");
|
||||
/**
|
||||
* Forms: (addr:ADDRESS,data:BYTES)
|
||||
*/
|
||||
public static final ActionName WRITE_MEM = builtIn("write_mem");
|
||||
public static final ActionName WRITE_MEM =
|
||||
create("write_mem", Show.BUILTIN, Enabler.ALWAYS, "Write Memory", null, "Write");
|
||||
|
||||
// NOTE: no read_reg. Use refresh(RegContainer), refresh(RegGroup), refresh(Register)
|
||||
/**
|
||||
* Forms: (frame:Frame,name:STRING,value:BYTES), (register:Register,value:BYTES)
|
||||
*/
|
||||
public static final ActionName WRITE_REG = builtIn("write_reg");
|
||||
public static final ActionName WRITE_REG =
|
||||
create("write_reg", Show.BUILTIN, Enabler.NOT_RUNNING, "Write Register", null, "Write");
|
||||
}
|
||||
|
|
|
@ -18,16 +18,17 @@ package ghidra.debug.api.target;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import ghidra.debug.api.target.ActionName.Show;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.trace.model.TraceExecutionState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceExecutionState;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
|
@ -58,28 +59,72 @@ public interface Target {
|
|||
* just invoked implicitly. Often, the two suppliers are implemented using lambda functions, and
|
||||
* those functions will keep whatever some means of querying UI and/or target context in their
|
||||
* closures.
|
||||
*
|
||||
* @param display the text to display on UI actions associated with this entry
|
||||
* @param name the name of a common debugger command this action implements
|
||||
* @param details text providing more details, usually displayed in a tool tip
|
||||
* @param requiresPrompt true if invoking the action requires further user interaction
|
||||
* @param specificity a relative score of specificity. These are only meaningful when compared
|
||||
* among entries returned in the same collection.
|
||||
* @param enabled a supplier to determine whether an associated action in the UI is enabled.
|
||||
* @param action a function for invoking this action asynchronously
|
||||
*/
|
||||
record ActionEntry(String display, ActionName name, String details, boolean requiresPrompt,
|
||||
long specificity, BooleanSupplier enabled,
|
||||
Function<Boolean, CompletableFuture<?>> action) {
|
||||
interface ActionEntry {
|
||||
|
||||
/**
|
||||
* Get the text to display on UI actions associated with this entry
|
||||
*
|
||||
* @return the display
|
||||
*/
|
||||
String display();
|
||||
|
||||
/**
|
||||
* Get the name of a common debugger command this action implements
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
ActionName name();
|
||||
|
||||
/**
|
||||
* Get the icon to display in menus and dialogs
|
||||
*
|
||||
* @return the icon
|
||||
*/
|
||||
Icon icon();
|
||||
|
||||
/**
|
||||
* Get the text providing more details, usually displayed in a tool tip
|
||||
*
|
||||
* @return the details
|
||||
*/
|
||||
String details();
|
||||
|
||||
/**
|
||||
* Check whether invoking the action requires further user interaction
|
||||
*
|
||||
* @return true if prompting is required
|
||||
*/
|
||||
boolean requiresPrompt();
|
||||
|
||||
/**
|
||||
* Get a relative score of specificity.
|
||||
*
|
||||
* <p>
|
||||
* These are only meaningful when compared among entries returned in the same collection.
|
||||
*
|
||||
* @return the specificity
|
||||
*/
|
||||
long specificity();
|
||||
|
||||
/**
|
||||
* Invoke the action asynchronously, prompting if desired.
|
||||
*
|
||||
* <p>
|
||||
* The implementation is not required to provide a timeout; however, downstream components
|
||||
* may.
|
||||
*
|
||||
* @param prompt whether or not to prompt the user for arguments
|
||||
* @return the future result, often {@link Void}
|
||||
*/
|
||||
CompletableFuture<?> invokeAsyncWithoutTimeout(boolean prompt);
|
||||
|
||||
/**
|
||||
* Check if this action is currently enabled
|
||||
*
|
||||
* @return true if enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled.getAsBoolean();
|
||||
}
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Invoke the action asynchronously, prompting if desired
|
||||
|
@ -90,8 +135,9 @@ public interface Target {
|
|||
* @param prompt whether or not to prompt the user for arguments
|
||||
* @return the future result, often {@link Void}
|
||||
*/
|
||||
public CompletableFuture<?> invokeAsync(boolean prompt) {
|
||||
return action.apply(prompt).orTimeout(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
||||
default CompletableFuture<?> invokeAsync(boolean prompt) {
|
||||
return invokeAsyncWithoutTimeout(prompt).orTimeout(TIMEOUT_MILLIS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +149,7 @@ public interface Target {
|
|||
*
|
||||
* @param prompt whether or not to prompt the user for arguments
|
||||
*/
|
||||
public void run(boolean prompt) {
|
||||
default void run(boolean prompt) {
|
||||
get(prompt);
|
||||
}
|
||||
|
||||
|
@ -113,7 +159,7 @@ public interface Target {
|
|||
* @param prompt whether or not to prompt the user for arguments
|
||||
* @return the resulting value, if applicable
|
||||
*/
|
||||
public Object get(boolean prompt) {
|
||||
default Object get(boolean prompt) {
|
||||
if (Swing.isSwingThread()) {
|
||||
throw new AssertionError("Refusing to block the Swing thread. Use a Task.");
|
||||
}
|
||||
|
@ -130,8 +176,8 @@ public interface Target {
|
|||
*
|
||||
* @return true if built in.
|
||||
*/
|
||||
public boolean builtIn() {
|
||||
return name != null && name.builtIn();
|
||||
default Show getShow() {
|
||||
return name() == null ? Show.EXTENDED : name().show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.async.AsyncUtils;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
@ -62,6 +64,20 @@ public interface RemoteMethod {
|
|||
*/
|
||||
String display();
|
||||
|
||||
/**
|
||||
* The icon to display in menu's and in the prompt dialog.
|
||||
*
|
||||
* @return the icon
|
||||
*/
|
||||
Icon icon();
|
||||
|
||||
/**
|
||||
* Text to display in the OK button of any prompt dialog.
|
||||
*
|
||||
* @return the text
|
||||
*/
|
||||
String okText();
|
||||
|
||||
/**
|
||||
* A description of the method.
|
||||
*
|
||||
|
|
|
@ -49,7 +49,8 @@ import ghidra.trace.model.guest.TracePlatform;
|
|||
import ghidra.trace.model.memory.TraceMemoryOperations;
|
||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.target.path.KeyPath;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
|
@ -1740,7 +1741,7 @@ public interface FlatDebuggerAPI {
|
|||
* @return true if alive
|
||||
*/
|
||||
default boolean isTargetAlive(Trace trace) {
|
||||
return getExecutionState(trace).isAlive();
|
||||
return getExecutionState(trace) != TraceExecutionState.TERMINATED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1763,7 +1764,7 @@ public interface FlatDebuggerAPI {
|
|||
* @return true if alive
|
||||
*/
|
||||
default boolean isThreadAlive(TraceThread thread) {
|
||||
return getExecutionState(thread).isAlive();
|
||||
return getExecutionState(thread) != TraceExecutionState.TERMINATED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1792,7 +1793,7 @@ public interface FlatDebuggerAPI {
|
|||
* @throws TimeoutException if the timeout expires
|
||||
*/
|
||||
default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException {
|
||||
if (!getExecutionState(trace).isRunning()) {
|
||||
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||
return;
|
||||
}
|
||||
var listener = new DomainObjectListener() {
|
||||
|
@ -1800,14 +1801,14 @@ public interface FlatDebuggerAPI {
|
|||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (!getExecutionState(trace).isRunning()) {
|
||||
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||
future.complete(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
trace.addListener(listener);
|
||||
try {
|
||||
if (!getExecutionState(trace).isRunning()) {
|
||||
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||
return;
|
||||
}
|
||||
listener.future.get(timeout, unit);
|
||||
|
|
|
@ -144,20 +144,10 @@ public class JdiConnector {
|
|||
}
|
||||
|
||||
public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) {
|
||||
String action = name;
|
||||
String display = name;
|
||||
String description = name;
|
||||
TraceMethod annot = m.getAnnotation(TraceMethod.class);
|
||||
if (annot == null) {
|
||||
return;
|
||||
}
|
||||
action = annot.action();
|
||||
if (annot.display() != null) {
|
||||
display = annot.display();
|
||||
}
|
||||
if (annot.description() != null) {
|
||||
description = annot.description();
|
||||
}
|
||||
int pcount = m.getParameterCount();
|
||||
if (pcount < 1) {
|
||||
return;
|
||||
|
@ -167,8 +157,8 @@ public class JdiConnector {
|
|||
* collection routines currently use the return type, so just use ANY for now.
|
||||
*/
|
||||
TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY;
|
||||
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display,
|
||||
description, schema, methods, m);
|
||||
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, annot.action(),
|
||||
annot.display(), annot.description(), annot.okText(), annot.icon(), schema, methods, m);
|
||||
remoteMethodRegistry.putMethod(name, method);
|
||||
}
|
||||
|
||||
|
|
|
@ -605,9 +605,11 @@ public class RmiClient {
|
|||
private Method buildMethod(RmiRemoteMethod method) {
|
||||
Method.Builder builder = Method.newBuilder()
|
||||
.setName(method.getName())
|
||||
.setDescription(method.getDescription())
|
||||
.setAction(method.getAction())
|
||||
.setDisplay(method.getDisplay());
|
||||
.setDisplay(method.getDisplay())
|
||||
.setDescription(method.getDescription())
|
||||
.setOkText(method.getOkText())
|
||||
.setIcon(method.getIcon());
|
||||
int i = 0;
|
||||
for (RmiRemoteMethodParameter p : method.getParameters()) {
|
||||
MethodParameter param = buildParameter(p);
|
||||
|
|
|
@ -32,6 +32,10 @@ public class RmiMethodRegistry {
|
|||
String display() default "";
|
||||
|
||||
String description() default "";
|
||||
|
||||
String okText() default "";
|
||||
|
||||
String icon() default "";
|
||||
}
|
||||
|
||||
Map<String, RmiRemoteMethod> map = new HashMap<>();
|
||||
|
|
|
@ -25,22 +25,27 @@ import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
|||
public class RmiRemoteMethod {
|
||||
|
||||
private final SchemaContext schemaContext;
|
||||
private String name;
|
||||
private String action;
|
||||
private String display;
|
||||
private String description;
|
||||
private RmiRemoteMethodParameter[] params;
|
||||
private TraceObjectSchema schema;
|
||||
private RmiMethods instance;
|
||||
private Method m;
|
||||
private final String name;
|
||||
private final String action;
|
||||
private final String display;
|
||||
private final String description;
|
||||
private final String okText;
|
||||
private final String icon;
|
||||
private final RmiRemoteMethodParameter[] params;
|
||||
private final TraceObjectSchema schema;
|
||||
private final RmiMethods instance;
|
||||
private final Method m;
|
||||
|
||||
public RmiRemoteMethod(SchemaContext schemaContext, String name, String action, String display,
|
||||
String description, TraceObjectSchema schema, RmiMethods instance, Method m) {
|
||||
String description, String okText, String icon, TraceObjectSchema schema,
|
||||
RmiMethods instance, Method m) {
|
||||
this.schemaContext = schemaContext;
|
||||
this.name = name;
|
||||
this.action = action;
|
||||
this.display = display;
|
||||
this.description = description;
|
||||
this.okText = okText;
|
||||
this.icon = icon;
|
||||
this.params = new RmiRemoteMethodParameter[m.getParameterCount()];
|
||||
this.schema = schema;
|
||||
this.instance = instance;
|
||||
|
@ -69,6 +74,14 @@ public class RmiRemoteMethod {
|
|||
return description;
|
||||
}
|
||||
|
||||
public String getOkText() {
|
||||
return okText;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,33 @@ package ghidra.app.plugin.core.debug.service.tracermi;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.debug.api.tracermi.*;
|
||||
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||
import ghidra.debug.api.tracermi.RemoteParameter;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||
|
||||
public record RecordRemoteMethod(TraceRmiHandler handler, String name, ActionName action,
|
||||
String display, String description, Map<String, RemoteParameter> parameters,
|
||||
SchemaName retType) implements RemoteMethod {
|
||||
String display, Icon icon, String okText, String description,
|
||||
Map<String, RemoteParameter> parameters, SchemaName retType) implements RemoteMethod {
|
||||
|
||||
public RecordRemoteMethod {
|
||||
if (display == null || display.isBlank()) {
|
||||
display = action.display();
|
||||
}
|
||||
if (display == null || display.isBlank()) { // still
|
||||
display = name;
|
||||
}
|
||||
if (icon == null) {
|
||||
icon = action.icon();
|
||||
}
|
||||
if (okText.isEmpty() || okText.isBlank()) {
|
||||
okText = action.okText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRemoteAsyncResult invokeAsync(Map<String, Object> arguments) {
|
||||
Trace trace = validate(arguments);
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.commons.lang3.ArrayUtils;
|
|||
import com.google.protobuf.ByteString;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
|
||||
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
|
||||
import ghidra.app.services.DebuggerControlService;
|
||||
|
@ -1027,7 +1028,9 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
|||
}
|
||||
for (Method m : req.getMethodsList()) {
|
||||
RemoteMethod rm = new RecordRemoteMethod(this, m.getName(),
|
||||
ActionName.name(m.getAction()), m.getDisplay(), m.getDescription(),
|
||||
ActionName.name(m.getAction()), m.getDisplay(),
|
||||
m.getIcon().isBlank() ? null : new GIcon(m.getIcon()), m.getOkText(),
|
||||
m.getDescription(),
|
||||
m.getParametersList()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)),
|
||||
|
|
|
@ -19,9 +19,11 @@ import java.io.IOException;
|
|||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
|
@ -60,6 +62,70 @@ import ghidra.util.Msg;
|
|||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class TraceRmiTarget extends AbstractTarget {
|
||||
|
||||
class TraceRmiActionEntry implements ActionEntry {
|
||||
private final RemoteMethod method;
|
||||
private final Map<String, Object> args;
|
||||
private final boolean requiresPrompt;
|
||||
private final long specificity;
|
||||
private final ParamAndObjectArg first;
|
||||
|
||||
public TraceRmiActionEntry(RemoteMethod method, Map<String, Object> args) {
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
|
||||
this.requiresPrompt = args.values().contains(Missing.MISSING);
|
||||
this.specificity = computeSpecificity(args);
|
||||
this.first = getFirstObjectArgument(method, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String display() {
|
||||
return method.display();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionName name() {
|
||||
return method.action();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon icon() {
|
||||
return method.icon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return method.description();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPrompt() {
|
||||
return requiresPrompt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long specificity() {
|
||||
return specificity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> invokeAsyncWithoutTimeout(boolean prompt) {
|
||||
return invokeMethod(prompt, method, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
if (first == null) {
|
||||
return true;
|
||||
}
|
||||
if (first.obj == null) {
|
||||
return false;
|
||||
}
|
||||
return name().enabler().isEnabled(first.obj, getSnap());
|
||||
}
|
||||
}
|
||||
|
||||
private final TraceRmiConnection connection;
|
||||
private final Trace trace;
|
||||
|
||||
|
@ -224,8 +290,12 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A singleton to indicate missing arguments
|
||||
*/
|
||||
public enum Missing {
|
||||
MISSING; // The argument requires a prompt
|
||||
/** The argument requires a prompt */
|
||||
MISSING;
|
||||
}
|
||||
|
||||
protected Object findArgument(ActionName action, RemoteParameter parameter,
|
||||
|
@ -266,28 +336,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
return args;
|
||||
}
|
||||
|
||||
private TraceExecutionState getStateOf(TraceObject object) {
|
||||
try {
|
||||
return object.getExecutionState(getSnap());
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
return TraceExecutionState.TERMINATED;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean whenState(TraceObject object,
|
||||
Predicate<TraceExecutionState> predicate) {
|
||||
try {
|
||||
TraceExecutionState state = getStateOf(object);
|
||||
return state == null || predicate.test(state);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Could not get state: " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected long computeSpecificity(Map<String, Object> args) {
|
||||
protected static long computeSpecificity(Map<String, Object> args) {
|
||||
long score = 0;
|
||||
for (Object o : args.values()) {
|
||||
if (o instanceof TraceObject obj) {
|
||||
|
@ -297,46 +346,34 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
return score;
|
||||
}
|
||||
|
||||
protected BooleanSupplier chooseEnabler(RemoteMethod method, Map<String, Object> args) {
|
||||
ActionName name = method.action();
|
||||
protected RemoteParameter getFirstObjectParameter(RemoteMethod method) {
|
||||
SchemaContext ctx = getSchemaContext();
|
||||
if (ctx == null) {
|
||||
return () -> true;
|
||||
return null;
|
||||
}
|
||||
RemoteParameter firstParam = method.parameters()
|
||||
return method.parameters()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(
|
||||
p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {}
|
||||
|
||||
protected ParamAndObjectArg getFirstObjectArgument(RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
RemoteParameter firstParam = getFirstObjectParameter(method);
|
||||
if (firstParam == null) {
|
||||
return () -> true;
|
||||
return null;
|
||||
}
|
||||
Object firstArg = args.get(firstParam.name());
|
||||
if (firstArg == null || firstArg == Missing.MISSING) {
|
||||
if (firstArg == null || !(firstArg instanceof TraceObject obj)) {
|
||||
Msg.trace(this, "MISSING first argument for " + method + "(" + firstParam + ")");
|
||||
return () -> false;
|
||||
return new ParamAndObjectArg(firstParam, null);
|
||||
}
|
||||
TraceObject obj = (TraceObject) firstArg;
|
||||
if (ActionName.RESUME.equals(name) ||
|
||||
ActionName.STEP_BACK.equals(name) ||
|
||||
ActionName.STEP_EXT.equals(name) ||
|
||||
ActionName.STEP_INTO.equals(name) ||
|
||||
ActionName.STEP_OUT.equals(name) ||
|
||||
ActionName.STEP_OVER.equals(name) ||
|
||||
ActionName.STEP_SKIP.equals(name)) {
|
||||
return () -> whenState(obj,
|
||||
state -> state != null && (state.isStopped() || state.isUnknown()));
|
||||
}
|
||||
else if (ActionName.INTERRUPT.equals(name)) {
|
||||
return () -> whenState(obj,
|
||||
state -> state == null || state.isRunning() || state.isUnknown());
|
||||
}
|
||||
else if (ActionName.KILL.equals(name)) {
|
||||
return () -> whenState(obj, state -> state == null || !state.isTerminated());
|
||||
}
|
||||
return () -> true;
|
||||
return new ParamAndObjectArg(firstParam, obj);
|
||||
}
|
||||
|
||||
private Map<String, Object> promptArgs(RemoteMethod method, Map<String, Object> defaults) {
|
||||
|
@ -346,7 +383,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
*/
|
||||
Map<String, ValStr<?>> defs = ValStr.fromPlainMap(defaults);
|
||||
RemoteMethodInvocationDialog dialog = new RemoteMethodInvocationDialog(tool,
|
||||
getSchemaContext(), method.display(), method.display(), null);
|
||||
getSchemaContext(), method.display(), method.okText(), method.icon());
|
||||
Map<String, ValStr<?>> args = dialog.promptArguments(method.parameters(), defs, defs);
|
||||
return args == null ? null : ValStr.toPlainMap(args);
|
||||
}
|
||||
|
@ -374,10 +411,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
||||
Map<String, Object> args = collectArguments(method, context, allowContextObject,
|
||||
allowCoordsObject, allowSuitableObject);
|
||||
boolean requiresPrompt = args.values().contains(Missing.MISSING);
|
||||
return new ActionEntry(method.display(), method.action(), method.description(),
|
||||
requiresPrompt, computeSpecificity(args), chooseEnabler(method, args),
|
||||
prompt -> invokeMethod(prompt, method, args));
|
||||
return new TraceRmiActionEntry(method, args);
|
||||
}
|
||||
|
||||
protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods,
|
||||
|
@ -432,6 +466,17 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ActionEntry> collectActions(ActionName name, ActionContext context) {
|
||||
if (name == null) {
|
||||
if (context instanceof ProgramLocationActionContext ctx) {
|
||||
return collectAddressActions(ctx);
|
||||
}
|
||||
return collectAllActions(context);
|
||||
}
|
||||
return collectByName(name, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, ActionEntry> collectResumeActions(ActionContext context) {
|
||||
return collectByName(ActionName.RESUME, context);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -423,6 +423,8 @@ message Method {
|
|||
// I'd like to make them all void, but I think executing a command and capturing its output
|
||||
// justifies being able to return a result. It should be used very sparingly.
|
||||
ValueType return_type = 6;
|
||||
string ok_text = 7;
|
||||
string icon = 8;
|
||||
}
|
||||
|
||||
message RequestNegotiate {
|
||||
|
|
|
@ -27,10 +27,11 @@ from . import sch
|
|||
from . import trace_rmi_pb2 as bufs
|
||||
from .util import send_delimited, recv_delimited
|
||||
|
||||
|
||||
# This need not be incremented every Ghidra release. When a breaking protocol
|
||||
# change is made, this should be updated to match the first Ghidra release that
|
||||
# includes the change.
|
||||
#
|
||||
#
|
||||
# Other places to change:
|
||||
# * every pyproject.toml file (incl. deps)
|
||||
# * TraceRmiHandler.VERSION
|
||||
|
@ -98,7 +99,8 @@ class Receiver(Thread):
|
|||
while not self._is_shutdown:
|
||||
#print("Receiving message")
|
||||
try:
|
||||
reply = recv_delimited(self.client.s, bufs.RootMessage(), dbg_seq)
|
||||
reply = recv_delimited(
|
||||
self.client.s, bufs.RootMessage(), dbg_seq)
|
||||
except BaseException as e:
|
||||
self._is_shutdown = True
|
||||
return
|
||||
|
@ -448,6 +450,8 @@ class RemoteMethod:
|
|||
name: str
|
||||
action: str
|
||||
display: str
|
||||
icon: str
|
||||
ok_text: str
|
||||
description: str
|
||||
parameters: List[RemoteParameter]
|
||||
return_schema: sch.Schema
|
||||
|
@ -508,30 +512,28 @@ class MethodRegistry(object):
|
|||
|
||||
@classmethod
|
||||
def create_method(cls, function, name=None, action=None, display=None,
|
||||
description=None) -> RemoteMethod:
|
||||
icon=None, ok_text=None, description=None) -> RemoteMethod:
|
||||
if name is None:
|
||||
name = function.__name__
|
||||
if action is None:
|
||||
action = name
|
||||
if display is None:
|
||||
display = name
|
||||
if description is None:
|
||||
description = function.__doc__ or ''
|
||||
description = function.__doc__
|
||||
sig = inspect.signature(function)
|
||||
params = []
|
||||
for p in sig.parameters.values():
|
||||
params.append(cls._make_param(p))
|
||||
return_schema = cls._to_schema(sig, sig.return_annotation)
|
||||
return RemoteMethod(name, action, display, description, params,
|
||||
return_schema, function)
|
||||
return RemoteMethod(name, action, display, icon, ok_text, description,
|
||||
params, return_schema, function)
|
||||
|
||||
def method(self, func=None, *, name=None, action=None, display=None,
|
||||
description='', condition=True):
|
||||
icon=None, ok_text=None, description=None, condition=True):
|
||||
|
||||
def _method(func):
|
||||
if condition:
|
||||
method = self.create_method(func, name, action, display,
|
||||
description)
|
||||
icon, ok_text, description)
|
||||
self.register_method(method)
|
||||
return func
|
||||
|
||||
|
@ -705,8 +707,10 @@ class Client(object):
|
|||
def _write_method(to: bufs.Method, method: RemoteMethod):
|
||||
to.name = method.name
|
||||
to.action = method.action
|
||||
to.display = method.display
|
||||
to.description = method.description
|
||||
to.display = method.display or ''
|
||||
to.icon = method.icon or ''
|
||||
to.ok_text = method.ok_text or ''
|
||||
to.description = method.description or ''
|
||||
Client._write_parameters(to.parameters, method.parameters)
|
||||
to.return_type.name = method.return_schema.name
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.function.Function;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.services.DebuggerTargetService;
|
||||
import ghidra.async.*;
|
||||
|
@ -56,6 +58,11 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
|
|||
String description, Map<String, RemoteParameter> parameters, SchemaName retType,
|
||||
AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue)
|
||||
implements RemoteMethod {
|
||||
|
||||
public TestRemoteMethod {
|
||||
Objects.requireNonNull(action);
|
||||
}
|
||||
|
||||
public TestRemoteMethod(String name, ActionName action, String display, String description,
|
||||
Map<String, RemoteParameter> parameters, SchemaName retType) {
|
||||
this(name, action, display, description, parameters, retType, new AsyncPairingQueue<>(),
|
||||
|
@ -106,6 +113,16 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
|
|||
result.thenApply(ar -> ar.ret).handle(AsyncUtils.copyTo(retQueue().give()));
|
||||
return result.thenApply(ar -> ar.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String okText() {
|
||||
return "OK";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon icon() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public record TestRemoteParameter(String name, SchemaName type, boolean required,
|
||||
|
|
|
@ -163,6 +163,13 @@
|
|||
"help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint
|
||||
Marker Actions</A> in the listings.</P>
|
||||
|
||||
<H3><A name="set_breakpoint"></A><IMG alt="" src="icon.debugger.add">Set Breakpoint</H3>
|
||||
|
||||
<P>This is a dropdown of actions provided by the back-end debugger, usually for setting
|
||||
breakpoints by symbol, expression, etc. Setting breakpoints by address is typically done from
|
||||
the Listings. If no such actions are available, or there is no live target, this action is
|
||||
disabled.</P>
|
||||
|
||||
<H3><A name="enable_breakpoints"></A><IMG alt="" src="icon.debugger.breakpoint.enable">
|
||||
Enable</H3>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
@ -0,0 +1,39 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import ghidra.app.plugin.core.debug.gui.control.TargetActionTask;
|
||||
import ghidra.debug.api.target.Target.ActionEntry;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
||||
public class InvokeActionEntryAction extends DockingAction {
|
||||
protected final PluginTool tool;
|
||||
protected final ActionEntry entry;
|
||||
|
||||
public InvokeActionEntryAction(Plugin plugin, ActionEntry entry) {
|
||||
super(entry.display(), plugin.getName());
|
||||
this.tool = plugin.getTool();
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
TargetActionTask.runAction(tool, entry.display(), entry);
|
||||
}
|
||||
}
|
|
@ -29,19 +29,23 @@ import docking.ActionContext;
|
|||
import docking.WindowPosition;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.menu.MultiActionDockingAction;
|
||||
import docking.widgets.table.*;
|
||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||
import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener;
|
||||
import ghidra.debug.api.control.ControlMode;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.debug.api.target.Target;
|
||||
import ghidra.debug.api.target.Target.ActionEntry;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.model.DomainObjectEvent;
|
||||
import ghidra.framework.plugintool.*;
|
||||
|
@ -263,6 +267,78 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
|||
return contextHasMatchingBreakpoints(context, lb -> true, loc -> true);
|
||||
}
|
||||
|
||||
protected class GenericSetBreakpointAction extends InvokeActionEntryAction {
|
||||
public GenericSetBreakpointAction(ActionEntry entry) {
|
||||
super(plugin, entry);
|
||||
setMenuBarData(new MenuData(new String[] { getName() }, entry.icon()));
|
||||
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
|
||||
}
|
||||
}
|
||||
|
||||
protected class StubSetBreakpointAction extends DockingAction {
|
||||
public StubSetBreakpointAction() {
|
||||
super("(Use the Listings to Set Breakpoints)", plugin.getName());
|
||||
setMenuBarData(new MenuData(new String[] { getName() }));
|
||||
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
}
|
||||
}
|
||||
|
||||
protected class SetBreakpointAction extends MultiActionDockingAction {
|
||||
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
|
||||
|
||||
private final List<DockingActionIf> stub = List.of(new StubSetBreakpointAction());
|
||||
|
||||
public SetBreakpointAction() {
|
||||
super("Set Breakpoint", plugin.getName());
|
||||
// TODO: Different icon?
|
||||
setToolBarData(new ToolBarData(DebuggerResources.ICON_ADD, GROUP));
|
||||
setHelpLocation(AbstractSetBreakpointAction.help(plugin));
|
||||
addLocalAction(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getActionList(ActionContext context) {
|
||||
if (traceManager == null) {
|
||||
return stub;
|
||||
}
|
||||
Trace trace = traceManager.getCurrentTrace();
|
||||
if (trace == null) {
|
||||
return stub;
|
||||
}
|
||||
|
||||
// TODO: Set-by-address (like the listing one) always present?
|
||||
if (controlService == null) {
|
||||
return stub;
|
||||
}
|
||||
ControlMode mode = controlService.getCurrentMode(trace);
|
||||
if (!mode.isTarget()) {
|
||||
return stub;
|
||||
// TODO: Consider a Sleigh expression for emulation?
|
||||
// Actually, any "Address" field could be a Sleigh expression....
|
||||
}
|
||||
|
||||
Target target = traceManager.getCurrent().getTarget();
|
||||
if (target == null) {
|
||||
return stub;
|
||||
}
|
||||
List<DockingActionIf> result = new ArrayList<>();
|
||||
for (ActionEntry entry : target.collectActions(ActionName.BREAK_EXT, context)
|
||||
.values()) {
|
||||
result.add(new GenericSetBreakpointAction(entry));
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
return stub;
|
||||
}
|
||||
Collections.sort(result, Comparator.comparing(a -> a.getName()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
protected class EnableSelectedBreakpointsAction
|
||||
extends AbstractEnableSelectedBreakpointsAction {
|
||||
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
|
||||
|
@ -708,6 +784,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
|||
new DebuggerMakeBreakpointsEffectiveActionContext();
|
||||
|
||||
// package access for testing
|
||||
SetBreakpointAction actionSetBreakpoint;
|
||||
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
|
||||
EnableAllBreakpointsAction actionEnableAllBreakpoints;
|
||||
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
|
||||
|
@ -1163,6 +1240,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
protected void createActions() {
|
||||
actionSetBreakpoint = new SetBreakpointAction();
|
||||
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
|
||||
actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
|
||||
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -288,6 +288,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
|
|||
}
|
||||
actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this)
|
||||
.description(entry.details())
|
||||
.toolBarIcon(entry.icon())
|
||||
.enabledWhen(ctx -> entry.isEnabled())
|
||||
.onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry))
|
||||
.build());
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -18,21 +18,19 @@ package ghidra.app.plugin.core.debug.gui.control;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.Tool;
|
||||
import docking.action.*;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.MenuData;
|
||||
import docking.actions.PopupActionProvider;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||
import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.debug.api.control.ControlMode;
|
||||
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.debug.api.target.Target;
|
||||
import ghidra.debug.api.target.Target.ActionEntry;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
|
@ -57,23 +55,10 @@ import ghidra.trace.model.program.TraceProgramView;
|
|||
public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider {
|
||||
public static final String GROUP_METHODS = "Debugger Methods";
|
||||
|
||||
class InvokeActionEntryAction extends DockingAction {
|
||||
private final ActionEntry entry;
|
||||
|
||||
public InvokeActionEntryAction(ActionEntry entry) {
|
||||
super(entry.display(), DebuggerMethodActionsPlugin.this.getName());
|
||||
this.entry = entry;
|
||||
Icon icon = null;
|
||||
if (ActionName.REFRESH.equals(entry.name())) {
|
||||
// TODO: Allow method annotation to specify icon?
|
||||
icon = DebuggerResources.ICON_REFRESH;
|
||||
}
|
||||
setPopupMenuData(new MenuData(new String[] { getName() }, icon, GROUP_METHODS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
TargetActionTask.runAction(tool, entry.display(), entry);
|
||||
class MethodAction extends InvokeActionEntryAction {
|
||||
public MethodAction(ActionEntry entry) {
|
||||
super(DebuggerMethodActionsPlugin.this, entry);
|
||||
setPopupMenuData(new MenuData(new String[] { getName() }, entry.icon(), GROUP_METHODS));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,10 +121,10 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
|
|||
List<DockingActionIf> result = new ArrayList<>();
|
||||
for (ActionEntry entry : target.collectActions(null, context).values()) {
|
||||
//if (entry.requiresPrompt() || entry.builtIn()) {
|
||||
if (!entry.isEnabled() || entry.builtIn()) {
|
||||
if (!entry.isEnabled() || !entry.getShow().isShowing(context)) {
|
||||
continue;
|
||||
}
|
||||
result.add(new InvokeActionEntryAction(entry));
|
||||
result.add(new MethodAction(entry));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -65,9 +65,11 @@ class TargetDockingAction extends DockingAction {
|
|||
protected void updateFromContext(ActionContext context) {
|
||||
entry = findEntry(context);
|
||||
if (entry == null) {
|
||||
getToolBarData().setIcon(action.icon());
|
||||
setDescription(defaultDescription);
|
||||
}
|
||||
else {
|
||||
getToolBarData().setIcon(entry.icon());
|
||||
setDescription(entry.details());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public enum TraceExecutionState {
|
|||
* This may apply, e.g., to a GDB "Inferior," which has no yet been used to launch or attach to
|
||||
* a process.
|
||||
*/
|
||||
INACTIVE(false, false, false, false),
|
||||
INACTIVE,
|
||||
|
||||
/**
|
||||
* The object is alive, but its execution state is unspecified
|
||||
|
@ -38,12 +38,12 @@ public enum TraceExecutionState {
|
|||
* <em>all</em> of its threads are stopped. For the clients' sakes, all models should implement
|
||||
* these conventions internally.
|
||||
*/
|
||||
ALIVE(true, false, false, false),
|
||||
ALIVE,
|
||||
|
||||
/**
|
||||
* The object is alive, but not executing
|
||||
*/
|
||||
STOPPED(true, false, true, false),
|
||||
STOPPED,
|
||||
|
||||
/**
|
||||
* The object is alive and executing
|
||||
|
@ -53,7 +53,7 @@ public enum TraceExecutionState {
|
|||
* thread is currently executing, waiting on an event, or scheduled for execution. It does not
|
||||
* necessarily mean it is executing on a CPU at this exact moment.
|
||||
*/
|
||||
RUNNING(true, true, false, false),
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* The object is no longer alive
|
||||
|
@ -63,63 +63,5 @@ public enum TraceExecutionState {
|
|||
* stale handles to objects which may still be queried (e.g., for a process exit code), or e.g.,
|
||||
* a GDB "Inferior," which could be re-used to launch or attach to another process.
|
||||
*/
|
||||
TERMINATED(false, false, false, true);
|
||||
|
||||
private final boolean alive;
|
||||
private final boolean running;
|
||||
private final boolean stopped;
|
||||
private final boolean terminated;
|
||||
|
||||
private TraceExecutionState(boolean alive, boolean running, boolean stopped,
|
||||
boolean terminated) {
|
||||
this.alive = alive;
|
||||
this.running = running;
|
||||
this.stopped = stopped;
|
||||
this.terminated = terminated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this state implies the object is alive
|
||||
*
|
||||
* @return true if alive
|
||||
*/
|
||||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this state implies the object is running
|
||||
*
|
||||
* @return true if running
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this state implies the object is stopped
|
||||
*
|
||||
* @return true if stopped
|
||||
*/
|
||||
public boolean isStopped() {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this state implies the object was terminated
|
||||
*
|
||||
* @return true if terminated
|
||||
*/
|
||||
public boolean isTerminated() {
|
||||
return terminated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this state is ambiguous
|
||||
*
|
||||
* @return true if terminated
|
||||
*/
|
||||
public boolean isUnknown() {
|
||||
return !stopped && !running && !terminated;
|
||||
}
|
||||
TERMINATED;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
|
|||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.*;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.debug.api.target.ActionName;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema;
|
||||
|
@ -57,7 +58,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
|||
protected void addMethods() {
|
||||
TestRemoteMethodRegistry reg = rmiCx.getMethods();
|
||||
|
||||
rmiMethodAdvance = new TestRemoteMethod("advance", null, "Advance",
|
||||
rmiMethodAdvance = new TestRemoteMethod("advance", ActionName.STEP_EXT, "Advance",
|
||||
"Advance to the given address", PrimitiveTraceObjectSchema.VOID,
|
||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||
"The thread to advance"),
|
||||
|
@ -65,14 +66,15 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
|||
"Target", "The target address"));
|
||||
reg.add(rmiMethodAdvance);
|
||||
|
||||
rmiMethodStepExt = new TestRemoteMethod("step_ext", null, "StepExt",
|
||||
rmiMethodStepExt = new TestRemoteMethod("step_ext", ActionName.STEP_EXT, "StepExt",
|
||||
"Step in some special way", PrimitiveTraceObjectSchema.VOID,
|
||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||
"The thread to step"));
|
||||
reg.add(rmiMethodStepExt);
|
||||
|
||||
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", null, "Advance With Flag",
|
||||
"Advance to the given address, with flag", PrimitiveTraceObjectSchema.VOID,
|
||||
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", ActionName.STEP_EXT,
|
||||
"Advance With Flag", "Advance to the given address, with flag",
|
||||
PrimitiveTraceObjectSchema.VOID,
|
||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||
"The thread to advance"),
|
||||
new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null,
|
||||
|
@ -81,7 +83,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
|||
"Flag", "The flag"));
|
||||
reg.add(rmiMethodAdvanceWithFlag);
|
||||
|
||||
rmiMethodBetween = new TestRemoteMethod("between", null, "Between",
|
||||
rmiMethodBetween = new TestRemoteMethod("between", ActionName.STEP_EXT, "Between",
|
||||
"Advance between two given addresses", PrimitiveTraceObjectSchema.VOID,
|
||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||
"The thread to advance"),
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
@ -160,22 +161,26 @@ Memory/Hex, and the Decompiler, right-click and select <img
|
|||
src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
|
||||
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
|
||||
double-click the margin.</li>
|
||||
<li>From the Breakpoints window, use the <strong>Set Breakpoint</strong>
|
||||
dropdown to to access the various breakpoint actions defined by
|
||||
GDB.</li>
|
||||
<li>From the Terminal window, use the GDB command, e.g.,
|
||||
<code>break main</code>.</li>
|
||||
</ol>
|
||||
<p>The advantage of using the listings is that you can quickly set a
|
||||
breakpoint at any address. The advantage of using the Terminal window is
|
||||
that you can specify something other than an address. Often, those
|
||||
specifications still resolve to addresses, and Ghidra will display them.
|
||||
Ghidra will memorize breakpoints by recording them as special bookmarks
|
||||
in the program database. There is some iconography to communicate the
|
||||
various states of a breakpoint. When all is well and normal, you should
|
||||
only see enabled <img src="images/breakpoint-enable.png"
|
||||
alt="enabled breakpoint" /> and disabled <img
|
||||
src="images/breakpoint-disable.png" alt="disabled breakpoint" />
|
||||
breakpoints. If the target is terminated (or not launched yet), you may
|
||||
also see ineffective <img src="images/breakpoint-enable-ineff.png"
|
||||
alt="ineffective breakpoint" /> breakpoints.</p>
|
||||
breakpoint at any address. The advantage of using the dropdown action or
|
||||
Terminal window is that you can specify something other than an address.
|
||||
Often, those specifications still resolve to addresses, and Ghidra will
|
||||
display them. Ghidra will memorize breakpoints by recording them as
|
||||
special bookmarks in the program database. There is some iconography to
|
||||
communicate the various states of a breakpoint. When all is well and
|
||||
normal, you should only see enabled <img
|
||||
src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
|
||||
disabled <img src="images/breakpoint-disable.png"
|
||||
alt="disabled breakpoint" /> breakpoints. If the target is terminated
|
||||
(or not launched yet), you may also see ineffective <img
|
||||
src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
|
||||
breakpoints.</p>
|
||||
</section>
|
||||
<section id="examining-minesweeper-board-setup" class="level2">
|
||||
<h2>Examining Minesweeper Board Setup</h2>
|
||||
|
|
|
@ -17,10 +17,11 @@ From here, you can toggle and delete existing breakpoints.
|
|||
There are several ways to set a new breakpoint:
|
||||
|
||||
1. From any static or dynamic listing window, including Disassembly, Memory/Hex, and the Decompiler, right-click and select  Set Breakpoint, press **`K`** on the keyboard, or double-click the margin.
|
||||
1. From the Breakpoints window, use the **Set Breakpoint** dropdown to to access the various breakpoint actions defined by GDB.
|
||||
1. From the Terminal window, use the GDB command, e.g., `break main`.
|
||||
|
||||
The advantage of using the listings is that you can quickly set a breakpoint at any address.
|
||||
The advantage of using the Terminal window is that you can specify something other than an address.
|
||||
The advantage of using the dropdown action or Terminal window is that you can specify something other than an address.
|
||||
Often, those specifications still resolve to addresses, and Ghidra will display them.
|
||||
Ghidra will memorize breakpoints by recording them as special bookmarks in the program database.
|
||||
There is some iconography to communicate the various states of a breakpoint.
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
@ -379,7 +380,7 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
|||
<span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">"The current program must be termmines"</span><span class="op">);</span></span>
|
||||
<span id="cb7-8"><a href="#cb7-8"></a><span class="op">}</span></span>
|
||||
<span id="cb7-9"><a href="#cb7-9"></a></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a><span class="cf">if</span> <span class="op">(</span><span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">).</span><span class="fu">isRunning</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10"></a><span class="cf">if</span> <span class="op">(</span><span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">)</span> <span class="op">!=</span> TraceExecutionState<span class="op">.</span><span class="fu">STOPPED</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-11"><a href="#cb7-11"></a> monitor<span class="op">.</span><span class="fu">setMessage</span><span class="op">(</span><span class="st">"Interrupting target and waiting for STOPPED"</span><span class="op">);</span></span>
|
||||
<span id="cb7-12"><a href="#cb7-12"></a> <span class="fu">interrupt</span><span class="op">();</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13"></a> <span class="fu">waitForBreak</span><span class="op">(</span><span class="dv">3</span><span class="op">,</span> <span class="bu">TimeUnit</span><span class="op">.</span><span class="fu">SECONDS</span><span class="op">);</span></span>
|
||||
|
|
|
@ -210,7 +210,7 @@ if (!"termmines".equals(currentProgram.getName())) {
|
|||
throw new AssertionError("The current program must be termmines");
|
||||
}
|
||||
|
||||
if (getExecutionState(trace).isRunning()) {
|
||||
if (getExecutionState(trace) != TraceExecutionState.STOPPED) {
|
||||
monitor.setMessage("Interrupting target and waiting for STOPPED");
|
||||
interrupt();
|
||||
waitForBreak(3, TimeUnit.SECONDS);
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||
class="advanced" href="B3-Scripting.html">Scripting</a><a
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a>
|
||||
class="advanced" href="B4-Modeling.html">Modeling</a><a
|
||||
class="advanced" href="B5-AddingDebuggers.html">Adding Debuggers</a>
|
||||
</header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Ghidra Debugger</h1>
|
||||
|
@ -634,46 +635,47 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
|||
<span id="cb7-39"><a href="#cb7-39"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-40"><a href="#cb7-40"></a></span>
|
||||
<span id="cb7-41"><a href="#cb7-41"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-42"><a href="#cb7-42"></a> <span class="kw">public</span> Expr <span class="fu">modBeforeStore</span><span class="op">(</span><span class="dt">int</span> sizeinAddress<span class="op">,</span> Expr inAddress<span class="op">,</span> <span class="dt">int</span> sizeinValue<span class="op">,</span></span>
|
||||
<span id="cb7-43"><a href="#cb7-43"></a> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-42"><a href="#cb7-42"></a> <span class="kw">public</span> Expr <span class="fu">modBeforeStore</span><span class="op">(</span><span class="dt">int</span> sizeinOffset<span class="op">,</span> AddressSpace space<span class="op">,</span> Expr inOffset<span class="op">,</span></span>
|
||||
<span id="cb7-43"><a href="#cb7-43"></a> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-44"><a href="#cb7-44"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
|
||||
<span id="cb7-45"><a href="#cb7-45"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-46"><a href="#cb7-46"></a></span>
|
||||
<span id="cb7-47"><a href="#cb7-47"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-48"><a href="#cb7-48"></a> <span class="kw">public</span> Expr <span class="fu">modAfterLoad</span><span class="op">(</span><span class="dt">int</span> sizeinAddress<span class="op">,</span> Expr inAddress<span class="op">,</span> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-49"><a href="#cb7-49"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
|
||||
<span id="cb7-50"><a href="#cb7-50"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-51"><a href="#cb7-51"></a></span>
|
||||
<span id="cb7-52"><a href="#cb7-52"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-53"><a href="#cb7-53"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">byte</span><span class="op">[]</span> value<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-54"><a href="#cb7-54"></a> <span class="cf">if</span> <span class="op">(</span>endian<span class="op">.</span><span class="fu">isBigEndian</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
<span id="cb7-55"><a href="#cb7-55"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> value<span class="op">),</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-56"><a href="#cb7-56"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-57"><a href="#cb7-57"></a> <span class="dt">byte</span><span class="op">[]</span> reversed <span class="op">=</span> <span class="bu">Arrays</span><span class="op">.</span><span class="fu">copyOf</span><span class="op">(</span>value<span class="op">,</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-58"><a href="#cb7-58"></a> ArrayUtils<span class="op">.</span><span class="fu">reverse</span><span class="op">(</span>reversed<span class="op">);</span></span>
|
||||
<span id="cb7-59"><a href="#cb7-59"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> reversed<span class="op">),</span> reversed<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-60"><a href="#cb7-60"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-61"><a href="#cb7-61"></a></span>
|
||||
<span id="cb7-62"><a href="#cb7-62"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-63"><a href="#cb7-63"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> <span class="dt">boolean</span> isContextreg<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-64"><a href="#cb7-64"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span>value<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb7-65"><a href="#cb7-65"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-66"><a href="#cb7-66"></a></span>
|
||||
<span id="cb7-67"><a href="#cb7-67"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-68"><a href="#cb7-68"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">long</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-69"><a href="#cb7-69"></a> <span class="cf">return</span> <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>value<span class="op">),</span> size<span class="op">);</span></span>
|
||||
<span id="cb7-70"><a href="#cb7-70"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-71"><a href="#cb7-71"></a></span>
|
||||
<span id="cb7-72"><a href="#cb7-72"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-73"><a href="#cb7-73"></a> <span class="kw">public</span> <span class="dt">byte</span><span class="op">[]</span> <span class="fu">toConcrete</span><span class="op">(</span>Expr value<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-74"><a href="#cb7-74"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb7-75"><a href="#cb7-75"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-76"><a href="#cb7-76"></a></span>
|
||||
<span id="cb7-77"><a href="#cb7-77"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-78"><a href="#cb7-78"></a> <span class="kw">public</span> <span class="dt">long</span> <span class="fu">sizeOf</span><span class="op">(</span>Expr value<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-79"><a href="#cb7-79"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb7-80"><a href="#cb7-80"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-81"><a href="#cb7-81"></a><span class="op">}</span></span></code></pre></div>
|
||||
<span id="cb7-48"><a href="#cb7-48"></a> <span class="kw">public</span> Expr <span class="fu">modAfterLoad</span><span class="op">(</span><span class="dt">int</span> sizeinOffset<span class="op">,</span> AddressSpace space<span class="op">,</span> Expr inOffset<span class="op">,</span></span>
|
||||
<span id="cb7-49"><a href="#cb7-49"></a> <span class="dt">int</span> sizeinValue<span class="op">,</span> Expr inValue<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-50"><a href="#cb7-50"></a> <span class="cf">return</span> inValue<span class="op">;</span></span>
|
||||
<span id="cb7-51"><a href="#cb7-51"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-52"><a href="#cb7-52"></a></span>
|
||||
<span id="cb7-53"><a href="#cb7-53"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-54"><a href="#cb7-54"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">byte</span><span class="op">[]</span> value<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-55"><a href="#cb7-55"></a> <span class="cf">if</span> <span class="op">(</span>endian<span class="op">.</span><span class="fu">isBigEndian</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
<span id="cb7-56"><a href="#cb7-56"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> value<span class="op">),</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-57"><a href="#cb7-57"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-58"><a href="#cb7-58"></a> <span class="dt">byte</span><span class="op">[]</span> reversed <span class="op">=</span> <span class="bu">Arrays</span><span class="op">.</span><span class="fu">copyOf</span><span class="op">(</span>value<span class="op">,</span> value<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-59"><a href="#cb7-59"></a> ArrayUtils<span class="op">.</span><span class="fu">reverse</span><span class="op">(</span>reversed<span class="op">);</span></span>
|
||||
<span id="cb7-60"><a href="#cb7-60"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigInteger</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> reversed<span class="op">),</span> reversed<span class="op">.</span><span class="fu">length</span><span class="op">);</span></span>
|
||||
<span id="cb7-61"><a href="#cb7-61"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-62"><a href="#cb7-62"></a></span>
|
||||
<span id="cb7-63"><a href="#cb7-63"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-64"><a href="#cb7-64"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> <span class="dt">boolean</span> isContextreg<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-65"><a href="#cb7-65"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LitExpr</span><span class="op">(</span>value<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb7-66"><a href="#cb7-66"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-67"><a href="#cb7-67"></a></span>
|
||||
<span id="cb7-68"><a href="#cb7-68"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-69"><a href="#cb7-69"></a> <span class="kw">public</span> Expr <span class="fu">fromConst</span><span class="op">(</span><span class="dt">long</span> value<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-70"><a href="#cb7-70"></a> <span class="cf">return</span> <span class="fu">fromConst</span><span class="op">(</span><span class="bu">BigInteger</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>value<span class="op">),</span> size<span class="op">);</span></span>
|
||||
<span id="cb7-71"><a href="#cb7-71"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-72"><a href="#cb7-72"></a></span>
|
||||
<span id="cb7-73"><a href="#cb7-73"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-74"><a href="#cb7-74"></a> <span class="kw">public</span> <span class="dt">byte</span><span class="op">[]</span> <span class="fu">toConcrete</span><span class="op">(</span>Expr value<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-75"><a href="#cb7-75"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb7-76"><a href="#cb7-76"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-77"><a href="#cb7-77"></a></span>
|
||||
<span id="cb7-78"><a href="#cb7-78"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb7-79"><a href="#cb7-79"></a> <span class="kw">public</span> <span class="dt">long</span> <span class="fu">sizeOf</span><span class="op">(</span>Expr value<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb7-80"><a href="#cb7-80"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb7-81"><a href="#cb7-81"></a> <span class="op">}</span></span>
|
||||
<span id="cb7-82"><a href="#cb7-82"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p>We have implemented two arithmetic models: one for big-endian
|
||||
languages and one for little-endian. The endianness comes into play when
|
||||
we encode constant values passed to <code>fromConst()</code>. We must
|
||||
|
@ -706,11 +708,10 @@ storage mechanism. For example, were this a dynamic taint analyzer, we
|
|||
could use <code>modAfterLoad()</code> to record that a value was
|
||||
retrieved via a tainted address. The <code>inValue</code> parameter
|
||||
gives the <code>Expr</code> actually retrieved from the emulator’s
|
||||
storage, and <code>inAddress</code> gives the address (really just the
|
||||
<code>Expr</code> piece) used to retrieve it. Conversely, in
|
||||
<code>modBeforeStore()</code>, <code>inValue</code> gives the value
|
||||
about to be stored, and <code>inAddress</code> gives the address used to
|
||||
store it.</p>
|
||||
storage, and <code>inOffset</code> gives the offset used to retrieve it.
|
||||
Conversely, in <code>modBeforeStore()</code>, <code>inValue</code> gives
|
||||
the value about to be stored, and <code>inOffset</code> gives the offset
|
||||
used to store it.</p>
|
||||
<p>We implement neither <code>toConcrete()</code> nor
|
||||
<code>sizeOf()</code>. Since we will be augmenting a concrete emulator,
|
||||
these methods will be provided by the concrete piece. If this model is
|
||||
|
|
|
@ -27,8 +27,8 @@ import ghidra.program.model.address.Address;
|
|||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.TraceExecutionState;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.TraceExecutionState;
|
||||
|
||||
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
||||
@Override
|
||||
|
@ -43,7 +43,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
|||
throw new AssertionError("The current program must be termmines");
|
||||
}
|
||||
|
||||
if (getExecutionState(trace).isRunning()) {
|
||||
if (getExecutionState(trace) != TraceExecutionState.STOPPED) {
|
||||
monitor.setMessage("Interrupting target and waiting for STOPPED");
|
||||
interrupt();
|
||||
waitForBreak(3, TimeUnit.SECONDS);
|
||||
|
@ -87,7 +87,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
|||
|
||||
// --------------------------------
|
||||
while (true) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
TraceExecutionState execState = getExecutionState(trace);
|
||||
switch (execState) {
|
||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 205 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 133 KiB |