Home | History | Annotate | Download | only in find_runtime_symbols
      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 
      7 Note that source file names are treated as symbols in this script while they
      8 are actually not.
      9 """
     10 
     11 import json
     12 import logging
     13 import os
     14 import sys
     15 
     16 from 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')
     21 sys.path.insert(0, _TOOLS_LINUX_PATH)
     22 
     23 
     24 from procfs import ProcMaps  # pylint: disable=F0401
     25 
     26 try:
     27   from collections import OrderedDict  # pylint: disable=E0611
     28 except 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 
     35 FUNCTION_SYMBOLS = 0
     36 SOURCEFILE_SYMBOLS = 1
     37 TYPEINFO_SYMBOLS = 2
     38 
     39 _MAPS_FILENAME = 'maps'
     40 _FILES_FILENAME = 'files.json'
     41 
     42 
     43 class 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 
    120 def _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 
    133 def _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 
    146 def _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 
    172 def find_runtime_symbols(symbol_type, symbols_in_process, addresses):
    173   return _INTERNAL_FINDERS[symbol_type](symbols_in_process, addresses)
    174 
    175 
    176 def 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 
    213 if __name__ == '__main__':
    214   sys.exit(main())
    215