Created
June 15, 2019 19:18
-
-
Save ben-cohen/b23401f25f2857a29b04f5079bb8849c to your computer and use it in GitHub Desktop.
GDB Python unwinder and frame decorator prototype
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# 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