2 # Copyright 2015 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.
6 """Utilities to get and manipulate symbols from a binary."""
16 0, os
.path
.join(os
.path
.dirname(__file__
), os
.pardir
, os
.pardir
,
17 'third_party', 'android_platform', 'development',
21 _MAX_WARNINGS_TO_PRINT
= 200
23 SymbolInfo
= collections
.namedtuple('SymbolInfo', ('name', 'offset', 'size',
26 def SetArchitecture(arch
):
27 """Set the architecture for binaries to be symbolized."""
31 def _FromObjdumpLine(line
):
32 """Create a SymbolInfo by parsing a properly formatted objdump output line.
35 line: line from objdump
38 An instance of SymbolInfo if the line represents a symbol, None otherwise.
40 # All of the symbol lines we care about are in the form
41 # 0000000000 g F .text.foo 000000000 [.hidden] foo
42 # where g (global) might also be l (local) or w (weak).
44 if len(parts
) < 6 or parts
[2] != 'F':
47 assert len(parts
) == 6 or (len(parts
) == 7 and parts
[5] == '.hidden')
48 accepted_scopes
= set(['g', 'l', 'w'])
49 assert parts
[1] in accepted_scopes
51 offset
= int(parts
[0], 16)
53 size
= int(parts
[4], 16)
54 name
= parts
[-1].rstrip('\n')
55 assert re
.match('^[a-zA-Z0-9_.]+$', name
)
56 return SymbolInfo(name
=name
, offset
=offset
, section
=section
, size
=size
)
59 def _SymbolInfosFromStream(objdump_lines
):
60 """Parses the output of objdump, and get all the symbols from a binary.
63 objdump_lines: An iterable of lines
69 for line
in objdump_lines
:
70 symbol_info
= _FromObjdumpLine(line
)
71 if symbol_info
is not None:
72 symbol_infos
.append(symbol_info
)
76 def SymbolInfosFromBinary(binary_filename
):
77 """Runs objdump to get all the symbols from a binary.
80 binary_filename: path to the binary.
83 A list of SymbolInfo from the binary.
85 command
= (symbol
.ToolPath('objdump'), '-t', '-w', binary_filename
)
86 p
= subprocess
.Popen(command
, shell
=False, stdout
=subprocess
.PIPE
)
88 result
= _SymbolInfosFromStream(p
.stdout
)
94 def GroupSymbolInfosByOffset(symbol_infos
):
95 """Create a dict {offset: [symbol_info1, ...], ...}.
97 As several symbols can be at the same offset, this is a 1-to-many
101 symbol_infos: iterable of SymbolInfo instances
104 a dict {offset: [symbol_info1, ...], ...}
106 offset_to_symbol_infos
= collections
.defaultdict(list)
107 for symbol_info
in symbol_infos
:
108 offset_to_symbol_infos
[symbol_info
.offset
].append(symbol_info
)
109 return dict(offset_to_symbol_infos
)
111 def GroupSymbolInfosByName(symbol_infos
):
112 """Create a dict {name: [symbol_info1, ...], ...}.
114 A symbol can have several offsets, this is a 1-to-many relationship.
117 symbol_infos: iterable of SymbolInfo instances
120 a dict {name: [symbol_info1, ...], ...}
122 name_to_symbol_infos
= collections
.defaultdict(list)
123 for symbol_info
in symbol_infos
:
124 name_to_symbol_infos
[symbol_info
.name
].append(symbol_info
)
125 return dict(name_to_symbol_infos
)
127 def CreateNameToSymbolInfo(symbol_infos
):
128 """Create a dict {name: symbol_info, ...}.
131 symbol_infos: iterable of SymbolInfo instances
134 a dict {name: symbol_info, ...}
135 If a symbol name corresponds to more than one symbol_info, the symbol_info
136 with the lowest offset is chosen.
138 #TODO(azarchs): move the functionality in this method into check_orderfile.
139 symbol_infos_by_name
= {}
141 for infos
in GroupSymbolInfosByName(symbol_infos
).itervalues():
142 first_symbol_info
= min(infos
, key
=lambda x
:x
.offset
)
143 symbol_infos_by_name
[first_symbol_info
.name
] = first_symbol_info
146 if collision_count
<= _MAX_WARNINGS_TO_PRINT
:
147 logging
.warning('Symbol %s appears at %d offsets: %s' %
148 (first_symbol_info
.name
,
150 ','.join([hex(x
.offset
) for x
in infos
])))
151 if collision_count
> _MAX_WARNINGS_TO_PRINT
:
152 logging
.warning('%d symbols at multiple offsets. First %d shown.' %
153 (collision_count
, _MAX_WARNINGS_TO_PRINT
))
154 return symbol_infos_by_name