blob: aef358962be0246f8c803367375acff73779356e [file] [log] [blame]
Shuhei Takahashi721d5a72017-02-10 15:38:28 +09001# Copyright 2017 The Chromium OS 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"""Generate minidump symbols for use by the Crash server.
6
7This script takes expanded crash symbols published by the Android build, and
8converts them to breakpad format.
9"""
10
Chris McDonald59650c32021-07-20 15:29:28 -060011import logging
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090012import multiprocessing
13import os
14import re
15import zipfile
16
17from chromite.lib import commandline
18from chromite.lib import cros_build_lib
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090019from chromite.lib import osutils
20from chromite.lib import parallel
21from chromite.scripts import cros_generate_breakpad_symbols
22
23
Alex Klein1699fab2022-09-08 08:46:06 -060024RELOCATION_PACKER_BIN = "relocation_packer"
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090025
26# These regexps match each type of address we have to adjust.
27ADDRESS_REGEXPS = (
Alex Klein1699fab2022-09-08 08:46:06 -060028 re.compile(r"^FUNC ([0-9a-f]+)"),
29 re.compile(r"^([0-9a-f]+)"),
30 re.compile(r"^PUBLIC ([0-9a-f]+)"),
31 re.compile(r"^STACK CFI INIT ([0-9a-f]+)"),
32 re.compile(r"^STACK CFI ([0-9a-f]+)"),
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090033)
34
35
36class OffsetDiscoveryError(Exception):
Alex Klein1699fab2022-09-08 08:46:06 -060037 """Raised if we can't find the offset after unpacking symbols."""
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090038
39
40def FindExpansionOffset(unpack_result):
Alex Klein1699fab2022-09-08 08:46:06 -060041 """Helper to extract symbols offset from relocation_packer output.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090042
Alex Klein1699fab2022-09-08 08:46:06 -060043 This can accept and handle both successful and failed unpack command output.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090044
Alex Klein1699fab2022-09-08 08:46:06 -060045 Will return 0 if no adjustment is needed.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090046
Alex Klein1699fab2022-09-08 08:46:06 -060047 Args:
48 unpack_result: CompletedProcess from the relocation_packer command.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090049
Alex Klein1699fab2022-09-08 08:46:06 -060050 Returns:
51 Integer offset to adjust symbols by. May be 0.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090052
Alex Klein1699fab2022-09-08 08:46:06 -060053 Raises:
54 OffsetDiscoveryError if the unpack succeeds, but we can't parse the output.
55 """
56 if unpack_result.returncode != 0:
57 return 0
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090058
Alex Klein1699fab2022-09-08 08:46:06 -060059 # Look for the number of relocations as a confidence check that we got the
60 # expected output. Note that we don't otherwise care about this value.
61 relocations_match = re.search(
62 r"INFO: Relocations +: +(\d+) entries", unpack_result.stdout
63 )
64 if not relocations_match:
65 raise OffsetDiscoveryError(
66 "No Relocations in: %s" % unpack_result.stdout
67 )
Lloyd Piquee5f4f0f2019-02-12 14:17:32 -080068
Alex Klein1699fab2022-09-08 08:46:06 -060069 # An "Expansion" line is only written if the value is nonzero.
70 offset_match = re.search(
71 r"INFO: Expansion +: +(\d+) bytes", unpack_result.stdout
72 )
73 if not offset_match:
74 return 0
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090075
Alex Klein1699fab2022-09-08 08:46:06 -060076 # Return offset as a negative number.
77 return -int(offset_match.group(1))
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090078
79
80def _AdjustLineSymbolOffset(line, offset):
Alex Klein1699fab2022-09-08 08:46:06 -060081 """Adjust the symbol offset for one line of a breakpad file.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090082
Alex Klein1699fab2022-09-08 08:46:06 -060083 Args:
84 line: One line of the file.
85 offset: int to adjust the symbol by.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090086
Alex Klein1699fab2022-09-08 08:46:06 -060087 Returns:
88 The adjusted line, or original line if there is no change.
89 """
90 for regexp in ADDRESS_REGEXPS:
91 m = regexp.search(line)
92 if m:
93 address = int(m.group(1), 16)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090094
Alex Klein1699fab2022-09-08 08:46:06 -060095 # We ignore 0 addresses, since the zero's are fillers for unknowns.
96 if address:
97 address += offset
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090098
Alex Klein1699fab2022-09-08 08:46:06 -060099 # Return the same line with address adjusted.
100 return "%s%x%s" % (line[: m.start(1)], address, line[m.end(1) :])
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900101
Alex Klein1699fab2022-09-08 08:46:06 -0600102 # Nothing recognized, no adjustment.
103 return line
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900104
105
106def _AdjustSymbolOffset(breakpad_file, offset):
Alex Klein1699fab2022-09-08 08:46:06 -0600107 """Given a breakpad file, adjust the symbols by offset.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 Updates the file in place.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900110
Alex Klein1699fab2022-09-08 08:46:06 -0600111 Args:
112 breakpad_file: File to read and update in place.
113 offset: Integer to move symbols by.
114 """
115 logging.info(
116 "Adjusting symbols in %s with offset %d.", breakpad_file, offset
117 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 # Keep newlines.
120 lines = osutils.ReadFile(breakpad_file).splitlines(True)
121 adjusted_lines = [_AdjustLineSymbolOffset(line, offset) for line in lines]
122 osutils.WriteFile(breakpad_file, "".join(adjusted_lines))
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900123
124
125def _UnpackGenerateBreakpad(elf_file, *args, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -0600126 """Unpack Android relocation symbols, and GenerateBreakpadSymbol
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 This method accepts exactly the same arguments as
129 cros_generate_breakpad_symbols.GenerateBreakpadSymbol, except that it requires
130 elf_file, and fills in dump_sym_cmd.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900131
Alex Klein1699fab2022-09-08 08:46:06 -0600132 Args:
133 elf_file: Name of the file to generate breakpad symbols for.
134 args: See cros_generate_breakpad_symbols.GenerateBreakpadSymbol.
135 kwargs: See cros_generate_breakpad_symbols.GenerateBreakpadSymbol.
136 """
137 # We try to unpack, and just see if it works. Real failures caused by
138 # something other than a binary that's already unpacked will be logged and
139 # ignored. We'll notice them when dump_syms fails later (which it will on
140 # packed binaries.).
141 unpack_cmd = [RELOCATION_PACKER_BIN, "-u", elf_file]
142 unpack_result = cros_build_lib.run(
143 unpack_cmd, stdout=True, check=False, encoding="utf8"
144 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900145
Alex Klein1699fab2022-09-08 08:46:06 -0600146 # If we unpacked, extract the offset, and remember it.
147 offset = FindExpansionOffset(unpack_result)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900148
Alex Klein1699fab2022-09-08 08:46:06 -0600149 if offset:
150 logging.info(
151 "Unpacked relocation symbols for %s with offset %d.",
152 elf_file,
153 offset,
154 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900155
Alex Klein1699fab2022-09-08 08:46:06 -0600156 # Now generate breakpad symbols from the binary.
157 breakpad_file = cros_generate_breakpad_symbols.GenerateBreakpadSymbol(
158 elf_file, *args, **kwargs
159 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900160
Alex Klein1699fab2022-09-08 08:46:06 -0600161 if isinstance(breakpad_file, int):
162 logging.error("Unable to generate symbols for %s", elf_file)
163 elif offset:
164 _AdjustSymbolOffset(breakpad_file, offset)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900165
166
167def GenerateBreakpadSymbols(breakpad_dir, symbols_dir):
Alex Klein1699fab2022-09-08 08:46:06 -0600168 """Generate symbols for all binaries in symbols_dir.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 Args:
171 breakpad_dir: The full path in which to write out breakpad symbols.
172 symbols_dir: The full path to the binaries to process from.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 Returns:
175 The number of errors that were encountered.
176 """
177 osutils.SafeMakedirs(breakpad_dir)
178 logging.info("generating breakpad symbols from %s", symbols_dir)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 num_errors = parallel.WrapMultiprocessing(multiprocessing.Value, "i")
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900181
Alex Klein1699fab2022-09-08 08:46:06 -0600182 # Now start generating symbols for the discovered elfs.
183 with parallel.BackgroundTaskRunner(
184 _UnpackGenerateBreakpad,
185 breakpad_dir=breakpad_dir,
186 num_errors=num_errors,
187 ) as queue:
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 for root, _, files in os.walk(symbols_dir):
190 for f in files:
191 queue.put([os.path.join(root, f)])
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900192
Alex Klein1699fab2022-09-08 08:46:06 -0600193 return num_errors.value
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900194
195
196def ProcessSymbolsZip(zip_archive, breakpad_dir):
Alex Klein1699fab2022-09-08 08:46:06 -0600197 """Extract, process, and upload all symbols in a symbols zip file.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900198
Alex Klein1699fab2022-09-08 08:46:06 -0600199 Take the symbols file build artifact from an Android build, process it into
200 breakpad format, and upload the results to the ChromeOS crashreporter.
201 Significant multiprocessing is done by helper libraries, and a remote swarm
202 server is used to reduce processing of duplicate symbol files.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 The symbols files are really expected to be unstripped elf files (or
205 libraries), possibly using packed relocation tables. No other file types are
206 expected in the zip.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 Args:
209 zip_archive: Name of the zip file to process.
210 breakpad_dir: Root directory for writing out breakpad files.
211 """
212 with osutils.TempDir(prefix="extracted-") as extract_dir:
213 logging.info("Extracting %s into %s", zip_archive, extract_dir)
214 with zipfile.ZipFile(zip_archive, "r") as zf:
215 # We are trusting the contents from a security point of view.
216 zf.extractall(extract_dir)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900217
Alex Klein1699fab2022-09-08 08:46:06 -0600218 logging.info(
219 "Generate breakpad symbols from %s into %s",
220 extract_dir,
221 breakpad_dir,
222 )
223 GenerateBreakpadSymbols(breakpad_dir, extract_dir)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900224
225
226def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600227 """Helper method mostly used for manual testing."""
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900228
Alex Klein1699fab2022-09-08 08:46:06 -0600229 parser = commandline.ArgumentParser(description=__doc__)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900230
Alex Klein1699fab2022-09-08 08:46:06 -0600231 parser.add_argument(
232 "--symbols_file", type="path", required=True, help="Zip file containing"
233 )
234 parser.add_argument(
235 "--breakpad_dir",
236 type="path",
237 default="/tmp/breakpad",
238 help="Root directory for breakpad symbol files.",
239 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900240
Alex Klein1699fab2022-09-08 08:46:06 -0600241 opts = parser.parse_args(argv)
242 opts.Freeze()
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900243
Alex Klein1699fab2022-09-08 08:46:06 -0600244 ProcessSymbolsZip(opts.symbols_file, opts.breakpad_dir)