1#!/usr/bin/python2.7 2# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6import itertools 7import os 8import re 9import subprocess 10import sys 11 12class Error(Exception): 13 """Module error class.""" 14 15 16class UnknownArchitectureError(Error): 17 """Raised if an architecture can not be handled.""" 18 19 20def GetCrossTool(tool): 21 chost = os.getenv('CHOST', '') 22 if chost: 23 return chost + '-' + tool 24 return tool 25 26 27def SymbolMap(object_filename, sort_numerically=True): 28 """Run nm tool to list symbols from object file.""" 29 cmd = [GetCrossTool('nm')] 30 if sort_numerically: 31 cmd.append('-n') 32 cmd.append(object_filename), 33 out = subprocess.check_output(cmd) 34 for line in out.splitlines(): 35 cols = line.split() 36 if len(cols) == 2: 37 addr = None 38 symbol_type, symbol_name = cols 39 elif len(cols) == 3: 40 addr, symbol_type, symbol_name = cols 41 else: 42 raise Error('Unexpected number of columns') 43 yield addr, symbol_type, symbol_name 44 45 46def Disassemble(object_filename, start_address, stop_address): 47 """Disassemble a portion of an object file using objdump.""" 48 return subprocess.check_output(( 49 GetCrossTool('objdump'), '-d', '--no-show-raw-insn', 50 '--start-address', '0x'+start_address, 51 '--stop-address', '0x'+stop_address, 52 object_filename)) 53 54 55ASSEMBLY_RE = re.compile( 56 r'^ +(?P<address>[0-9A-Fa-f]+):\t(?P<mnemonic>\S+)\s+(?P<operands>.*)$') 57X86_CONDITIONAL_BRANCH_INSTRUCTIONS = set([ 58 'jo', # opcode: 0x70 59 'jno', # 0x71 60 'jb', 'jnae', 'jc', # 0x72 61 'jnb', 'jae', 'jnc', # 0x73 62 'jz', 'je' # 0x74 63 'jnz', 'jne', # 0x75 64 'jbe', 'jna', # 0x76 65 'jnbe', 'ja', # 0x77 66 'js', # 0x78 67 'jns', # 0x79 68 'jp', 'jpe', # 0x7a 69 'jnp', 'jpo', # 0x7b 70 'jl', 'jnge', # 0x7c 71 'jnl', 'jge', # 0x7d 72 'jle', 'jng', # 0x7e 73 'jnle', 'jg', # 0x7f 74 'loopnz', 'loopne', # 0xe0 75 'loopz', 'loope', # 0xe1 76 'loop', # 0xe2 77 'jcxz', 'jecxz', 'jrcxz',]) # 0xe3 78 79 80def IsBranch_x86(mnemonic): 81 return mnemonic in X86_CONDITIONAL_BRANCH_INSTRUCTIONS 82 83 84ARM_BRANCH_INSTRUCTIONS = [ 85 'b', 'bl', 'blx', 'bx', 'bxj', 'cbz', 'cbnz'] 86ARM_CONDITIONS = [ 87 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs', 'vc', 88 'hi', 'ls', 'ge', 'lt', 'gt', 'le', 'al'] 89ARM_ALL_BRANCH_INSTRUCTIONS = set( 90 instr + cond 91 for instr, cond 92 in itertools.product(ARM_BRANCH_INSTRUCTIONS, ARM_CONDITIONS)) 93 94 95def IsBranch_arm(mnemonic): 96 if '.' in mnemonic: 97 mnemonic, width = mnemonic.split('.', 1) 98 return mnemonic in ARM_ALL_BRANCH_INSTRUCTIONS 99 100 101X86_ARCH_CHOST_RE = re.compile(r'^(x86|i[2346]86)') 102 103 104def ChooseIsBranchForChost(chost): 105 if X86_ARCH_CHOST_RE.match(chost): 106 return IsBranch_x86 107 if chost.startswith('arm'): 108 return IsBranch_arm 109 raise UnknownArchitectureError(chost) 110 111 112def _FindLoopBranches(disassembly, is_branch): 113 for line in disassembly.splitlines(): 114 m = ASSEMBLY_RE.match(line) 115 if not m: 116 continue 117 address, mnemonic, operands = m.group('address', 'mnemonic', 'operands') 118 if is_branch(mnemonic): 119 target_address, target_label = operands.split() 120 yield address, target_address 121 122 123def FindLoopBranches(disassembly): 124 chost = os.getenv('CHOST', '') 125 is_branch = ChooseIsBranchForChost(chost) 126 return _FindLoopBranches(disassembly, is_branch) 127 128 129def main(): 130 object_filename = sys.argv[1] 131 for addr, symbol_type, symbol_name in SymbolMap(object_filename): 132 if symbol_name == 'the_loop_start': 133 loop_start = addr 134 if symbol_name == 'the_loop_end': 135 loop_end = addr 136 disassembly = Disassemble(object_filename, loop_start, loop_end) 137 try: 138 for source, target in FindLoopBranches(disassembly): 139 print source, target 140 except UnknownArchitectureError as e: 141 print >> sys.stderr, 'Unknown architecture for chost ' + e.args[0] 142 143 144 145if __name__ == '__main__': 146 main() 147