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 |