find_runtime_symbols.py 6.4 KB

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