]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - utils/test/disasm.py
Vendor import of lldb trunk r256945:
[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 def is_exe(fpath):
14     """Check whether fpath is an executable."""
15     return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
16
17 def 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
30 def do_llvm_mc_disassembly(gdb_commands, gdb_options, exe, func, mc, mc_options):
31     from cStringIO import StringIO 
32     import pexpect
33
34     gdb_prompt = "\r\n\(gdb\) "
35     gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb')
36     # Turn on logging for what gdb sends back.
37     gdb.logfile_read = sys.stdout
38     gdb.expect(gdb_prompt)
39
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.
50     gdb.sendline('disassemble %s' % func)
51     gdb.expect(gdb_prompt)
52
53     # Get the output from gdb.
54     gdb_output = gdb.before
55
56     # Use StringIO to record the memory dump as well as the gdb assembler code.
57     mc_input = StringIO()
58
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
88         if prev_addr and addr_diff > 0:
89             # 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
93             # 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()
97             #print "\nbytes:", memory_dump
98             disasm_str = prev_line.partition('>:')[2]
99             print >> mc_input, '%s # %s' % (memory_dump, disasm_str)
100
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
109     # Write the memory dump into a file.
110     with open('disasm-input.txt', 'w') as f:
111         f.write(mc_input.getvalue())
112
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
124 def 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="""\
131 Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
132 and display the disassembly result.
133
134 Usage: %prog [options]
135 """)
136     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.""")
144     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',
161                       help="""The options passed to 'llvm-mc -disassemble' command if specified.""")
162
163     opts, args = parser.parse_args()
164
165     gdb_commands = opts.gdb_commands
166     gdb_options = opts.gdb_options
167
168     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.
188     print "gdb commands:", gdb_commands
189     print "gdb options:", gdb_options
190     print "executable:", executable
191     print "function:", function
192     print "llvm-mc:", llvm_mc
193     print "llvm-mc options:", llvm_mc_options
194
195     do_llvm_mc_disassembly(gdb_commands, gdb_options, executable, function, llvm_mc, llvm_mc_options)
196
197 if __name__ == '__main__':
198     main()