| OLD | NEW |
| 1 # Copyright 2017 The Chromium Authors. All rights reserved. | 1 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 | 6 |
| 7 import models | 7 import models |
| 8 | 8 |
| 9 # About linker maps: |
| 10 # * "Discarded input sections" include symbols merged with other symbols |
| 11 # (aliases), so the information there is not actually a list of unused things. |
| 12 # * Linker maps include symbols that do not have names (with object path), |
| 13 # whereas "nm" skips over these (they don't account for much though). |
| 14 # * The parse time for compressed linker maps is dominated by ungzipping. |
| 15 |
| 9 | 16 |
| 10 class MapFileParser(object): | 17 class MapFileParser(object): |
| 11 """Parses a linker map file (tested only on files from gold linker).""" | 18 """Parses a linker map file (tested only on files from gold linker).""" |
| 12 # Map file writer for gold linker: | 19 # Map file writer for gold linker: |
| 13 # https://github.com/gittup/binutils/blob/HEAD/gold/mapfile.cc | 20 # https://github.com/gittup/binutils/blob/HEAD/gold/mapfile.cc |
| 14 | 21 |
| 15 def __init__(self): | 22 def __init__(self): |
| 23 self._common_symbols = [] |
| 16 self._symbols = [] | 24 self._symbols = [] |
| 17 self._section_sizes = {} | 25 self._section_sizes = {} |
| 18 self._lines = None | 26 self._lines = None |
| 19 | 27 |
| 20 def Parse(self, lines): | 28 def Parse(self, lines): |
| 21 """Parses a linker map file. | 29 """Parses a linker map file. |
| 22 | 30 |
| 23 Args: | 31 Args: |
| 24 lines: Iterable of lines. | 32 lines: Iterable of lines. |
| 25 | 33 |
| 26 Returns: | 34 Returns: |
| 27 A tuple of (section_sizes, symbols). | 35 A tuple of (section_sizes, symbols). |
| 28 """ | 36 """ |
| 29 self._lines = iter(lines) | 37 self._lines = iter(lines) |
| 30 logging.info('Parsing common symbols') | 38 logging.info('Parsing common symbols') |
| 31 self._ParseCommonSymbols() | 39 self._common_symbols = self._ParseCommonSymbols() |
| 32 logging.debug('.bss common entries: %d', len(self._symbols)) | 40 logging.debug('.bss common entries: %d', len(self._common_symbols)) |
| 33 logging.info('Parsing section symbols') | 41 logging.info('Parsing section symbols') |
| 34 self._ParseSections() | 42 self._ParseSections() |
| 35 return self._section_sizes, self._symbols | 43 return self._section_sizes, self._symbols |
| 36 | 44 |
| 37 def _SkipToLineWithPrefix(self, prefix): | 45 def _SkipToLineWithPrefix(self, prefix): |
| 38 for l in self._lines: | 46 for l in self._lines: |
| 39 if l.startswith(prefix): | 47 if l.startswith(prefix): |
| 40 return l | 48 return l |
| 41 | 49 |
| 42 def _ParsePossiblyWrappedParts(self, line, count): | 50 def _ParsePossiblyWrappedParts(self, line, count): |
| 43 parts = line.split(None, count - 1) | 51 parts = line.split(None, count - 1) |
| 44 if not parts: | 52 if not parts: |
| 45 return None | 53 return None |
| 46 if len(parts) != count: | 54 if len(parts) != count: |
| 47 line = next(self._lines) | 55 line = next(self._lines) |
| 48 parts.extend(line.split(None, count - len(parts) - 1)) | 56 parts.extend(line.split(None, count - len(parts) - 1)) |
| 49 assert len(parts) == count, 'parts: ' + ' '.join(parts) | 57 assert len(parts) == count, 'parts: ' + ' '.join(parts) |
| 50 parts[-1] = parts[-1].rstrip() | 58 parts[-1] = parts[-1].rstrip() |
| 51 return parts | 59 return parts |
| 52 | 60 |
| 53 def _ParseCommonSymbols(self): | 61 def _ParseCommonSymbols(self): |
| 54 # Common symbol size file | 62 # Common symbol size file |
| 55 # | 63 # |
| 56 # ff_cos_131072 0x40000 obj/third_party/<snip> | 64 # ff_cos_131072 0x40000 obj/third_party/<snip> |
| 57 # ff_cos_131072_fixed | 65 # ff_cos_131072_fixed |
| 58 # 0x20000 obj/third_party/<snip> | 66 # 0x20000 obj/third_party/<snip> |
| 67 ret = [] |
| 59 self._SkipToLineWithPrefix('Common symbol') | 68 self._SkipToLineWithPrefix('Common symbol') |
| 60 next(self._lines) # Skip past blank line | 69 next(self._lines) # Skip past blank line |
| 61 | 70 |
| 62 name, size_str, path = None, None, None | 71 name, size_str, path = None, None, None |
| 63 for l in self._lines: | 72 for l in self._lines: |
| 64 parts = self._ParsePossiblyWrappedParts(l, 3) | 73 parts = self._ParsePossiblyWrappedParts(l, 3) |
| 65 if not parts: | 74 if not parts: |
| 66 break | 75 break |
| 67 name, size_str, path = parts | 76 name, size_str, path = parts |
| 68 self._symbols.append( | 77 sym = models.Symbol('.bss', int(size_str[2:], 16), name=name, |
| 69 models.Symbol('.bss', int(size_str[2:], 16), name=name, | 78 object_path=path) |
| 70 object_path=path)) | 79 ret.append(sym) |
| 80 return ret |
| 71 | 81 |
| 72 def _ParseSections(self): | 82 def _ParseSections(self): |
| 73 # .text 0x0028c600 0x22d3468 | 83 # .text 0x0028c600 0x22d3468 |
| 74 # .text.startup._GLOBAL__sub_I_bbr_sender.cc | 84 # .text.startup._GLOBAL__sub_I_bbr_sender.cc |
| 75 # 0x0028c600 0x38 obj/net/net/bbr_sender.o | 85 # 0x0028c600 0x38 obj/net/net/bbr_sender.o |
| 76 # .text._reset 0x00339d00 0xf0 obj/third_party/icu/icuuc/ucnv.o | 86 # .text._reset 0x00339d00 0xf0 obj/third_party/icu/icuuc/ucnv.o |
| 77 # ** fill 0x0255fb00 0x02 | 87 # ** fill 0x0255fb00 0x02 |
| 78 # .text._ZN4base8AutoLockD2Ev | 88 # .text._ZN4base8AutoLockD2Ev |
| 79 # 0x00290710 0xe obj/net/net/file_name.o | 89 # 0x00290710 0xe obj/net/net/file_name.o |
| 80 # 0x00290711 base::AutoLock::~AutoLock() | 90 # 0x00290711 base::AutoLock::~AutoLock() |
| (...skipping 25 matching lines...) Expand all Loading... |
| 106 try: | 116 try: |
| 107 # Parse section name and size. | 117 # Parse section name and size. |
| 108 parts = self._ParsePossiblyWrappedParts(line, 3) | 118 parts = self._ParsePossiblyWrappedParts(line, 3) |
| 109 if not parts: | 119 if not parts: |
| 110 break | 120 break |
| 111 section_name, address, size_str = parts | 121 section_name, address, size_str = parts |
| 112 self._section_sizes[section_name] = int(size_str[2:], 16) | 122 self._section_sizes[section_name] = int(size_str[2:], 16) |
| 113 if (section_name in ('.bss', '.rodata', '.text') or | 123 if (section_name in ('.bss', '.rodata', '.text') or |
| 114 section_name.startswith('.data')): | 124 section_name.startswith('.data')): |
| 115 logging.info('Parsing %s', section_name) | 125 logging.info('Parsing %s', section_name) |
| 126 if section_name == '.bss': |
| 127 syms.extend(self._common_symbols) |
| 116 prefix_len = len(section_name) + 1 # + 1 for the trailing . | 128 prefix_len = len(section_name) + 1 # + 1 for the trailing . |
| 117 merge_symbol_start_address = 0 | 129 merge_symbol_start_address = 0 |
| 118 sym_count_at_start = len(syms) | 130 sym_count_at_start = len(syms) |
| 119 line = next(self._lines) | 131 line = next(self._lines) |
| 120 # Parse section symbols. | 132 # Parse section symbols. |
| 121 while True: | 133 while True: |
| 122 if not line or line.isspace(): | 134 if not line or line.isspace(): |
| 123 break | 135 break |
| 124 if line.startswith(' **'): | 136 if line.startswith(' **'): |
| 125 zero_index = line.find('0') | 137 zero_index = line.find('0') |
| 126 if zero_index == -1: | 138 if zero_index == -1: |
| 127 # Line wraps. | 139 # Line wraps. |
| 128 name = line.strip() | 140 name = line.strip() |
| 129 line = next(self._lines) | 141 line = next(self._lines) |
| 130 else: | 142 else: |
| 131 # Line does not wrap. | 143 # Line does not wrap. |
| 132 name = line[:zero_index].strip() | 144 name = line[:zero_index].strip() |
| 133 line = line[zero_index:] | 145 line = line[zero_index:] |
| 134 address_str, size_str = self._ParsePossiblyWrappedParts(line, 2) | 146 address_str, size_str = self._ParsePossiblyWrappedParts(line, 2) |
| 135 line = next(self._lines) | 147 line = next(self._lines) |
| 136 # These bytes are already accounted for. | 148 # These bytes are already accounted for. |
| 137 if name == '** common': | 149 if name == '** common': |
| 138 continue | 150 continue |
| 139 address = int(address_str[2:], 16) | 151 address = int(address_str[2:], 16) |
| 140 size = int(size_str[2:], 16) | 152 size = int(size_str[2:], 16) |
| 141 path = None | 153 path = None |
| 142 syms.append( | 154 sym = models.Symbol(section_name, size, address=address, |
| 143 models.Symbol(section_name, size, address=address, name=name, | 155 name=name, object_path=path) |
| 144 object_path=path)) | 156 syms.append(sym) |
| 145 else: | 157 else: |
| 146 # A normal symbol entry. | 158 # A normal symbol entry. |
| 147 subsection_name, address_str, size_str, path = ( | 159 subsection_name, address_str, size_str, path = ( |
| 148 self._ParsePossiblyWrappedParts(line, 4)) | 160 self._ParsePossiblyWrappedParts(line, 4)) |
| 149 size = int(size_str[2:], 16) | 161 size = int(size_str[2:], 16) |
| 150 assert subsection_name.startswith(section_name), ( | 162 assert subsection_name.startswith(section_name), ( |
| 151 'subsection name was: ' + subsection_name) | 163 'subsection name was: ' + subsection_name) |
| 152 mangled_name = subsection_name[prefix_len:] | 164 mangled_name = subsection_name[prefix_len:] |
| 153 name = None | 165 name = None |
| 154 address_str2 = None | 166 address_str2 = None |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 195 # Set size=0 so that it will show up as padding. | 207 # Set size=0 so that it will show up as padding. |
| 196 sym = models.Symbol( | 208 sym = models.Symbol( |
| 197 section_name, 0, | 209 section_name, 0, |
| 198 address=address, | 210 address=address, |
| 199 name='** symbol gap %d' % symbol_gap_count, | 211 name='** symbol gap %d' % symbol_gap_count, |
| 200 object_path=path) | 212 object_path=path) |
| 201 symbol_gap_count += 1 | 213 symbol_gap_count += 1 |
| 202 syms.append(sym) | 214 syms.append(sym) |
| 203 merge_symbol_start_address = 0 | 215 merge_symbol_start_address = 0 |
| 204 | 216 |
| 205 syms.append(models.Symbol(section_name, size, address=address, | 217 sym = models.Symbol(section_name, size, address=address, |
| 206 name=name or mangled_name, | 218 name=name or mangled_name, object_path=path) |
| 207 object_path=path)) | 219 syms.append(sym) |
| 208 logging.debug('Symbol count for %s: %d', section_name, | 220 logging.debug('Symbol count for %s: %d', section_name, |
| 209 len(syms) - sym_count_at_start) | 221 len(syms) - sym_count_at_start) |
| 210 except: | 222 except: |
| 211 logging.error('Problem line: %r', line) | 223 logging.error('Problem line: %r', line) |
| 212 logging.error('In section: %r', section_name) | 224 logging.error('In section: %r', section_name) |
| 213 raise | 225 raise |
| OLD | NEW |