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 cygprofile_utils
33 import symbol_extractor
35 # Prefixes for the symbols. We strip them from the incoming symbols, and add
36 # them back in the output file.
37 _PREFIXES
= ('.text.startup.', '.text.hot.', '.text.unlikely.', '.text.')
40 def _RemoveClone(name
):
41 """Return name up to the ".clone." marker."""
42 clone_index
= name
.find('.clone.')
44 return name
[:clone_index
]
48 def _GroupSymbolInfos(symbol_infos
):
49 """Group the symbol infos by name and offset.
52 symbol_infos: an iterable of SymbolInfo
55 The same output as _GroupSymbolInfosFromBinary.
57 # Map the addresses to symbols.
58 offset_to_symbol_infos
= collections
.defaultdict(list)
59 name_to_symbol_infos
= collections
.defaultdict(list)
60 for symbol
in symbol_infos
:
61 symbol
= symbol_extractor
.SymbolInfo(name
=_RemoveClone(symbol
.name
),
64 section
=symbol
.section
)
65 offset_to_symbol_infos
[symbol
.offset
].append(symbol
)
66 name_to_symbol_infos
[symbol
.name
].append(symbol
)
67 return (dict(offset_to_symbol_infos
), dict(name_to_symbol_infos
))
70 def _GroupSymbolInfosFromBinary(binary_filename
):
71 """Group all the symbols from a binary by name and offset.
74 binary_filename: path to the binary.
78 (offset_to_symbol_infos, name_to_symbol_infos):
79 - offset_to_symbol_infos: {offset: [symbol_info1, ...]}
80 - name_to_symbol_infos: {name: [symbol_info1, ...]}
82 symbol_infos
= symbol_extractor
.SymbolInfosFromBinary(binary_filename
)
83 return _GroupSymbolInfos(symbol_infos
)
86 def _StripPrefix(line
):
87 """Get the symbol from a line with a linker section name.
90 line: a line from an orderfile, usually in the form:
94 The symbol, SymbolName in the example above.
96 line
= line
.rstrip('\n')
97 for prefix
in _PREFIXES
:
98 if line
.startswith(prefix
):
99 return line
[len(prefix
):]
100 return line
# Unprefixed case
103 def _GetSymbolsFromStream(lines
):
104 """Get the symbols from an iterable of lines.
105 Filters out wildcards and lines which do not correspond to symbols.
108 lines: iterable of lines from an orderfile.
111 Same as GetSymbolsFromOrderfile
113 # TODO(lizeb): Retain the prefixes later in the processing stages.
115 unique_symbols
= set()
117 line
= _StripPrefix(line
)
118 name
= _RemoveClone(line
)
119 if name
== '' or name
== '*' or name
== '.text':
121 if not line
in unique_symbols
:
123 unique_symbols
.add(line
)
127 def GetSymbolsFromOrderfile(filename
):
128 """Return the symbols from an orderfile.
131 filename: The name of the orderfile.
134 A list of symbol names.
136 with
open(filename
, 'r') as f
:
137 return _GetSymbolsFromStream(f
.xreadlines())
139 def _SymbolsWithSameOffset(profiled_symbol
, name_to_symbol_info
,
140 offset_to_symbol_info
):
141 """Expand a profiled symbol to include all symbols which share an offset
144 profiled_symbol: the string symbol name to be expanded.
145 name_to_symbol_info: {name: [symbol_info1], ...}, as returned by
146 GetSymbolInfosFromBinary
147 offset_to_symbol_info: {offset: [symbol_info1, ...], ...}
150 A list of symbol names, or an empty list if profiled_symbol was not in
153 if not profiled_symbol
in name_to_symbol_info
:
155 symbol_infos
= name_to_symbol_info
[profiled_symbol
]
157 for symbol_info
in symbol_infos
:
158 expanded
+= (s
.name
for s
in offset_to_symbol_info
[symbol_info
.offset
])
161 def _ExpandSymbols(profiled_symbols
, name_to_symbol_infos
,
162 offset_to_symbol_infos
):
163 """Expand all of the symbols in profiled_symbols to include any symbols which
164 share the same address.
167 profiled_symbols: Symbols to match
168 name_to_symbol_infos: {name: [symbol_info1], ...}, as returned by
169 GetSymbolInfosFromBinary
170 offset_to_symbol_infos: {offset: [symbol_info1, ...], ...}
173 A list of the symbol names.
178 for name
in profiled_symbols
:
179 expansion
= _SymbolsWithSameOffset(name
,
180 name_to_symbol_infos
, offset_to_symbol_infos
)
183 all_symbols
+= expansion
185 all_symbols
.append(name
)
186 missing_symbols
.append(name
)
187 logging
.info('symbols found: %d\n' % found_symbols
)
188 if missing_symbols
> 0:
189 logging
.warning('%d missing symbols.' % len(missing_symbols
))
190 missing_symbols_to_show
= min(100, len(missing_symbols
))
191 logging
.warning('First %d missing symbols:\n%s' % (
192 missing_symbols_to_show
,
193 '\n'.join(missing_symbols
[:missing_symbols_to_show
])))
197 def _PrintSymbolsWithPrefixes(symbol_names
, output_file
):
198 """For each symbol, outputs it to output_file with the prefixes."""
199 unique_outputs
= set()
200 for name
in symbol_names
:
201 for prefix
in _PREFIXES
:
202 linker_section
= prefix
+ name
203 if not linker_section
in unique_outputs
:
204 output_file
.write(linker_section
+ '\n')
205 unique_outputs
.add(linker_section
)
209 parser
= optparse
.OptionParser(usage
=
210 'usage: %prog [options] <unpatched_orderfile> <library>')
211 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
)
216 options
.arch
= cygprofile_utils
.DetectArchitecture()
220 orderfile_filename
= argv
[1]
221 binary_filename
= argv
[2]
222 symbol_extractor
.SetArchitecture(options
.arch
)
223 (offset_to_symbol_infos
, name_to_symbol_infos
) = _GroupSymbolInfosFromBinary(
225 profiled_symbols
= GetSymbolsFromOrderfile(orderfile_filename
)
226 expanded_symbols
= _ExpandSymbols(
227 profiled_symbols
, name_to_symbol_infos
, offset_to_symbol_infos
)
228 _PrintSymbolsWithPrefixes(expanded_symbols
, sys
.stdout
)
229 # The following is needed otherwise Gold only applies a partial sort.
230 print '.text' # gets methods not in a section, such as assembly
231 print '.text.*' # gets everything else
235 if __name__
== '__main__':
236 logging
.basicConfig(level
=logging
.INFO
)
237 sys
.exit(main(sys
.argv
))