244 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| """
 | |
| Run lldb to disassemble all the available functions for an executable image.
 | |
| 
 | |
| """
 | |
| 
 | |
| 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 + ',',
 | |
|         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, atexit, 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()
 |