summaryrefslogtreecommitdiff
path: root/utils/test/disasm.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/test/disasm.py')
-rwxr-xr-xutils/test/disasm.py198
1 files changed, 198 insertions, 0 deletions
diff --git a/utils/test/disasm.py b/utils/test/disasm.py
new file mode 100755
index 000000000000..46660299f188
--- /dev/null
+++ b/utils/test/disasm.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+
+"""
+Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
+and display the disassembly result.
+
+"""
+
+import os
+import sys
+from optparse import OptionParser
+
+def is_exe(fpath):
+ """Check whether fpath is an executable."""
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+def which(program):
+ """Find the full path to a program, or return None."""
+ fpath, fname = os.path.split(program)
+ if fpath:
+ if is_exe(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+ return None
+
+def do_llvm_mc_disassembly(gdb_commands, gdb_options, exe, func, mc, mc_options):
+ from cStringIO import StringIO
+ import pexpect
+
+ gdb_prompt = "\r\n\(gdb\) "
+ gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb')
+ # Turn on logging for what gdb sends back.
+ gdb.logfile_read = sys.stdout
+ gdb.expect(gdb_prompt)
+
+ # See if there any extra command(s) to execute before we issue the file command.
+ for cmd in gdb_commands:
+ gdb.sendline(cmd)
+ gdb.expect(gdb_prompt)
+
+ # Now issue the file command.
+ gdb.sendline('file %s' % exe)
+ gdb.expect(gdb_prompt)
+
+ # Send the disassemble command.
+ gdb.sendline('disassemble %s' % func)
+ gdb.expect(gdb_prompt)
+
+ # Get the output from gdb.
+ gdb_output = gdb.before
+
+ # Use StringIO to record the memory dump as well as the gdb assembler code.
+ mc_input = StringIO()
+
+ # These keep track of the states of our simple gdb_output parser.
+ prev_line = None
+ prev_addr = None
+ curr_addr = None
+ addr_diff = 0
+ looking = False
+ for line in gdb_output.split(os.linesep):
+ if line.startswith('Dump of assembler code'):
+ looking = True
+ continue
+
+ if line.startswith('End of assembler dump.'):
+ looking = False
+ prev_addr = curr_addr
+ if mc_options and mc_options.find('arm') != -1:
+ addr_diff = 4
+ if mc_options and mc_options.find('thumb') != -1:
+ # It is obviously wrong to assume the last instruction of the
+ # function has two bytes.
+ # FIXME
+ addr_diff = 2
+
+ if looking and line.startswith('0x'):
+ # It's an assembler code dump.
+ prev_addr = curr_addr
+ curr_addr = line.split(None, 1)[0]
+ if prev_addr and curr_addr:
+ addr_diff = int(curr_addr, 16) - int(prev_addr, 16)
+
+ if prev_addr and addr_diff > 0:
+ # Feed the examining memory command to gdb.
+ gdb.sendline('x /%db %s' % (addr_diff, prev_addr))
+ gdb.expect(gdb_prompt)
+ x_output = gdb.before
+ # Get the last output line from the gdb examine memory command,
+ # split the string into a 3-tuple with separator '>:' to handle
+ # objc method names.
+ memory_dump = x_output.split(os.linesep)[-1].partition('>:')[2].strip()
+ #print "\nbytes:", memory_dump
+ disasm_str = prev_line.partition('>:')[2]
+ print >> mc_input, '%s # %s' % (memory_dump, disasm_str)
+
+ # We're done with the processing. Assign the current line to be prev_line.
+ prev_line = line
+
+ # Close the gdb session now that we are done with it.
+ gdb.sendline('quit')
+ gdb.expect(pexpect.EOF)
+ gdb.close()
+
+ # Write the memory dump into a file.
+ with open('disasm-input.txt', 'w') as f:
+ f.write(mc_input.getvalue())
+
+ mc_cmd = '%s -disassemble %s disasm-input.txt' % (mc, mc_options)
+ print "\nExecuting command:", mc_cmd
+ os.system(mc_cmd)
+
+ # And invoke llvm-mc with the just recorded file.
+ #mc = pexpect.spawn('%s -disassemble %s disasm-input.txt' % (mc, mc_options))
+ #mc.logfile_read = sys.stdout
+ #print "mc:", mc
+ #mc.close()
+
+
+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 gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
+and display the disassembly result.
+
+Usage: %prog [options]
+""")
+ parser.add_option('-C', '--gdb-command',
+ type='string', action='append', metavar='COMMAND',
+ default=[], dest='gdb_commands',
+ help='Command(s) gdb executes after starting up (can be empty)')
+ parser.add_option('-O', '--gdb-options',
+ type='string', action='store',
+ dest='gdb_options',
+ help="""The options passed to 'gdb' command if specified.""")
+ parser.add_option('-e', '--executable',
+ type='string', action='store',
+ dest='executable',
+ help="""The executable to do disassembly on.""")
+ parser.add_option('-f', '--function',
+ type='string', action='store',
+ dest='function',
+ help="""The function name (could be an address to gdb) for disassembly.""")
+ parser.add_option('-m', '--llvm-mc',
+ type='string', action='store',
+ dest='llvm_mc',
+ help="""The llvm-mc executable full path, if specified.
+ Otherwise, it must be present in your PATH environment.""")
+
+ parser.add_option('-o', '--options',
+ type='string', action='store',
+ dest='llvm_mc_options',
+ help="""The options passed to 'llvm-mc -disassemble' command if specified.""")
+
+ opts, args = parser.parse_args()
+
+ gdb_commands = opts.gdb_commands
+ gdb_options = opts.gdb_options
+
+ if not opts.executable:
+ parser.print_help()
+ sys.exit(1)
+ executable = opts.executable
+
+ if not opts.function:
+ parser.print_help()
+ sys.exit(1)
+ function = opts.function
+
+ llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc')
+ if not llvm_mc:
+ parser.print_help()
+ sys.exit(1)
+
+ # This is optional. For example:
+ # --options='-triple=arm-apple-darwin -debug-only=arm-disassembler'
+ llvm_mc_options = opts.llvm_mc_options
+
+ # We have parsed the options.
+ print "gdb commands:", gdb_commands
+ print "gdb options:", gdb_options
+ print "executable:", executable
+ print "function:", function
+ print "llvm-mc:", llvm_mc
+ print "llvm-mc options:", llvm_mc_options
+
+ do_llvm_mc_disassembly(gdb_commands, gdb_options, executable, function, llvm_mc, llvm_mc_options)
+
+if __name__ == '__main__':
+ main()