| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 2 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Patch an orderfile. | 6 """Patch an orderfile. |
| 7 | 7 |
| 8 Starting with a list of symbols in a binary and an orderfile (ordered list of | 8 Starting with a list of symbols in a binary and an orderfile (ordered list of |
| 9 symbols), matches the symbols in the orderfile and augments each symbol with the | 9 symbols), matches the symbols in the orderfile and augments each symbol with the |
| 10 symbols residing at the same address (due to having identical code). | 10 symbols residing at the same address (due to having identical code). |
| 11 | 11 |
| 12 Note: It is possible to have. | 12 Note: It is possible to have. |
| 13 - Several symbols mapping to the same offset in the binary. | 13 - Several symbols mapping to the same offset in the binary. |
| 14 - Several offsets for a given symbol (because we strip the ".clone." suffix) | 14 - Several offsets for a given symbol (because we strip the ".clone." suffix) |
| 15 | 15 |
| 16 TODO(lizeb): Since the suffix ".clone." is only used with -O3 that we don't | 16 TODO(lizeb): Since the suffix ".clone." is only used with -O3 that we don't |
| 17 currently use, simplify the logic by removing the suffix handling. | 17 currently use, simplify the logic by removing the suffix handling. |
| 18 | 18 |
| 19 The general pipeline is: | 19 The general pipeline is: |
| 20 1. Get the symbol infos (offset, length, name) from the binary | 20 1. Get the symbol infos (offset, length, name) from the binary |
| 21 2. Get the symbol names from the orderfile | 21 2. Get the symbol names from the orderfile |
| 22 3. Find the orderfile symbol names in the symbols coming from the binary | 22 3. Find the orderfile symbol names in the symbols coming from the binary |
| 23 4. For each symbol found, get all the symbols at the same address | 23 4. For each symbol found, get all the symbols at the same address |
| 24 5. Output them to an updated orderfile, with several different prefixes | 24 5. Output them to an updated orderfile, with several different prefixes |
| 25 """ | 25 """ |
| 26 | 26 |
| 27 import collections | 27 import collections |
| 28 import logging | 28 import logging |
| 29 import subprocess | |
| 30 import sys | 29 import sys |
| 31 | 30 |
| 31 import symbol_extractor |
| 32 |
| 32 # Prefixes for the symbols. We strip them from the incoming symbols, and add | 33 # Prefixes for the symbols. We strip them from the incoming symbols, and add |
| 33 # them back in the output file. | 34 # them back in the output file. |
| 34 _PREFIXES = ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.') | 35 _PREFIXES = ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.') |
| 35 | 36 |
| 36 | 37 |
| 37 SymbolInfo = collections.namedtuple('SymbolInfo', ['offset', 'size', 'name']) | |
| 38 | |
| 39 | |
| 40 def _RemoveClone(name): | 38 def _RemoveClone(name): |
| 41 """Return name up to the ".clone." marker.""" | 39 """Return name up to the ".clone." marker.""" |
| 42 clone_index = name.find('.clone.') | 40 clone_index = name.find('.clone.') |
| 43 if clone_index != -1: | 41 if clone_index != -1: |
| 44 return name[:clone_index] | 42 return name[:clone_index] |
| 45 return name | 43 return name |
| 46 | 44 |
| 47 | 45 |
| 48 def _GetSymbolInfosFromStream(nm_lines): | 46 def _GroupSymbolInfos(symbol_infos): |
| 49 """Parses the output of nm, and get all the symbols from a binary. | 47 """Group the symbol infos by name and offset. |
| 50 | 48 |
| 51 Args: | 49 Args: |
| 52 nm_lines: An iterable of lines | 50 symbol_infos: an iterable of SymbolInfo |
| 53 | 51 |
| 54 Returns: | 52 Returns: |
| 55 The same output as GetSymbolsFromBinary. | 53 The same output as _GroupSymbolInfosFromBinary. |
| 56 """ | 54 """ |
| 57 # TODO(lizeb): Consider switching to objdump to simplify parsing. | |
| 58 symbol_infos = [] | |
| 59 for line in nm_lines: | |
| 60 # We are interested in two types of lines: | |
| 61 # This: | |
| 62 # 00210d59 00000002 t _ZN34BrowserPluginHostMsg_Attach_ParamsD2Ev | |
| 63 # offset size <symbol_type> symbol_name | |
| 64 # And that: | |
| 65 # 0070ee8c T WebRtcSpl_ComplexBitReverse | |
| 66 # In the second case we don't have a size, so use -1 as a sentinel | |
| 67 parts = line.split() | |
| 68 if len(parts) == 4: | |
| 69 symbol_infos.append(SymbolInfo( | |
| 70 offset=int(parts[0], 16), size=int(parts[1], 16), name=parts[3])) | |
| 71 elif len(parts) == 3: | |
| 72 symbol_infos.append(SymbolInfo( | |
| 73 offset=int(parts[0], 16), size=-1, name=parts[2])) | |
| 74 # Map the addresses to symbols. | 55 # Map the addresses to symbols. |
| 75 offset_to_symbol_infos = collections.defaultdict(list) | 56 offset_to_symbol_infos = collections.defaultdict(list) |
| 76 name_to_symbol_infos = collections.defaultdict(list) | 57 name_to_symbol_infos = collections.defaultdict(list) |
| 77 for symbol in symbol_infos: | 58 for symbol in symbol_infos: |
| 78 symbol = SymbolInfo(symbol[0], symbol[1], _RemoveClone(symbol[2])) | 59 symbol = symbol_extractor.SymbolInfo(name=_RemoveClone(symbol.name), |
| 60 offset=symbol.offset, |
| 61 size=symbol.size, |
| 62 section=symbol.section) |
| 79 offset_to_symbol_infos[symbol.offset].append(symbol) | 63 offset_to_symbol_infos[symbol.offset].append(symbol) |
| 80 name_to_symbol_infos[symbol.name].append(symbol) | 64 name_to_symbol_infos[symbol.name].append(symbol) |
| 81 return (offset_to_symbol_infos, name_to_symbol_infos) | 65 return (dict(offset_to_symbol_infos), dict(name_to_symbol_infos)) |
| 82 | 66 |
| 83 | 67 |
| 84 def _GetSymbolInfosFromBinary(binary_filename): | 68 def _GroupSymbolInfosFromBinary(binary_filename): |
| 85 """Runs nm to get all the symbols from a binary. | 69 """Group all the symbols from a binary by name and offset. |
| 86 | 70 |
| 87 Args: | 71 Args: |
| 88 binary_filename: path to the binary. | 72 binary_filename: path to the binary. |
| 89 | 73 |
| 90 Returns: | 74 Returns: |
| 91 A tuple of collection.defaultdict: | 75 A tuple of dict: |
| 92 (offset_to_symbol_infos, name_to_symbol_infos): | 76 (offset_to_symbol_infos, name_to_symbol_infos): |
| 93 - offset_to_symbol_infos: {offset: [symbol_info1, ...]} | 77 - offset_to_symbol_infos: {offset: [symbol_info1, ...]} |
| 94 - name_to_symbol_infos: {name: [symbol_info1, ...]} | 78 - name_to_symbol_infos: {name: [symbol_info1, ...]} |
| 95 """ | 79 """ |
| 96 command = 'nm -S -n %s | egrep "( t )|( W )|( T )"' % binary_filename | 80 symbol_infos = symbol_extractor.SymbolInfosFromBinary(binary_filename) |
| 97 p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) | 81 return _GroupSymbolInfos(symbol_infos) |
| 98 try: | |
| 99 result = _GetSymbolInfosFromStream(p.stdout) | |
| 100 return result | |
| 101 finally: | |
| 102 p.wait() | |
| 103 | 82 |
| 104 | 83 |
| 105 def _StripPrefix(line): | 84 def _StripPrefix(line): |
| 106 """Get the symbol from a line with a linker section name. | 85 """Get the symbol from a line with a linker section name. |
| 107 | 86 |
| 108 Args: | 87 Args: |
| 109 line: a line from an orderfile, usually in the form: | 88 line: a line from an orderfile, usually in the form: |
| 110 .text.SymbolName | 89 .text.SymbolName |
| 111 | 90 |
| 112 Returns: | 91 Returns: |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 output_file.write(linker_section + '\n') | 202 output_file.write(linker_section + '\n') |
| 224 unique_outputs.add(linker_section) | 203 unique_outputs.add(linker_section) |
| 225 | 204 |
| 226 | 205 |
| 227 def main(argv): | 206 def main(argv): |
| 228 if len(argv) != 3: | 207 if len(argv) != 3: |
| 229 print 'Usage: %s <unpatched_orderfile> <libchrome.so>' % argv[0] | 208 print 'Usage: %s <unpatched_orderfile> <libchrome.so>' % argv[0] |
| 230 return 1 | 209 return 1 |
| 231 orderfile_filename = argv[1] | 210 orderfile_filename = argv[1] |
| 232 binary_filename = argv[2] | 211 binary_filename = argv[2] |
| 233 (offset_to_symbol_infos, name_to_symbol_infos) = _GetSymbolInfosFromBinary( | 212 (offset_to_symbol_infos, name_to_symbol_infos) = _GroupSymbolInfosFromBinary( |
| 234 binary_filename) | 213 binary_filename) |
| 235 profiled_symbols = _GetSymbolsFromOrderfile(orderfile_filename) | 214 profiled_symbols = _GetSymbolsFromOrderfile(orderfile_filename) |
| 236 expanded_symbols = _ExpandSymbols( | 215 expanded_symbols = _ExpandSymbols( |
| 237 profiled_symbols, name_to_symbol_infos, offset_to_symbol_infos) | 216 profiled_symbols, name_to_symbol_infos, offset_to_symbol_infos) |
| 238 _PrintSymbolsWithPrefixes(expanded_symbols, sys.stdout) | 217 _PrintSymbolsWithPrefixes(expanded_symbols, sys.stdout) |
| 239 # The following is needed otherwise Gold only applies a partial sort. | 218 # The following is needed otherwise Gold only applies a partial sort. |
| 240 print '.text' # gets methods not in a section, such as assembly | 219 print '.text' # gets methods not in a section, such as assembly |
| 241 print '.text.*' # gets everything else | 220 print '.text.*' # gets everything else |
| 242 return 0 | 221 return 0 |
| 243 | 222 |
| 244 | 223 |
| 245 if __name__ == '__main__': | 224 if __name__ == '__main__': |
| 246 logging.basicConfig(level=logging.INFO) | 225 logging.basicConfig(level=logging.INFO) |
| 247 sys.exit(main(sys.argv)) | 226 sys.exit(main(sys.argv)) |
| OLD | NEW |