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: 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