blob: 5f8a8594b8f8180e931af9f63667103a80bd2788 [file] [log] [blame]
Johnny Chen5e28aa52011-03-28 22:40:32 +00001#!/usr/bin/env python
2
3"""
4Run lldb to disassemble all the available functions for an executable image.
5
6"""
7
8import os
9import sys
10from optparse import OptionParser
11
12def setupSysPath():
13 """
Johnny Chen4044fdc2011-03-28 22:48:25 +000014 Add LLDB.framework/Resources/Python and the test dir to the sys.path.
Johnny Chen5e28aa52011-03-28 22:40:32 +000015 """
16 # Get the directory containing the current script.
17 scriptPath = sys.path[0]
18 if not scriptPath.endswith(os.path.join('utils', 'test')):
19 print "This script expects to reside in lldb's utils/test directory."
20 sys.exit(-1)
21
22 # This is our base name component.
23 base = os.path.abspath(os.path.join(scriptPath, os.pardir, os.pardir))
24
25 # This is for the goodies in the test directory under base.
26 sys.path.append(os.path.join(base,'test'))
27
28 # These are for xcode build directories.
29 xcode3_build_dir = ['build']
30 xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
31 dbg = ['Debug']
32 rel = ['Release']
33 bai = ['BuildAndIntegration']
34 python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
35
36 dbgPath = os.path.join(base, *(xcode3_build_dir + dbg + python_resource_dir))
37 dbgPath2 = os.path.join(base, *(xcode4_build_dir + dbg + python_resource_dir))
38 relPath = os.path.join(base, *(xcode3_build_dir + rel + python_resource_dir))
39 relPath2 = os.path.join(base, *(xcode4_build_dir + rel + python_resource_dir))
40 baiPath = os.path.join(base, *(xcode3_build_dir + bai + python_resource_dir))
41 baiPath2 = os.path.join(base, *(xcode4_build_dir + bai + python_resource_dir))
42
43 lldbPath = None
44 if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
45 lldbPath = dbgPath
46 elif os.path.isfile(os.path.join(dbgPath2, 'lldb.py')):
47 lldbPath = dbgPath2
48 elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
49 lldbPath = relPath
50 elif os.path.isfile(os.path.join(relPath2, 'lldb.py')):
51 lldbPath = relPath2
52 elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
53 lldbPath = baiPath
54 elif os.path.isfile(os.path.join(baiPath2, 'lldb.py')):
55 lldbPath = baiPath2
56
57 if not lldbPath:
58 print 'This script requires lldb.py to be in either ' + dbgPath + ',',
59 print relPath + ', or ' + baiPath
60 sys.exit(-1)
61
62 # This is to locate the lldb.py module. Insert it right after sys.path[0].
63 sys.path[1:1] = [lldbPath]
64 print "sys.path:", sys.path
65
66
Johnny Chen6454e152011-03-29 01:07:00 +000067def run_command(ci, cmd, res, echoInput=True, echoOutput=True):
68 if echoInput:
69 print "run command:", cmd
Johnny Chen5e28aa52011-03-28 22:40:32 +000070 ci.HandleCommand(cmd, res)
71 if res.Succeeded():
Johnny Chen6454e152011-03-29 01:07:00 +000072 if echoOutput:
73 print "run_command output:", res.GetOutput()
Johnny Chen5e28aa52011-03-28 22:40:32 +000074 else:
Johnny Chen6454e152011-03-29 01:07:00 +000075 if echoOutput:
76 print "run command failed!"
77 print "run_command error:", res.GetError()
Johnny Chen5e28aa52011-03-28 22:40:32 +000078
Johnny Chen318e7ba2011-03-30 18:47:54 +000079def do_lldb_disassembly(lldb_commands, exe, disassemble_options, num_symbols, symbols_to_disassemble):
Johnny Chen6454e152011-03-29 01:07:00 +000080 import lldb, lldbutil, atexit, re
Johnny Chen5e28aa52011-03-28 22:40:32 +000081
82 # Create the debugger instance now.
83 dbg = lldb.SBDebugger.Create()
84 if not dbg.IsValid():
85 raise Exception('Invalid debugger instance')
86
87 # Register an exit callback.
88 atexit.register(lambda: lldb.SBDebugger.Terminate())
89
90 # We want our debugger to be synchronous.
91 dbg.SetAsync(False)
92
93 # Get the command interpreter from the debugger.
94 ci = dbg.GetCommandInterpreter()
95 if not ci:
96 raise Exception('Could not get the command interpreter')
97
98 # And the associated result object.
99 res = lldb.SBCommandReturnObject()
100
101 # See if there any extra command(s) to execute before we issue the file command.
102 for cmd in lldb_commands:
103 run_command(ci, cmd, res)
104
105 # Now issue the file command.
106 run_command(ci, 'file %s' % exe, res)
107
108 # Send the 'image dump symtab' command.
Johnny Chen6454e152011-03-29 01:07:00 +0000109 run_command(ci, 'image dump symtab', res, echoOutput=False)
110
111 if not res.Succeeded():
112 print "Symbol table dump failed!"
113 sys.exit(-2)
114
115 # Do disassembly on the symbols.
116 # The following line from the 'image dump symtab' gives us a hint as to the
117 # starting char position of the symbol name.
118 # Index UserID DSX Type File Address/Value Load Address Size Flags Name
119 # ------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ----------------------------------
120 # [ 0] 0 Code 0x0000000000000820 0x0000000000000000 0x000e0008 sandbox_init_internal
121 symtab_dump = res.GetOutput()
122 symbol_pos = -1
123 code_type_pos = -1
124 code_type_end = -1
125
126 # Heuristics: the first 50 lines should give us the answer for symbol_pos and code_type_pos.
127 for line in symtab_dump.splitlines()[:50]:
128 print "line:", line
129 if re.match("^Index.*Name$", line):
130 symbol_pos = line.rfind('Name')
131 #print "symbol_pos:", symbol_pos
132 code_type_pos = line.find('Type')
133 code_type_end = code_type_pos + 4
134 #print "code_type_pos:", code_type_pos
135 break
136
Johnny Chen318e7ba2011-03-30 18:47:54 +0000137 # Define a generator for the symbols to disassemble.
138 def symbol_iter(num, symbols, symtab_dump):
139 # If we specify the symbols to disassemble, ignore symbol table dump.
140 if symbols:
141 for i in range(len(symbols)):
142 print "symbol:", symbols[i]
143 yield symbols[i]
144 else:
145 limited = True if num != -1 else False
Johnny Chen6454e152011-03-29 01:07:00 +0000146 if limited:
Johnny Chen318e7ba2011-03-30 18:47:54 +0000147 count = 0
148 for line in symtab_dump.splitlines():
149 if limited and count >= num:
150 return
151 if line[code_type_pos:code_type_end] == 'Code':
152 symbol = line[symbol_pos:]
153 print "symbol:", symbol
154 if limited:
155 count = count + 1
156 print "symbol count:", count
157 yield symbol
158
159 # Disassembly time.
160 for symbol in symbol_iter(num_symbols, symbols_to_disassemble, symtab_dump):
161 cmd = "disassemble %s '%s'" % (disassemble_options, symbol)
162 run_command(ci, cmd, res)
Johnny Chen6454e152011-03-29 01:07:00 +0000163
Johnny Chen5e28aa52011-03-28 22:40:32 +0000164
165def main():
166 # This is to set up the Python path to include the pexpect-2.4 dir.
167 # Remember to update this when/if things change.
168 scriptPath = sys.path[0]
169 sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4'))
170
171 parser = OptionParser(usage="""\
172Run lldb to disassemble all the available functions for an executable image.
173
174Usage: %prog [options]
175""")
176 parser.add_option('-C', '--lldb-command',
177 type='string', action='append', metavar='COMMAND',
178 default=[], dest='lldb_commands',
179 help='Command(s) lldb executes after starting up (can be empty)')
Johnny Chen5e28aa52011-03-28 22:40:32 +0000180 parser.add_option('-e', '--executable',
181 type='string', action='store',
182 dest='executable',
Johnny Chen6454e152011-03-29 01:07:00 +0000183 help="""Mandatory: the executable to do disassembly on.""")
184 parser.add_option('-o', '--options',
185 type='string', action='store',
186 dest='disassemble_options',
187 help="""Mandatory: the options passed to lldb's 'disassemble' command.""")
188 parser.add_option('-n', '--num-symbols',
Johnny Chen318e7ba2011-03-30 18:47:54 +0000189 type='int', action='store', default=-1,
Johnny Chen6454e152011-03-29 01:07:00 +0000190 dest='num_symbols',
191 help="""The number of symbols to disassemble, if specified.""")
Johnny Chen318e7ba2011-03-30 18:47:54 +0000192 parser.add_option('-s', '--symbol',
193 type='string', action='append', metavar='SYMBOL', default=[],
194 dest='symbols_to_disassemble',
195 help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""")
196
Johnny Chen5e28aa52011-03-28 22:40:32 +0000197 opts, args = parser.parse_args()
198
199 lldb_commands = opts.lldb_commands
Johnny Chen5e28aa52011-03-28 22:40:32 +0000200
Johnny Chen6454e152011-03-29 01:07:00 +0000201 if not opts.executable or not opts.disassemble_options:
Johnny Chen5e28aa52011-03-28 22:40:32 +0000202 parser.print_help()
203 sys.exit(1)
Johnny Chen6454e152011-03-29 01:07:00 +0000204
Johnny Chen5e28aa52011-03-28 22:40:32 +0000205 executable = opts.executable
Johnny Chen6454e152011-03-29 01:07:00 +0000206 disassemble_options = opts.disassemble_options
Johnny Chen318e7ba2011-03-30 18:47:54 +0000207 num_symbols = opts.num_symbols
208 symbols_to_disassemble = opts.symbols_to_disassemble
Johnny Chen5e28aa52011-03-28 22:40:32 +0000209
210 # We have parsed the options.
211 print "lldb commands:", lldb_commands
Johnny Chen5e28aa52011-03-28 22:40:32 +0000212 print "executable:", executable
Johnny Chen6454e152011-03-29 01:07:00 +0000213 print "disassemble options:", disassemble_options
214 print "num of symbols to disassemble:", num_symbols
Johnny Chen318e7ba2011-03-30 18:47:54 +0000215 print "symbols to disassemble:", symbols_to_disassemble
Johnny Chen5e28aa52011-03-28 22:40:32 +0000216
217 setupSysPath()
Johnny Chen318e7ba2011-03-30 18:47:54 +0000218 do_lldb_disassembly(lldb_commands, executable, disassemble_options, num_symbols, symbols_to_disassemble)
Johnny Chen5e28aa52011-03-28 22:40:32 +0000219
220if __name__ == '__main__':
221 main()