diff options
Diffstat (limited to 'utils/test/disasm.py')
-rwxr-xr-x | utils/test/disasm.py | 198 |
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() |