forked from OSchip/llvm-project
295 lines
9.6 KiB
Python
Executable File
295 lines
9.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""
|
|
Run lldb to disassemble all the available functions for an executable image.
|
|
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
from optparse import OptionParser
|
|
|
|
|
|
def setupSysPath():
|
|
"""
|
|
Add LLDB.framework/Resources/Python and the test dir to the sys.path.
|
|
"""
|
|
# Get the directory containing the current script.
|
|
scriptPath = sys.path[0]
|
|
if not scriptPath.endswith(os.path.join('utils', 'test')):
|
|
print("This script expects to reside in lldb's utils/test directory.")
|
|
sys.exit(-1)
|
|
|
|
# This is our base name component.
|
|
base = os.path.abspath(os.path.join(scriptPath, os.pardir, os.pardir))
|
|
|
|
# This is for the goodies in the test directory under base.
|
|
sys.path.append(os.path.join(base, 'test'))
|
|
|
|
# These are for xcode build directories.
|
|
xcode3_build_dir = ['build']
|
|
xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
|
|
dbg = ['Debug']
|
|
rel = ['Release']
|
|
bai = ['BuildAndIntegration']
|
|
python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
|
|
|
|
dbgPath = os.path.join(
|
|
base, *(xcode3_build_dir + dbg + python_resource_dir))
|
|
dbgPath2 = os.path.join(
|
|
base, *(xcode4_build_dir + dbg + python_resource_dir))
|
|
relPath = os.path.join(
|
|
base, *(xcode3_build_dir + rel + python_resource_dir))
|
|
relPath2 = os.path.join(
|
|
base, *(xcode4_build_dir + rel + python_resource_dir))
|
|
baiPath = os.path.join(
|
|
base, *(xcode3_build_dir + bai + python_resource_dir))
|
|
baiPath2 = os.path.join(
|
|
base, *(xcode4_build_dir + bai + python_resource_dir))
|
|
|
|
lldbPath = None
|
|
if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
|
|
lldbPath = dbgPath
|
|
elif os.path.isfile(os.path.join(dbgPath2, 'lldb.py')):
|
|
lldbPath = dbgPath2
|
|
elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
|
|
lldbPath = relPath
|
|
elif os.path.isfile(os.path.join(relPath2, 'lldb.py')):
|
|
lldbPath = relPath2
|
|
elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
|
|
lldbPath = baiPath
|
|
elif os.path.isfile(os.path.join(baiPath2, 'lldb.py')):
|
|
lldbPath = baiPath2
|
|
|
|
if not lldbPath:
|
|
print('This script requires lldb.py to be in either ' + dbgPath + ',', end=' ')
|
|
print(relPath + ', or ' + baiPath)
|
|
sys.exit(-1)
|
|
|
|
# This is to locate the lldb.py module. Insert it right after sys.path[0].
|
|
sys.path[1:1] = [lldbPath]
|
|
# print "sys.path:", sys.path
|
|
|
|
|
|
def run_command(ci, cmd, res, echo=True):
|
|
if echo:
|
|
print("run command:", cmd)
|
|
ci.HandleCommand(cmd, res)
|
|
if res.Succeeded():
|
|
if echo:
|
|
print("run_command output:", res.GetOutput())
|
|
else:
|
|
if echo:
|
|
print("run command failed!")
|
|
print("run_command error:", res.GetError())
|
|
|
|
|
|
def do_lldb_disassembly(lldb_commands, exe, disassemble_options, num_symbols,
|
|
symbols_to_disassemble,
|
|
re_symbol_pattern,
|
|
quiet_disassembly):
|
|
import lldb
|
|
import atexit
|
|
import re
|
|
|
|
# Create the debugger instance now.
|
|
dbg = lldb.SBDebugger.Create()
|
|
if not dbg:
|
|
raise Exception('Invalid debugger instance')
|
|
|
|
# Register an exit callback.
|
|
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
|
|
|
# We want our debugger to be synchronous.
|
|
dbg.SetAsync(False)
|
|
|
|
# Get the command interpreter from the debugger.
|
|
ci = dbg.GetCommandInterpreter()
|
|
if not ci:
|
|
raise Exception('Could not get the command interpreter')
|
|
|
|
# And the associated result object.
|
|
res = lldb.SBCommandReturnObject()
|
|
|
|
# See if there any extra command(s) to execute before we issue the file
|
|
# command.
|
|
for cmd in lldb_commands:
|
|
run_command(ci, cmd, res, not quiet_disassembly)
|
|
|
|
# Now issue the file command.
|
|
run_command(ci, 'file %s' % exe, res, not quiet_disassembly)
|
|
|
|
# Create a target.
|
|
#target = dbg.CreateTarget(exe)
|
|
target = dbg.GetSelectedTarget()
|
|
stream = lldb.SBStream()
|
|
|
|
def IsCodeType(symbol):
|
|
"""Check whether an SBSymbol represents code."""
|
|
return symbol.GetType() == lldb.eSymbolTypeCode
|
|
|
|
# Define a generator for the symbols to disassemble.
|
|
def symbol_iter(num, symbols, re_symbol_pattern, target, verbose):
|
|
# If we specify the symbols to disassemble, ignore symbol table dump.
|
|
if symbols:
|
|
for i in range(len(symbols)):
|
|
if verbose:
|
|
print("symbol:", symbols[i])
|
|
yield symbols[i]
|
|
else:
|
|
limited = True if num != -1 else False
|
|
if limited:
|
|
count = 0
|
|
if re_symbol_pattern:
|
|
pattern = re.compile(re_symbol_pattern)
|
|
stream = lldb.SBStream()
|
|
for m in target.module_iter():
|
|
if verbose:
|
|
print("module:", m)
|
|
for s in m:
|
|
if limited and count >= num:
|
|
return
|
|
# If a regexp symbol pattern is supplied, consult it.
|
|
if re_symbol_pattern:
|
|
# If the pattern does not match, look for the next
|
|
# symbol.
|
|
if not pattern.match(s.GetName()):
|
|
continue
|
|
|
|
# If we come here, we're ready to disassemble the symbol.
|
|
if verbose:
|
|
print("symbol:", s.GetName())
|
|
if IsCodeType(s):
|
|
if limited:
|
|
count = count + 1
|
|
if verbose:
|
|
print("returning symbol:", s.GetName())
|
|
yield s.GetName()
|
|
if verbose:
|
|
print("start address:", s.GetStartAddress())
|
|
print("end address:", s.GetEndAddress())
|
|
s.GetDescription(stream)
|
|
print("symbol description:", stream.GetData())
|
|
stream.Clear()
|
|
|
|
# Disassembly time.
|
|
for symbol in symbol_iter(
|
|
num_symbols,
|
|
symbols_to_disassemble,
|
|
re_symbol_pattern,
|
|
target,
|
|
not quiet_disassembly):
|
|
cmd = "disassemble %s '%s'" % (disassemble_options, symbol)
|
|
run_command(ci, cmd, res, not quiet_disassembly)
|
|
|
|
|
|
def main():
|
|
# This is to set up the Python path to include the pexpect-2.4 dir.
|
|
# Remember to update this when/if things change.
|
|
scriptPath = sys.path[0]
|
|
sys.path.append(
|
|
os.path.join(
|
|
scriptPath,
|
|
os.pardir,
|
|
os.pardir,
|
|
'test',
|
|
'pexpect-2.4'))
|
|
|
|
parser = OptionParser(usage="""\
|
|
Run lldb to disassemble all the available functions for an executable image.
|
|
|
|
Usage: %prog [options]
|
|
""")
|
|
parser.add_option(
|
|
'-C',
|
|
'--lldb-command',
|
|
type='string',
|
|
action='append',
|
|
metavar='COMMAND',
|
|
default=[],
|
|
dest='lldb_commands',
|
|
help='Command(s) lldb executes after starting up (can be empty)')
|
|
parser.add_option(
|
|
'-e',
|
|
'--executable',
|
|
type='string',
|
|
action='store',
|
|
dest='executable',
|
|
help="""Mandatory: the executable to do disassembly on.""")
|
|
parser.add_option(
|
|
'-o',
|
|
'--options',
|
|
type='string',
|
|
action='store',
|
|
dest='disassemble_options',
|
|
help="""Mandatory: the options passed to lldb's 'disassemble' command.""")
|
|
parser.add_option(
|
|
'-q',
|
|
'--quiet-disassembly',
|
|
action='store_true',
|
|
default=False,
|
|
dest='quiet_disassembly',
|
|
help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""")
|
|
parser.add_option(
|
|
'-n',
|
|
'--num-symbols',
|
|
type='int',
|
|
action='store',
|
|
default=-1,
|
|
dest='num_symbols',
|
|
help="""The number of symbols to disassemble, if specified.""")
|
|
parser.add_option(
|
|
'-p',
|
|
'--symbol_pattern',
|
|
type='string',
|
|
action='store',
|
|
dest='re_symbol_pattern',
|
|
help="""The regular expression of symbols to invoke lldb's 'disassemble' command.""")
|
|
parser.add_option(
|
|
'-s',
|
|
'--symbol',
|
|
type='string',
|
|
action='append',
|
|
metavar='SYMBOL',
|
|
default=[],
|
|
dest='symbols_to_disassemble',
|
|
help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""")
|
|
|
|
opts, args = parser.parse_args()
|
|
|
|
lldb_commands = opts.lldb_commands
|
|
|
|
if not opts.executable or not opts.disassemble_options:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
executable = opts.executable
|
|
disassemble_options = opts.disassemble_options
|
|
quiet_disassembly = opts.quiet_disassembly
|
|
num_symbols = opts.num_symbols
|
|
symbols_to_disassemble = opts.symbols_to_disassemble
|
|
re_symbol_pattern = opts.re_symbol_pattern
|
|
|
|
# We have parsed the options.
|
|
if not quiet_disassembly:
|
|
print("lldb commands:", lldb_commands)
|
|
print("executable:", executable)
|
|
print("disassemble options:", disassemble_options)
|
|
print("quiet disassembly output:", quiet_disassembly)
|
|
print("num of symbols to disassemble:", num_symbols)
|
|
print("symbols to disassemble:", symbols_to_disassemble)
|
|
print("regular expression of symbols to disassemble:", re_symbol_pattern)
|
|
|
|
setupSysPath()
|
|
do_lldb_disassembly(lldb_commands, executable, disassemble_options,
|
|
num_symbols,
|
|
symbols_to_disassemble,
|
|
re_symbol_pattern,
|
|
quiet_disassembly)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|