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
4 # found in the LICENSE file.
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
10 symbols residing at the same address (due to having identical code).
12 Note: It is possible to have.
13 - Several symbols mapping to the same offset in the binary.
14 - Several offsets for a given symbol (because we strip the ".clone." suffix)
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.
19 The general pipeline is:
20 1. Get the symbol infos (name, offset, size, section) from the binary
21 2. Get the symbol names from the orderfile
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
24 5. Output them to an updated orderfile, with several different prefixes
32 import symbol_extractor
34 # Prefixes for the symbols. We strip them from the incoming symbols, and add
35 # them back in the output file.
36 _PREFIXES
= ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.')
39 def _RemoveClone(name
):
40 """Return name up to the ".clone." marker."""
41 clone_index
= name
.find('.clone.')
43 return name
[:clone_index
]
47 def _GroupSymbolInfos(symbol_infos
):
48 """Group the symbol infos by name and offset.
51 symbol_infos: an iterable of SymbolInfo
54 The same output as _GroupSymbolInfosFromBinary.
56 # Map the addresses to symbols.
57 offset_to_symbol_infos
= collections
.defaultdict(list)
58 name_to_symbol_infos
= collections
.defaultdict(list)
59 for symbol
in symbol_infos
:
60 symbol
= symbol_extractor
.SymbolInfo(name
=_RemoveClone(symbol
.name
),
63 section
=symbol
.section
)
64 offset_to_symbol_infos
[symbol
.offset
].append(symbol
)
65 name_to_symbol_infos
[symbol
.name
].append(symbol
)
66 return (dict(offset_to_symbol_infos
), dict(name_to_symbol_infos
))
69 def _GroupSymbolInfosFromBinary(binary_filename
):
70 """Group all the symbols from a binary by name and offset.
73 binary_filename: path to the binary.
77 (offset_to_symbol_infos, name_to_symbol_infos):
78 - offset_to_symbol_infos: {offset: [symbol_info1, ...]}
79 - name_to_symbol_infos: {name: [symbol_info1, ...]}
81 symbol_infos
= symbol_extractor
.SymbolInfosFromBinary(binary_filename
)
82 return _GroupSymbolInfos(symbol_infos
)
85 def _StripPrefix(line
):
86 """Get the symbol from a line with a linker section name.
89 line: a line from an orderfile, usually in the form:
93 The symbol, SymbolName in the example above.
95 line
= line
.rstrip('\n')
96 for prefix
in _PREFIXES
:
97 if line
.startswith(prefix
):
98 return line
[len(prefix
):]
99 return line
# Unprefixed case
102 def _GetSymbolsFromStream(lines
):
103 """Get the symbols from an iterable of lines.
104 Filters out wildcards and lines which do not correspond to symbols.
107 lines: iterable of lines from an orderfile.
110 Same as GetSymbolsFromOrderfile
112 # TODO(lizeb): Retain the prefixes later in the processing stages.
114 unique_symbols
= set()
116 line
= _StripPrefix(line
)
117 name
= _RemoveClone(line
)
118 if name
== '' or name
== '*' or name
== '.text':
120 if not line
in unique_symbols
:
122 unique_symbols
.add(line
)
126 def GetSymbolsFromOrderfile(filename
):
127 """Return the symbols from an orderfile.
130 filename: The name of the orderfile.
133 A list of symbol names.
135 with
open(filename
, 'r') as f
:
136 return _GetSymbolsFromStream(f
.xreadlines())
138 def _SymbolsWithSameOffset(profiled_symbol
, name_to_symbol_info
,
139 offset_to_symbol_info
):
140 """Expand a profiled symbol to include all symbols which share an offset
143 profiled_symbol: the string symbol name to be expanded.
144 name_to_symbol_info: {name: [symbol_info1], ...}, as returned by
145 GetSymbolInfosFromBinary
146 offset_to_symbol_info: {offset: [symbol_info1, ...], ...}
149 A list of symbol names, or an empty list if profiled_symbol was not in
152 if not profiled_symbol
in name_to_symbol_info
:
154 symbol_infos
= name_to_symbol_info
[profiled_symbol
]
156 for symbol_info
in symbol_infos
:
157 expanded
+= (s
.name
for s
in offset_to_symbol_info
[symbol_info
.offset
])
160 def _ExpandSymbols(profiled_symbols
, name_to_symbol_infos
,
161 offset_to_symbol_infos
):
162 """Expand all of the symbols in profiled_symbols to include any symbols which
163 share the same address.
166 profiled_symbols: Symbols to match
167 name_to_symbol_infos: {name: [symbol_info1], ...}, as returned by
168 GetSymbolInfosFromBinary
169 offset_to_symbol_infos: {offset: [symbol_info1, ...], ...}
172 A list of the symbol names.
177 for name
in profiled_symbols
:
178 expansion
= _SymbolsWithSameOffset(name
,
179 name_to_symbol_infos
, offset_to_symbol_infos
)
182 all_symbols
+= expansion
184 all_symbols
.append(name
)
185 missing_symbols
.append(name
)
186 logging
.info('symbols found: %d\n' % found_symbols
)
187 if missing_symbols
> 0:
188 logging
.warning('%d missing symbols.' % len(missing_symbols
))
189 missing_symbols_to_show
= min(100, len(missing_symbols
))
190 logging
.warning('First %d missing symbols:\n%s' % (
191 missing_symbols_to_show
,
192 '\n'.join(missing_symbols
[:missing_symbols_to_show
])))
196 def _PrintSymbolsWithPrefixes(symbol_names
, output_file
):
197 """For each symbol, outputs it to output_file with the prefixes."""
198 unique_outputs
= set()
199 for name
in symbol_names
:
200 for prefix
in _PREFIXES
:
201 linker_section
= prefix
+ name
202 if not linker_section
in unique_outputs
:
203 output_file
.write(linker_section
+ '\n')
204 unique_outputs
.add(linker_section
)
208 parser
= optparse
.OptionParser(usage
=
209 'usage: %prog [options] <unpatched_orderfile> <library>')
210 parser
.add_option('--target-arch', action
='store', dest
='arch',
212 choices
=['arm', 'arm64', 'x86', 'x86_64', 'x64', 'mips'],
213 help='The target architecture for the library.')
214 options
, argv
= parser
.parse_args(argv
)
218 orderfile_filename
= argv
[1]
219 binary_filename
= argv
[2]
220 symbol_extractor
.SetArchitecture(options
.arch
)
221 (offset_to_symbol_infos
, name_to_symbol_infos
) = _GroupSymbolInfosFromBinary(
223 profiled_symbols
= GetSymbolsFromOrderfile(orderfile_filename
)
224 expanded_symbols
= _ExpandSymbols(
225 profiled_symbols
, name_to_symbol_infos
, offset_to_symbol_infos
)
226 _PrintSymbolsWithPrefixes(expanded_symbols
, sys
.stdout
)
227 # The following is needed otherwise Gold only applies a partial sort.
228 print '.text' # gets methods not in a section, such as assembly
229 print '.text.*' # gets everything else
233 if __name__
== '__main__':
234 logging
.basicConfig(level
=logging
.INFO
)
235 sys
.exit(main(sys
.argv
))