diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py index f4e77e7972..acf5633093 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/arch.py @@ -117,7 +117,7 @@ def get_endian(): def get_osabi(): parm = gdb.parameter('osabi') - if not parm in ['auto', 'default']: + if not parm in ['', 'auto', 'default']: return parm # We have to hack around the fact the GDB won't give us the current OS ABI # via the API if it is "auto" or "default". Using "show", we can get it, but 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 c958acb6db..a388570962 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 @@ -165,21 +165,22 @@ def cmd(cli_name, mi_name, cli_class, cli_repeat): _CLICmd.__doc__ = func.__doc__ _CLICmd() - class _MICmd(gdb.MICommand): + if hasattr(gdb, 'MICommand'): + class _MICmd(gdb.MICommand): - def __init__(self): - super().__init__(mi_name) + def __init__(self): + super().__init__(mi_name) - def invoke(self, argv): - try: - return func(*argv, is_mi=True) - except TypeError as e: - raise gdb.GdbError(e.args[0].replace(func.__name__ + "()", - mi_name)) + def invoke(self, argv): + try: + return func(*argv, is_mi=True) + except TypeError as e: + raise gdb.GdbError(e.args[0].replace(func.__name__ + "()", + mi_name)) - _MICmd.__doc__ = func.__doc__ - _MICmd() - return func + _MICmd.__doc__ = func.__doc__ + _MICmd() + return func return _cmd @@ -586,7 +587,7 @@ def ghidra_trace_delmem(address, length, *, is_mi, **kwargs): def putreg(frame, reg_descs): inf = gdb.selected_inferior() space = REGS_PATTERN.format(infnum=inf.num, tnum=gdb.selected_thread().num, - level=frame.level()) + level=util.get_level(frame)) STATE.trace.create_overlay_space('register', space) cobj = STATE.trace.create_object(space) cobj.insert() @@ -594,12 +595,12 @@ def putreg(frame, reg_descs): keys = [] values = [] for desc in reg_descs: - v = frame.read_register(desc) + v = frame.read_register(desc.name) rv = mapper.map_value(inf, desc.name, v) values.append(rv) # TODO: Key by gdb's name or mapped name? I think gdb's. rpath = REG_PATTERN.format(infnum=inf.num, tnum=gdb.selected_thread( - ).num, level=frame.level(), regname=desc.name) + ).num, level=util.get_level(frame), regname=desc.name) keys.append(REG_KEY_PATTERN.format(regname=desc.name)) robj = STATE.trace.create_object(rpath) robj.set_value('_value', rv.value) @@ -621,7 +622,7 @@ def ghidra_trace_putreg(group='all', *, is_mi, **kwargs): STATE.require_tx() frame = gdb.selected_frame() with STATE.client.batch() as b: - return putreg(frame, frame.architecture().registers(group)) + return putreg(frame, util.get_register_descs(frame.architecture(), group)) @cmd('ghidra trace delreg', '-ghidra-trace-delreg', gdb.COMMAND_DATA, True) @@ -636,11 +637,11 @@ def ghidra_trace_delreg(group='all', *, is_mi, **kwargs): inf = gdb.selected_inferior() frame = gdb.selected_frame() space = 'Inferiors[{}].Threads[{}].Stack[{}].Registers'.format( - inf.num, gdb.selected_thread().num, frame.level() + inf.num, gdb.selected_thread().num, util.get_level(frame) ) mapper = STATE.trace.register_mapper names = [] - for desc in frame.architecture().registers(group): + for desc in util.get_register_descs(frame.architecture(), group): names.append(mapper.map_name(inf, desc.name)) return STATE.trace.delete_registers(space, names) @@ -962,7 +963,7 @@ def activate(path=None): else: frame = gdb.selected_frame() path = FRAME_PATTERN.format( - infnum=inf.num, tnum=t.num, level=frame.level()) + infnum=inf.num, tnum=t.num, level=util.get_level(frame)) trace.proxy_object_path(path).activate() @@ -1402,19 +1403,21 @@ def put_frames(): bt = gdb.execute('bt', to_string=True).strip().split('\n') f = newest_frame(gdb.selected_frame()) keys = [] + level = 0 while f is not None: fpath = FRAME_PATTERN.format( - infnum=inf.num, tnum=t.num, level=f.level()) + infnum=inf.num, tnum=t.num, level=level) fobj = STATE.trace.create_object(fpath) - keys.append(FRAME_KEY_PATTERN.format(level=f.level())) + keys.append(FRAME_KEY_PATTERN.format(level=level)) base, pc = mapper.map(inf, f.pc()) if base != pc.space: STATE.trace.create_overlay_space(base, pc.space) fobj.set_value('_pc', pc) fobj.set_value('_func', str(f.function())) fobj.set_value( - '_display', bt[f.level()].strip().replace('\\s+', ' ')) + '_display', bt[level].strip().replace('\\s+', ' ')) f = f.older() + level += 1 fobj.insert() STATE.trace.proxy_object_path(STACK_PATTERN.format( infnum=inf.num, tnum=t.num)).retain_values(keys) 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 e3578f6fbe..df9db85bb2 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 @@ -19,7 +19,7 @@ import traceback import gdb -from . import commands +from . import commands, util class GhidraHookPrefix(gdb.Command): @@ -89,10 +89,10 @@ class InferiorState(object): commands.put_frames() self.visited.add(thread) frame = gdb.selected_frame() - hashable_frame = (thread, frame.level()) + hashable_frame = (thread, util.get_level(frame)) if first or hashable_frame not in self.visited: commands.putreg( - frame, frame.architecture().registers('general')) + frame, util.get_register_descs(frame.architecture(), 'general')) commands.putmem("$pc", "1", from_tty=False) commands.putmem("$sp", "1", from_tty=False) self.visited.add(hashable_frame) @@ -234,7 +234,7 @@ def on_frame_selected(): t = gdb.selected_thread() f = gdb.selected_frame() HOOK_STATE.ensure_batch() - with trace.open_tx("Frame {}.{}.{} selected".format(inf.num, t.num, f.level())): + with trace.open_tx("Frame {}.{}.{} selected".format(inf.num, t.num, util.get_level(f))): INF_STATES[inf.num].record() commands.activate() @@ -274,7 +274,7 @@ def on_register_changed(event): # For now, just record the lot HOOK_STATE.ensure_batch() with trace.open_tx("Register {} changed".format(event.regnum)): - commands.putreg(event.frame, event.frame.architecture().registers()) + commands.putreg(event.frame, util.get_register_descs(event.frame.architecture())) @log_errors @@ -549,8 +549,10 @@ def install_hooks(): cont end """) - HOOK_STATE.mem_catchpoint = ( - set(gdb.breakpoints()) - breaks_before).pop() + bpts = gdb.breakpoints() + # NB: this is unnecessary for gdb 11+ + if len(bpts) > 0: + HOOK_STATE.mem_catchpoint = (set(bpts) - breaks_before).pop() gdb.events.cont.connect(on_cont) gdb.events.stop.connect(on_stop) 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 3cdca5b4f9..5fb10254c6 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 @@ -28,17 +28,17 @@ from . import commands, hooks, util @contextmanager def no_pagination(): before = gdb.parameter('pagination') - gdb.set_parameter('pagination', False) + util.set_bool_param('pagination', False) yield - gdb.set_parameter('pagination', before) + util.set_bool_param('pagination', before) @contextmanager def no_confirm(): before = gdb.parameter('confirm') - gdb.set_parameter('confirm', False) + util.set_bool_param('confirm', False) yield - gdb.set_parameter('confirm', before) + util.set_bool_param('confirm', before) class GdbExecutor(Executor): @@ -175,7 +175,7 @@ def find_frame_by_level(thread, level): f = gdb.selected_frame() # Navigate up or down, because I can't just get by level - down = level - f.level() + down = level - util.get_level(f) while down > 0: f = f.older() if f is None: @@ -188,7 +188,6 @@ def find_frame_by_level(thread, level): raise KeyError( f"Inferiors[{thread.inferior.num}].Threads[{thread.num}].Stack[{level}] does not exist") down += 1 - assert f.level() == level return f @@ -215,7 +214,7 @@ def find_frame_by_regs_obj(object): # Because there's no method to get a register by name.... def find_reg_by_name(f, name): - for reg in f.architecture().registers(): + for reg in util.get_register_descs(f.architecture()): # TODO: gdb appears to be case sensitive, but until we encounter a # situation where case matters, we'll be insensitive if reg.name.lower() == name.lower(): 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 87e8d2c46f..47d141936f 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 @@ -289,6 +289,33 @@ class BreakpointLocationInfoReaderV8(object): pass def get_locations(self, breakpoint): + inf = gdb.selected_inferior() + thread_groups = [inf.num] + if breakpoint.location is not None and breakpoint.location.startswith("*0x"): + address = int(breakpoint.location[1:],16) + loc = BreakpointLocation(address, breakpoint.enabled, thread_groups) + return [loc] + return [] + + +class BreakpointLocationInfoReaderV9(object): + def breakpoint_from_line(self, line): + pass + + def location_from_line(self, line): + pass + + def get_locations(self, breakpoint): + inf = gdb.selected_inferior() + thread_groups = [inf.num] + if breakpoint.location is None: + return [] + try: + address = gdb.parse_and_eval(breakpoint.location).address + loc = BreakpointLocation(address, breakpoint.enabled, thread_groups) + return [loc] + except Exception as e: + print(f"Error parsing bpt location = {breakpoint.location}") return [] @@ -298,13 +325,62 @@ class BreakpointLocationInfoReaderV13(object): def _choose_breakpoint_location_info_reader(): - if 8 <= GDB_VERSION.major < 13: - return BreakpointLocationInfoReaderV8() - elif GDB_VERSION.major >= 13: + if GDB_VERSION.major >= 13: return BreakpointLocationInfoReaderV13() + if GDB_VERSION.major >= 9: + return BreakpointLocationInfoReaderV9() + if GDB_VERSION.major >= 8: + return BreakpointLocationInfoReaderV8() else: raise gdb.GdbError( "GDB version not recognized by ghidragdb: " + GDB_VERSION.full) BREAKPOINT_LOCATION_INFO_READER = _choose_breakpoint_location_info_reader() + +def set_bool_param_by_api(name, value): + gdb.set_parameter(name, value) + + +def set_bool_param_by_cmd(name, value): + val = 'on' if value else 'off' + gdb.execute(f'set {name} {val}') + + +def choose_set_parameter(): + if GDB_VERSION.major >= 13: + return set_bool_param_by_api + else: + return set_bool_param_by_cmd + +set_bool_param = choose_set_parameter() + + +def get_level(frame): + if hasattr(frame, "level"): + return frame.level() + else: + level = -1; + f = frame + while f is not None: + level += 1 + f = f.newer() + return level + + +class RegisterDesc(namedtuple('BaseRegisterDesc', ['name'])): + pass + +def get_register_descs(arch, group='all'): + if hasattr(arch, "registers"): + return arch.registers(group) + else: + descs = [] + regset = gdb.execute(f"info registers {group}", to_string=True).strip().split('\n') + for line in regset: + if not line.startswith(" "): + tokens = line.strip().split() + descs.append(RegisterDesc(tokens[0])) + return descs + +