Skip to content

Commit

Permalink
[Android Unwinder] Add new unwind info generation to build script
Browse files Browse the repository at this point in the history
This CL adds the generation of the new unwind info to build script
behind a build flag "use_android_unwinder_v2".

Bug: 1240698
Change-Id: Ie63879b5396f03f2253ff1f79f2c4d00c69ef108
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3259978
Reviewed-by: Mike Wittman <wittman@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@google.com>
Commit-Queue: Charlie Hu <chenleihu@google.com>
Cr-Commit-Position: refs/heads/main@{#949287}
  • Loading branch information
Charlie Hu authored and Chromium LUCI CQ committed Dec 8, 2021
1 parent ba8e41e commit d98dc69
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 18 deletions.
61 changes: 58 additions & 3 deletions build/android/gyp/create_unwind_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
"""Creates a table of unwind information in Android Chrome's bespoke format."""

import abc
import argparse
import collections
import enum
import logging
import os
import re
import collections
import struct
import subprocess
import sys
from typing import (Dict, Iterable, List, NamedTuple, Sequence, TextIO, Tuple,
Union)

from util import build_utils

_STACK_CFI_INIT_REGEX = re.compile(
r'^STACK CFI INIT ([0-9a-f]+) ([0-9a-f]+) (.+)$')
_STACK_CFI_REGEX = re.compile(r'^STACK CFI ([0-9a-f]+) (.+)$')
Expand Down Expand Up @@ -653,6 +659,7 @@ def GetPageOffset(address: int) -> int:
text_section_start_address: int = sorted_function_unwinds[0].address
prev_func_end_address: int = sorted_function_unwinds[0].address

gaps = 0
for unwind in sorted_function_unwinds:
assert prev_func_end_address <= unwind.address, (
'Detected overlap between functions.')
Expand All @@ -661,6 +668,7 @@ def GetPageOffset(address: int) -> int:
# Gaps between functions are typically filled by regions of thunks which
# do not alter the stack pointer. Filling these gaps with TRIVIAL_UNWIND
# is the appropriate unwind strategy.
gaps += 1
yield EncodedFunctionUnwind(GetPageNumber(prev_func_end_address),
GetPageOffset(prev_func_end_address),
TRIVIAL_UNWIND)
Expand All @@ -676,6 +684,9 @@ def GetPageOffset(address: int) -> int:
GetPageOffset(prev_func_end_address),
REFUSE_TO_UNWIND)

logging.info('%d/%d gaps between functions filled with trivial unwind.', gaps,
len(sorted_function_unwinds))


