| #!/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() |