1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium 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"""Find symbols in a binary corresponding to given runtime virtual addresses.
6
7Note that source file names are treated as symbols in this script while they
8are actually not.
9"""
10
11import json
12import logging
13import os
14import sys
15
16from static_symbols import StaticSymbolsInFile
17
18
19_BASE_PATH = os.path.dirname(os.path.abspath(__file__))
20_TOOLS_LINUX_PATH = os.path.join(_BASE_PATH, os.pardir, 'linux')
21sys.path.insert(0, _TOOLS_LINUX_PATH)
22
23
24from procfs import ProcMaps  # pylint: disable=F0401
25
26try:
27  from collections import OrderedDict  # pylint: disable=E0611
28except ImportError:
29  _SIMPLEJSON_PATH = os.path.join(_BASE_PATH, os.pardir, os.pardir,
30                                  'third_party')
31  sys.path.insert(0, _SIMPLEJSON_PATH)
32  from simplejson import OrderedDict
33
34
35FUNCTION_SYMBOLS = 0
36SOURCEFILE_SYMBOLS = 1
37TYPEINFO_SYMBOLS = 2
38
39_MAPS_FILENAME = 'maps'
40_FILES_FILENAME = 'files.json'
41
42
43class RuntimeSymbolsInProcess(object):
44  def __init__(self):
45    self._maps = None
46    self._static_symbols_in_filse = {}
47
48  def find_procedure(self, runtime_address):
49    for vma in self._maps.iter(ProcMaps.executable):
50      if vma.begin <= runtime_address < vma.end:
51        static_symbols = self._static_symbols_in_filse.get(vma.name)
52        if static_symbols:
53          return static_symbols.find_procedure_by_runtime_address(
54              runtime_address, vma)
55        else:
56          return None
57    return None
58
59  def find_sourcefile(self, runtime_address):
60    for vma in self._maps.iter(ProcMaps.executable):
61      if vma.begin <= runtime_address < vma.end:
62        static_symbols = self._static_symbols_in_filse.get(vma.name)
63        if static_symbols:
64          return static_symbols.find_sourcefile_by_runtime_address(
65              runtime_address, vma)
66        else:
67          return None
68    return None
69
70  def find_typeinfo(self, runtime_address):
71    for vma in self._maps.iter(ProcMaps.constants):
72      if vma.begin <= runtime_address < vma.end:
73        static_symbols = self._static_symbols_in_filse.get(vma.name)
74        if static_symbols:
75          return static_symbols.find_typeinfo_by_runtime_address(
76              runtime_address, vma)
77        else:
78          return None
79    return None
80
81  @staticmethod
82  def load(prepared_data_dir):
83    symbols_in_process = RuntimeSymbolsInProcess()
84
85    with open(os.path.join(prepared_data_dir, _MAPS_FILENAME), mode='r') as f:
86      symbols_in_process._maps = ProcMaps.load_file(f)
87    with open(os.path.join(prepared_data_dir, _FILES_FILENAME), mode='r') as f:
88      files = json.load(f)
89
90    # pylint: disable=W0212
91    for vma in symbols_in_process._maps.iter(ProcMaps.executable_and_constants):
92      file_entry = files.get(vma.name)
93      if not file_entry:
94        continue
95
96      static_symbols = StaticSymbolsInFile(vma.name)
97
98      nm_entry = file_entry.get('nm')
99      if nm_entry and nm_entry['format'] == 'bsd':
100        with open(os.path.join(prepared_data_dir, nm_entry['file']), 'r') as f:
101          static_symbols.load_nm_bsd(f, nm_entry['mangled'])
102
103      readelf_entry = file_entry.get('readelf-e')
104      if readelf_entry:
105        with open(os.path.join(prepared_data_dir, readelf_entry['file']),
106                  'r') as f:
107          static_symbols.load_readelf_ew(f)
108
109      decodedline_file_entry = file_entry.get('readelf-debug-decodedline-file')
110      if decodedline_file_entry:
111        with open(os.path.join(prepared_data_dir,
112                               decodedline_file_entry['file']), 'r') as f:
113          static_symbols.load_readelf_debug_decodedline_file(f)
114
115      symbols_in_process._static_symbols_in_filse[vma.name] = static_symbols
116
117    return symbols_in_process
118
119
120def _find_runtime_function_symbols(symbols_in_process, addresses):
121  result = OrderedDict()
122  for address in addresses:
123    if isinstance(address, basestring):
124      address = int(address, 16)
125    found = symbols_in_process.find_procedure(address)
126    if found:
127      result[address] = found.name
128    else:
129      result[address] = '0x%016x' % address
130  return result
131
132
133def _find_runtime_sourcefile_symbols(symbols_in_process, addresses):
134  result = OrderedDict()
135  for address in addresses:
136    if isinstance(address, basestring):
137      address = int(address, 16)
138    found = symbols_in_process.find_sourcefile(address)
139    if found:
140      result[address] = found
141    else:
142      result[address] = ''
143  return result
144
145
146def _find_runtime_typeinfo_symbols(symbols_in_process, addresses):
147  result = OrderedDict()
148  for address in addresses:
149    if isinstance(address, basestring):
150      address = int(address, 16)
151    if address == 0:
152      result[address] = 'no typeinfo'
153    else:
154      found = symbols_in_process.find_typeinfo(address)
155      if found:
156        if found.startswith('typeinfo for '):
157          result[address] = found[13:]
158        else:
159          result[address] = found
160      else:
161        result[address] = '0x%016x' % address
162  return result
163
164
165_INTERNAL_FINDERS = {
166    FUNCTION_SYMBOLS: _find_runtime_function_symbols,
167    SOURCEFILE_SYMBOLS: _find_runtime_sourcefile_symbols,
168    TYPEINFO_SYMBOLS: _find_runtime_typeinfo_symbols,
169    }
170
171
172def find_runtime_symbols(symbol_type, symbols_in_process, addresses):
173  return _INTERNAL_FINDERS[symbol_type](symbols_in_process, addresses)
174
175
176def main():
177  # FIX: Accept only .pre data
178  if len(sys.argv) < 2:
179    sys.stderr.write("""Usage:
180%s /path/to/prepared_data_dir/ < addresses.txt
181""" % sys.argv[0])
182    return 1
183
184  log = logging.getLogger('find_runtime_symbols')
185  log.setLevel(logging.WARN)
186  handler = logging.StreamHandler()
187  handler.setLevel(logging.WARN)
188  formatter = logging.Formatter('%(message)s')
189  handler.setFormatter(formatter)
190  log.addHandler(handler)
191
192  prepared_data_dir = sys.argv[1]
193  if not os.path.exists(prepared_data_dir):
194    log.warn("Nothing found: %s" % prepared_data_dir)
195    return 1
196  if not os.path.isdir(prepared_data_dir):
197    log.warn("Not a directory: %s" % prepared_data_dir)
198    return 1
199
200  symbols_in_process = RuntimeSymbolsInProcess.load(prepared_data_dir)
201  symbols_dict = find_runtime_symbols(FUNCTION_SYMBOLS,
202                                      symbols_in_process,
203                                      sys.stdin)
204  for address, symbol in symbols_dict.iteritems():
205    if symbol:
206      print '%016x %s' % (address, symbol)
207    else:
208      print '%016x' % address
209
210  return 0
211
212
213if __name__ == '__main__':
214  sys.exit(main())
215