Home | History | Annotate | Download | only in test
      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()
    199