blob: 46660299f188f6d8e0703d9fe9f10d68c7b536ae [file] [log] [blame]
Johnny Chen9c407f52011-03-17 00:59:57 +00001#!/usr/bin/env python
2
3"""
4Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
5and display the disassembly result.
6
7"""
8
9import os
10import sys
11from optparse import OptionParser
12
13def is_exe(fpath):
14 """Check whether fpath is an executable."""
15 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
16
17def which(program):
18 """Find the full path to a program, or return None."""
19 fpath, fname = os.path.split(program)
20 if fpath:
21 if is_exe(program):
22 return program
23 else:
24 for path in os.environ["PATH"].split(os.pathsep):
25 exe_file = os.path.join(path, program)
26 if is_exe(exe_file):
27 return exe_file
28 return None
29
Johnny Chenc4a23442011-03-21 23:44:44 +000030def do_llvm_mc_disassembly(gdb_commands, gdb_options, exe, func, mc, mc_options):
Johnny Chen0251bc72011-03-17 19:05:34 +000031 from cStringIO import StringIO
Johnny Chen9c407f52011-03-17 00:59:57 +000032 import pexpect
33
34 gdb_prompt = "\r\n\(gdb\) "
Johnny Chenc4a23442011-03-21 23:44:44 +000035 gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb')
Johnny Chen9c407f52011-03-17 00:59:57 +000036 # Turn on logging for what gdb sends back.
37 gdb.logfile_read = sys.stdout
Johnny Chen9c407f52011-03-17 00:59:57 +000038 gdb.expect(gdb_prompt)
Johnny Chenc4a23442011-03-21 23:44:44 +000039
40 # See if there any extra command(s) to execute before we issue the file command.
41 for cmd in gdb_commands:
42 gdb.sendline(cmd)
43 gdb.expect(gdb_prompt)
44
45 # Now issue the file command.
46 gdb.sendline('file %s' % exe)
47 gdb.expect(gdb_prompt)
48
49 # Send the disassemble command.
Johnny Chen9c407f52011-03-17 00:59:57 +000050 gdb.sendline('disassemble %s' % func)
51 gdb.expect(gdb_prompt)
52
53 # Get the output from gdb.
54 gdb_output = gdb.before
55
Johnny Chen0251bc72011-03-17 19:05:34 +000056 # Use StringIO to record the memory dump as well as the gdb assembler code.
57 mc_input = StringIO()
Johnny Chen9c407f52011-03-17 00:59:57 +000058
59 # These keep track of the states of our simple gdb_output parser.
60 prev_line = None
61 prev_addr = None
62 curr_addr = None
63 addr_diff = 0
64 looking = False
65 for line in gdb_output.split(os.linesep):
66 if line.startswith('Dump of assembler code'):
67 looking = True
68 continue
69
70 if line.startswith('End of assembler dump.'):
71 looking = False
72 prev_addr = curr_addr
73 if mc_options and mc_options.find('arm') != -1:
74 addr_diff = 4
75 if mc_options and mc_options.find('thumb') != -1:
76 # It is obviously wrong to assume the last instruction of the
77 # function has two bytes.
78 # FIXME
79 addr_diff = 2
80
81 if looking and line.startswith('0x'):
82 # It's an assembler code dump.
83 prev_addr = curr_addr
84 curr_addr = line.split(None, 1)[0]
85 if prev_addr and curr_addr:
86 addr_diff = int(curr_addr, 16) - int(prev_addr, 16)
87
Johnny Chen0251bc72011-03-17 19:05:34 +000088 if prev_addr and addr_diff > 0:
Johnny Chen9c407f52011-03-17 00:59:57 +000089 # Feed the examining memory command to gdb.
90 gdb.sendline('x /%db %s' % (addr_diff, prev_addr))
91 gdb.expect(gdb_prompt)
92 x_output = gdb.before
Johnny Chence1eefd2011-03-19 01:24:25 +000093 # Get the last output line from the gdb examine memory command,
94 # split the string into a 3-tuple with separator '>:' to handle
95 # objc method names.
96 memory_dump = x_output.split(os.linesep)[-1].partition('>:')[2].strip()
Johnny Chen9c407f52011-03-17 00:59:57 +000097 #print "\nbytes:", memory_dump
Johnny Chence1eefd2011-03-19 01:24:25 +000098 disasm_str = prev_line.partition('>:')[2]
Johnny Chen0251bc72011-03-17 19:05:34 +000099 print >> mc_input, '%s # %s' % (memory_dump, disasm_str)
Johnny Chen9c407f52011-03-17 00:59:57 +0000100
101 # We're done with the processing. Assign the current line to be prev_line.
102 prev_line = line
103
104 # Close the gdb session now that we are done with it.
105 gdb.sendline('quit')
106 gdb.expect(pexpect.EOF)
107 gdb.close()
108
Johnny Chen0251bc72011-03-17 19:05:34 +0000109 # Write the memory dump into a file.
110 with open('disasm-input.txt', 'w') as f:
111 f.write(mc_input.getvalue())
Johnny Chen9c407f52011-03-17 00:59:57 +0000112
113 mc_cmd = '%s -disassemble %s disasm-input.txt' % (mc, mc_options)
114 print "\nExecuting command:", mc_cmd
115 os.system(mc_cmd)
116
117 # And invoke llvm-mc with the just recorded file.
118 #mc = pexpect.spawn('%s -disassemble %s disasm-input.txt' % (mc, mc_options))
119 #mc.logfile_read = sys.stdout
120 #print "mc:", mc
121 #mc.close()
122
123
124def main():
125 # This is to set up the Python path to include the pexpect-2.4 dir.
126 # Remember to update this when/if things change.
127 scriptPath = sys.path[0]
128 sys.path.append(os.path.join(scriptPath, os.pardir, os.pardir, 'test', 'pexpect-2.4'))
129
130 parser = OptionParser(usage="""\
131Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
132and display the disassembly result.
133
134Usage: %prog [options]
135""")
Johnny Chenc4a23442011-03-21 23:44:44 +0000136 parser.add_option('-C', '--gdb-command',
137 type='string', action='append', metavar='COMMAND',
138 default=[], dest='gdb_commands',
139 help='Command(s) gdb executes after starting up (can be empty)')
140 parser.add_option('-O', '--gdb-options',
141 type='string', action='store',
142 dest='gdb_options',
143 help="""The options passed to 'gdb' command if specified.""")
Johnny Chen9c407f52011-03-17 00:59:57 +0000144 parser.add_option('-e', '--executable',
145 type='string', action='store',
146 dest='executable',
147 help="""The executable to do disassembly on.""")
148 parser.add_option('-f', '--function',
149 type='string', action='store',
150 dest='function',
151 help="""The function name (could be an address to gdb) for disassembly.""")
152 parser.add_option('-m', '--llvm-mc',
153 type='string', action='store',
154 dest='llvm_mc',
155 help="""The llvm-mc executable full path, if specified.
156 Otherwise, it must be present in your PATH environment.""")
157
158 parser.add_option('-o', '--options',
159 type='string', action='store',
160 dest='llvm_mc_options',
Johnny Chenc4a23442011-03-21 23:44:44 +0000161 help="""The options passed to 'llvm-mc -disassemble' command if specified.""")
Johnny Chen9c407f52011-03-17 00:59:57 +0000162
163 opts, args = parser.parse_args()
164
Johnny Chenc4a23442011-03-21 23:44:44 +0000165 gdb_commands = opts.gdb_commands
166 gdb_options = opts.gdb_options
167
Johnny Chen9c407f52011-03-17 00:59:57 +0000168 if not opts.executable:
169 parser.print_help()
170 sys.exit(1)
171 executable = opts.executable
172
173 if not opts.function:
174 parser.print_help()
175 sys.exit(1)
176 function = opts.function
177
178 llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc')
179 if not llvm_mc:
180 parser.print_help()
181 sys.exit(1)
182
183 # This is optional. For example:
184 # --options='-triple=arm-apple-darwin -debug-only=arm-disassembler'
185 llvm_mc_options = opts.llvm_mc_options
186
187 # We have parsed the options.
Johnny Chenc4a23442011-03-21 23:44:44 +0000188 print "gdb commands:", gdb_commands
189 print "gdb options:", gdb_options
Johnny Chen9c407f52011-03-17 00:59:57 +0000190 print "executable:", executable
191 print "function:", function
192 print "llvm-mc:", llvm_mc
193 print "llvm-mc options:", llvm_mc_options
194
Johnny Chenc4a23442011-03-21 23:44:44 +0000195 do_llvm_mc_disassembly(gdb_commands, gdb_options, executable, function, llvm_mc, llvm_mc_options)
Johnny Chen9c407f52011-03-17 00:59:57 +0000196
197if __name__ == '__main__':
198 main()