diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py index 6423fc61ad..146c7073b9 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py @@ -595,6 +595,7 @@ def putreg(frame, reg_descs): cobj.insert() mapper = STATE.trace.register_mapper values = [] + # NB: This command will fail if the process is running endian = arch.get_endian() for desc in reg_descs: v = frame.read_register(desc.name) @@ -616,7 +617,7 @@ def ghidra_trace_putreg(group='all', *, is_mi, **kwargs): """ STATE.require_tx() - frame = gdb.selected_frame() + frame = util.selected_frame() with STATE.client.batch() as b: return putreg(frame, util.get_register_descs(frame.architecture(), group)) @@ -631,7 +632,7 @@ def ghidra_trace_delreg(group='all', *, is_mi, **kwargs): STATE.require_tx() inf = gdb.selected_inferior() - frame = gdb.selected_frame() + frame = util.selected_frame() space = 'Inferiors[{}].Threads[{}].Stack[{}].Registers'.format( inf.num, gdb.selected_thread().num, util.get_level(frame) ) @@ -957,9 +958,10 @@ def activate(path=None): if t is None: path = INFERIOR_PATTERN.format(infnum=inf.num) else: - frame = gdb.selected_frame() - path = FRAME_PATTERN.format( - infnum=inf.num, tnum=t.num, level=util.get_level(frame)) + frame = util.selected_frame() + if frame is not None: + path = FRAME_PATTERN.format( + infnum=inf.num, tnum=t.num, level=util.get_level(frame)) trace.proxy_object_path(path).activate() @@ -1329,7 +1331,7 @@ def convert_tid(t): @contextmanager def restore_frame(): - f = gdb.selected_frame() + f = util.selected_frame() yield f.select() @@ -1400,8 +1402,12 @@ def put_frames(): t = gdb.selected_thread() if t is None: return + # NB: This command will fail if the process is running bt = gdb.execute('bt', to_string=True).strip().split('\n') - f = newest_frame(gdb.selected_frame()) + f = util.selected_frame() + if f is None: + return + f = newest_frame(f) keys = [] level = 0 while f is not None: diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py index 7f04b7784b..70094b6abb 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py @@ -33,13 +33,14 @@ GhidraHookPrefix() class HookState(object): - __slots__ = ('installed', 'mem_catchpoint', 'batch', 'skip_continue') + __slots__ = ('installed', 'mem_catchpoint', 'batch', 'skip_continue', 'in_break_w_cont') def __init__(self): self.installed = False self.mem_catchpoint = None self.batch = None self.skip_continue = False + self.in_break_w_cont = False def ensure_batch(self): if self.batch is None: @@ -86,9 +87,12 @@ class InferiorState(object): thread = gdb.selected_thread() if thread is not None: if first or thread not in self.visited: + # NB: This command will fail if the process is running commands.put_frames() self.visited.add(thread) - frame = gdb.selected_frame() + frame = util.selected_frame() + if frame is None: + return hashable_frame = (thread, util.get_level(frame)) if first or hashable_frame not in self.visited: commands.putreg( @@ -102,6 +106,7 @@ class InferiorState(object): except MemoryError as e: print(f"Couldn't record page with SP: {e}") self.visited.add(hashable_frame) + # NB: These commands (put_modules/put_regions) will fail if the process is running if first or self.regions or self.threads or self.modules: # Sections, memory syscalls, or stack allocations commands.put_modules() @@ -238,7 +243,9 @@ def on_frame_selected(): if trace is None: return t = gdb.selected_thread() - f = gdb.selected_frame() + f = util.selected_frame() + if f is None: + return HOOK_STATE.ensure_batch() with trace.open_tx("Frame {}.{}.{} selected".format(inf.num, t.num, util.get_level(f))): INF_STATES[inf.num].record() @@ -299,11 +306,28 @@ def on_cont(event): state.record_continued() +def check_for_continue(event): + if hasattr(event, 'breakpoints'): + if HOOK_STATE.in_break_w_cont: + return True + for brk in event.breakpoints: + if hasattr(brk, 'commands'): + for cmd in brk.commands: + if cmd == 'c' or cmd.startswith('cont'): + HOOK_STATE.in_break_w_cont = True + return True + HOOK_STATE.in_break_w_cont = False + return False + + @log_errors def on_stop(event): if hasattr(event, 'breakpoints') and HOOK_STATE.mem_catchpoint in event.breakpoints: HOOK_STATE.skip_continue = True return + if check_for_continue(event): + HOOK_STATE.skip_continue = True + return inf = gdb.selected_inferior() if inf.num not in INF_STATES: return diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py index 5f06a5e209..7d34d33edb 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py @@ -171,7 +171,9 @@ def find_thread_by_stack_obj(object): def find_frame_by_level(thread, level): # Because threads don't have any attribute to get at frames thread.switch() - f = gdb.selected_frame() + f = util.selected_frame() + if f is None: + return None # Navigate up or down, because I can't just get by level down = level - util.get_level(f) @@ -340,7 +342,10 @@ def refresh_stack(node: sch.Schema('Stack')): @REGISTRY.method(action='refresh', display='Refresh Registers') def refresh_registers(node: sch.Schema('RegisterValueContainer')): """Refresh the register values for the frame.""" - find_frame_by_regs_obj(node).select() + f = find_frame_by_regs_obj(node) + if f is None: + return + f.select() # TODO: Groups? with commands.open_tracked_tx('Refresh Registers'): gdb.execute('ghidra trace putreg') diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py index dc85fc987d..29158ce26b 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/util.py @@ -391,3 +391,10 @@ def get_register_descs(arch, group='all'): tokens = line.strip().split() descs.append(RegisterDesc(tokens[0])) return descs + +def selected_frame(): + try: + return gdb.selected_frame() + except Exception as e: + print("No selected frame") + return None