GP-4350: more review-related changes

GP-4350: attempted fix for expression-based bpts
GP-4350: working for at least 8+
GP-4350: fix for f.level (working for 10+)
GP-4350: good for 11+
GP-4350: good for 11+
This commit is contained in:
d-millar 2024-03-15 19:16:25 -04:00
parent 9a7ab128df
commit f2319e61be
5 changed files with 120 additions and 40 deletions

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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():

View file

@ -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