| OLD | NEW |
| (Empty) |
| 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 | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import logging | |
| 6 | |
| 7 import models | |
| 8 | |
| 9 | |
| 10 class MapFileParser(object): | |
| 11 """Parses a linker map file (tested only on files from gold linker).""" | |
| 12 # Map file writer for gold linker: | |
| 13 # https://github.com/gittup/binutils/blob/HEAD/gold/mapfile.cc | |
| 14 | |
| 15 def __init__(self): | |
| 16 self._symbols = [] | |
| 17 self._section_sizes = {} | |
| 18 self._lines = None | |
| 19 | |
| 20 def Parse(self, lines): | |
| 21 """Parses a linker map file. | |
| 22 | |
| 23 Args: | |
| 24 lines: Iterable of lines. | |
| 25 | |
| 26 Returns: | |
| 27 A tuple of (section_sizes, symbols). | |
| 28 """ | |
| 29 self._lines = iter(lines) | |
| 30 logging.info('Parsing common symbols') | |
| 31 self._ParseCommonSymbols() | |
| 32 logging.debug('.bss common entries: %d', len(self._symbols)) | |
| 33 logging.info('Parsing section symbols') | |
| 34 self._ParseSections() | |
| 35 return self._section_sizes, self._symbols | |
| 36 | |
| 37 def _SkipToLineWithPrefix(self, prefix): | |
| 38 for l in self._lines: | |
| 39 if l.startswith(prefix): | |
| 40 return l | |
| 41 | |
| 42 def _ParsePossiblyWrappedParts(self, line, count): | |
| 43 parts = line.split(None, count - 1) | |
| 44 if not parts: | |
| 45 return None | |
| 46 if len(parts) != count: | |
| 47 line = next(self._lines) | |
| 48 parts.extend(line.split(None, count - len(parts) - 1)) | |
| 49 assert len(parts) == count, 'parts: ' + ' '.join(parts) | |
| 50 parts[-1] = parts[-1].rstrip() | |
| 51 return parts | |
| 52 | |
| 53 def _ParseCommonSymbols(self): | |
| 54 # Common symbol size file | |
| 55 # | |
| 56 # ff_cos_131072 0x40000 obj/third_party/<snip> | |
| 57 # ff_cos_131072_fixed | |
| 58 # 0x20000 obj/third_party/<snip> | |
| 59 self._SkipToLineWithPrefix('Common symbol') | |
| 60 next(self._lines) # Skip past blank line | |
| 61 | |
| 62 name, size_str, path = None, None, None | |
| 63 for l in self._lines: | |
| 64 parts = self._ParsePossiblyWrappedParts(l, 3) | |
| 65 if not parts: | |
| 66 break | |
| 67 name, size_str, path = parts | |
| 68 self._symbols.append( | |
| 69 models.Symbol('.bss', int(size_str[2:], 16), name=name, | |
| 70 object_path=path)) | |
| 71 | |
| 72 def _ParseSections(self): | |
| 73 # .text 0x0028c600 0x22d3468 | |
| 74 # .text.startup._GLOBAL__sub_I_bbr_sender.cc | |
| 75 # 0x0028c600 0x38 obj/net/net/bbr_sender.o | |
| 76 # .text._reset 0x00339d00 0xf0 obj/third_party/icu/icuuc/ucnv.o | |
| 77 # ** fill 0x0255fb00 0x02 | |
| 78 # .text._ZN4base8AutoLockD2Ev | |
| 79 # 0x00290710 0xe obj/net/net/file_name.o | |
| 80 # 0x00290711 base::AutoLock::~AutoLock() | |
| 81 # 0x00290711 base::AutoLock::~AutoLock() | |
| 82 # .text._ZNK5blink15LayoutBlockFlow31mustSeparateMarginAfterForChildERK... | |
| 83 # 0xffffffffffffffff 0x46 obj/... | |
| 84 # 0x006808e1 blink::LayoutBlockFlow::... | |
| 85 # .bss | |
| 86 # .bss._ZGVZN11GrProcessor11initClassIDI10LightingFPEEvvE8kClassID | |
| 87 # 0x02d4b294 0x4 obj/skia/skia/SkLightingShader.o | |
| 88 # 0x02d4b294 guard variable for void GrProcessor::initClassID | |
| 89 # .data 0x0028c600 0x22d3468 | |
| 90 # .data.rel.ro._ZTVN3gvr7android19ScopedJavaGlobalRefIP12_jfloatArrayEE | |
| 91 # 0x02d1e668 0x10 ../../third_party/.../libfoo.a(bar.o) | |
| 92 # 0x02d1e668 vtable for gvr::android::GlobalRef<_jfloatArray*> | |
| 93 # ** merge strings | |
| 94 # 0x0255fb00 0x1f2424 | |
| 95 # ** merge constants | |
| 96 # 0x0255fb00 0x8 | |
| 97 # ** common 0x02db5700 0x13ab48 | |
| 98 self._SkipToLineWithPrefix('Memory map') | |
| 99 syms = self._symbols | |
| 100 symbol_gap_count = 0 | |
| 101 while True: | |
| 102 line = self._SkipToLineWithPrefix('.') | |
| 103 if not line: | |
| 104 break | |
| 105 section_name = None | |
| 106 try: | |
| 107 # Parse section name and size. | |
| 108 parts = self._ParsePossiblyWrappedParts(line, 3) | |
| 109 if not parts: | |
| 110 break | |
| 111 section_name, address, size_str = parts | |
| 112 self._section_sizes[section_name] = int(size_str[2:], 16) | |
| 113 if (section_name in ('.bss', '.rodata', '.text') or | |
| 114 section_name.startswith('.data')): | |
| 115 logging.info('Parsing %s', section_name) | |
| 116 prefix_len = len(section_name) + 1 # + 1 for the trailing . | |
| 117 merge_symbol_start_address = 0 | |
| 118 sym_count_at_start = len(syms) | |
| 119 line = next(self._lines) | |
| 120 # Parse section symbols. | |
| 121 while True: | |
| 122 if not line or line.isspace(): | |
| 123 break | |
| 124 if line.startswith(' **'): | |
| 125 zero_index = line.find('0') | |
| 126 if zero_index == -1: | |
| 127 # Line wraps. | |
| 128 name = line.strip() | |
| 129 line = next(self._lines) | |
| 130 else: | |
| 131 # Line does not wrap. | |
| 132 name = line[:zero_index].strip() | |
| 133 line = line[zero_index:] | |
| 134 address_str, size_str = self._ParsePossiblyWrappedParts(line, 2) | |
| 135 line = next(self._lines) | |
| 136 # These bytes are already accounted for. | |
| 137 if name == '** common': | |
| 138 continue | |
| 139 address = int(address_str[2:], 16) | |
| 140 size = int(size_str[2:], 16) | |
| 141 path = None | |
| 142 syms.append( | |
| 143 models.Symbol(section_name, size, address=address, name=name, | |
| 144 object_path=path)) | |
| 145 else: | |
| 146 # A normal symbol entry. | |
| 147 subsection_name, address_str, size_str, path = ( | |
| 148 self._ParsePossiblyWrappedParts(line, 4)) | |
| 149 size = int(size_str[2:], 16) | |
| 150 assert subsection_name.startswith(section_name), ( | |
| 151 'subsection name was: ' + subsection_name) | |
| 152 mangled_name = subsection_name[prefix_len:] | |
| 153 name = None | |
| 154 address_str2 = None | |
| 155 while True: | |
| 156 line = next(self._lines).rstrip() | |
| 157 if not line or line.startswith(' .'): | |
| 158 break | |
| 159 # clang includes ** fill, but gcc does not. | |
| 160 if line.startswith(' ** fill'): | |
| 161 # Alignment explicitly recorded in map file. Rather than | |
| 162 # record padding based on these entries, we calculate it | |
| 163 # using addresses. We do this because fill lines are not | |
| 164 # present when compiling with gcc (only for clang). | |
| 165 continue | |
| 166 elif line.startswith(' **'): | |
| 167 break | |
| 168 elif name is None: | |
| 169 address_str2, name = self._ParsePossiblyWrappedParts(line, 2) | |
| 170 | |
| 171 if address_str == '0xffffffffffffffff': | |
| 172 # The section needs special handling (e.g., a merge section) | |
| 173 # It also generally has a large offset after it, so don't | |
| 174 # penalize the subsequent symbol for this gap (e.g. a 50kb gap). | |
| 175 # There seems to be no corelation between where these gaps occur | |
| 176 # and the symbols they come in-between. | |
| 177 # TODO(agrieve): Learn more about why this happens. | |
| 178 if address_str2: | |
| 179 address = int(address_str2[2:], 16) - 1 | |
| 180 elif syms and syms[-1].address > 0: | |
| 181 # Merge sym with no second line showing real address. | |
| 182 address = syms[-1].end_address | |
| 183 else: | |
| 184 logging.warning('First symbol of section had address -1') | |
| 185 address = 0 | |
| 186 | |
| 187 merge_symbol_start_address = address + size | |
| 188 else: | |
| 189 address = int(address_str[2:], 16) | |
| 190 # Finish off active address gap / merge section. | |
| 191 if merge_symbol_start_address: | |
| 192 merge_size = address - merge_symbol_start_address | |
| 193 logging.debug('Merge symbol of size %d found at:\n %r', | |
| 194 merge_size, syms[-1]) | |
| 195 # Set size=0 so that it will show up as padding. | |
| 196 sym = models.Symbol( | |
| 197 section_name, 0, | |
| 198 address=address, | |
| 199 name='** symbol gap %d' % symbol_gap_count, | |
| 200 object_path=path) | |
| 201 symbol_gap_count += 1 | |
| 202 syms.append(sym) | |
| 203 merge_symbol_start_address = 0 | |
| 204 | |
| 205 syms.append(models.Symbol(section_name, size, address=address, | |
| 206 name=name or mangled_name, | |
| 207 object_path=path)) | |
| 208 logging.debug('Symbol count for %s: %d', section_name, | |
| 209 len(syms) - sym_count_at_start) | |
| 210 except: | |
| 211 logging.error('Problem line: %r', line) | |
| 212 logging.error('In section: %r', section_name) | |
| 213 raise | |
| OLD | NEW |