GP-4546: Add 'Set breakpoint' multi-action to Breakpoints window.
|
@ -405,7 +405,7 @@ def kill(process: sch.Schema('Process')):
|
||||||
commands.ghidra_trace_kill()
|
commands.ghidra_trace_kill()
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='resume')
|
@REGISTRY.method(action='resume', display="Go")
|
||||||
def go(process: sch.Schema('Process')):
|
def go(process: sch.Schema('Process')):
|
||||||
"""Continue execution of the process."""
|
"""Continue execution of the process."""
|
||||||
util.dbg.run_async(lambda: dbg().go())
|
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)
|
dbg().bp(expr=address.offset)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='break_sw_execute')
|
@REGISTRY.method(action='break_ext', display='Set Breakpoint')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_expression(expression: str):
|
def break_expression(expression: str):
|
||||||
"""Set a breakpoint."""
|
"""Set a breakpoint."""
|
||||||
|
@ -472,7 +472,7 @@ def break_hw_address(process: sch.Schema('Process'), address: Address):
|
||||||
dbg().ba(expr=address.offset)
|
dbg().ba(expr=address.offset)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='break_hw_execute')
|
@REGISTRY.method(action='break_ext', display='Set Hardware Breakpoint')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_hw_expression(expression: str):
|
def break_hw_expression(expression: str):
|
||||||
"""Set a hardware-assisted breakpoint."""
|
"""Set a hardware-assisted breakpoint."""
|
||||||
|
@ -482,50 +482,50 @@ def break_hw_expression(expression: str):
|
||||||
@REGISTRY.method(action='break_read')
|
@REGISTRY.method(action='break_read')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
||||||
"""Set a read watchpoint."""
|
"""Set a read breakpoint."""
|
||||||
find_proc_by_obj(process)
|
find_proc_by_obj(process)
|
||||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ)
|
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
|
@util.dbg.eng_thread
|
||||||
def break_read_expression(expression: str):
|
def break_read_expression(expression: str):
|
||||||
"""Set a read watchpoint."""
|
"""Set a read breakpoint."""
|
||||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='break_write')
|
@REGISTRY.method(action='break_write')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
||||||
"""Set a watchpoint."""
|
"""Set a write breakpoint."""
|
||||||
find_proc_by_obj(process)
|
find_proc_by_obj(process)
|
||||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE)
|
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
|
@util.dbg.eng_thread
|
||||||
def break_write_expression(expression: str):
|
def break_write_expression(expression: str):
|
||||||
"""Set a watchpoint."""
|
"""Set a write breakpoint."""
|
||||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='break_access')
|
@REGISTRY.method(action='break_access')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||||
"""Set an access watchpoint."""
|
"""Set an access breakpoint."""
|
||||||
find_proc_by_obj(process)
|
find_proc_by_obj(process)
|
||||||
dbg().ba(expr=range.min, size=range.length(),
|
dbg().ba(expr=range.min, size=range.length(),
|
||||||
access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
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
|
@util.dbg.eng_thread
|
||||||
def break_access_expression(expression: str):
|
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)
|
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
|
@util.dbg.eng_thread
|
||||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||||
"""Toggle a breakpoint."""
|
"""Toggle a breakpoint."""
|
||||||
|
@ -536,7 +536,7 @@ def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||||
dbg().bd(bpt.GetId())
|
dbg().bd(bpt.GetId())
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='delete')
|
@REGISTRY.method(action='delete', display='Delete Breakpoint')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||||
"""Delete a breakpoint."""
|
"""Delete a breakpoint."""
|
||||||
|
|
|
@ -391,19 +391,19 @@ def refresh_sections(node: sch.Schema('Module')):
|
||||||
gdb.execute(f'ghidra trace put-sections "{modname}"')
|
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')):
|
def activate_inferior(inferior: sch.Schema('Inferior')):
|
||||||
"""Switch to the inferior."""
|
"""Switch to the inferior."""
|
||||||
switch_inferior(find_inf_by_obj(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')):
|
def activate_thread(thread: sch.Schema('Thread')):
|
||||||
"""Switch to the thread."""
|
"""Switch to the thread."""
|
||||||
find_thread_by_obj(thread).switch()
|
find_thread_by_obj(thread).switch()
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='activate')
|
@REGISTRY.method(action='activate', display="Activate Frame")
|
||||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||||
"""Select the frame."""
|
"""Select the frame."""
|
||||||
find_frame_by_obj(frame).select()
|
find_frame_by_obj(frame).select()
|
||||||
|
@ -415,7 +415,7 @@ def add_inferior(container: sch.Schema('InferiorContainer')):
|
||||||
gdb.execute('add-inferior')
|
gdb.execute('add-inferior')
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='delete')
|
@REGISTRY.method(action='delete', display="Delete Inferior")
|
||||||
def delete_inferior(inferior: sch.Schema('Inferior')):
|
def delete_inferior(inferior: sch.Schema('Inferior')):
|
||||||
"""Remove the inferior."""
|
"""Remove the inferior."""
|
||||||
inf = find_inf_by_obj(inferior)
|
inf = find_inf_by_obj(inferior)
|
||||||
|
@ -577,7 +577,7 @@ def break_sw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
|
||||||
gdb.execute(f'break *0x{offset:x}')
|
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):
|
def break_sw_execute_expression(expression: str):
|
||||||
"""Set a breakpoint (break)."""
|
"""Set a breakpoint (break)."""
|
||||||
# TODO: Escape?
|
# TODO: Escape?
|
||||||
|
@ -592,7 +592,7 @@ def break_hw_execute_address(inferior: sch.Schema('Inferior'), address: Address)
|
||||||
gdb.execute(f'hbreak *0x{offset:x}')
|
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):
|
def break_hw_execute_expression(expression: str):
|
||||||
"""Set a hardware-assisted breakpoint (hbreak)."""
|
"""Set a hardware-assisted breakpoint (hbreak)."""
|
||||||
# TODO: Escape?
|
# 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})')
|
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):
|
def break_read_expression(expression: str):
|
||||||
"""Set a read watchpoint (rwatch)."""
|
"""Set a read watchpoint (rwatch)."""
|
||||||
gdb.execute(f'rwatch {expression}')
|
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})')
|
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):
|
def break_write_expression(expression: str):
|
||||||
"""Set a watchpoint (watch)."""
|
"""Set a watchpoint (watch)."""
|
||||||
gdb.execute(f'watch {expression}')
|
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})')
|
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):
|
def break_access_expression(expression: str):
|
||||||
"""Set an access watchpoint (awatch)."""
|
"""Set an access watchpoint (awatch)."""
|
||||||
gdb.execute(f'awatch {expression}')
|
gdb.execute(f'awatch {expression}')
|
||||||
|
@ -653,21 +653,23 @@ def break_event(inferior: sch.Schema('Inferior'), spec: str):
|
||||||
gdb.execute(f'catch {spec}')
|
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):
|
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||||
"""Toggle a breakpoint."""
|
"""Toggle a breakpoint."""
|
||||||
bpt = find_bpt_by_obj(breakpoint)
|
bpt = find_bpt_by_obj(breakpoint)
|
||||||
bpt.enabled = enabled
|
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):
|
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||||
"""Toggle a breakpoint location."""
|
"""Toggle a breakpoint location."""
|
||||||
loc = find_bpt_loc_by_obj(location)
|
loc = find_bpt_loc_by_obj(location)
|
||||||
loc.enabled = enabled
|
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):
|
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||||
"""Toggle a breakpoint location."""
|
"""Toggle a breakpoint location."""
|
||||||
bptnum, locnum = find_bptlocnum_by_obj(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}')
|
gdb.execute(f'{cmd} {bptnum}.{locnum}')
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='delete')
|
@REGISTRY.method(action='delete', display="Delete Breakpoint")
|
||||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||||
"""Delete a breakpoint."""
|
"""Delete a breakpoint."""
|
||||||
bpt = find_bpt_by_obj(breakpoint)
|
bpt = find_bpt_by_obj(breakpoint)
|
||||||
|
|
|
@ -17,11 +17,11 @@ from concurrent.futures import Future, ThreadPoolExecutor
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import lldb
|
||||||
|
|
||||||
from ghidratrace import sch
|
from ghidratrace import sch
|
||||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||||
|
|
||||||
import lldb
|
|
||||||
|
|
||||||
from . import commands, util
|
from . import commands, util
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ def execute(cmd: str, to_string: bool=False):
|
||||||
return exec_convert_errors(cmd, to_string)
|
return exec_convert_errors(cmd, to_string)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method
|
@REGISTRY.method(display='Evaluate')
|
||||||
def evaluate(expr: str):
|
def evaluate(expr: str):
|
||||||
"""Evaluate an expression."""
|
"""Evaluate an expression."""
|
||||||
value = util.get_target().EvaluateExpression(expr)
|
value = util.get_target().EvaluateExpression(expr)
|
||||||
|
@ -257,7 +257,7 @@ def evaluate(expr: str):
|
||||||
return commands.convert_value(value)
|
return commands.convert_value(value)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method
|
@REGISTRY.method(display="Python Evaluate")
|
||||||
def pyeval(expr: str):
|
def pyeval(expr: str):
|
||||||
return eval(expr)
|
return eval(expr)
|
||||||
|
|
||||||
|
@ -345,28 +345,28 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
|
||||||
exec_convert_errors('ghidra trace put-modules')
|
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')):
|
def activate_process(process: sch.Schema('Process')):
|
||||||
"""Switch to the process."""
|
"""Switch to the process."""
|
||||||
# TODO
|
# TODO
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='activate')
|
@REGISTRY.method(action='activate', display='Activate Thread')
|
||||||
def activate_thread(thread: sch.Schema('Thread')):
|
def activate_thread(thread: sch.Schema('Thread')):
|
||||||
"""Switch to the thread."""
|
"""Switch to the thread."""
|
||||||
t = find_thread_by_obj(thread)
|
t = find_thread_by_obj(thread)
|
||||||
t.process.SetSelectedThread(t)
|
t.process.SetSelectedThread(t)
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='activate')
|
@REGISTRY.method(action='activate', display='Activate Frame')
|
||||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||||
"""Select the frame."""
|
"""Select the frame."""
|
||||||
f = find_frame_by_obj(frame)
|
f = find_frame_by_obj(frame)
|
||||||
f.thread.SetSelectedFrame(f.GetFrameID())
|
f.thread.SetSelectedFrame(f.GetFrameID())
|
||||||
|
|
||||||
|
|
||||||
@REGISTRY.method(action='delete')
|
@REGISTRY.method(action='delete', display='Remove Process')
|
||||||
def remove_process(process: sch.Schema('Process')):
|
def remove_process(process: sch.Schema('Process')):
|
||||||
"""Remove the process."""
|
"""Remove the process."""
|
||||||
proc = find_proc_by_obj(process)
|
proc = find_proc_by_obj(process)
|
||||||
|
@ -442,7 +442,7 @@ def kill(process: sch.Schema('Process')):
|
||||||
exec_convert_errors('process kill')
|
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')):
|
def _continue(process: sch.Schema('Process')):
|
||||||
"""Continue execution of the process."""
|
"""Continue execution of the process."""
|
||||||
exec_convert_errors('process continue')
|
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}')
|
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):
|
def break_expression(expression: str):
|
||||||
"""Set a breakpoint."""
|
"""Set a breakpoint."""
|
||||||
# TODO: Escape?
|
# 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}')
|
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):
|
def break_hw_expression(expression: str):
|
||||||
"""Set a hardware-assisted breakpoint."""
|
"""Set a hardware-assisted breakpoint."""
|
||||||
# TODO: Escape?
|
# 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}')
|
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):
|
def break_read_expression(expression: str, size=None):
|
||||||
"""Set a read watchpoint."""
|
"""Set a read watchpoint."""
|
||||||
size_part = '' if size is None else f'-s {size}'
|
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}')
|
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):
|
def break_write_expression(expression: str, size=None):
|
||||||
"""Set a watchpoint."""
|
"""Set a watchpoint."""
|
||||||
size_part = '' if size is None else f'-s {size}'
|
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')
|
@REGISTRY.method(action='break_access')
|
||||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
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)
|
proc = find_proc_by_obj(process)
|
||||||
offset_start = process.trace.memory_mapper.map_back(
|
offset_start = process.trace.memory_mapper.map_back(
|
||||||
proc, Address(range.space, range.min))
|
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}')
|
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):
|
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}'
|
size_part = '' if size is None else f'-s {size}'
|
||||||
exec_convert_errors(
|
exec_convert_errors(
|
||||||
f'watchpoint set expression {size_part} -w read_write -- {expression}')
|
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}')
|
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):
|
def toggle_watchpoint(watchpoint: sch.Schema('WatchpointSpec'), enabled: bool):
|
||||||
"""Toggle a watchpoint."""
|
"""Toggle a watchpoint."""
|
||||||
wpt = find_wpt_by_obj(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()}')
|
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):
|
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||||
"""Toggle a breakpoint."""
|
"""Toggle a breakpoint."""
|
||||||
bpt = find_bpt_by_obj(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()}')
|
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):
|
def toggle_breakpoint_location(location: sch.Schema('BreakpointLocation'), enabled: bool):
|
||||||
"""Toggle a breakpoint location."""
|
"""Toggle a breakpoint location."""
|
||||||
bptnum, locnum = find_bptlocnum_by_obj(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}')
|
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')):
|
def delete_watchpoint(watchpoint: sch.Schema('WatchpointSpec')):
|
||||||
"""Delete a watchpoint."""
|
"""Delete a watchpoint."""
|
||||||
wpt = find_wpt_by_obj(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}')
|
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')):
|
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||||
"""Delete a breakpoint."""
|
"""Delete a breakpoint."""
|
||||||
bpt = find_bpt_by_obj(breakpoint)
|
bpt = find_bpt_by_obj(breakpoint)
|
||||||
|
|
|
@ -15,8 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.debug.api.target;
|
package ghidra.debug.api.target;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.awt.event.InputEvent;
|
||||||
import java.util.Map;
|
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.
|
* A name for a commonly-recognized target action.
|
||||||
|
@ -35,21 +43,137 @@ import java.util.Map;
|
||||||
* the UI to decide what is presented where.
|
* the UI to decide what is presented where.
|
||||||
*
|
*
|
||||||
* @param name the name of the action (given as the action attribute on method annotations)
|
* @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
|
* @param show when to show the general UI action for this debugger action
|
||||||
* reserved for built-in, purpose-specific actions
|
* @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<>();
|
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) {
|
public static ActionName name(String name) {
|
||||||
synchronized (NAMES) {
|
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) {
|
synchronized (NAMES) {
|
||||||
ActionName action = new ActionName(name, true);
|
ActionName action = new ActionName(name, show, enabler, display, icon, okText);
|
||||||
if (NAMES.put(name, action) != null) {
|
if (NAMES.put(name, action) != null) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
@ -57,24 +181,16 @@ public record ActionName(String name, boolean builtIn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActionName extended(String name) {
|
public static final ActionName REFRESH =
|
||||||
synchronized (NAMES) {
|
create("refresh", Show.EXTENDED, Enabler.ALWAYS, "Refresh", ICON_REFRESH, "Refresh");
|
||||||
ActionName action = new ActionName(name, false);
|
|
||||||
if (NAMES.put(name, action) != null) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final ActionName REFRESH = extended("refresh");
|
|
||||||
/**
|
/**
|
||||||
* Activate a given object and optionally a time
|
* Activate a given object and optionally a time
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Forms: (focus:Object), (focus:Object, snap:LONG), (focus:Object, time:STR)
|
* 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.
|
* 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
|
* used to communicate selection (i.e., highlight) of the object. Whereas, double-clicking or
|
||||||
* pressing enter would more likely invoke 'activate.'
|
* pressing enter would more likely invoke 'activate.'
|
||||||
*/
|
*/
|
||||||
public static final ActionName FOCUS = builtIn("focus");
|
public static final ActionName FOCUS =
|
||||||
public static final ActionName TOGGLE = builtIn("toggle");
|
create("focus", Show.BUILTIN, Enabler.ALWAYS, "Focus", null, "Focus");
|
||||||
public static final ActionName DELETE = builtIn("delete");
|
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
|
* Execute a CLI command
|
||||||
|
@ -93,7 +212,8 @@ public record ActionName(String name, boolean builtIn) {
|
||||||
* <p>
|
* <p>
|
||||||
* Forms: (cmd:STRING):STRING; Optional arguments: capture:BOOL
|
* 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
|
* Connect the back-end to a (usually remote) target
|
||||||
|
@ -101,23 +221,31 @@ public record ActionName(String name, boolean builtIn) {
|
||||||
* <p>
|
* <p>
|
||||||
* Forms: (spec:STRING)
|
* 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)
|
* Forms: (target:Attachable), (pid:INT), (spec:STRING)
|
||||||
*/
|
*/
|
||||||
public static final ActionName ATTACH = extended("attach");
|
public static final ActionName ATTACH =
|
||||||
public static final ActionName DETACH = extended("detach");
|
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),
|
* Forms: (command_line:STRING), (file:STRING,args:STRING), (file:STRING,args:STRING_ARRAY),
|
||||||
* (ANY*)
|
* (ANY*)
|
||||||
*/
|
*/
|
||||||
public static final ActionName LAUNCH = extended("launch");
|
public static final ActionName LAUNCH =
|
||||||
public static final ActionName KILL = builtIn("kill");
|
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 RESUME =
|
||||||
public static final ActionName INTERRUPT = builtIn("interrupt");
|
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
|
* 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
|
* 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.
|
* 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_INTO =
|
||||||
public static final ActionName STEP_OVER = builtIn("step_over");
|
create("step_into", Show.BUILTIN, Enabler.NOT_RUNNING, "Step Into", ICON_STEP_INTO, "Step");
|
||||||
public static final ActionName STEP_OUT = builtIn("step_out");
|
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
|
* 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
|
* 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
|
* 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.
|
* 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)
|
* Step back is not typically available, except in emulators and timeless (or time-travel)
|
||||||
* debuggers.
|
* 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.
|
* 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)
|
* 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
|
* The client may pass either null or "" for condition and/or commands to indicate omissions of
|
||||||
* those arguments.
|
* those arguments.
|
||||||
*/
|
*/
|
||||||
public static final ActionName BREAK_SW_EXECUTE = builtIn("break_sw_execute");
|
public static final ActionName BREAK_SW_EXECUTE =
|
||||||
public static final ActionName BREAK_HW_EXECUTE = builtIn("break_hw_execute");
|
create("break_sw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Software Breakpoint",
|
||||||
public static final ActionName BREAK_READ = builtIn("break_read");
|
ICON_SET_BREAKPOINT, "Set");
|
||||||
public static final ActionName BREAK_WRITE = builtIn("break_write");
|
public static final ActionName BREAK_HW_EXECUTE =
|
||||||
public static final ActionName BREAK_ACCESS = builtIn("break_access");
|
create("break_hw_execute", Show.BUILTIN, Enabler.ALWAYS, "Set Hardware Breakpoint",
|
||||||
public static final ActionName BREAK_EXT = extended("break_ext");
|
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)
|
* 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)
|
* 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)
|
// NOTE: no read_reg. Use refresh(RegContainer), refresh(RegGroup), refresh(Register)
|
||||||
/**
|
/**
|
||||||
* Forms: (frame:Frame,name:STRING,value:BYTES), (register:Register,value:BYTES)
|
* 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.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
import java.util.function.Function;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
|
import ghidra.debug.api.target.ActionName.Show;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.Register;
|
import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.lang.RegisterValue;
|
import ghidra.program.model.lang.RegisterValue;
|
||||||
import ghidra.trace.model.TraceExecutionState;
|
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
|
import ghidra.trace.model.TraceExecutionState;
|
||||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
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
|
* 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
|
* those functions will keep whatever some means of querying UI and/or target context in their
|
||||||
* closures.
|
* 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,
|
interface ActionEntry {
|
||||||
long specificity, BooleanSupplier enabled,
|
|
||||||
Function<Boolean, CompletableFuture<?>> action) {
|
/**
|
||||||
|
* 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
|
* Check if this action is currently enabled
|
||||||
*
|
*
|
||||||
* @return true if enabled
|
* @return true if enabled
|
||||||
*/
|
*/
|
||||||
public boolean isEnabled() {
|
boolean isEnabled();
|
||||||
return enabled.getAsBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke the action asynchronously, prompting if desired
|
* 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
|
* @param prompt whether or not to prompt the user for arguments
|
||||||
* @return the future result, often {@link Void}
|
* @return the future result, often {@link Void}
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<?> invokeAsync(boolean prompt) {
|
default CompletableFuture<?> invokeAsync(boolean prompt) {
|
||||||
return action.apply(prompt).orTimeout(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
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
|
* @param prompt whether or not to prompt the user for arguments
|
||||||
*/
|
*/
|
||||||
public void run(boolean prompt) {
|
default void run(boolean prompt) {
|
||||||
get(prompt);
|
get(prompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +159,7 @@ public interface Target {
|
||||||
* @param prompt whether or not to prompt the user for arguments
|
* @param prompt whether or not to prompt the user for arguments
|
||||||
* @return the resulting value, if applicable
|
* @return the resulting value, if applicable
|
||||||
*/
|
*/
|
||||||
public Object get(boolean prompt) {
|
default Object get(boolean prompt) {
|
||||||
if (Swing.isSwingThread()) {
|
if (Swing.isSwingThread()) {
|
||||||
throw new AssertionError("Refusing to block the Swing thread. Use a Task.");
|
throw new AssertionError("Refusing to block the Swing thread. Use a Task.");
|
||||||
}
|
}
|
||||||
|
@ -130,8 +176,8 @@ public interface Target {
|
||||||
*
|
*
|
||||||
* @return true if built in.
|
* @return true if built in.
|
||||||
*/
|
*/
|
||||||
public boolean builtIn() {
|
default Show getShow() {
|
||||||
return name != null && name.builtIn();
|
return name() == null ? Show.EXTENDED : name().show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import ghidra.async.AsyncUtils;
|
import ghidra.async.AsyncUtils;
|
||||||
import ghidra.debug.api.target.ActionName;
|
import ghidra.debug.api.target.ActionName;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
|
@ -62,6 +64,20 @@ public interface RemoteMethod {
|
||||||
*/
|
*/
|
||||||
String display();
|
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.
|
* 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.TraceMemoryOperations;
|
||||||
import ghidra.trace.model.memory.TraceMemorySpace;
|
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
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.target.path.KeyPath;
|
||||||
import ghidra.trace.model.thread.TraceObjectThread;
|
import ghidra.trace.model.thread.TraceObjectThread;
|
||||||
import ghidra.trace.model.thread.TraceThread;
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
|
@ -1740,7 +1741,7 @@ public interface FlatDebuggerAPI {
|
||||||
* @return true if alive
|
* @return true if alive
|
||||||
*/
|
*/
|
||||||
default boolean isTargetAlive(Trace trace) {
|
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
|
* @return true if alive
|
||||||
*/
|
*/
|
||||||
default boolean isThreadAlive(TraceThread thread) {
|
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
|
* @throws TimeoutException if the timeout expires
|
||||||
*/
|
*/
|
||||||
default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException {
|
default void waitForBreak(Trace trace, long timeout, TimeUnit unit) throws TimeoutException {
|
||||||
if (!getExecutionState(trace).isRunning()) {
|
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var listener = new DomainObjectListener() {
|
var listener = new DomainObjectListener() {
|
||||||
|
@ -1800,14 +1801,14 @@ public interface FlatDebuggerAPI {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||||
if (!getExecutionState(trace).isRunning()) {
|
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||||
future.complete(null);
|
future.complete(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
trace.addListener(listener);
|
trace.addListener(listener);
|
||||||
try {
|
try {
|
||||||
if (!getExecutionState(trace).isRunning()) {
|
if (getExecutionState(trace) != TraceExecutionState.RUNNING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
listener.future.get(timeout, unit);
|
listener.future.get(timeout, unit);
|
||||||
|
|
|
@ -144,20 +144,10 @@ public class JdiConnector {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerRemoteMethod(JdiMethods methods, java.lang.reflect.Method m, String name) {
|
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);
|
TraceMethod annot = m.getAnnotation(TraceMethod.class);
|
||||||
if (annot == null) {
|
if (annot == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
action = annot.action();
|
|
||||||
if (annot.display() != null) {
|
|
||||||
display = annot.display();
|
|
||||||
}
|
|
||||||
if (annot.description() != null) {
|
|
||||||
description = annot.description();
|
|
||||||
}
|
|
||||||
int pcount = m.getParameterCount();
|
int pcount = m.getParameterCount();
|
||||||
if (pcount < 1) {
|
if (pcount < 1) {
|
||||||
return;
|
return;
|
||||||
|
@ -167,8 +157,8 @@ public class JdiConnector {
|
||||||
* collection routines currently use the return type, so just use ANY for now.
|
* collection routines currently use the return type, so just use ANY for now.
|
||||||
*/
|
*/
|
||||||
TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY;
|
TraceObjectSchema schema = PrimitiveTraceObjectSchema.ANY;
|
||||||
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, action, display,
|
RmiRemoteMethod method = new RmiRemoteMethod(rootSchema.getContext(), name, annot.action(),
|
||||||
description, schema, methods, m);
|
annot.display(), annot.description(), annot.okText(), annot.icon(), schema, methods, m);
|
||||||
remoteMethodRegistry.putMethod(name, method);
|
remoteMethodRegistry.putMethod(name, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -605,9 +605,11 @@ public class RmiClient {
|
||||||
private Method buildMethod(RmiRemoteMethod method) {
|
private Method buildMethod(RmiRemoteMethod method) {
|
||||||
Method.Builder builder = Method.newBuilder()
|
Method.Builder builder = Method.newBuilder()
|
||||||
.setName(method.getName())
|
.setName(method.getName())
|
||||||
.setDescription(method.getDescription())
|
|
||||||
.setAction(method.getAction())
|
.setAction(method.getAction())
|
||||||
.setDisplay(method.getDisplay());
|
.setDisplay(method.getDisplay())
|
||||||
|
.setDescription(method.getDescription())
|
||||||
|
.setOkText(method.getOkText())
|
||||||
|
.setIcon(method.getIcon());
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (RmiRemoteMethodParameter p : method.getParameters()) {
|
for (RmiRemoteMethodParameter p : method.getParameters()) {
|
||||||
MethodParameter param = buildParameter(p);
|
MethodParameter param = buildParameter(p);
|
||||||
|
|
|
@ -32,6 +32,10 @@ public class RmiMethodRegistry {
|
||||||
String display() default "";
|
String display() default "";
|
||||||
|
|
||||||
String description() default "";
|
String description() default "";
|
||||||
|
|
||||||
|
String okText() default "";
|
||||||
|
|
||||||
|
String icon() default "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, RmiRemoteMethod> map = new HashMap<>();
|
Map<String, RmiRemoteMethod> map = new HashMap<>();
|
||||||
|
|
|
@ -25,22 +25,27 @@ import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||||
public class RmiRemoteMethod {
|
public class RmiRemoteMethod {
|
||||||
|
|
||||||
private final SchemaContext schemaContext;
|
private final SchemaContext schemaContext;
|
||||||
private String name;
|
private final String name;
|
||||||
private String action;
|
private final String action;
|
||||||
private String display;
|
private final String display;
|
||||||
private String description;
|
private final String description;
|
||||||
private RmiRemoteMethodParameter[] params;
|
private final String okText;
|
||||||
private TraceObjectSchema schema;
|
private final String icon;
|
||||||
private RmiMethods instance;
|
private final RmiRemoteMethodParameter[] params;
|
||||||
private Method m;
|
private final TraceObjectSchema schema;
|
||||||
|
private final RmiMethods instance;
|
||||||
|
private final Method m;
|
||||||
|
|
||||||
public RmiRemoteMethod(SchemaContext schemaContext, String name, String action, String display,
|
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.schemaContext = schemaContext;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.display = display;
|
this.display = display;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
this.okText = okText;
|
||||||
|
this.icon = icon;
|
||||||
this.params = new RmiRemoteMethodParameter[m.getParameterCount()];
|
this.params = new RmiRemoteMethodParameter[m.getParameterCount()];
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
@ -69,6 +74,14 @@ public class RmiRemoteMethod {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOkText() {
|
||||||
|
return okText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAction() {
|
public String getAction() {
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,33 @@ package ghidra.app.plugin.core.debug.service.tracermi;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import ghidra.debug.api.target.ActionName;
|
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.Trace;
|
||||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||||
|
|
||||||
public record RecordRemoteMethod(TraceRmiHandler handler, String name, ActionName action,
|
public record RecordRemoteMethod(TraceRmiHandler handler, String name, ActionName action,
|
||||||
String display, String description, Map<String, RemoteParameter> parameters,
|
String display, Icon icon, String okText, String description,
|
||||||
SchemaName retType) implements RemoteMethod {
|
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
|
@Override
|
||||||
public DefaultRemoteAsyncResult invokeAsync(Map<String, Object> arguments) {
|
public DefaultRemoteAsyncResult invokeAsync(Map<String, Object> arguments) {
|
||||||
Trace trace = validate(arguments);
|
Trace trace = validate(arguments);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.commons.lang3.ArrayUtils;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import db.Transaction;
|
import db.Transaction;
|
||||||
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
|
import ghidra.app.plugin.core.debug.disassemble.DebuggerDisassemblerPlugin;
|
||||||
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
|
import ghidra.app.plugin.core.debug.disassemble.TraceDisassembleCommand;
|
||||||
import ghidra.app.services.DebuggerControlService;
|
import ghidra.app.services.DebuggerControlService;
|
||||||
|
@ -1027,7 +1028,9 @@ public class TraceRmiHandler extends AbstractTraceRmiConnection {
|
||||||
}
|
}
|
||||||
for (Method m : req.getMethodsList()) {
|
for (Method m : req.getMethodsList()) {
|
||||||
RemoteMethod rm = new RecordRemoteMethod(this, m.getName(),
|
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()
|
m.getParametersList()
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)),
|
.collect(Collectors.toMap(MethodParameter::getName, this::makeParameter)),
|
||||||
|
|
|
@ -19,9 +19,11 @@ import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.*;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
|
@ -60,6 +62,70 @@ import ghidra.util.Msg;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class TraceRmiTarget extends AbstractTarget {
|
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 TraceRmiConnection connection;
|
||||||
private final Trace trace;
|
private final Trace trace;
|
||||||
|
|
||||||
|
@ -224,8 +290,12 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton to indicate missing arguments
|
||||||
|
*/
|
||||||
public enum Missing {
|
public enum Missing {
|
||||||
MISSING; // The argument requires a prompt
|
/** The argument requires a prompt */
|
||||||
|
MISSING;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object findArgument(ActionName action, RemoteParameter parameter,
|
protected Object findArgument(ActionName action, RemoteParameter parameter,
|
||||||
|
@ -266,28 +336,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TraceExecutionState getStateOf(TraceObject object) {
|
protected static long computeSpecificity(Map<String, Object> args) {
|
||||||
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) {
|
|
||||||
long score = 0;
|
long score = 0;
|
||||||
for (Object o : args.values()) {
|
for (Object o : args.values()) {
|
||||||
if (o instanceof TraceObject obj) {
|
if (o instanceof TraceObject obj) {
|
||||||
|
@ -297,46 +346,34 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BooleanSupplier chooseEnabler(RemoteMethod method, Map<String, Object> args) {
|
protected RemoteParameter getFirstObjectParameter(RemoteMethod method) {
|
||||||
ActionName name = method.action();
|
|
||||||
SchemaContext ctx = getSchemaContext();
|
SchemaContext ctx = getSchemaContext();
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
return () -> true;
|
return null;
|
||||||
}
|
}
|
||||||
RemoteParameter firstParam = method.parameters()
|
return method.parameters()
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(
|
.filter(
|
||||||
p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType()))
|
p -> TraceObjectInterfaceUtils.isTraceObject(ctx.getSchema(p.type()).getType()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
record ParamAndObjectArg(RemoteParameter param, TraceObject obj) {}
|
||||||
|
|
||||||
|
protected ParamAndObjectArg getFirstObjectArgument(RemoteMethod method,
|
||||||
|
Map<String, Object> args) {
|
||||||
|
RemoteParameter firstParam = getFirstObjectParameter(method);
|
||||||
if (firstParam == null) {
|
if (firstParam == null) {
|
||||||
return () -> true;
|
return null;
|
||||||
}
|
}
|
||||||
Object firstArg = args.get(firstParam.name());
|
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 + ")");
|
Msg.trace(this, "MISSING first argument for " + method + "(" + firstParam + ")");
|
||||||
return () -> false;
|
return new ParamAndObjectArg(firstParam, null);
|
||||||
}
|
}
|
||||||
TraceObject obj = (TraceObject) firstArg;
|
return new ParamAndObjectArg(firstParam, obj);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> promptArgs(RemoteMethod method, Map<String, Object> defaults) {
|
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);
|
Map<String, ValStr<?>> defs = ValStr.fromPlainMap(defaults);
|
||||||
RemoteMethodInvocationDialog dialog = new RemoteMethodInvocationDialog(tool,
|
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);
|
Map<String, ValStr<?>> args = dialog.promptArguments(method.parameters(), defs, defs);
|
||||||
return args == null ? null : ValStr.toPlainMap(args);
|
return args == null ? null : ValStr.toPlainMap(args);
|
||||||
}
|
}
|
||||||
|
@ -374,10 +411,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
boolean allowContextObject, boolean allowCoordsObject, boolean allowSuitableObject) {
|
||||||
Map<String, Object> args = collectArguments(method, context, allowContextObject,
|
Map<String, Object> args = collectArguments(method, context, allowContextObject,
|
||||||
allowCoordsObject, allowSuitableObject);
|
allowCoordsObject, allowSuitableObject);
|
||||||
boolean requiresPrompt = args.values().contains(Missing.MISSING);
|
return new TraceRmiActionEntry(method, args);
|
||||||
return new ActionEntry(method.display(), method.action(), method.description(),
|
|
||||||
requiresPrompt, computeSpecificity(args), chooseEnabler(method, args),
|
|
||||||
prompt -> invokeMethod(prompt, method, args));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods,
|
protected Map<String, ActionEntry> collectFromMethods(Collection<RemoteMethod> methods,
|
||||||
|
@ -432,6 +466,17 @@ public class TraceRmiTarget extends AbstractTarget {
|
||||||
true);
|
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
|
@Override
|
||||||
protected Map<String, ActionEntry> collectResumeActions(ActionContext context) {
|
protected Map<String, ActionEntry> collectResumeActions(ActionContext context) {
|
||||||
return collectByName(ActionName.RESUME, context);
|
return collectByName(ActionName.RESUME, context);
|
||||||
|
|
|
@ -423,6 +423,8 @@ message Method {
|
||||||
// I'd like to make them all void, but I think executing a command and capturing its output
|
// 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.
|
// justifies being able to return a result. It should be used very sparingly.
|
||||||
ValueType return_type = 6;
|
ValueType return_type = 6;
|
||||||
|
string ok_text = 7;
|
||||||
|
string icon = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RequestNegotiate {
|
message RequestNegotiate {
|
||||||
|
|
|
@ -27,6 +27,7 @@ from . import sch
|
||||||
from . import trace_rmi_pb2 as bufs
|
from . import trace_rmi_pb2 as bufs
|
||||||
from .util import send_delimited, recv_delimited
|
from .util import send_delimited, recv_delimited
|
||||||
|
|
||||||
|
|
||||||
# This need not be incremented every Ghidra release. When a breaking protocol
|
# 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
|
# change is made, this should be updated to match the first Ghidra release that
|
||||||
# includes the change.
|
# includes the change.
|
||||||
|
@ -98,7 +99,8 @@ class Receiver(Thread):
|
||||||
while not self._is_shutdown:
|
while not self._is_shutdown:
|
||||||
#print("Receiving message")
|
#print("Receiving message")
|
||||||
try:
|
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:
|
except BaseException as e:
|
||||||
self._is_shutdown = True
|
self._is_shutdown = True
|
||||||
return
|
return
|
||||||
|
@ -448,6 +450,8 @@ class RemoteMethod:
|
||||||
name: str
|
name: str
|
||||||
action: str
|
action: str
|
||||||
display: str
|
display: str
|
||||||
|
icon: str
|
||||||
|
ok_text: str
|
||||||
description: str
|
description: str
|
||||||
parameters: List[RemoteParameter]
|
parameters: List[RemoteParameter]
|
||||||
return_schema: sch.Schema
|
return_schema: sch.Schema
|
||||||
|
@ -508,30 +512,28 @@ class MethodRegistry(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_method(cls, function, name=None, action=None, display=None,
|
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:
|
if name is None:
|
||||||
name = function.__name__
|
name = function.__name__
|
||||||
if action is None:
|
if action is None:
|
||||||
action = name
|
action = name
|
||||||
if display is None:
|
|
||||||
display = name
|
|
||||||
if description is None:
|
if description is None:
|
||||||
description = function.__doc__ or ''
|
description = function.__doc__
|
||||||
sig = inspect.signature(function)
|
sig = inspect.signature(function)
|
||||||
params = []
|
params = []
|
||||||
for p in sig.parameters.values():
|
for p in sig.parameters.values():
|
||||||
params.append(cls._make_param(p))
|
params.append(cls._make_param(p))
|
||||||
return_schema = cls._to_schema(sig, sig.return_annotation)
|
return_schema = cls._to_schema(sig, sig.return_annotation)
|
||||||
return RemoteMethod(name, action, display, description, params,
|
return RemoteMethod(name, action, display, icon, ok_text, description,
|
||||||
return_schema, function)
|
params, return_schema, function)
|
||||||
|
|
||||||
def method(self, func=None, *, name=None, action=None, display=None,
|
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):
|
def _method(func):
|
||||||
if condition:
|
if condition:
|
||||||
method = self.create_method(func, name, action, display,
|
method = self.create_method(func, name, action, display,
|
||||||
description)
|
icon, ok_text, description)
|
||||||
self.register_method(method)
|
self.register_method(method)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
@ -705,8 +707,10 @@ class Client(object):
|
||||||
def _write_method(to: bufs.Method, method: RemoteMethod):
|
def _write_method(to: bufs.Method, method: RemoteMethod):
|
||||||
to.name = method.name
|
to.name = method.name
|
||||||
to.action = method.action
|
to.action = method.action
|
||||||
to.display = method.display
|
to.display = method.display or ''
|
||||||
to.description = method.description
|
to.icon = method.icon or ''
|
||||||
|
to.ok_text = method.ok_text or ''
|
||||||
|
to.description = method.description or ''
|
||||||
Client._write_parameters(to.parameters, method.parameters)
|
Client._write_parameters(to.parameters, method.parameters)
|
||||||
to.return_type.name = method.return_schema.name
|
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.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
import db.Transaction;
|
import db.Transaction;
|
||||||
import ghidra.app.services.DebuggerTargetService;
|
import ghidra.app.services.DebuggerTargetService;
|
||||||
import ghidra.async.*;
|
import ghidra.async.*;
|
||||||
|
@ -56,6 +58,11 @@ public abstract class TestTraceRmiConnection extends AbstractTraceRmiConnection
|
||||||
String description, Map<String, RemoteParameter> parameters, SchemaName retType,
|
String description, Map<String, RemoteParameter> parameters, SchemaName retType,
|
||||||
AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue)
|
AsyncPairingQueue<Map<String, Object>> argQueue, AsyncPairingQueue<Object> retQueue)
|
||||||
implements RemoteMethod {
|
implements RemoteMethod {
|
||||||
|
|
||||||
|
public TestRemoteMethod {
|
||||||
|
Objects.requireNonNull(action);
|
||||||
|
}
|
||||||
|
|
||||||
public TestRemoteMethod(String name, ActionName action, String display, String description,
|
public TestRemoteMethod(String name, ActionName action, String display, String description,
|
||||||
Map<String, RemoteParameter> parameters, SchemaName retType) {
|
Map<String, RemoteParameter> parameters, SchemaName retType) {
|
||||||
this(name, action, display, description, parameters, retType, new AsyncPairingQueue<>(),
|
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()));
|
result.thenApply(ar -> ar.ret).handle(AsyncUtils.copyTo(retQueue().give()));
|
||||||
return result.thenApply(ar -> ar.args);
|
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,
|
public record TestRemoteParameter(String name, SchemaName type, boolean required,
|
||||||
|
|
|
@ -163,6 +163,13 @@
|
||||||
"help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint
|
"help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html">Breakpoint
|
||||||
Marker Actions</A> in the listings.</P>
|
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">
|
<H3><A name="enable_breakpoints"></A><IMG alt="" src="icon.debugger.breakpoint.enable">
|
||||||
Enable</H3>
|
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.WindowPosition;
|
||||||
import docking.action.*;
|
import docking.action.*;
|
||||||
import docking.action.builder.ActionBuilder;
|
import docking.action.builder.ActionBuilder;
|
||||||
|
import docking.menu.MultiActionDockingAction;
|
||||||
import docking.widgets.table.*;
|
import docking.widgets.table.*;
|
||||||
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
|
||||||
import ghidra.app.context.ProgramLocationActionContext;
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
|
||||||
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
|
||||||
|
import ghidra.app.plugin.core.debug.gui.InvokeActionEntryAction;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
|
import ghidra.app.services.DebuggerControlService.ControlModeChangeListener;
|
||||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
||||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
|
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
|
||||||
import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener;
|
import ghidra.debug.api.breakpoint.LogicalBreakpointsChangeListener;
|
||||||
import ghidra.debug.api.control.ControlMode;
|
import ghidra.debug.api.control.ControlMode;
|
||||||
|
import ghidra.debug.api.target.ActionName;
|
||||||
import ghidra.debug.api.target.Target;
|
import ghidra.debug.api.target.Target;
|
||||||
|
import ghidra.debug.api.target.Target.ActionEntry;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.framework.model.DomainObjectEvent;
|
import ghidra.framework.model.DomainObjectEvent;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
|
@ -263,6 +267,78 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||||
return contextHasMatchingBreakpoints(context, lb -> true, loc -> true);
|
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
|
protected class EnableSelectedBreakpointsAction
|
||||||
extends AbstractEnableSelectedBreakpointsAction {
|
extends AbstractEnableSelectedBreakpointsAction {
|
||||||
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
|
public static final String GROUP = DebuggerResources.GROUP_BREAKPOINTS;
|
||||||
|
@ -708,6 +784,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||||
new DebuggerMakeBreakpointsEffectiveActionContext();
|
new DebuggerMakeBreakpointsEffectiveActionContext();
|
||||||
|
|
||||||
// package access for testing
|
// package access for testing
|
||||||
|
SetBreakpointAction actionSetBreakpoint;
|
||||||
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
|
EnableSelectedBreakpointsAction actionEnableSelectedBreakpoints;
|
||||||
EnableAllBreakpointsAction actionEnableAllBreakpoints;
|
EnableAllBreakpointsAction actionEnableAllBreakpoints;
|
||||||
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
|
DisableSelectedBreakpointsAction actionDisableSelectedBreakpoints;
|
||||||
|
@ -1163,6 +1240,7 @@ public class DebuggerBreakpointsProvider extends ComponentProviderAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createActions() {
|
protected void createActions() {
|
||||||
|
actionSetBreakpoint = new SetBreakpointAction();
|
||||||
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
|
actionEnableSelectedBreakpoints = new EnableSelectedBreakpointsAction();
|
||||||
actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
|
actionEnableAllBreakpoints = new EnableAllBreakpointsAction();
|
||||||
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();
|
actionDisableSelectedBreakpoints = new DisableSelectedBreakpointsAction();
|
||||||
|
|
|
@ -288,6 +288,7 @@ public class DebuggerControlPlugin extends AbstractDebuggerPlugin
|
||||||
}
|
}
|
||||||
actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this)
|
actionsTargetStepExt.add(TargetStepExtAction.builder(entry.display(), this)
|
||||||
.description(entry.details())
|
.description(entry.details())
|
||||||
|
.toolBarIcon(entry.icon())
|
||||||
.enabledWhen(ctx -> entry.isEnabled())
|
.enabledWhen(ctx -> entry.isEnabled())
|
||||||
.onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry))
|
.onAction(ctx -> TargetActionTask.runAction(tool, entry.display(), entry))
|
||||||
.build());
|
.build());
|
||||||
|
|
|
@ -18,21 +18,19 @@ package ghidra.app.plugin.core.debug.gui.control;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
import docking.Tool;
|
import docking.Tool;
|
||||||
import docking.action.*;
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.action.MenuData;
|
||||||
import docking.actions.PopupActionProvider;
|
import docking.actions.PopupActionProvider;
|
||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.context.ProgramLocationActionContext;
|
import ghidra.app.context.ProgramLocationActionContext;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
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.app.services.*;
|
||||||
import ghidra.debug.api.control.ControlMode;
|
import ghidra.debug.api.control.ControlMode;
|
||||||
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
import ghidra.debug.api.model.DebuggerObjectActionContext;
|
||||||
import ghidra.debug.api.target.ActionName;
|
|
||||||
import ghidra.debug.api.target.Target;
|
import ghidra.debug.api.target.Target;
|
||||||
import ghidra.debug.api.target.Target.ActionEntry;
|
import ghidra.debug.api.target.Target.ActionEntry;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
|
@ -57,23 +55,10 @@ import ghidra.trace.model.program.TraceProgramView;
|
||||||
public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider {
|
public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionProvider {
|
||||||
public static final String GROUP_METHODS = "Debugger Methods";
|
public static final String GROUP_METHODS = "Debugger Methods";
|
||||||
|
|
||||||
class InvokeActionEntryAction extends DockingAction {
|
class MethodAction extends InvokeActionEntryAction {
|
||||||
private final ActionEntry entry;
|
public MethodAction(ActionEntry entry) {
|
||||||
|
super(DebuggerMethodActionsPlugin.this, entry);
|
||||||
public InvokeActionEntryAction(ActionEntry entry) {
|
setPopupMenuData(new MenuData(new String[] { getName() }, entry.icon(), GROUP_METHODS));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,10 +121,10 @@ public class DebuggerMethodActionsPlugin extends Plugin implements PopupActionPr
|
||||||
List<DockingActionIf> result = new ArrayList<>();
|
List<DockingActionIf> result = new ArrayList<>();
|
||||||
for (ActionEntry entry : target.collectActions(null, context).values()) {
|
for (ActionEntry entry : target.collectActions(null, context).values()) {
|
||||||
//if (entry.requiresPrompt() || entry.builtIn()) {
|
//if (entry.requiresPrompt() || entry.builtIn()) {
|
||||||
if (!entry.isEnabled() || entry.builtIn()) {
|
if (!entry.isEnabled() || !entry.getShow().isShowing(context)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
result.add(new InvokeActionEntryAction(entry));
|
result.add(new MethodAction(entry));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,11 @@ class TargetDockingAction extends DockingAction {
|
||||||
protected void updateFromContext(ActionContext context) {
|
protected void updateFromContext(ActionContext context) {
|
||||||
entry = findEntry(context);
|
entry = findEntry(context);
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
|
getToolBarData().setIcon(action.icon());
|
||||||
setDescription(defaultDescription);
|
setDescription(defaultDescription);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
getToolBarData().setIcon(entry.icon());
|
||||||
setDescription(entry.details());
|
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
|
* This may apply, e.g., to a GDB "Inferior," which has no yet been used to launch or attach to
|
||||||
* a process.
|
* a process.
|
||||||
*/
|
*/
|
||||||
INACTIVE(false, false, false, false),
|
INACTIVE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object is alive, but its execution state is unspecified
|
* 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
|
* <em>all</em> of its threads are stopped. For the clients' sakes, all models should implement
|
||||||
* these conventions internally.
|
* these conventions internally.
|
||||||
*/
|
*/
|
||||||
ALIVE(true, false, false, false),
|
ALIVE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object is alive, but not executing
|
* The object is alive, but not executing
|
||||||
*/
|
*/
|
||||||
STOPPED(true, false, true, false),
|
STOPPED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object is alive and executing
|
* 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
|
* 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.
|
* necessarily mean it is executing on a CPU at this exact moment.
|
||||||
*/
|
*/
|
||||||
RUNNING(true, true, false, false),
|
RUNNING,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object is no longer alive
|
* 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.,
|
* 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.
|
* a GDB "Inferior," which could be re-used to launch or attach to another process.
|
||||||
*/
|
*/
|
||||||
TERMINATED(false, false, false, true);
|
TERMINATED;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.modules.DebuggerStaticMappingServicePlugin;
|
||||||
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.*;
|
import ghidra.app.plugin.core.debug.service.tracermi.TestTraceRmiConnection.*;
|
||||||
import ghidra.app.services.DebuggerStaticMappingService;
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
|
import ghidra.debug.api.target.ActionName;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.trace.model.Lifespan;
|
import ghidra.trace.model.Lifespan;
|
||||||
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema;
|
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema;
|
||||||
|
@ -57,7 +58,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
||||||
protected void addMethods() {
|
protected void addMethods() {
|
||||||
TestRemoteMethodRegistry reg = rmiCx.getMethods();
|
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,
|
"Advance to the given address", PrimitiveTraceObjectSchema.VOID,
|
||||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||||
"The thread to advance"),
|
"The thread to advance"),
|
||||||
|
@ -65,14 +66,15 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
||||||
"Target", "The target address"));
|
"Target", "The target address"));
|
||||||
reg.add(rmiMethodAdvance);
|
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,
|
"Step in some special way", PrimitiveTraceObjectSchema.VOID,
|
||||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||||
"The thread to step"));
|
"The thread to step"));
|
||||||
reg.add(rmiMethodStepExt);
|
reg.add(rmiMethodStepExt);
|
||||||
|
|
||||||
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", null, "Advance With Flag",
|
rmiMethodAdvanceWithFlag = new TestRemoteMethod("advance_flag", ActionName.STEP_EXT,
|
||||||
"Advance to the given address, with flag", PrimitiveTraceObjectSchema.VOID,
|
"Advance With Flag", "Advance to the given address, with flag",
|
||||||
|
PrimitiveTraceObjectSchema.VOID,
|
||||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||||
"The thread to advance"),
|
"The thread to advance"),
|
||||||
new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null,
|
new TestRemoteParameter("target", PrimitiveTraceObjectSchema.ADDRESS, true, null,
|
||||||
|
@ -81,7 +83,7 @@ public class DebuggerMethodActionsPluginTest extends AbstractGhidraHeadedDebugge
|
||||||
"Flag", "The flag"));
|
"Flag", "The flag"));
|
||||||
reg.add(rmiMethodAdvanceWithFlag);
|
reg.add(rmiMethodAdvanceWithFlag);
|
||||||
|
|
||||||
rmiMethodBetween = new TestRemoteMethod("between", null, "Between",
|
rmiMethodBetween = new TestRemoteMethod("between", ActionName.STEP_EXT, "Between",
|
||||||
"Advance between two given addresses", PrimitiveTraceObjectSchema.VOID,
|
"Advance between two given addresses", PrimitiveTraceObjectSchema.VOID,
|
||||||
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
new TestRemoteParameter("thread", new SchemaName("Thread"), true, null, "Thread",
|
||||||
"The thread to advance"),
|
"The thread to advance"),
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<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
|
src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
|
||||||
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
|
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
|
||||||
double-click the margin.</li>
|
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.,
|
<li>From the Terminal window, use the GDB command, e.g.,
|
||||||
<code>break main</code>.</li>
|
<code>break main</code>.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>The advantage of using the listings is that you can quickly set a
|
<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
|
breakpoint at any address. The advantage of using the dropdown action or
|
||||||
that you can specify something other than an address. Often, those
|
Terminal window is that you can specify something other than an address.
|
||||||
specifications still resolve to addresses, and Ghidra will display them.
|
Often, those specifications still resolve to addresses, and Ghidra will
|
||||||
Ghidra will memorize breakpoints by recording them as special bookmarks
|
display them. Ghidra will memorize breakpoints by recording them as
|
||||||
in the program database. There is some iconography to communicate the
|
special bookmarks in the program database. There is some iconography to
|
||||||
various states of a breakpoint. When all is well and normal, you should
|
communicate the various states of a breakpoint. When all is well and
|
||||||
only see enabled <img src="images/breakpoint-enable.png"
|
normal, you should only see enabled <img
|
||||||
alt="enabled breakpoint" /> and disabled <img
|
src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
|
||||||
src="images/breakpoint-disable.png" alt="disabled breakpoint" />
|
disabled <img src="images/breakpoint-disable.png"
|
||||||
breakpoints. If the target is terminated (or not launched yet), you may
|
alt="disabled breakpoint" /> breakpoints. If the target is terminated
|
||||||
also see ineffective <img src="images/breakpoint-enable-ineff.png"
|
(or not launched yet), you may also see ineffective <img
|
||||||
alt="ineffective breakpoint" /> breakpoints.</p>
|
src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
|
||||||
|
breakpoints.</p>
|
||||||
</section>
|
</section>
|
||||||
<section id="examining-minesweeper-board-setup" class="level2">
|
<section id="examining-minesweeper-board-setup" class="level2">
|
||||||
<h2>Examining Minesweeper Board Setup</h2>
|
<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:
|
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 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`.
|
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 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.
|
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.
|
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.
|
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="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -35,7 +35,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -35,7 +35,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<h1 class="title">Ghidra Debugger</h1>
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<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-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-8"><a href="#cb7-8"></a><span class="op">}</span></span>
|
||||||
<span id="cb7-9"><a href="#cb7-9"></a></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-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-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>
|
<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");
|
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");
|
monitor.setMessage("Interrupting target and waiting for STOPPED");
|
||||||
interrupt();
|
interrupt();
|
||||||
waitForBreak(3, TimeUnit.SECONDS);
|
waitForBreak(3, TimeUnit.SECONDS);
|
||||||
|
|
|
@ -99,7 +99,8 @@
|
||||||
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
|
||||||
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
class="advanced" href="B2-Emulation.html">Emulation</a><a
|
||||||
class="advanced" href="B3-Scripting.html">Scripting</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>
|
||||||
<header id="title-block-header">
|
<header id="title-block-header">
|
||||||
<h1 class="title">Ghidra Debugger</h1>
|
<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-39"><a href="#cb7-39"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-40"><a href="#cb7-40"></a></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-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-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> Expr inValue<span class="op">)</span> <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-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-45"><a href="#cb7-45"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-46"><a href="#cb7-46"></a></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-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-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="cf">return</span> inValue<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="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>
|
<span id="cb7-51"><a href="#cb7-51"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-52"><a href="#cb7-52"></a> <span class="at">@Override</span></span>
|
<span id="cb7-52"><a href="#cb7-52"></a></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-53"><a href="#cb7-53"></a> <span class="at">@Override</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-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">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-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="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="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-57"><a href="#cb7-57"></a> <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-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> <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-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="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>
|
<span id="cb7-61"><a href="#cb7-61"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-62"><a href="#cb7-62"></a> <span class="at">@Override</span></span>
|
<span id="cb7-62"><a href="#cb7-62"></a></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-63"><a href="#cb7-63"></a> <span class="at">@Override</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-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="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>
|
<span id="cb7-66"><a href="#cb7-66"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-67"><a href="#cb7-67"></a> <span class="at">@Override</span></span>
|
<span id="cb7-67"><a href="#cb7-67"></a></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-68"><a href="#cb7-68"></a> <span class="at">@Override</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-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="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>
|
<span id="cb7-71"><a href="#cb7-71"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-72"><a href="#cb7-72"></a> <span class="at">@Override</span></span>
|
<span id="cb7-72"><a href="#cb7-72"></a></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-73"><a href="#cb7-73"></a> <span class="at">@Override</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-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="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>
|
<span id="cb7-76"><a href="#cb7-76"></a> <span class="op">}</span></span>
|
||||||
<span id="cb7-77"><a href="#cb7-77"></a> <span class="at">@Override</span></span>
|
<span id="cb7-77"><a href="#cb7-77"></a></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-78"><a href="#cb7-78"></a> <span class="at">@Override</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-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="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></code></pre></div>
|
<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
|
<p>We have implemented two arithmetic models: one for big-endian
|
||||||
languages and one for little-endian. The endianness comes into play when
|
languages and one for little-endian. The endianness comes into play when
|
||||||
we encode constant values passed to <code>fromConst()</code>. We must
|
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
|
could use <code>modAfterLoad()</code> to record that a value was
|
||||||
retrieved via a tainted address. The <code>inValue</code> parameter
|
retrieved via a tainted address. The <code>inValue</code> parameter
|
||||||
gives the <code>Expr</code> actually retrieved from the emulator’s
|
gives the <code>Expr</code> actually retrieved from the emulator’s
|
||||||
storage, and <code>inAddress</code> gives the address (really just the
|
storage, and <code>inOffset</code> gives the offset used to retrieve it.
|
||||||
<code>Expr</code> piece) used to retrieve it. Conversely, in
|
Conversely, in <code>modBeforeStore()</code>, <code>inValue</code> gives
|
||||||
<code>modBeforeStore()</code>, <code>inValue</code> gives the value
|
the value about to be stored, and <code>inOffset</code> gives the offset
|
||||||
about to be stored, and <code>inAddress</code> gives the address used to
|
used to store it.</p>
|
||||||
store it.</p>
|
|
||||||
<p>We implement neither <code>toConcrete()</code> nor
|
<p>We implement neither <code>toConcrete()</code> nor
|
||||||
<code>sizeOf()</code>. Since we will be augmenting a concrete emulator,
|
<code>sizeOf()</code>. Since we will be augmenting a concrete emulator,
|
||||||
these methods will be provided by the concrete piece. If this model is
|
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.listing.Function;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.trace.model.TraceExecutionState;
|
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
|
import ghidra.trace.model.TraceExecutionState;
|
||||||
|
|
||||||
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,7 +43,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
||||||
throw new AssertionError("The current program must be termmines");
|
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");
|
monitor.setMessage("Interrupting target and waiting for STOPPED");
|
||||||
interrupt();
|
interrupt();
|
||||||
waitForBreak(3, TimeUnit.SECONDS);
|
waitForBreak(3, TimeUnit.SECONDS);
|
||||||
|
@ -87,7 +87,7 @@ public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
|
||||||
|
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
while (true) {
|
while (true) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCancelled();
|
||||||
|
|
||||||
TraceExecutionState execState = getExecutionState(trace);
|
TraceExecutionState execState = getExecutionState(trace);
|
||||||
switch (execState) {
|
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 |