Created
February 11, 2014 00:15
-
-
Save martijnthe/8926923 to your computer and use it in GitHub Desktop.
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
import imp | |
try: | |
imp.find_module("gdb") | |
except ImportError: | |
raise Exception("This file is a GDB script.\n" | |
"It is not intended to be run outside of GDB.\n" | |
"Hint: to load a script in GDB, use `source this_file.py`") | |
import gdb | |
import re | |
class Address(int): | |
""" Convenience subclass of `int` that accepts a hexadecimal string in its | |
constructor. It also accepts a gdb.Value in its constructor, in which case | |
the address of the value will be attempted to be used to create the object. | |
Its `__repr__` prints its value formatted as hexadecimal. | |
""" | |
ADDR_REGEX = re.compile("^\s*(0x[a-fA-F0-9]{8})") | |
def __new__(cls, *args, **kwargs): | |
if args: | |
val = args[0] | |
if isinstance(val, gdb.Value): | |
if val.address: | |
# If the value has an address, use it: | |
val = str(val.address) | |
else: | |
# Otherwise, attempt to use that the value as an int: | |
val = int(val) | |
if isinstance(val, str): | |
# GDB tends to append symbolic info to the string, even for | |
# a gdb.Value that was acquired from the `address` attribute... | |
match = Address.ADDR_REGEX.match(val) | |
if match: | |
val = match.group(1) | |
return super(Address, cls).__new__(cls, val, base=16) | |
return super(Address, cls).__new__(cls, *args, **kwargs) | |
def __repr__(self): | |
return "0x%08x" % self | |
def __str__(self): | |
return self.__repr__() | |
class ActionBreakpoint(gdb.Breakpoint): | |
""" | |
Convenience wrapper around gdb.Breakpoint. | |
The first argument to the constructor is a Callable, the ActionBreakpoint's | |
attribute `action_callable` will be set to this object. The Callable must | |
accept one argument, which will be set to the instance of the | |
ActionBreakpoint instance itself. | |
The second (optional) argument to the constructor is the name (str) of the | |
symbol on which to set the breakpoint. If not specified, the __name__ of | |
the callable is used as the name of the symbol, for convenience. | |
ActionBreakpoint implements the method `def handle_break(self)` that is | |
called when the breakpoint is hit. The base implementation just calls the | |
`action_callable` attribute. | |
Usage example: | |
def window_stack_push(breakpoint): | |
print "window_stack_push() called!" | |
bp = ActionBreakpoint(window_stack_push) | |
""" | |
def stop_handler(event): | |
if isinstance(event, gdb.BreakpointEvent): | |
for breakpoint in event.breakpoints: | |
if isinstance(breakpoint, ActionBreakpoint): | |
breakpoint.handle_break() | |
gdb.events.stop.connect(stop_handler) # Register with gdb module | |
def __init__(self, action_callable, symbol_name=None, addr=None, | |
auto_continue=True): | |
if addr and symbol_name: | |
raise Exception("Can't use arguments `symbol_name` and " | |
"`addr` simultaneously!") | |
if addr: | |
# When an address is specified, the expression must be prepended | |
# with an `*` (not to be confused with a dereference...): | |
# https://sourceware.org/gdb/onlinedocs/gdb/Specify-Location.html | |
symbol_name = "*" + str(addr) | |
if not symbol_name: | |
symbol_name = action_callable.__name__ | |
super(ActionBreakpoint, self).__init__(symbol_name) | |
self.action_callable = action_callable | |
self.auto_continue = auto_continue | |
def handle_break(self): | |
self.action_callable(self) | |
if self.auto_continue: | |
gdb.execute("continue") | |
class MonkeyPatch(ActionBreakpoint): | |
""" | |
Object that `overrides` an existing function in the program being | |
debugged, using ActionBreakpoint and the `return` GDB command. | |
The first argument is the callable that provides the return value as a GDB | |
expression (str) or returns `None` in case the function returns void. | |
The (optional) second argument is a string of the symbol of the function to | |
monkey-patch. If not specified, the __name__ of the callable is used as the | |
name of the symbol, for convenience. | |
Usage example: | |
def my_function_override(monkey_patch): | |
return "(int) 123" | |
patch = MonkeyPatch(my_function_override, "my_existing_function") | |
""" | |
def handle_break(self): | |
return_value_str = self.action_callable(self) | |
gdb.write("Hit monkey patch %s, returning `%s`" % | |
(self, return_value_str)) | |
if return_value_str: | |
gdb.execute("return (%s)" % return_value_str) | |
else: | |
gdb.execute("return") | |
gdb.execute("continue") | |
GET_ARGS_RE = re.compile("^[_A-z]+[_A-z0-9]*") | |
def get_args(): | |
""" | |
Returns a dict of gdb.Value objects of the arguments in scope. | |
""" | |
args = {} | |
info_args_str = gdb.execute("info args", to_string=True) | |
print info_args_str | |
for line in info_args_str.splitlines(): | |
match = GET_ARGS_RE.search(line) | |
var_name = match.group() | |
args[var_name] = gdb.parse_and_eval(var_name) | |
return args |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment