blob: a55021ed8378abf9a63705b365f35f8f2751e607 [file] [log] [blame]
#!/usr/bin/env python
import gdb
import os
import traceback
import re
print 'Loading gdb scripts for debugging DynamoRIO...'
# FIXME i#531: Support loading symbols after attaching.
# If we were sourced directly instead of auto-loaded, try to guess where DR is
# so we can make RunDR work. We don't need this for anything else yet.
try:
DR_LIBDIR = os.path.dirname(os.path.abspath(__file__))
except:
DR_LIBDIR = None
class DROption(gdb.Parameter):
def __init__(self, dr_option, param_class):
super(DROption, self).__init__("dr-" + dr_option, gdb.COMMAND_OBSCURE,
param_class)
self.dr_option = dr_option
set_doc = ("DynamoRIO option of the same name.")
show_doc = set_doc
def get_set_string(self):
return str(self.value)
def get_show_string(self, svalue):
return svalue
# The client and client-args options are special in that they do not go in
# DYNAMORIO_OPTIONS without being massaged first. We also specify
# documentation strings for them.
class DRClient(DROption):
def __init__(self):
super(DRClient, self).__init__("client", gdb.PARAM_OPTIONAL_FILENAME)
set_doc = ("Path to DynamoRIO client to run when invoking DR. "
"Leave blank to run without a client.")
show_doc = set_doc
class DRClientArgs(DROption):
def __init__(self):
super(DRClientArgs, self).__init__("client-args", gdb.PARAM_STRING)
set_doc = ("DynamoRIO client arguments.")
show_doc = set_doc
# Client specification options.
dr_client = DRClient()
dr_client_args = DRClientArgs()
# Other useful flags to pass to DynamoRIO.
dr_options = [
DROption('msgbox_mask', gdb.PARAM_ZINTEGER),
DROption('loglevel', gdb.PARAM_ZINTEGER),
DROption('logmask', gdb.PARAM_ZINTEGER),
]
class RunDR(gdb.Command):
"""Run the application under DynamoRIO with the current options.
Goes through the drrun script to avoid depending on the config file format.
"""
def __init__(self):
super(RunDR, self).__init__("rundr", gdb.COMMAND_OBSCURE)
def invoke(self, arg, from_tty):
# Find drrun.
parts = DR_LIBDIR.split(os.sep)
build_mode = parts[-1]
arch = parts[-2][-2:]
if build_mode not in ('debug', 'release'):
print "Unrecognized build_mode %s." % build_mode
return
if arch not in ('32', '64'):
print ("Unable to find drrun using libdir %r, unrecognized arch %s."
% (DR_LIBDIR, arch))
return
drrun_path = os.sep.join(parts[:-2])
drrun_path = os.path.join(drrun_path, 'bin%s/drrun' % arch)
gdb.execute("set exec-wrapper %r -%s" % (drrun_path, build_mode))
# Build options string.
# FIXME: The escaping is most likely wrong here. It's tricky because
# the command is parsed by gdb and DynamoRIO.
env_opts = os.environ.get('DYNAMORIO_OPTIONS', '')
param_opts = ' '.join("-%s %s" % (p.dr_option, p.value)
for p in dr_options)
client_opts = ''
if dr_client.value:
client_opts = ('-code_api -client_lib %s;0;%s' %
(dr_client.value, dr_client_args.value))
dr_opts = ' '.join([env_opts, param_opts, client_opts])
gdb.execute("set env DYNAMORIO_OPTIONS " + dr_opts)
gdb.execute("run " + arg)
RunDR()
def gdb_has_breakpoints():
match = re.match(r'^(\d+)\.(\d+)', gdb.VERSION)
if not match:
print "Error parsing gdb version (%s)" % gdb.VERSION
return False
major = int(match.group(1))
minor = int(match.group(2))
if major > 7:
return True
elif major == 7 and minor >= 3:
return True
return False
# gdb 7.2 doesn't really support breakpoints in the Python API. We do a version
# check so we can print an informative message rather than dying with an
# exception on "class PrivloadBP(gdb.Breakpoint)".
if gdb_has_breakpoints():
class PrivloadBP(gdb.Breakpoint):
# Enable to debug this breakpoint.
DEBUG = False
DYNAMORIO_BP = True
def __init__(self):
super(PrivloadBP, self).__init__("dr_gdb_add_symbol_file",
internal=not self.DEBUG)
def stop(self):
try:
frame = gdb.newest_frame()
filename = frame.read_var("filename").string()
textaddr = long(frame.read_var("textaddr"))
cmd = "add-symbol-file '%s' %s" % (filename, hex(textaddr))
print "Executing gdb command:", cmd
# We suppress output to the screen with to_string unless we're
# debugging.
gdb.execute(cmd, to_string=not self.DEBUG)
return self.DEBUG # Controls whether the user stops here or not.
except:
# gdb won't print a Python stack trace if we raise an exception,
# so we do it ourselves.
traceback.print_exc()
return True
# Delete all breakpoints set from previous runs and initializations and
# replace them with new ones.
def remove_old_bps():
bps = gdb.breakpoints()
if not bps:
return
for bp in bps:
if getattr(bp, 'DYNAMORIO_BP', False):
bp.delete()
remove_old_bps()
# We need pending breakpoints in order to wait for LD_PRELOAD to bring in
# the library with the symbol.
gdb.execute("set breakpoint pending on")
PrivloadBP()
else:
print ("This version of gdb does not support breakpoints from Python. "
"Libraries loaded by DynamoRIO will not be automatically "
"registered with gdb.")