Skip to content

Instantly share code, notes, and snippets.

@ben-cohen
Created June 15, 2019 19:18
Show Gist options
  • Save ben-cohen/b23401f25f2857a29b04f5079bb8849c to your computer and use it in GitHub Desktop.
Save ben-cohen/b23401f25f2857a29b04f5079bb8849c to your computer and use it in GitHub Desktop.
GDB Python unwinder and frame decorator prototype
#
# GDB Python unwinder and frame decorator prototype
#
# This is a very basic demonstration for how to get GDB to unwind the
# "frametest" example crash in https://github.com/ben-cohen/corelibgen and
# apply a frame decorator to the backtrace.
#
# It needs a recent version of gdb and only works on i386 or x86_64.
# You need to manually fill in the PC value of the crash at "XXX" below!
#
# References:
# https://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
# https://github.com/tromey/spidermonkey-unwinder
# https://tromey.com/blog/?p=927
#
# Ben Cohen, June 2019
#
import itertools
import gdb
from gdb.unwinder import Unwinder
from gdb.FrameDecorator import FrameDecorator
# XXX: The PC value of the crash
CRASH_PC = 0x7f83a1c586a5
arch = gdb.selected_inferior().architecture().name()
if arch == "i386:x86-64":
SP_REGISTER = 'rsp'
PC_REGISTER = 'rip'
BP_REGISTER = 'rbp'
WORD_SIZE = 8
elif arch == "i386":
SP_REGISTER = 'esp'
PC_REGISTER = 'eip'
BP_REGISTER = 'ebp'
WORD_SIZE = 8
class FrameId(object):
def __init__(self, sp, pc):
self.sp = sp
self.pc = pc
class SymValueWrapper():
def __init__(self, symbol, value):
self.sym = symbol
self.val = value
def value(self):
return self.val
def symbol(self):
return self.sym
class TestFrameDecorator(FrameDecorator):
def __init__(self, base):
super(TestFrameDecorator, self).__init__(base)
self.base = base
def function(self):
# Make a fake function name
return "crash_frame"
def frame_args(self):
# Make fake function arguments
args = []
args.append(SymValueWrapper("foo", 42))
return args
class TestFrameFilter():
def __init__(self):
self.name = "Foo"
self.priority = 100
self.enabled = True
gdb.frame_filters[self.name] = self
def decorate_crashed_frame(self, frame):
if frame.inferior_frame().pc() == CRASH_PC:
return TestFrameDecorator(frame)
else:
return frame
def filter(self, frame_iter):
return itertools.imap(self.decorate_crashed_frame, frame_iter)
class TestUnwinder(Unwinder):
def __init__(self):
super(TestUnwinder, self).__init__("Test")
def __call__(self, pending_frame):
pc = pending_frame.read_register(PC_REGISTER)
if pc != CRASH_PC:
return None
sp = pending_frame.read_register(SP_REGISTER)
unwind_info = pending_frame.create_unwind_info(FrameId(sp, pc))
# This is specific for unwinding the "frametest" example!
caller_sp = sp + 0x12345678
caller_bp = gdb.parse_and_eval('*(long*)%s'%caller_sp)
caller_sp += WORD_SIZE
caller_pc = gdb.parse_and_eval('*(long*)%s'%caller_sp)
caller_sp += WORD_SIZE
unwind_info.add_saved_register(SP_REGISTER, caller_sp)
unwind_info.add_saved_register(PC_REGISTER, caller_pc)
unwind_info.add_saved_register(BP_REGISTER, caller_bp)
return unwind_info
unwinder = TestUnwinder()
gdb.unwinder.register_unwinder(None, unwinder, replace=True)
filter = TestFrameFilter()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment