blob: 069617beb8dea41098757144b8cb896c77afb93e [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2017 The ChromiumOS Authors
Shuhei Takahashi721d5a72017-02-10 15:38:28 +09002# 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:
Alex Klein8b444532023-04-11 16:35:24 -060048 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:
Alex Klein8b444532023-04-11 16:35:24 -060051 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:
Alex Klein8b444532023-04-11 16:35:24 -060054 OffsetDiscoveryError: if the unpack succeeds, but we can't parse the
55 output.
Alex Klein1699fab2022-09-08 08:46:06 -060056 """
57 if unpack_result.returncode != 0:
58 return 0
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090059
Alex Klein1699fab2022-09-08 08:46:06 -060060 # Look for the number of relocations as a confidence check that we got the
61 # expected output. Note that we don't otherwise care about this value.
62 relocations_match = re.search(
63 r"INFO: Relocations +: +(\d+) entries", unpack_result.stdout
64 )
65 if not relocations_match:
66 raise OffsetDiscoveryError(
67 "No Relocations in: %s" % unpack_result.stdout
68 )
Lloyd Piquee5f4f0f2019-02-12 14:17:32 -080069
Alex Klein1699fab2022-09-08 08:46:06 -060070 # An "Expansion" line is only written if the value is nonzero.
71 offset_match = re.search(
72 r"INFO: Expansion +: +(\d+) bytes", unpack_result.stdout
73 )
74 if not offset_match:
75 return 0
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090076
Alex Klein1699fab2022-09-08 08:46:06 -060077 # Return offset as a negative number.
78 return -int(offset_match.group(1))
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090079
80
81def _AdjustLineSymbolOffset(line, offset):
Alex Klein1699fab2022-09-08 08:46:06 -060082 """Adjust the symbol offset for one line of a breakpad file.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090083
Alex Klein1699fab2022-09-08 08:46:06 -060084 Args:
Alex Klein8b444532023-04-11 16:35:24 -060085 line: One line of the file.
86 offset: int to adjust the symbol by.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090087
Alex Klein1699fab2022-09-08 08:46:06 -060088 Returns:
Alex Klein8b444532023-04-11 16:35:24 -060089 The adjusted line, or original line if there is no change.
Alex Klein1699fab2022-09-08 08:46:06 -060090 """
91 for regexp in ADDRESS_REGEXPS:
92 m = regexp.search(line)
93 if m:
94 address = int(m.group(1), 16)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090095
Alex Klein8b444532023-04-11 16:35:24 -060096 # We ignore 0 addresses, since the zeros are fillers for unknowns.
Alex Klein1699fab2022-09-08 08:46:06 -060097 if address:
98 address += offset
Shuhei Takahashi721d5a72017-02-10 15:38:28 +090099
Alex Klein1699fab2022-09-08 08:46:06 -0600100 # Return the same line with address adjusted.
101 return "%s%x%s" % (line[: m.start(1)], address, line[m.end(1) :])
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 # Nothing recognized, no adjustment.
104 return line
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900105
106
107def _AdjustSymbolOffset(breakpad_file, offset):
Alex Klein1699fab2022-09-08 08:46:06 -0600108 """Given a breakpad file, adjust the symbols by offset.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900109
Alex Klein1699fab2022-09-08 08:46:06 -0600110 Updates the file in place.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900111
Alex Klein1699fab2022-09-08 08:46:06 -0600112 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600113 breakpad_file: File to read and update in place.
114 offset: Integer to move symbols by.
Alex Klein1699fab2022-09-08 08:46:06 -0600115 """
116 logging.info(
117 "Adjusting symbols in %s with offset %d.", breakpad_file, offset
118 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 # Keep newlines.
121 lines = osutils.ReadFile(breakpad_file).splitlines(True)
122 adjusted_lines = [_AdjustLineSymbolOffset(line, offset) for line in lines]
123 osutils.WriteFile(breakpad_file, "".join(adjusted_lines))
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900124
125
126def _UnpackGenerateBreakpad(elf_file, *args, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -0600127 """Unpack Android relocation symbols, and GenerateBreakpadSymbol
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900128
Alex Klein1699fab2022-09-08 08:46:06 -0600129 This method accepts exactly the same arguments as
Alex Klein8b444532023-04-11 16:35:24 -0600130 cros_generate_breakpad_symbols.GenerateBreakpadSymbol, except that it
131 requires elf_file, and fills in dump_sym_cmd.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 Args:
Alex Klein361062b2023-04-05 09:45:28 -0600134 elf_file: Name of the file to generate breakpad symbols for.
135 *args: See cros_generate_breakpad_symbols.GenerateBreakpadSymbol.
136 **kwargs: See cros_generate_breakpad_symbols.GenerateBreakpadSymbol.
Alex Klein1699fab2022-09-08 08:46:06 -0600137 """
138 # We try to unpack, and just see if it works. Real failures caused by
139 # something other than a binary that's already unpacked will be logged and
140 # ignored. We'll notice them when dump_syms fails later (which it will on
141 # packed binaries.).
142 unpack_cmd = [RELOCATION_PACKER_BIN, "-u", elf_file]
143 unpack_result = cros_build_lib.run(
Mike Frysinger31fdddd2023-02-24 15:50:55 -0500144 unpack_cmd, stdout=True, check=False, encoding="utf-8"
Alex Klein1699fab2022-09-08 08:46:06 -0600145 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900146
Alex Klein1699fab2022-09-08 08:46:06 -0600147 # If we unpacked, extract the offset, and remember it.
148 offset = FindExpansionOffset(unpack_result)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 if offset:
151 logging.info(
152 "Unpacked relocation symbols for %s with offset %d.",
153 elf_file,
154 offset,
155 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900156
Alex Klein1699fab2022-09-08 08:46:06 -0600157 # Now generate breakpad symbols from the binary.
158 breakpad_file = cros_generate_breakpad_symbols.GenerateBreakpadSymbol(
159 elf_file, *args, **kwargs
160 )
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 if isinstance(breakpad_file, int):
163 logging.error("Unable to generate symbols for %s", elf_file)
164 elif offset:
165 _AdjustSymbolOffset(breakpad_file, offset)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900166
167
168def GenerateBreakpadSymbols(breakpad_dir, symbols_dir):
Alex Klein1699fab2022-09-08 08:46:06 -0600169 """Generate symbols for all binaries in symbols_dir.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 Args:
Alex Klein8b444532023-04-11 16:35:24 -0600172 breakpad_dir: The full path in which to write out breakpad symbols.
173 symbols_dir: The full path to the binaries to process from.
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 Returns:
Alex Klein8b444532023-04-11 16:35:24 -0600176 The number of errors that were encountered.
Alex Klein1699fab2022-09-08 08:46:06 -0600177 """
178 osutils.SafeMakedirs(breakpad_dir)
179 logging.info("generating breakpad symbols from %s", symbols_dir)
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900180
Alex Klein1699fab2022-09-08 08:46:06 -0600181 num_errors = parallel.WrapMultiprocessing(multiprocessing.Value, "i")
Shuhei Takahashi721d5a72017-02-10 15:38:28 +0900182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 # Now start generating symbols for the discovered elfs.
184 with parallel.BackgroundTaskRunner(
185 _UnpackGenerateBreakpad,
186 breakpad_dir=breakpad_dir,
187 num_errors=num_errors,
188 ) as queue:
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:
Alex Klein8b444532023-04-11 16:35:24 -0600209 zip_archive: Name of the zip file to process.
210 breakpad_dir: Root directory for writing out breakpad files.
Alex Klein1699fab2022-09-08 08:46:06 -0600211 """
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)