OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # |
| 3 # Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. |
| 6 |
| 7 |
| 8 """Identifies address adjustments required for native crash dumps.""" |
| 9 |
| 10 import glob |
| 11 import os.path |
| 12 import subprocess |
| 13 |
| 14 |
| 15 _BASE_APK = 'base.apk' |
| 16 _LIBCHROME_SO = 'libchrome.so' |
| 17 |
| 18 |
| 19 def GetTargetAndroidVersionNumber(lines): |
| 20 """Return the Android major version number from the build fingerprint. |
| 21 |
| 22 Args: |
| 23 lines: Lines read from the tombstone file, before preprocessing. |
| 24 Returns: |
| 25 5, 6, etc, or None if not determinable (developer build?) |
| 26 """ |
| 27 # For example, "Build fingerprint: 'Android/aosp_flo/flo:5.1.1/...'" is 5. |
| 28 for line in lines: |
| 29 if line.startswith('Build fingerprint: '): |
| 30 fingerprint = line.split()[2] |
| 31 version = fingerprint.split('/')[2].split(':')[1].split('.')[0] |
| 32 try: |
| 33 return int(version) |
| 34 except ValueError: |
| 35 return None |
| 36 return None |
| 37 |
| 38 |
| 39 def _HasElfHeader(path): |
| 40 """Return True if the file at the given path has an ELF magic header. |
| 41 |
| 42 Minimal check only, for 'ELF' in bytes 1 to 3 of the file. Filters out |
| 43 the zero-byte false-positives such as libchromeview.so returned by glob. |
| 44 |
| 45 Args: |
| 46 path: Path to file to check. |
| 47 Returns: |
| 48 True or False |
| 49 """ |
| 50 with open(path) as stream: |
| 51 elf_header = stream.read(4) |
| 52 return len(elf_header) == 4 and elf_header[1:4] == 'ELF' |
| 53 |
| 54 |
| 55 def _ReadElfProgramHeaders(lib): |
| 56 """Return an iterable of program headers, from 'readelf -l ...'. |
| 57 |
| 58 Uses the platform readelf in all cases. This is somewhat lazy, but suffices |
| 59 in practice because program headers in ELF files are architecture-agnostic. |
| 60 |
| 61 Args: |
| 62 lib: Library file to read. |
| 63 Returns: |
| 64 [readelf -l output line, ...] |
| 65 """ |
| 66 string = subprocess.check_output(['readelf', '-l', lib]) |
| 67 return string.split('\n') |
| 68 |
| 69 |
| 70 def _FindMinLoadVaddr(lib): |
| 71 """Return the minimum VirtAddr field of all library LOAD segments. |
| 72 |
| 73 Args: |
| 74 lib: Library file to read. |
| 75 Returns: |
| 76 Min VirtAddr field for all LOAD segments, or 0 if none found. |
| 77 """ |
| 78 vaddrs = [] |
| 79 # Locate LOAD lines and find the smallest VirtAddr field, eg: |
| 80 # Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align |
| 81 # LOAD 0x000000 0x001d6000 0x001d6000 0x20f63fc 0x20f63fc R E 0x1000 |
| 82 # LOAD 0x20f6970 0x022cd970 0x022cd970 0x182df8 0x1b4490 RW 0x1000 |
| 83 # would return 0x1d6000. Ignores all non-LOAD lines. |
| 84 for line in _ReadElfProgramHeaders(lib): |
| 85 elements = line.split() |
| 86 if elements and elements[0] == 'LOAD': |
| 87 vaddrs.append(int(elements[2], 16)) |
| 88 if vaddrs: |
| 89 return min(vaddrs) |
| 90 return 0 |
| 91 |
| 92 |
| 93 def GetLoadVaddrs(apk_dir): |
| 94 """Return a dictionary of minimum VirtAddr fields for libraries in apk_dir. |
| 95 |
| 96 The dictionary returned may be passed to stack_core.ConvertTrace(). In |
| 97 pre-M Android releases the addresses printed by debuggerd into tombstones |
| 98 do not take account of non-zero vaddrs. Here we collect this information, |
| 99 so that we can use it later to correct such debuggerd tombstones. |
| 100 |
| 101 Args: |
| 102 apk_dir: Path to APK staging directory. |
| 103 Returns: |
| 104 {'libchrome.so': 12345, ...} |
| 105 """ |
| 106 pathname = apk_dir + '/libs/*/*.so' |
| 107 libs = [lib for lib in glob.glob(pathname) if _HasElfHeader(lib)] |
| 108 |
| 109 load_vaddrs = {} |
| 110 for lib in libs: |
| 111 min_vaddr = _FindMinLoadVaddr(lib) |
| 112 if min_vaddr: |
| 113 # Store with the library basename as the key. This is because once on |
| 114 # the device its path may not fully match its place in the APK staging |
| 115 # directory |
| 116 load_vaddrs[os.path.basename(lib)] = min_vaddr |
| 117 |
| 118 # Direct load from APK causes debuggerd to tag trace lines as if from the |
| 119 # file .../base.apk. So if we encounter a libchrome.so with packed |
| 120 # relocations, replicate this as base.apk so that later adjustment code |
| 121 # finds the appropriate adjustment. |
| 122 if _LIBCHROME_SO in load_vaddrs: |
| 123 load_vaddrs[_BASE_APK] = load_vaddrs[_LIBCHROME_SO] |
| 124 |
| 125 return load_vaddrs |
OLD | NEW |