1#!/usr/bin/env python 2 3""" 4Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command, 5and display the disassembly result. 6 7""" 8 9import os 10import sys 11from optparse import OptionParser 12 13def is_exe(fpath): 14 """Check whether fpath is an executable.""" 15 return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 16 17def 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 30def 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 124def 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="""\ 131Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command, 132and display the disassembly result. 133 134Usage: %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 197if __name__ == '__main__': 198 main() 199