def EncodeFunctionOffsetTable(
encoded_address_unwind_sequences: Iterable[
Expand Down Expand Up @@ -869,8 +880,8 @@ def GenerateUnwinds(function_cfis: Iterable[FunctionCfi],
epilogues_seen += 1
break

logging.info('unrecognized CFI: %x %s.' %
(address_cfi.address, address_cfi.unwind_instructions))
logging.info('unrecognized CFI: %x %s.', address_cfi.address,
address_cfi.unwind_instructions)

if address_unwinds:
# We expect that the unwind information for every function starts with a
Expand Down Expand Up @@ -996,3 +1007,47 @@ def GenerateUnwindTables(

return (page_table, function_table, function_offset_table,
unwind_instruction_table)


def main():
build_utils.InitLogging('CREATE_UNWIND_TABLE_DEBUG')
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--input_path',
help='Path to the unstripped binary.',
required=True,
metavar='FILE')
parser.add_argument('--output_path',
help='Path to unwind info binary output.',
required=True,
metavar='FILE')
parser.add_argument('--dump_syms_path',
required=True,
help='The path of the dump_syms binary.',
metavar='FILE')

args = parser.parse_args()
proc = subprocess.Popen(['./' + args.dump_syms_path, args.input_path, '-v'],
stdout=subprocess.PIPE,
encoding='ascii')

function_cfis = ReadFunctionCfi(proc.stdout)
function_unwinds = GenerateUnwinds(function_cfis, parsers=ALL_PARSERS)
encoded_function_unwinds = EncodeFunctionUnwinds(function_unwinds)
(page_table, function_table, function_offset_table,
unwind_instruction_table) = GenerateUnwindTables(encoded_function_unwinds)
unwind_info: bytes = EncodeUnwindInfo(page_table, function_table,
function_offset_table,
unwind_instruction_table)

if proc.wait():
logging.critical('dump_syms exited with return code %d', proc.returncode)
sys.exit(proc.returncode)

with open(args.output_path, 'wb') as f:
f.write(unwind_info)

return 0


if __name__ == '__main__':
sys.exit(main())
44 changes: 44 additions & 0 deletions build/config/android/create_unwind_table.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/android/rules.gni")

template("unwind_table_asset_v2") {
_asset_path = "${target_out_dir}/${target_name}/unwind_cfi_32_v2"
_unwind_action = "${target_name}__create_unwind_table"

action(_unwind_action) {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)

_root_dir = "$root_out_dir"
if (defined(android_secondary_abi_cpu)) {
_root_dir = get_label_info(":foo($android_secondary_abi_toolchain)",
"root_out_dir")
}

script = "//build/android/gyp/create_unwind_table.py"
outputs = [ _asset_path ]
inputs = [ "${_root_dir}/lib.unstripped/$shlib_prefix${invoker.library_target}$shlib_extension" ]

args = [
"--input_path",
rebase_path(
"${_root_dir}/lib.unstripped/$shlib_prefix${invoker.library_target}$shlib_extension",
root_build_dir),
"--output_path",
rebase_path(_asset_path, root_build_dir),
"--dump_syms_path",
rebase_path("$root_out_dir/dump_syms", root_build_dir),
]
deps = invoker.deps
deps += [ "//third_party/breakpad:dump_syms" ]
}

android_assets(target_name) {
forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
sources = [ _asset_path ]
disable_compression = true
deps = [ ":$_unwind_action" ]
}
}
3 changes: 3 additions & 0 deletions build/config/compiler/compiler.gni
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ can_unwind_with_cfi_table = is_android && !is_component_build &&
enable_arm_cfi_table = is_android && !is_component_build && current_cpu == "arm"

declare_args() {
# Set to true to use the android unwinder V2 implementation.
use_android_unwinder_v2 = false

# If this running on a GPU FYI bot.
# TODO(https://crbug.com/1233871): Remove this again.
is_gpu_fyi_bot = false
Expand Down
46 changes: 34 additions & 12 deletions chrome/android/chrome_public_apk_tmpl.gni
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import("//android_webview/variables.gni")
import("//base/android/linker/config.gni")
import("//base/android/resource_exclusions.gni")
import("//build/config/android/create_unwind_table.gni")
import("//build/config/android/extract_unwind_tables.gni")
import("//build/config/android/rules.gni")
import("//build/config/compiler/compiler.gni")
Expand Down Expand Up @@ -100,25 +101,46 @@ template("chrome_public_common_apk_or_module_tmpl") {

if (_add_unwind_tables) {
_unwind_asset_target = "${target_name}__unwind_assets"
unwind_table_asset(_unwind_asset_target) {
if (defined(invoker.testonly)) {
testonly = invoker.testonly
}

if (defined(invoker.shared_library_for_unwind_asset)) {
library_target = invoker.shared_library_for_unwind_asset
} else {
if (_is_monochrome || _is_trichrome) {
if (use_android_unwinder_v2) {
unwind_table_asset_v2(_unwind_asset_target) {
if (defined(invoker.testonly)) {
testonly = invoker.testonly
}

if (defined(invoker.shared_library_for_unwind_asset)) {
library_target = invoker.shared_library_for_unwind_asset
} else if (_is_monochrome || _is_trichrome) {
library_target = "monochrome"
} else {
library_target = "chrome"
}

if (android_64bit_target_cpu) {
deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
} else {
deps = [ "//chrome/android:lib${library_target}" ]
}
}
} else {
unwind_table_asset(_unwind_asset_target) {
if (defined(invoker.testonly)) {
testonly = invoker.testonly
}

if (android_64bit_target_cpu) {
deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
} else {
deps = [ "//chrome/android:lib${library_target}" ]
if (defined(invoker.shared_library_for_unwind_asset)) {
library_target = invoker.shared_library_for_unwind_asset
} else if (_is_monochrome || _is_trichrome) {
library_target = "monochrome"
} else {
library_target = "chrome"
}

if (android_64bit_target_cpu) {
deps = [ "//chrome/android:lib${library_target}($android_secondary_abi_toolchain)" ]
} else {
deps = [ "//chrome/android:lib${library_target}" ]
}
}
}
} else if (defined(invoker.shared_library_for_unwind_asset)) {
Expand Down
14 changes: 11 additions & 3 deletions testing/test.gni
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ declare_args() {

if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/create_unwind_table.gni")
import("//build/config/android/extract_unwind_tables.gni")
import("//build/config/android/rules.gni")
import("//build/config/sanitizers/sanitizers.gni")
Expand Down Expand Up @@ -198,9 +199,16 @@ template("test") {
if (defined(invoker.add_unwind_tables_in_apk) &&
invoker.add_unwind_tables_in_apk) {
_unwind_table_asset_name = "${target_name}_unwind_assets"
unwind_table_asset(_unwind_table_asset_name) {
library_target = _library_target
deps = [ ":$_library_target" ]
if (use_android_unwinder_v2) {
unwind_table_asset_v2(_unwind_table_asset_name) {
library_target = _library_target
deps = [ ":$_library_target" ]
}
} else {
unwind_table_asset(_unwind_table_asset_name) {
library_target = _library_target
deps = [ ":$_library_target" ]
}
}
}

Expand Down

0 comments on commit d98dc69

Please sign in to comment.