]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - utils/test/disasm.py
Vendor import of lldb trunk r290819:
[FreeBSD/FreeBSD.git] / utils / test / disasm.py
1 #!/usr/bin/env python
2
3 """
4 Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
5 and display the disassembly result.
6
7 """
8
9 import os
10 import sys
11 from optparse import OptionParser
12
13
14 def is_exe(fpath):
15     """Check whether fpath is an executable."""
16     return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
17
18
19 def which(program):
20     """Find the full path to a program, or return None."""
21     fpath, fname = os.path.split(program)
22     if fpath:
23         if is_exe(program):
24             return program
25     else:
26         for path in os.environ["PATH"].split(os.pathsep):
27             exe_file = os.path.join(path, program)
28             if is_exe(exe_file):
29                 return exe_file
30     return None
31
32
33 def do_llvm_mc_disassembly(
34         gdb_commands,
35         gdb_options,
36         exe,
37         func,
38         mc,
39         mc_options):
40     from cStringIO import StringIO
41     import pexpect
42
43     gdb_prompt = "\r\n\(gdb\) "
44     gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb')
45     # Turn on logging for what gdb sends back.
46     gdb.logfile_read = sys.stdout
47     gdb.expect(gdb_prompt)
48
49     # See if there any extra command(s) to execute before we issue the file
50     # command.
51     for cmd in gdb_commands:
52         gdb.sendline(cmd)
53         gdb.expect(gdb_prompt)
54
55     # Now issue the file command.
56     gdb.sendline('file %s' % exe)
57     gdb.expect(gdb_prompt)
58
59     # Send the disassemble command.
60     gdb.sendline('disassemble %s' % func)
61     gdb.expect(gdb_prompt)
62
63     # Get the output from gdb.
64     gdb_output = gdb.before
65
66     # Use StringIO to record the memory dump as well as the gdb assembler code.
67     mc_input = StringIO()
68
69     # These keep track of the states of our simple gdb_output parser.
70     prev_line = None
71     prev_addr = None
72     curr_addr = None
73     addr_diff = 0
74     looking = False
75     for line in gdb_output.split(os.linesep):
76         if line.startswith('Dump of assembler code'):
77             looking = True
78             continue
79
80         if line.startswith('End of assembler dump.'):
81             looking = False
82             prev_addr = curr_addr
83             if mc_options and mc_options.find('arm') != -1:
84                 addr_diff = 4
85             if mc_options and mc_options.find('thumb') != -1:
86                 # It is obviously wrong to assume the last instruction of the
87                 # function has two bytes.
88                 # FIXME
89                 addr_diff = 2
90
91         if looking and line.startswith('0x'):
92             # It's an assembler code dump.
93             prev_addr = curr_addr
94             curr_addr = line.split(None, 1)[0]
95             if prev_addr and curr_addr:
96                 addr_diff = int(curr_addr, 16) - int(prev_addr, 16)
97
98         if prev_addr and addr_diff > 0:
99             # Feed the examining memory command to gdb.
100             gdb.sendline('x /%db %s' % (addr_diff, prev_addr))
101             gdb.expect(gdb_prompt)
102             x_output = gdb.before
103             # Get the last output line from the gdb examine memory command,
104             # split the string into a 3-tuple with separator '>:' to handle
105             # objc method names.
106             memory_dump = x_output.split(
107                 os.linesep)[-1].partition('>:')[2].strip()
108             # print "\nbytes:", memory_dump
109             disasm_str = prev_line.partition('>:')[2]
110             print >> mc_input, '%s # %s' % (memory_dump, disasm_str)
111
112         # We're done with the processing.  Assign the current line to be
113         # prev_line.
114         prev_line = line
115
116     # Close the gdb session now that we are done with it.
117     gdb.sendline('quit')
118     gdb.expect(pexpect.EOF)
119     gdb.close()
120
121     # Write the memory dump into a file.
122     with open('disasm-input.txt', 'w') as f:
123         f.write(mc_input.getvalue())
124
125     mc_cmd = '%s -disassemble %s disasm-input.txt' % (mc, mc_options)
126     print "\nExecuting command:", mc_cmd
127     os.system(mc_cmd)
128
129     # And invoke llvm-mc with the just recorded file.
130     #mc = pexpect.spawn('%s -disassemble %s disasm-input.txt' % (mc, mc_options))
131     #mc.logfile_read = sys.stdout
132     # print "mc:", mc
133     # mc.close()
134
135
136 def main():
137     # This is to set up the Python path to include the pexpect-2.4 dir.
138     # Remember to update this when/if things change.
139     scriptPath = sys.path[0]
140     sys.path.append(
141         os.path.join(
142             scriptPath,
143             os.pardir,
144             os.pardir,
145             'test',
146             'pexpect-2.4'))
147
148     parser = OptionParser(usage="""\
149 Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
150 and display the disassembly result.
151
152 Usage: %prog [options]
153 """)
154     parser.add_option(
155         '-C',
156         '--gdb-command',
157         type='string',
158         action='append',
159         metavar='COMMAND',
160         default=[],
161         dest='gdb_commands',
162         help='Command(s) gdb executes after starting up (can be empty)')
163     parser.add_option(
164         '-O',
165         '--gdb-options',
166         type='string',
167         action='store',
168         dest='gdb_options',
169         help="""The options passed to 'gdb' command if specified.""")
170     parser.add_option('-e', '--executable',
171                       type='string', action='store',
172                       dest='executable',
173                       help="""The executable to do disassembly on.""")
174     parser.add_option(
175         '-f',
176         '--function',
177         type='string',
178         action='store',
179         dest='function',
180         help="""The function name (could be an address to gdb) for disassembly.""")
181     parser.add_option('-m', '--llvm-mc',
182                       type='string', action='store',
183                       dest='llvm_mc',
184                       help="""The llvm-mc executable full path, if specified.
185                       Otherwise, it must be present in your PATH environment.""")
186
187     parser.add_option(
188         '-o',
189         '--options',
190         type='string',
191         action='store',
192         dest='llvm_mc_options',
193         help="""The options passed to 'llvm-mc -disassemble' command if specified.""")
194
195     opts, args = parser.parse_args()
196
197     gdb_commands = opts.gdb_commands
198     gdb_options = opts.gdb_options
199
200     if not opts.executable:
201         parser.print_help()
202         sys.exit(1)
203     executable = opts.executable
204
205     if not opts.function:
206         parser.print_help()
207         sys.exit(1)
208     function = opts.function
209
210     llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc')
211     if not llvm_mc:
212         parser.print_help()
213         sys.exit(1)
214
215     # This is optional.  For example:
216     # --options='-triple=arm-apple-darwin -debug-only=arm-disassembler'
217     llvm_mc_options = opts.llvm_mc_options
218
219     # We have parsed the options.
220     print "gdb commands:", gdb_commands
221     print "gdb options:", gdb_options
222     print "executable:", executable
223     print "function:", function
224     print "llvm-mc:", llvm_mc
225     print "llvm-mc options:", llvm_mc_options
226
227     do_llvm_mc_disassembly(
228         gdb_commands,
229         gdb_options,
230         executable,
231         function,
232         llvm_mc,
233         llvm_mc_options)
234
235 if __name__ == '__main__':
236     main()