• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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