Merge master bac601e785fc into doc
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9bfc20d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,59 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*.S text eol=lf
+*.asm text eol=lf
+*.c text eol=lf
+*.cc text eol=lf
+*.cmx text eol=lf
+*.css text eol=lf
+*.defs text eol=lf
+*.doxy text eol=lf
+*.gn text eol=lf
+*.gni text eol=lf
+*.go text eol=lf
+*.gyp text eol=lf
+*.gypi text eol=lf
+*.h text eol=lf
+*.m text eol=lf
+*.md text eol=lf
+*.mm text eol=lf
+*.pem text eol=lf
+*.plist text eol=lf
+*.proctype text eol=lf
+*.py text eol=lf
+*.sh text eol=lf
+*.sym text eol=lf
+*.txt text eol=lf
+*.yaml text eol=lf
+.clang-format text eol=lf
+.gitattributes text eol=lf
+.gitignore text eol=lf
+.vpython text eol=lf
+/AUTHORS text eol=lf
+/CONTRIBUTORS text eol=lf
+/LICENSE text eol=lf
+/codereview.settings text eol=lf
+APPLE_LICENSE text eol=lf
+COPYING.LIB text eol=lf
+DEPS text eol=lf
+README text eol=lf
+README.crashpad text eol=lf
+
+*.dat binary
+*.dll binary
+*.ico binary
+*.obj binary
+*.png binary
+*.so binary
diff --git a/.gitignore b/.gitignore
index 982a95e..fe8a94c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,17 @@
+# Copyright 2014 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
*.Makefile
*.ninja
*.pyc
@@ -10,6 +24,7 @@
.gdbinit
/Makefile
/out
+/third_party/edo/edo
/third_party/fuchsia/.cipd
/third_party/fuchsia/clang
/third_party/fuchsia/qemu
@@ -19,6 +34,7 @@
/third_party/linux/.cipd
/third_party/linux/clang
/third_party/linux/sysroot
+/third_party/lss/lss
/third_party/gyp/gyp
/third_party/mini_chromium/mini_chromium
/third_party/zlib/zlib
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000..8aaf9b6
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,16 @@
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[style]
+based_on_style = google
diff --git a/.vpython b/.vpython
new file mode 100644
index 0000000..c2001fc
--- /dev/null
+++ b/.vpython
@@ -0,0 +1,32 @@
+# Copyright 2018 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a vpython "spec" file.
+#
+# It describes patterns for python wheel dependencies of the python scripts.
+#
+# Read more about `vpython` and how to modify this file here:
+# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md
+
+# This is needed for snapshot/win/end_to_end_test.py.
+wheel: <
+ name: "infra/python/wheels/pypiwin32/${vpython_platform}"
+ version: "version:219"
+ match_tag: <
+ platform: "win32"
+ >
+ match_tag: <
+ platform: "win_amd64"
+ >
+>
diff --git a/BUILD.gn b/BUILD.gn
index 065a5e1..98218af 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -14,29 +14,71 @@
import("build/crashpad_buildconfig.gni")
import("build/test.gni")
+import("util/net/tls.gni")
config("crashpad_config") {
include_dirs = [ "." ]
}
+# TODO(fuchsia:46805): Remove this once instances of UB have been cleaned up.
+config("disable_ubsan") {
+ if (crashpad_is_in_fuchsia) {
+ cflags = [ "-fno-sanitize=undefined" ]
+ }
+ visibility = [
+ "snapshot:snapshot",
+ "minidump:minidump_test",
+ "third_party/gtest:gtest",
+ "util:util",
+ ]
+}
+
if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
test("crashpad_tests") {
deps = [
"client:client_test",
- "handler:handler_test",
"minidump:minidump_test",
- "snapshot:snapshot_test",
"test:gmock_main",
"test:test_test",
- "util:util_test",
]
+ if (!crashpad_is_ios) {
+ deps += [ "snapshot:snapshot_test" ]
+ }
+ if (!crashpad_is_ios && !crashpad_is_fuchsia) {
+ deps += [ "handler:handler_test" ]
+ }
+ if (crashpad_is_in_fuchsia) {
+ # TODO(fuchsia:46559): Fix the leaks and remove this.
+ deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ]
+ }
+ if (crashpad_is_android) {
+ use_raw_android_executable = true
+
+ copy("crashpad_test_data") {
+ testonly = true
+ sources = [
+ "test/test_paths_test_data_root.txt",
+ "util/net/testdata/ascii_http_body.txt",
+ "util/net/testdata/binary_http_body.dat",
+ ]
+
+ outputs = [ "$root_out_dir/crashpad_test_data/{{source}}" ]
+ }
+
+ deps += [ ":crashpad_test_data" ]
+
+ extra_dist_files = [
+ "$root_out_dir/crashpad_handler",
+ "$root_out_dir/crashpad_test_test_multiprocess_exec_test_child",
+ "$root_out_dir/crashpad_test_data",
+ ]
+ }
}
if (crashpad_is_in_fuchsia) {
import("//build/package.gni")
package("crashpad_test") {
testonly = true
- deprecated_system_image = true
deps = [
":crashpad_tests",
@@ -45,7 +87,6 @@
"snapshot:crashpad_snapshot_test_module_large",
"snapshot:crashpad_snapshot_test_module_small",
"test:crashpad_test_test_multiprocess_exec_test_child",
- "util:generate_test_server_key",
"util:http_transport_test_server",
]
@@ -53,24 +94,23 @@
{
name = "crashpad_tests"
},
+ ]
+
+ meta = [
+ {
+ path = "test/fuchsia_crashpad_tests.cmx"
+ dest = "crashpad_tests.cmx"
+ },
+ ]
+
+ binaries = [
{
name = "crashpad_test_test_multiprocess_exec_test_child"
- dest = "crashpad_test_data/crashpad_test_test_multiprocess_exec_test_child"
+ dest = "crashpad_test_test_multiprocess_exec_test_child"
},
{
name = "http_transport_test_server"
- dest = "crashpad_test_data/http_transport_test_server"
- },
-
- # These aren't actually tests, but that seems to be the only way to
- # convince package() to get them from the output directory.
- {
- name = "crashpad_util_test_cert.pem"
- dest = "crashpad_test_data/crashpad_util_test_cert.pem"
- },
- {
- name = "crashpad_util_test_key.pem"
- dest = "crashpad_test_data/crashpad_util_test_key.pem"
+ dest = "http_transport_test_server"
},
]
@@ -92,43 +132,39 @@
resources = [
{
path = "util/net/testdata/ascii_http_body.txt"
- dest = "crashpad_test_data/util/net/testdata/ascii_http_body.txt"
+ dest = "util/net/testdata/ascii_http_body.txt"
},
{
path = "util/net/testdata/binary_http_body.dat"
- dest = "crashpad_test_data/util/net/testdata/binary_http_body.dat"
+ dest = "util/net/testdata/binary_http_body.dat"
},
{
path = "test/test_paths_test_data_root.txt"
- dest = "crashpad_test_data/test/test_paths_test_data_root.txt"
+ dest = "test/test_paths_test_data_root.txt"
},
]
- }
- package("crashpad_handler") {
- deprecated_system_image = true
-
- deps = [
- "handler:crashpad_handler",
- ]
-
- binaries = [
- {
- name = "crashpad_handler"
- },
- ]
+ if (crashpad_use_boringssl_for_http_transport_socket) {
+ resources += [
+ {
+ path = "util/net/testdata/crashpad_util_test_cert.pem"
+ dest = "util/net/testdata/crashpad_util_test_cert.pem"
+ },
+ {
+ path = "util/net/testdata/crashpad_util_test_key.pem"
+ dest = "util/net/testdata/crashpad_util_test_key.pem"
+ },
+ ]
+ }
}
package("crashpad_database_util") {
- deprecated_system_image = true
-
- deps = [
- "tools:crashpad_database_util",
- ]
+ deps = [ "tools:crashpad_database_util" ]
binaries = [
{
name = "crashpad_database_util"
+ shell = true
},
]
}
@@ -146,6 +182,9 @@
"handler:handler_test",
"test:gtest_main",
]
+ if (crashpad_is_ios || crashpad_is_fuchsia) {
+ deps -= [ "handler:handler_test" ]
+ }
}
test("crashpad_minidump_test") {
@@ -160,6 +199,9 @@
"snapshot:snapshot_test",
"test:gtest_main",
]
+ if (crashpad_is_ios) {
+ deps -= [ "snapshot:snapshot_test" ]
+ }
}
test("crashpad_test_test") {
@@ -176,3 +218,10 @@
]
}
}
+
+if (crashpad_is_ios) {
+ group("ios_xcuitests") {
+ testonly = true
+ deps = [ "test/ios:all_tests" ]
+ }
+}
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 2080cc4..8724b7f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -12,3 +12,4 @@
Mark Mentovai <mark@chromium.org>
Robert Sesek <rsesek@chromium.org>
Scott Graham <scottmg@chromium.org>
+Joshua Peraza <jperaza@chromium.org>
diff --git a/DEPS b/DEPS
index 34f1ef6..81b9583 100644
--- a/DEPS
+++ b/DEPS
@@ -15,22 +15,34 @@
vars = {
'chromium_git': 'https://chromium.googlesource.com',
'pull_linux_clang': False,
- 'pull_win_toolchain': False
+ 'pull_win_toolchain': False,
+ # Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of
+ # gclient hooks. It is enabled by default for developer's convenience. It can
+ # be disabled with custom_vars (done automatically on the bots).
+ 'run_setup_ios_gn': True,
}
deps = {
'buildtools':
- Var('chromium_git') + '/chromium/buildtools.git@' +
- '6fe4a3251488f7af86d64fc25cf442e817cf6133',
+ Var('chromium_git') + '/chromium/src/buildtools.git@' +
+ '4164a305626786b1912d467003acf4c4995bec7d',
+ 'crashpad/third_party/edo/edo': {
+ 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' +
+ '243fc89ae95b24717d41f3786f6a9abeeef87c92',
+ 'condition': 'checkout_ios',
+ },
'crashpad/third_party/gtest/gtest':
Var('chromium_git') + '/external/github.com/google/googletest@' +
- 'c091b0469ab4c04ee9411ef770f32360945f4c53',
+ 'e3f0319d89f4cbf32993de595d984183b1a9fc57',
'crashpad/third_party/gyp/gyp':
Var('chromium_git') + '/external/gyp@' +
- '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f',
+ '8bee09f4a57807136593ddc906b0b213c21f9014',
+ 'crashpad/third_party/lss/lss':
+ Var('chromium_git') + '/linux-syscall-support.git@' +
+ '7bde79cc274d06451bf65ae82c012a5d3e476b5a',
'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' +
- '793e94e2c652831af2d25bb5288b04e59048c62d',
+ '8ca5ea356cdb97913d62d379d503567a80d90726',
'crashpad/third_party/libfuzzer/src':
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
'fda403cf93ecb8792cb1d061564d89a6553ca020',
@@ -49,7 +61,7 @@
'condition': 'checkout_linux and pull_linux_clang',
'dep_type': 'cipd'
},
- 'crashpad/third_party/linux/clang/mac-amd64': {
+ 'crashpad/third_party/fuchsia/clang/mac-amd64': {
'packages': [
{
'package': 'fuchsia/clang/mac-amd64',
@@ -89,22 +101,24 @@
'condition': 'checkout_fuchsia and host_os == "linux"',
'dep_type': 'cipd'
},
- 'crashpad/third_party/fuchsia/sdk/linux-amd64': {
- # The SDK is keyed to the host system because it contains build tools.
- # Currently, linux-amd64 is the only SDK published (see
- # https://chrome-infra-packages.appspot.com/#/?path=fuchsia/sdk).
- # As long as this is the case, use that SDK package
- # even on other build hosts.
- # The sysroot (containing headers and libraries) and other components are
- # related to the target and should be functional with an appropriate
- # toolchain that runs on the build host (fuchsia_clang, above).
+ 'crashpad/third_party/fuchsia/sdk/mac-amd64': {
'packages': [
{
- 'package': 'fuchsia/sdk/linux-amd64',
+ 'package': 'fuchsia/sdk/gn/mac-amd64',
'version': 'latest'
},
],
- 'condition': 'checkout_fuchsia',
+ 'condition': 'checkout_fuchsia and host_os == "mac"',
+ 'dep_type': 'cipd'
+ },
+ 'crashpad/third_party/fuchsia/sdk/linux-amd64': {
+ 'packages': [
+ {
+ 'package': 'fuchsia/sdk/gn/linux-amd64',
+ 'version': 'latest'
+ },
+ ],
+ 'condition': 'checkout_fuchsia and host_os == "linux"',
'dep_type': 'cipd'
},
'crashpad/third_party/win/toolchain': {
@@ -164,45 +178,6 @@
],
},
{
- 'name': 'gn_mac',
- 'pattern': '.',
- 'condition': 'host_os == "mac"',
- 'action': [
- 'download_from_google_storage',
- '--no_resume',
- '--no_auth',
- '--bucket=chromium-gn',
- '--sha1_file',
- 'buildtools/mac/gn.sha1',
- ],
- },
- {
- 'name': 'gn_linux',
- 'pattern': '.',
- 'condition': 'host_os == "linux"',
- 'action': [
- 'download_from_google_storage',
- '--no_resume',
- '--no_auth',
- '--bucket=chromium-gn',
- '--sha1_file',
- 'buildtools/linux64/gn.sha1',
- ],
- },
- {
- 'name': 'gn_win',
- 'pattern': '.',
- 'condition': 'host_os == "win"',
- 'action': [
- 'download_from_google_storage',
- '--no_resume',
- '--no_auth',
- '--bucket=chromium-gn',
- '--sha1_file',
- 'buildtools/win/gn.exe.sha1',
- ],
- },
- {
# If using a local clang ("pull_linux_clang" above), also pull down a
# sysroot.
'name': 'sysroot_linux',
@@ -213,9 +188,13 @@
],
},
{
- 'name': 'gyp',
- 'pattern': '\.gypi?$',
- 'action': ['python', 'crashpad/build/gyp_crashpad.py'],
+ 'name': 'setup_gn_ios',
+ 'pattern': '.',
+ 'condition': 'run_setup_ios_gn and checkout_ios',
+ 'action': [
+ 'python',
+ 'crashpad/build/ios/setup_ios_gn.py'
+ ],
},
]
diff --git a/README.md b/README.md
index d74c1bd..b20777b 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@
* Bugs can be reported at the [Crashpad issue
tracker](https://crashpad.chromium.org/bug/).
- * The [Crashpad Buildbots](https://build.chromium.org/p/client.crashpad)
+ * The [Crashpad bots](https://ci.chromium.org/p/crashpad/g/main/console)
perform automated builds and tests.
* [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev)
is the Crashpad developers’ mailing list.
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 0ef1127..6e6ccd6 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -32,9 +32,7 @@
group("default_exe_manifest_win") {
if (crashpad_is_in_chromium) {
- deps = [
- "//build/win:default_exe_manifest",
- ]
+ deps = [ "//build/win:default_exe_manifest" ]
}
}
@@ -45,7 +43,26 @@
"-fsanitize=fuzzer",
]
- ldflags = [
- "-fsanitize=address",
- ]
+ ldflags = [ "-fsanitize=address" ]
+}
+
+if (crashpad_is_ios) {
+ group("ios_enable_arc") {
+ if (crashpad_is_in_chromium) {
+ public_configs = [ "//build/config/compiler:enable_arc" ]
+ } else if (crashpad_is_standalone) {
+ public_configs =
+ [ "//third_party/mini_chromium/mini_chromium/build:ios_enable_arc" ]
+ }
+ }
+
+ group("ios_xctest") {
+ if (crashpad_is_in_chromium) {
+ public_configs = [ "//build/config/ios:xctest_config" ]
+ } else if (crashpad_is_standalone) {
+ public_configs = [
+ "//third_party/mini_chromium/mini_chromium/build/ios:xctest_config",
+ ]
+ }
+ }
}
diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn
index 95dc277..bc53108 100644
--- a/build/BUILDCONFIG.gn
+++ b/build/BUILDCONFIG.gn
@@ -55,6 +55,7 @@
_default_configs = [
"//third_party/mini_chromium/mini_chromium/build:default",
"//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
+ "//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
]
if (crashpad_use_libfuzzer) {
diff --git a/build/crashpad.gypi b/build/crashpad.gypi
index 166e768..2accb5a 100644
--- a/build/crashpad.gypi
+++ b/build/crashpad.gypi
@@ -35,6 +35,11 @@
}],
],
}],
+ ['OS=="android"', {
+ 'ldflags': [
+ '-static-libstdc++',
+ ],
+ }],
],
},
}
diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni
index f767027..7540fb2 100644
--- a/build/crashpad_buildconfig.gni
+++ b/build/crashpad_buildconfig.gni
@@ -15,28 +15,28 @@
declare_args() {
# Determines various flavors of build configuration, and which concrete
# targets to use for dependencies. Valid values are "standalone", "chromium",
- # and "fuchsia".
+ # "fuchsia", "dart" or "external".
crashpad_dependencies = "standalone"
if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
- # Determines various flavors of build configuration, and which concrete
- # targets to use for dependencies. Valid values are "standalone",
- # "chromium", and "fuchsia". Defaulted to "fuchsia" because
- # "is_fuchsia_tree" is set.
crashpad_dependencies = "fuchsia"
}
}
assert(
crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
- crashpad_dependencies == "standalone")
+ crashpad_dependencies == "standalone" ||
+ crashpad_dependencies == "external" || crashpad_dependencies == "dart")
crashpad_is_in_chromium = crashpad_dependencies == "chromium"
crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
+crashpad_is_in_dart = crashpad_dependencies == "dart"
+crashpad_is_external = crashpad_dependencies == "external"
crashpad_is_standalone = crashpad_dependencies == "standalone"
if (crashpad_is_in_chromium) {
crashpad_is_mac = is_mac
+ crashpad_is_ios = is_ios
crashpad_is_win = is_win
crashpad_is_linux = is_linux
crashpad_is_android = is_android
@@ -46,11 +46,18 @@
crashpad_is_clang = is_clang
} else {
- # Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
- # the same location in both cases.
- import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
- import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
+ # External and Dart SDK builds assume crashpad and mini_chromium are peers.
+ if (crashpad_is_external || crashpad_is_in_dart) {
+ import("../../../mini_chromium/mini_chromium/build/compiler.gni")
+ import("../../../mini_chromium/mini_chromium/build/platform.gni")
+ } else {
+ # Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
+ # the same location in both cases.
+ import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
+ import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
+ }
crashpad_is_mac = mini_chromium_is_mac
+ crashpad_is_ios = mini_chromium_is_ios
crashpad_is_win = mini_chromium_is_win
crashpad_is_linux = mini_chromium_is_linux
crashpad_is_android = mini_chromium_is_android
@@ -63,7 +70,12 @@
template("crashpad_executable") {
executable(target_name) {
- forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
+ forward_variables_from(invoker,
+ "*",
+ [
+ "configs",
+ "remove_configs",
+ ])
if (defined(invoker.remove_configs)) {
configs -= invoker.remove_configs
}
@@ -72,21 +84,23 @@
configs += invoker.configs
}
- if (!defined(deps)) {
- deps = []
- }
-
- if (crashpad_is_in_chromium) {
- deps += [ "//build/config:exe_and_shlib_deps" ]
- } else if (crashpad_is_in_fuchsia) {
- configs += [ "//build/config/fuchsia:fdio_config" ]
+ if (crashpad_is_in_fuchsia) {
+ fdio_config = [ "//build/config/fuchsia:fdio_config" ]
+ if (configs + fdio_config - fdio_config == configs) {
+ configs += fdio_config
+ }
}
}
}
template("crashpad_loadable_module") {
loadable_module(target_name) {
- forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
+ forward_variables_from(invoker,
+ "*",
+ [
+ "configs",
+ "remove_configs",
+ ])
if (defined(invoker.remove_configs)) {
configs -= invoker.remove_configs
}
@@ -95,13 +109,7 @@
configs += invoker.configs
}
- if (!defined(deps)) {
- deps = []
- }
-
- if (crashpad_is_in_chromium) {
- deps += [ "//build/config:exe_and_shlib_deps" ]
- } else if (crashpad_is_in_fuchsia) {
+ if (crashpad_is_in_fuchsia) {
configs += [ "//build/config/fuchsia:fdio_config" ]
}
}
diff --git a/build/crashpad_dependencies.gni b/build/crashpad_dependencies.gni
deleted file mode 100644
index 1964fac..0000000
--- a/build/crashpad_dependencies.gni
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2017 The Crashpad Authors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-declare_args() {
- # Determines various flavors of build configuration, and which concrete
- # targets to use for dependencies. Valid values are "standalone", "chromium",
- # and "fuchsia".
- crashpad_dependencies = "standalone"
-}
-
-assert(
- crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
- crashpad_dependencies == "standalone")
-
-crashpad_is_in_chromium = crashpad_dependencies == "chromium"
-crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
-crashpad_is_standalone = crashpad_dependencies == "standalone"
-
-if (crashpad_is_in_chromium) {
- crashpad_is_posix = is_posix
-} else if (crashpad_is_in_fuchsia) {
- import("//third_party/mini_chromium/build/is_posix.gni")
- crashpad_is_posix = mini_chromium_is_posix
-} else if (crashpad_is_standalone) {
- import("../third_party/mini_chromium/mini_chromium/build/is_posix.gni")
- crashpad_is_posix = mini_chromium_is_posix
-}
-
-if (crashpad_is_in_chromium) {
- import("//testing/test.gni")
-} else {
- template("test") {
- executable(target_name) {
- testonly = true
- forward_variables_from(invoker, "*")
- }
- }
-
- set_defaults("test") {
- configs = default_executable_configs
- }
-}
diff --git a/build/crashpad_fuzzer_test.gni b/build/crashpad_fuzzer_test.gni
index 8e0eaa9..cc709b0 100644
--- a/build/crashpad_fuzzer_test.gni
+++ b/build/crashpad_fuzzer_test.gni
@@ -14,8 +14,11 @@
import("crashpad_buildconfig.gni")
import("test.gni")
+if (crashpad_is_in_chromium) {
+ import("//testing/libfuzzer/fuzzer_test.gni")
+}
-template("fuzzer_test") {
+template("crashpad_fuzzer_test") {
if (crashpad_is_standalone && crashpad_use_libfuzzer) {
test(target_name) {
forward_variables_from(invoker,
@@ -38,6 +41,15 @@
}
cflags += [ "-fsanitize=fuzzer" ]
}
+ if (defined(invoker.seed_corpus)) {
+ not_needed(invoker, [ "seed_corpus" ])
+ }
+ } else if (crashpad_is_in_chromium && use_fuzzing_engine) {
+ # Append "crashpad_" to the beginning of the fuzzer's name to make it easier
+ # in Chromium to recognize where fuzzer came from.
+ forward_variables_from(invoker, "*")
+ fuzzer_test("crashpad_" + target_name) {
+ }
} else {
not_needed(invoker, "*")
group(target_name) {
diff --git a/build/gyp_crashpad.py b/build/gyp_crashpad.py
index 8485660..41ab4b9 100755
--- a/build/gyp_crashpad.py
+++ b/build/gyp_crashpad.py
@@ -19,83 +19,84 @@
def ChooseDependencyPath(local_path, external_path):
- """Chooses between a dependency located at local path and an external path.
+ """Chooses between a dependency located at local path and an external path.
- The local path, used in standalone builds, is preferred. If it is not present
- but the external path is, the external path will be used. If neither path is
- present, the local path will be used, so that error messages uniformly refer
- to the local path.
+ The local path, used in standalone builds, is preferred. If it is not
+ present but the external path is, the external path will be used. If neither
+ path is present, the local path will be used, so that error messages
+ uniformly refer to the local path.
- Args:
- local_path: The preferred local path to use for a standalone build.
- external_path: The external path to fall back to.
+ Args:
+ local_path: The preferred local path to use for a standalone build.
+ external_path: The external path to fall back to.
- Returns:
- A 2-tuple. The first element is None or 'external', depending on whether
- local_path or external_path was chosen. The second element is the chosen
- path.
- """
- if os.path.exists(local_path) or not os.path.exists(external_path):
- return (None, local_path)
- return ('external', external_path)
+ Returns:
+ A 2-tuple. The first element is None or 'external', depending on whether
+ local_path or external_path was chosen. The second element is the chosen
+ path.
+ """
+ if os.path.exists(local_path) or not os.path.exists(external_path):
+ return (None, local_path)
+ return ('external', external_path)
script_dir = os.path.dirname(__file__)
-crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir)
- else os.pardir)
+crashpad_dir = (os.path.dirname(script_dir)
+ if script_dir not in ('', os.curdir) else os.pardir)
-sys.path.insert(0,
- ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp',
- 'pylib'),
- os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp',
- 'pylib'))[1])
+sys.path.insert(
+ 0,
+ ChooseDependencyPath(
+ os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'),
+ os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp', 'pylib'))[1])
import gyp
def main(args):
- if 'GYP_GENERATORS' not in os.environ:
- os.environ['GYP_GENERATORS'] = 'ninja'
+ if 'GYP_GENERATORS' not in os.environ:
+ os.environ['GYP_GENERATORS'] = 'ninja'
- crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir
+ crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir
- (dependencies, mini_chromium_common_gypi) = (ChooseDependencyPath(
- os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
- 'mini_chromium', 'build', 'common.gypi'),
- os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium',
- 'mini_chromium', 'build', 'common.gypi')))
- if dependencies is not None:
- args.extend(['-D', 'crashpad_dependencies=%s' % dependencies])
- args.extend(['--include', mini_chromium_common_gypi])
- args.extend(['--depth', crashpad_dir_or_dot])
- args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
+ (dependencies, mini_chromium_common_gypi) = (ChooseDependencyPath(
+ os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
+ 'mini_chromium', 'build', 'common.gypi'),
+ os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium',
+ 'mini_chromium', 'build', 'common.gypi')))
+ if dependencies is not None:
+ args.extend(['-D', 'crashpad_dependencies=%s' % dependencies])
+ args.extend(['--include', mini_chromium_common_gypi])
+ args.extend(['--depth', crashpad_dir_or_dot])
+ args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
- result = gyp.main(args)
- if result != 0:
- return result
-
- if sys.platform == 'win32':
- # Check to make sure that no target_arch was specified. target_arch may be
- # set during a cross build, such as a cross build for Android.
- has_target_arch = False
- for arg_index in range(0, len(args)):
- arg = args[arg_index]
- if (arg.startswith('-Dtarget_arch=') or
- (arg == '-D' and arg_index + 1 < len(args) and
- args[arg_index + 1].startswith('target_arch='))):
- has_target_arch = True
- break
-
- if not has_target_arch:
- # Also generate the x86 build.
- result = gyp.main(args + ['-D', 'target_arch=ia32', '-G', 'config=Debug'])
- if result != 0:
+ result = gyp.main(args)
+ if result != 0:
return result
- result = gyp.main(
- args + ['-D', 'target_arch=ia32', '-G', 'config=Release'])
- return result
+ if sys.platform == 'win32':
+ # Check to make sure that no target_arch was specified. target_arch may
+ # be set during a cross build, such as a cross build for Android.
+ has_target_arch = False
+ for arg_index in range(0, len(args)):
+ arg = args[arg_index]
+ if (arg.startswith('-Dtarget_arch=') or
+ (arg == '-D' and arg_index + 1 < len(args) and
+ args[arg_index + 1].startswith('target_arch='))):
+ has_target_arch = True
+ break
+
+ if not has_target_arch:
+ # Also generate the x86 build.
+ result = gyp.main(args +
+ ['-D', 'target_arch=ia32', '-G', 'config=Debug'])
+ if result != 0:
+ return result
+ result = gyp.main(
+ args + ['-D', 'target_arch=ia32', '-G', 'config=Release'])
+
+ return result
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/build/gyp_crashpad_android.py b/build/gyp_crashpad_android.py
index f78c05b..327fa21 100755
--- a/build/gyp_crashpad_android.py
+++ b/build/gyp_crashpad_android.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# Copyright 2017 The Crashpad Authors. All rights reserved.
#
@@ -25,89 +24,46 @@
def main(args):
- parser = argparse.ArgumentParser(
- description='Set up an Android cross build',
- epilog='Additional arguments will be passed to gyp_crashpad.py.')
- parser.add_argument('--ndk', required=True, help='Standalone NDK toolchain')
- parser.add_argument('--compiler',
- default='clang',
- choices=('clang', 'gcc'),
- help='The compiler to use, clang by default')
- (parsed, extra_command_line_args) = parser.parse_known_args(args)
+ parser = argparse.ArgumentParser(
+ description='Set up an Android cross build',
+ epilog='Additional arguments will be passed to gyp_crashpad.py.')
+ parser.add_argument('--arch', required=True, help='Target architecture')
+ parser.add_argument('--api-level', required=True, help='Target API level')
+ parser.add_argument('--ndk', required=True, help='Standalone NDK toolchain')
+ (parsed, extra_command_line_args) = parser.parse_known_args(args)
- NDK_ERROR=(
- 'NDK must be a valid standalone NDK toolchain.\n' +
- 'See https://developer.android.com/ndk/guides/standalone_toolchain.html')
- arch_dirs = glob.glob(os.path.join(parsed.ndk, '*-linux-android*'))
- if len(arch_dirs) != 1:
- parser.error(NDK_ERROR)
+ ndk_bin_dir = os.path.join(parsed.ndk, 'toolchains', 'llvm', 'prebuilt',
+ 'linux-x86_64', 'bin')
+ if not os.path.exists(ndk_bin_dir):
+ parser.error("missing toolchain")
- arch_triplet = os.path.basename(arch_dirs[0])
- ARCH_TRIPLET_TO_ARCH = {
- 'arm-linux-androideabi': 'arm',
- 'aarch64-linux-android': 'arm64',
- 'i686-linux-android': 'ia32',
- 'x86_64-linux-android': 'x64',
- 'mipsel-linux-android': 'mips',
- 'mips64el-linux-android': 'mips64',
- }
- if arch_triplet not in ARCH_TRIPLET_TO_ARCH:
- parser.error(NDK_ERROR)
- arch = ARCH_TRIPLET_TO_ARCH[arch_triplet]
+ ARCH_TO_ARCH_TRIPLET = {
+ 'arm': 'armv7a-linux-androideabi',
+ 'arm64': 'aarch64-linux-android',
+ 'ia32': 'i686-linux-android',
+ 'x64': 'x86_64-linux-android',
+ }
- ndk_bin_dir = os.path.join(parsed.ndk, 'bin')
-
- clang_path = os.path.join(ndk_bin_dir, 'clang')
- extra_args = []
-
- if parsed.compiler == 'clang':
- os.environ['CC_target'] = clang_path
- os.environ['CXX_target'] = os.path.join(ndk_bin_dir, 'clang++')
- elif parsed.compiler == 'gcc':
- os.environ['CC_target'] = os.path.join(ndk_bin_dir,
- '%s-gcc' % arch_triplet)
+ clang_prefix = ARCH_TO_ARCH_TRIPLET[parsed.arch] + parsed.api_level
+ os.environ['CC_target'] = os.path.join(ndk_bin_dir, clang_prefix + '-clang')
os.environ['CXX_target'] = os.path.join(ndk_bin_dir,
- '%s-g++' % arch_triplet)
+ clang_prefix + '-clang++')
- # Unlike the Clang build, when using GCC with unified headers, __ANDROID_API__
- # isn’t set automatically and must be pushed in to the build. Fish the correct
- # value out of the Clang wrapper script. If deprecated headers are in use, the
- # Clang wrapper won’t mention __ANDROID_API__, but the standalone toolchain’s
- # <android/api-level.h> will #define it for both Clang and GCC.
- #
- # android_api_level is extracted in this manner even when compiling with Clang
- # so that it’s available for use in GYP conditions that need to test the API
- # level, but beware that it’ll only be available when unified headers are in
- # use.
- #
- # Unified headers are the way of the future, according to
- # https://android.googlesource.com/platform/ndk/+/ndk-r14/CHANGELOG.md and
- # https://android.googlesource.com/platform/ndk/+/master/docs/UnifiedHeaders.md.
- # Traditional (deprecated) headers have been removed entirely as of NDK r16.
- # https://android.googlesource.com/platform/ndk/+/ndk-release-r16/CHANGELOG.md.
- with open(clang_path, 'r') as file:
- clang_script_contents = file.read()
- matches = re.finditer(r'\s-D__ANDROID_API__=([\d]+)\s',
- clang_script_contents)
- match = next(matches, None)
- if match:
- android_api = int(match.group(1))
- extra_args.extend(['-D', 'android_api_level=%d' % android_api])
- if next(matches, None):
- raise AssertionError('__ANDROID_API__ defined too many times')
+ extra_args = ['-D', 'android_api_level=' + parsed.api_level]
- for tool in ('ar', 'nm', 'readelf'):
- os.environ['%s_target' % tool.upper()] = (
- os.path.join(ndk_bin_dir, '%s-%s' % (arch_triplet, tool)))
+ # ARM only includes 'v7a' in the tool prefix for clang
+ tool_prefix = ('arm-linux-androideabi' if parsed.arch == 'arm' else
+ ARCH_TO_ARCH_TRIPLET[parsed.arch])
- return gyp_crashpad.main(
- ['-D', 'OS=android',
- '-D', 'target_arch=%s' % arch,
- '-D', 'clang=%d' % (1 if parsed.compiler == 'clang' else 0),
- '-f', 'ninja-android'] +
- extra_args +
- extra_command_line_args)
+ for tool in ('ar', 'nm', 'readelf'):
+ os.environ['%s_target' % tool.upper()] = (os.path.join(
+ ndk_bin_dir, '%s-%s' % (tool_prefix, tool)))
+
+ return gyp_crashpad.main([
+ '-D', 'OS=android', '-D',
+ 'target_arch=%s' % parsed.arch, '-D', 'clang=1', '-f', 'ninja-android'
+ ] + extra_args + extra_command_line_args)
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/build/install_linux_sysroot.py b/build/install_linux_sysroot.py
index afa8815..97f2c14 100755
--- a/build/install_linux_sysroot.py
+++ b/build/install_linux_sysroot.py
@@ -23,7 +23,6 @@
import sys
import urllib2
-
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
# Sysroot revision from:
@@ -33,42 +32,43 @@
REVISION = '3c248ba4290a5ad07085b7af07e6785bf1ae5b66'
FILENAME = 'debian_stretch_amd64_sysroot.tar.xz'
+
def main():
- url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME)
+ url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME)
- sysroot = os.path.join(SCRIPT_DIR, os.pardir,
- 'third_party', 'linux', 'sysroot')
+ sysroot = os.path.join(SCRIPT_DIR, os.pardir, 'third_party', 'linux',
+ 'sysroot')
- stamp = os.path.join(sysroot, '.stamp')
- if os.path.exists(stamp):
- with open(stamp) as s:
- if s.read() == url:
- return
+ stamp = os.path.join(sysroot, '.stamp')
+ if os.path.exists(stamp):
+ with open(stamp) as s:
+ if s.read() == url:
+ return
- print 'Installing Debian root image from %s' % url
+ print 'Installing Debian root image from %s' % url
- if os.path.isdir(sysroot):
- shutil.rmtree(sysroot)
- os.mkdir(sysroot)
- tarball = os.path.join(sysroot, FILENAME)
- print 'Downloading %s' % url
+ if os.path.isdir(sysroot):
+ shutil.rmtree(sysroot)
+ os.mkdir(sysroot)
+ tarball = os.path.join(sysroot, FILENAME)
+ print 'Downloading %s' % url
- for _ in range(3):
- response = urllib2.urlopen(url)
- with open(tarball, 'wb') as f:
- f.write(response.read())
- break
- else:
- raise Exception('Failed to download %s' % url)
+ for _ in range(3):
+ response = urllib2.urlopen(url)
+ with open(tarball, 'wb') as f:
+ f.write(response.read())
+ break
+ else:
+ raise Exception('Failed to download %s' % url)
- subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])
+ subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])
- os.remove(tarball)
+ os.remove(tarball)
- with open(stamp, 'w') as s:
- s.write(url)
+ with open(stamp, 'w') as s:
+ s.write(url)
if __name__ == '__main__':
- main()
- sys.exit(0)
+ main()
+ sys.exit(0)
diff --git a/build/ios/Default.png b/build/ios/Default.png
new file mode 100644
index 0000000..8c9089d
--- /dev/null
+++ b/build/ios/Default.png
Binary files differ
diff --git a/build/ios/Unittest-Info.plist b/build/ios/Unittest-Info.plist
new file mode 100644
index 0000000..9256fb4
--- /dev/null
+++ b/build/ios/Unittest-Info.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleIdentifier</key>
+ <string>${IOS_BUNDLE_ID_PREFIX}.gtest.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier}</string>
+ <key>UIApplicationDelegate</key>
+ <string>CrashpadUnitTestDelegate</string>
+</dict>
+</plist>
diff --git a/build/ios/convert_gn_xcodeproj.py b/build/ios/convert_gn_xcodeproj.py
new file mode 100755
index 0000000..c00d331
--- /dev/null
+++ b/build/ios/convert_gn_xcodeproj.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python
+
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Convert GN Xcode projects to platform and configuration independent targets.
+
+GN generates Xcode projects that build one configuration only. However, typical
+iOS development involves using the Xcode IDE to toggle the platform and
+configuration. This script replaces the 'gn' configuration with 'Debug',
+'Release' and 'Profile', and changes the ninja invocation to honor these
+configurations.
+"""
+
+import argparse
+import collections
+import copy
+import filecmp
+import json
+import hashlib
+import os
+import plistlib
+import random
+import shutil
+import subprocess
+import sys
+import tempfile
+
+
+class XcodeProject(object):
+
+ def __init__(self, objects, counter=0):
+ self.objects = objects
+ self.counter = 0
+
+ def AddObject(self, parent_name, obj):
+ while True:
+ self.counter += 1
+ str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter)
+ new_id = hashlib.sha1(str_id).hexdigest()[:24].upper()
+
+ # Make sure ID is unique. It's possible there could be an id
+ # conflict since this is run after GN runs.
+ if new_id not in self.objects:
+ self.objects[new_id] = obj
+ return new_id
+
+
+def CopyFileIfChanged(source_path, target_path):
+ """Copy |source_path| to |target_path| is different."""
+ target_dir = os.path.dirname(target_path)
+ if not os.path.isdir(target_dir):
+ os.makedirs(target_dir)
+ if (not os.path.exists(target_path) or
+ not filecmp.cmp(source_path, target_path)):
+ shutil.copyfile(source_path, target_path)
+
+
+def LoadXcodeProjectAsJSON(path):
+ """Return Xcode project at |path| as a JSON string."""
+ return subprocess.check_output(
+ ['plutil', '-convert', 'json', '-o', '-', path])
+
+
+def WriteXcodeProject(output_path, json_string):
+ """Save Xcode project to |output_path| as XML."""
+ with tempfile.NamedTemporaryFile() as temp_file:
+ temp_file.write(json_string)
+ temp_file.flush()
+ subprocess.check_call(['plutil', '-convert', 'xml1', temp_file.name])
+ CopyFileIfChanged(temp_file.name, output_path)
+
+
+def UpdateProductsProject(file_input, file_output, configurations, root_dir):
+ """Update Xcode project to support multiple configurations.
+
+ Args:
+ file_input: path to the input Xcode project
+ file_output: path to the output file
+ configurations: list of string corresponding to the configurations that
+ need to be supported by the tweaked Xcode projects, must contains at
+ least one value.
+ """
+ json_data = json.loads(LoadXcodeProjectAsJSON(file_input))
+ project = XcodeProject(json_data['objects'])
+
+ objects_to_remove = []
+ for value in project.objects.values():
+ isa = value['isa']
+
+ # Teach build shell script to look for the configuration and platform.
+ if isa == 'PBXShellScriptBuildPhase':
+ value['shellScript'] = value['shellScript'].replace(
+ 'ninja -C .',
+ 'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"')
+
+ # Add new configuration, using the first one as default.
+ if isa == 'XCConfigurationList':
+ value['defaultConfigurationName'] = configurations[0]
+ objects_to_remove.extend(value['buildConfigurations'])
+
+ build_config_template = project.objects[value['buildConfigurations']
+ [0]]
+ build_settings = build_config_template['buildSettings']
+ build_settings['CONFIGURATION_BUILD_DIR'] = (
+ '$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)')
+ build_settings['CODE_SIGN_IDENTITY'] = ''
+
+ value['buildConfigurations'] = []
+ for configuration in configurations:
+ new_build_config = copy.copy(build_config_template)
+ new_build_config['name'] = configuration
+ value['buildConfigurations'].append(
+ project.AddObject('products', new_build_config))
+
+ for object_id in objects_to_remove:
+ del project.objects[object_id]
+
+ AddMarkdownToProject(project, root_dir, json_data['rootObject'])
+
+ objects = collections.OrderedDict(sorted(project.objects.iteritems()))
+ WriteXcodeProject(file_output, json.dumps(json_data))
+
+
+def AddMarkdownToProject(project, root_dir, root_object):
+ list_files_cmd = ['git', '-C', root_dir, 'ls-files', '*.md']
+ paths = subprocess.check_output(list_files_cmd).splitlines()
+ ios_internal_dir = os.path.join(root_dir, 'ios_internal')
+ if os.path.exists(ios_internal_dir):
+ list_files_cmd = ['git', '-C', ios_internal_dir, 'ls-files', '*.md']
+ ios_paths = subprocess.check_output(list_files_cmd).splitlines()
+ paths.extend(["ios_internal/" + path for path in ios_paths])
+ for path in paths:
+ new_markdown_entry = {
+ "fileEncoding": "4",
+ "isa": "PBXFileReference",
+ "lastKnownFileType": "net.daringfireball.markdown",
+ "name": os.path.basename(path),
+ "path": path,
+ "sourceTree": "<group>"
+ }
+ new_markdown_entry_id = project.AddObject('sources', new_markdown_entry)
+ folder = GetFolderForPath(project, root_object, os.path.dirname(path))
+ folder['children'].append(new_markdown_entry_id)
+
+
+def GetFolderForPath(project, rootObject, path):
+ objects = project.objects
+ # 'Sources' is always the first child of
+ # project->rootObject->mainGroup->children.
+ root = objects[objects[objects[rootObject]['mainGroup']]['children'][0]]
+ if not path:
+ return root
+ for folder in path.split('/'):
+ children = root['children']
+ new_root = None
+ for child in children:
+ if (objects[child]['isa'] == 'PBXGroup' and
+ objects[child]['name'] == folder):
+ new_root = objects[child]
+ break
+ if not new_root:
+ # If the folder isn't found we could just cram it into the leaf
+ # existing folder, but that leads to folders with tons of README.md
+ # inside.
+ new_group = {
+ "children": [],
+ "isa": "PBXGroup",
+ "name": folder,
+ "sourceTree": "<group>"
+ }
+ new_group_id = project.AddObject('sources', new_group)
+ children.append(new_group_id)
+ new_root = objects[new_group_id]
+ root = new_root
+ return root
+
+
+def DisableNewBuildSystem(output_dir):
+ """Disables the new build system due to crbug.com/852522 """
+ xcwspacesharedsettings = os.path.join(output_dir, 'all.xcworkspace',
+ 'xcshareddata',
+ 'WorkspaceSettings.xcsettings')
+ if os.path.isfile(xcwspacesharedsettings):
+ json_data = json.loads(LoadXcodeProjectAsJSON(xcwspacesharedsettings))
+ else:
+ json_data = {}
+ json_data['BuildSystemType'] = 'Original'
+ WriteXcodeProject(xcwspacesharedsettings, json.dumps(json_data))
+
+
+def ConvertGnXcodeProject(root_dir, input_dir, output_dir, configurations):
+ '''Tweak the Xcode project generated by gn to support multiple
+ configurations.
+
+ The Xcode projects generated by "gn gen --ide" only supports a single
+ platform and configuration (as the platform and configuration are set per
+ output directory). This method takes as input such projects and add support
+ for multiple configurations and platforms (to allow devs to select them in
+ Xcode).
+
+ Args:
+ input_dir: directory containing the XCode projects created by "gn gen
+ --ide"
+ output_dir: directory where the tweaked Xcode projects will be saved
+ configurations: list of string corresponding to the configurations that
+ need to be supported by the tweaked Xcode projects, must contains at
+ least one value.
+ '''
+ # Update products project.
+ products = os.path.join('products.xcodeproj', 'project.pbxproj')
+ product_input = os.path.join(input_dir, products)
+ product_output = os.path.join(output_dir, products)
+ UpdateProductsProject(product_input, product_output, configurations,
+ root_dir)
+
+ # Copy all workspace.
+ xcwspace = os.path.join('all.xcworkspace', 'contents.xcworkspacedata')
+ CopyFileIfChanged(os.path.join(input_dir, xcwspace),
+ os.path.join(output_dir, xcwspace))
+
+ # TODO(crbug.com/852522): Disable new BuildSystemType.
+ DisableNewBuildSystem(output_dir)
+
+ # TODO(crbug.com/679110): gn has been modified to remove 'sources.xcodeproj'
+ # and keep 'all.xcworkspace' and 'products.xcodeproj'. The following code is
+ # here to support both old and new projects setup and will be removed once
+ # gn has rolled past it.
+ sources = os.path.join('sources.xcodeproj', 'project.pbxproj')
+ if os.path.isfile(os.path.join(input_dir, sources)):
+ CopyFileIfChanged(os.path.join(input_dir, sources),
+ os.path.join(output_dir, sources))
+
+
+def Main(args):
+ parser = argparse.ArgumentParser(
+ description='Convert GN Xcode projects for iOS.')
+ parser.add_argument(
+ 'input', help='directory containing [product|all] Xcode projects.')
+ parser.add_argument(
+ 'output', help='directory where to generate the iOS configuration.')
+ parser.add_argument('--add-config',
+ dest='configurations',
+ default=[],
+ action='append',
+ help='configuration to add to the Xcode project')
+ parser.add_argument('--root',
+ type=os.path.abspath,
+ required=True,
+ help='root directory of the project')
+ args = parser.parse_args(args)
+
+ if not os.path.isdir(args.input):
+ sys.stderr.write('Input directory does not exists.\n')
+ return 1
+
+ required = set(['products.xcodeproj', 'all.xcworkspace'])
+ if not required.issubset(os.listdir(args.input)):
+ sys.stderr.write(
+ 'Input directory does not contain all necessary Xcode projects.\n')
+ return 1
+
+ if not args.configurations:
+ sys.stderr.write(
+ 'At least one configuration required, see --add-config.\n')
+ return 1
+
+ ConvertGnXcodeProject(args.root, args.input, args.output,
+ args.configurations)
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/build/ios/setup_ios_gn.config b/build/ios/setup_ios_gn.config
new file mode 100644
index 0000000..30c3111
--- /dev/null
+++ b/build/ios/setup_ios_gn.config
@@ -0,0 +1,39 @@
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[goma]
+# Controls whether goma is enabled or not. If you generally use goma but
+# want to disable goma for a single build, consider using the environment
+# variable GOMA_DISABLED.
+enabled = False
+install = "$GOMA_DIR"
+
+[xcode]
+# Controls settings for the generated Xcode project. If jobs is non-zero
+# it will be passed to the ninja invocation in Xcode project.
+jobs = 0
+
+[build]
+# Controls the build output. The only supported values are "64-bit", "32-bit"
+# and "fat" (for a fat binary supporting both "32-bit" and "64-bit" cpus).
+arch = "64-bit"
+
+[gn_args]
+# Values in that section will be copied verbatim in the generated args.gn file.
+target_os = "ios"
+
+[filters]
+# List of target files to pass to --filters argument of gn gen when generating
+# the Xcode project. By default, list all targets from ios/ and ios_internal/
+# and the targets corresponding to the unit tests run on the bots.
diff --git a/build/ios/setup_ios_gn.py b/build/ios/setup_ios_gn.py
new file mode 100755
index 0000000..934b67c
--- /dev/null
+++ b/build/ios/setup_ios_gn.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python
+
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import convert_gn_xcodeproj
+import errno
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import ConfigParser
+
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator')
+SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official', 'Coverage')
+
+
+class ConfigParserWithStringInterpolation(ConfigParser.SafeConfigParser):
+ '''A .ini file parser that supports strings and environment variables.'''
+
+ ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)')
+
+ def values(self, section):
+ return map(lambda (k, v): self._UnquoteString(self._ExpandEnvVar(v)),
+ ConfigParser.SafeConfigParser.items(self, section))
+
+ def getstring(self, section, option):
+ return self._UnquoteString(self._ExpandEnvVar(self.get(section,
+ option)))
+
+ def _UnquoteString(self, string):
+ if not string or string[0] != '"' or string[-1] != '"':
+ return string
+ return string[1:-1]
+
+ def _ExpandEnvVar(self, value):
+ match = self.ENV_VAR_PATTERN.search(value)
+ if not match:
+ return value
+ name, (begin, end) = match.group(1), match.span(0)
+ prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])
+ return prefix + os.environ.get(name, '') + suffix
+
+
+class GnGenerator(object):
+ '''Holds configuration for a build and method to generate gn default
+ files.'''
+
+ FAT_BUILD_DEFAULT_ARCH = '64-bit'
+
+ TARGET_CPU_VALUES = {
+ 'iphoneos': {
+ '32-bit': '"arm"',
+ '64-bit': '"arm64"',
+ },
+ 'iphonesimulator': {
+ '32-bit': '"x86"',
+ '64-bit': '"x64"',
+ }
+ }
+
+ def __init__(self, settings, config, target):
+ assert target in SUPPORTED_TARGETS
+ assert config in SUPPORTED_CONFIGS
+ self._settings = settings
+ self._config = config
+ self._target = target
+
+ def _GetGnArgs(self):
+ """Build the list of arguments to pass to gn.
+
+ Returns:
+ A list of tuple containing gn variable names and variable values (it
+ is not a dictionary as the order needs to be preserved).
+ """
+ args = []
+
+ args.append(('is_debug', self._config in ('Debug', 'Coverage')))
+
+ if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
+ args.append(('use_system_xcode', False))
+
+ cpu_values = self.TARGET_CPU_VALUES[self._target]
+ build_arch = self._settings.getstring('build', 'arch')
+ if build_arch == 'fat':
+ target_cpu = cpu_values[self.FAT_BUILD_DEFAULT_ARCH]
+ args.append(('target_cpu', target_cpu))
+ args.append(
+ ('additional_target_cpus',
+ [cpu for cpu in cpu_values.itervalues() if cpu != target_cpu]))
+ else:
+ args.append(('target_cpu', cpu_values[build_arch]))
+
+ # Add user overrides after the other configurations so that they can
+ # refer to them and override them.
+ args.extend(self._settings.items('gn_args'))
+ return args
+
+ def Generate(self, gn_path, root_path, out_path):
+ buf = StringIO.StringIO()
+ self.WriteArgsGn(buf)
+ WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
+ buf.getvalue(),
+ overwrite=True)
+
+ subprocess.check_call(
+ self.GetGnCommand(gn_path, root_path, out_path, True))
+
+ def CreateGnRules(self, gn_path, root_path, out_path):
+ buf = StringIO.StringIO()
+ self.WriteArgsGn(buf)
+ WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
+ buf.getvalue(),
+ overwrite=True)
+
+ buf = StringIO.StringIO()
+ gn_command = self.GetGnCommand(gn_path, root_path, out_path, False)
+ self.WriteBuildNinja(buf, gn_command)
+ WriteToFileIfChanged(os.path.join(out_path, 'build.ninja'),
+ buf.getvalue(),
+ overwrite=False)
+
+ buf = StringIO.StringIO()
+ self.WriteBuildNinjaDeps(buf)
+ WriteToFileIfChanged(os.path.join(out_path, 'build.ninja.d'),
+ buf.getvalue(),
+ overwrite=False)
+
+ def WriteArgsGn(self, stream):
+ stream.write('# This file was generated by setup-gn.py. Do not edit\n')
+ stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
+ stream.write('# to configure settings.\n')
+ stream.write('\n')
+
+ if self._settings.has_section('$imports$'):
+ for import_rule in self._settings.values('$imports$'):
+ stream.write('import("%s")\n' % import_rule)
+ stream.write('\n')
+
+ gn_args = self._GetGnArgs()
+ for name, value in gn_args:
+ if isinstance(value, bool):
+ stream.write('%s = %s\n' % (name, str(value).lower()))
+ elif isinstance(value, list):
+ stream.write('%s = [%s' %
+ (name, '\n' if len(value) > 1 else ''))
+ if len(value) == 1:
+ prefix = ' '
+ suffix = ' '
+ else:
+ prefix = ' '
+ suffix = ',\n'
+ for item in value:
+ if isinstance(item, bool):
+ stream.write('%s%s%s' %
+ (prefix, str(item).lower(), suffix))
+ else:
+ stream.write('%s%s%s' % (prefix, item, suffix))
+ stream.write(']\n')
+ else:
+ stream.write('%s = %s\n' % (name, value))
+
+ def WriteBuildNinja(self, stream, gn_command):
+ stream.write('rule gn\n')
+ stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
+ stream.write(' description = Regenerating ninja files\n')
+ stream.write('\n')
+ stream.write('build build.ninja: gn\n')
+ stream.write(' generator = 1\n')
+ stream.write(' depfile = build.ninja.d\n')
+
+ def WriteBuildNinjaDeps(self, stream):
+ stream.write('build.ninja: nonexistant_file.gn\n')
+
+ def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project):
+ gn_command = [gn_path, '--root=%s' % os.path.realpath(src_path), '-q']
+ if generate_xcode_project:
+ gn_command.append('--ide=xcode')
+ gn_command.append('--root-target=gn_all')
+ if self._settings.getboolean('goma', 'enabled'):
+ ninja_jobs = self._settings.getint('xcode', 'jobs') or 200
+ gn_command.append('--ninja-extra-args=-j%s' % ninja_jobs)
+ if self._settings.has_section('filters'):
+ target_filters = self._settings.values('filters')
+ if target_filters:
+ gn_command.append('--filters=%s' % ';'.join(target_filters))
+ # TODO(justincohen): --check is currently failing in crashpad.
+ # else:
+ # gn_command.append('--check')
+ gn_command.append('gen')
+ gn_command.append('//%s' % os.path.relpath(os.path.abspath(out_path),
+ os.path.abspath(src_path)))
+ return gn_command
+
+
+def WriteToFileIfChanged(filename, content, overwrite):
+ '''Write |content| to |filename| if different. If |overwrite| is False
+ and the file already exists it is left untouched.'''
+ if os.path.exists(filename):
+ if not overwrite:
+ return
+ with open(filename) as file:
+ if file.read() == content:
+ return
+ if not os.path.isdir(os.path.dirname(filename)):
+ os.makedirs(os.path.dirname(filename))
+ with open(filename, 'w') as file:
+ file.write(content)
+
+
+def NinjaNeedEscape(arg):
+ '''Returns True if |arg| needs to be escaped when written to .ninja file.'''
+ return ':' in arg or '*' in arg or ';' in arg
+
+
+def NinjaEscapeCommand(command):
+ '''Escapes |command| in order to write it to .ninja file.'''
+ result = []
+ for arg in command:
+ if NinjaNeedEscape(arg):
+ arg = arg.replace(':', '$:')
+ arg = arg.replace(';', '\\;')
+ arg = arg.replace('*', '\\*')
+ else:
+ result.append(arg)
+ return ' '.join(result)
+
+
+def FindGn():
+ '''Returns absolute path to gn binary looking at the PATH env variable.'''
+ for path in os.environ['PATH'].split(os.path.pathsep):
+ gn_path = os.path.join(path, 'gn')
+ if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):
+ return gn_path
+ return None
+
+
+def GenerateXcodeProject(gn_path, root_dir, out_dir, settings):
+ '''Convert GN generated Xcode project into multi-configuration Xcode
+ project.'''
+
+ temp_path = tempfile.mkdtemp(
+ prefix=os.path.abspath(os.path.join(out_dir, '_temp')))
+ try:
+ generator = GnGenerator(settings, 'Debug', 'iphonesimulator')
+ generator.Generate(gn_path, root_dir, temp_path)
+ convert_gn_xcodeproj.ConvertGnXcodeProject(
+ root_dir, os.path.join(temp_path), os.path.join(out_dir, 'build'),
+ SUPPORTED_CONFIGS)
+ finally:
+ if os.path.exists(temp_path):
+ shutil.rmtree(temp_path)
+
+
+def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
+ '''Generates all template configurations for gn.'''
+ for config in SUPPORTED_CONFIGS:
+ for target in SUPPORTED_TARGETS:
+ build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
+ generator = GnGenerator(settings, config, target)
+ generator.CreateGnRules(gn_path, root_dir, build_dir)
+
+
+def Main(args):
+ default_root = os.path.normpath(
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+
+ parser = argparse.ArgumentParser(
+ description='Generate build directories for use with gn.')
+ parser.add_argument(
+ 'root',
+ default=default_root,
+ nargs='?',
+ help='root directory where to generate multiple out configurations')
+ parser.add_argument('--import',
+ action='append',
+ dest='import_rules',
+ default=[],
+ help='path to file defining default gn variables')
+ args = parser.parse_args(args)
+
+ # Load configuration (first global and then any user overrides).
+ settings = ConfigParserWithStringInterpolation()
+ settings.read([
+ os.path.splitext(__file__)[0] + '.config',
+ os.path.expanduser('~/.setup-gn'),
+ ])
+
+ # Add private sections corresponding to --import argument.
+ if args.import_rules:
+ settings.add_section('$imports$')
+ for i, import_rule in enumerate(args.import_rules):
+ if not import_rule.startswith('//'):
+ import_rule = '//%s' % os.path.relpath(
+ os.path.abspath(import_rule), os.path.abspath(args.root))
+ settings.set('$imports$', '$rule%d$' % i, import_rule)
+
+ # Validate settings.
+ if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):
+ sys.stderr.write('ERROR: invalid value for build.arch: %s\n' %
+ settings.getstring('build', 'arch'))
+ sys.exit(1)
+
+ if settings.getboolean('goma', 'enabled'):
+ if settings.getint('xcode', 'jobs') < 0:
+ sys.stderr.write('ERROR: invalid value for xcode.jobs: %s\n' %
+ settings.get('xcode', 'jobs'))
+ sys.exit(1)
+ goma_install = os.path.expanduser(settings.getstring('goma', 'install'))
+ if not os.path.isdir(goma_install):
+ sys.stderr.write('WARNING: goma.install directory not found: %s\n' %
+ settings.get('goma', 'install'))
+ sys.stderr.write('WARNING: disabling goma\n')
+ settings.set('goma', 'enabled', 'false')
+
+ # Find gn binary in PATH.
+ gn_path = FindGn()
+ if gn_path is None:
+ sys.stderr.write('ERROR: cannot find gn in PATH\n')
+ sys.exit(1)
+
+ out_dir = os.path.join(args.root, 'out')
+ if not os.path.isdir(out_dir):
+ os.makedirs(out_dir)
+
+ GenerateXcodeProject(gn_path, args.root, out_dir, settings)
+ GenerateGnBuildRules(gn_path, args.root, out_dir, settings)
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/build/run_fuchsia_qemu.py b/build/run_fuchsia_qemu.py
index 135b314..aff0efd 100755
--- a/build/run_fuchsia_qemu.py
+++ b/build/run_fuchsia_qemu.py
@@ -13,7 +13,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used
for running tests without a device.
"""
@@ -30,105 +29,117 @@
import time
try:
- from subprocess import DEVNULL
+ from subprocess import DEVNULL
except ImportError:
- DEVNULL = open(os.devnull, 'r+b')
+ DEVNULL = open(os.devnull, 'r+b')
CRASHPAD_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
os.pardir)
def _Stop(pid_file):
- if os.path.isfile(pid_file):
- with open(pid_file, 'rb') as f:
- pid = int(f.read().strip())
- try:
- os.kill(pid, signal.SIGTERM)
- except:
- print('Unable to kill pid %d, continuing' % pid, file=sys.stderr)
- os.unlink(pid_file)
+ if os.path.isfile(pid_file):
+ with open(pid_file, 'rb') as f:
+ pid = int(f.read().strip())
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except:
+ print('Unable to kill pid %d, continuing' % pid, file=sys.stderr)
+ os.unlink(pid_file)
def _CheckForTun():
- """Check for networking. TODO(scottmg): Currently, this is Linux-specific.
- """
- returncode = subprocess.call(
- ['tunctl', '-b', '-u', getpass.getuser(), '-t', 'qemu'],
- stdout=DEVNULL, stderr=DEVNULL)
- if returncode != 0:
- print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
- file=sys.stderr)
- print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
- file=sys.stderr)
- return 2
- return 0
+ """Check for networking. TODO(scottmg): Currently, this is Linux-specific.
+ """
+ returncode = subprocess.call(
+ ['tunctl', '-b', '-u',
+ getpass.getuser(), '-t', 'qemu'],
+ stdout=DEVNULL,
+ stderr=DEVNULL)
+ if returncode != 0:
+ print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
+ file=sys.stderr)
+ print(
+ ' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
+ file=sys.stderr)
+ return 2
+ return 0
def _Start(pid_file):
- tun_result = _CheckForTun()
- if tun_result != 0:
- return tun_result
+ tun_result = _CheckForTun()
+ if tun_result != 0:
+ return tun_result
- arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
- fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia')
- qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin',
- 'qemu-system-x86_64')
- kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64')
- kernel_path = os.path.join(kernel_data_dir, 'zircon.bin')
- initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
+ arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
+ fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia')
+ qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin',
+ 'qemu-system-x86_64')
+ kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64')
+ kernel_path = os.path.join(kernel_data_dir, 'zircon.bin')
+ initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
- mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
- instance_name = 'crashpad_qemu_' + \
- ''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8))
+ mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
+ instance_name = (
+ 'crashpad_qemu_' +
+ ''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8)))
- # These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
- popen = subprocess.Popen([
- qemu_path,
- '-m', '2048',
- '-nographic',
- '-kernel', kernel_path,
- '-initrd', initrd_path,
- '-smp', '4',
- '-serial', 'stdio',
- '-monitor', 'none',
- '-machine', 'q35',
- '-cpu', 'host,migratable=no',
- '-enable-kvm',
- '-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
- '-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
- '-append', 'TERM=dumb zircon.nodename=' + instance_name,
- ], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
+ # These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
- with open(pid_file, 'wb') as f:
- f.write('%d\n' % popen.pid)
+ # yapf: disable
+ popen = subprocess.Popen([
+ qemu_path,
+ '-m', '2048',
+ '-nographic',
+ '-kernel', kernel_path,
+ '-initrd', initrd_path,
+ '-smp', '4',
+ '-serial', 'stdio',
+ '-monitor', 'none',
+ '-machine', 'q35',
+ '-cpu', 'host,migratable=no',
+ '-enable-kvm',
+ '-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
+ '-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
+ '-append', 'TERM=dumb zircon.nodename=' + instance_name,
+ ],
+ stdin=DEVNULL,
+ stdout=DEVNULL,
+ stderr=DEVNULL)
+ # yapf: enable
- for i in range(10):
- netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr')
- if subprocess.call([netaddr_path, '--nowait', instance_name],
- stdout=open(os.devnull), stderr=open(os.devnull)) == 0:
- break
- time.sleep(.5)
- else:
- print('instance did not respond after start', file=sys.stderr)
- return 1
+ with open(pid_file, 'wb') as f:
+ f.write('%d\n' % popen.pid)
- return 0
+ for i in range(10):
+ netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools',
+ 'netaddr')
+ if subprocess.call([netaddr_path, '--nowait', instance_name],
+ stdout=open(os.devnull),
+ stderr=open(os.devnull)) == 0:
+ break
+ time.sleep(.5)
+ else:
+ print('instance did not respond after start', file=sys.stderr)
+ return 1
+
+ return 0
def main(args):
- if len(args) != 1 or args[0] not in ('start', 'stop'):
- print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr)
- return 1
+ if len(args) != 1 or args[0] not in ('start', 'stop'):
+ print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr)
+ return 1
- command = args[0]
+ command = args[0]
- pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
- _Stop(pid_file)
- if command == 'start':
- return _Start(pid_file)
+ pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
+ _Stop(pid_file)
+ if command == 'start':
+ return _Start(pid_file)
- return 0
+ return 0
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/build/run_tests.py b/build/run_tests.py
index df7d8af..2f1919b 100755
--- a/build/run_tests.py
+++ b/build/run_tests.py
@@ -24,6 +24,7 @@
import re
import subprocess
import sys
+import tempfile
import uuid
CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
@@ -32,511 +33,603 @@
def _FindGNFromBinaryDir(binary_dir):
- """Attempts to determine the path to a GN binary used to generate the build
- files in the given binary_dir. This is necessary because `gn` might not be in
- the path or might be in a non-standard location, particularly on build
- machines."""
+ """Attempts to determine the path to a GN binary used to generate the build
+ files in the given binary_dir. This is necessary because `gn` might not be
+ in the path or might be in a non-standard location, particularly on build
+ machines."""
- build_ninja = os.path.join(binary_dir, 'build.ninja')
- if os.path.isfile(build_ninja):
- with open(build_ninja, 'rb') as f:
- # Look for the always-generated regeneration rule of the form:
- #
- # rule gn
- # command = <gn binary> ... arguments ...
- #
- # to extract the gn binary's full path.
- found_rule_gn = False
- for line in f:
- if line.strip() == 'rule gn':
- found_rule_gn = True
- continue
- if found_rule_gn:
- if len(line) == 0 or line[0] != ' ':
- return None
- if line.startswith(' command = '):
- gn_command_line_parts = line.strip().split(' ')
- if len(gn_command_line_parts) > 2:
- return os.path.join(binary_dir, gn_command_line_parts[2])
+ build_ninja = os.path.join(binary_dir, 'build.ninja')
+ if os.path.isfile(build_ninja):
+ with open(build_ninja, 'rb') as f:
+ # Look for the always-generated regeneration rule of the form:
+ #
+ # rule gn
+ # command = <gn binary> ... arguments ...
+ #
+ # to extract the gn binary's full path.
+ found_rule_gn = False
+ for line in f:
+ if line.strip() == 'rule gn':
+ found_rule_gn = True
+ continue
+ if found_rule_gn:
+ if len(line) == 0 or line[0] != ' ':
+ return None
+ if line.startswith(' command = '):
+ gn_command_line_parts = line.strip().split(' ')
+ if len(gn_command_line_parts) > 2:
+ return os.path.join(binary_dir,
+ gn_command_line_parts[2])
- return None
+ return None
def _BinaryDirTargetOS(binary_dir):
- """Returns the apparent target OS of binary_dir, or None if none appear to be
- explicitly specified."""
+ """Returns the apparent target OS of binary_dir, or None if none appear to
+ be explicitly specified."""
- gn_path = _FindGNFromBinaryDir(binary_dir)
+ gn_path = _FindGNFromBinaryDir(binary_dir)
- if gn_path:
- # Look for a GN “target_os”.
- popen = subprocess.Popen([gn_path, '--root=' + CRASHPAD_DIR,
- 'args', binary_dir,
- '--list=target_os', '--short'],
- shell=IS_WINDOWS_HOST,
- stdout=subprocess.PIPE, stderr=open(os.devnull))
- value = popen.communicate()[0]
- if popen.returncode == 0:
- match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
- if match:
- return match.group(1)
+ if gn_path:
+ # Look for a GN “target_os”.
+ popen = subprocess.Popen([
+ gn_path, '--root=' + CRASHPAD_DIR, 'args', binary_dir,
+ '--list=target_os', '--short'
+ ],
+ shell=IS_WINDOWS_HOST,
+ stdout=subprocess.PIPE,
+ stderr=open(os.devnull))
+ value = popen.communicate()[0]
+ if popen.returncode == 0:
+ match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
+ if match:
+ return match.group(1)
- # For GYP with Ninja, look for the appearance of “linux-android” in the path
- # to ar. This path is configured by gyp_crashpad_android.py.
- build_ninja_path = os.path.join(binary_dir, 'build.ninja')
- if os.path.exists(build_ninja_path):
- with open(build_ninja_path) as build_ninja_file:
- build_ninja_content = build_ninja_file.read()
- match = re.search('^ar = .+-linux-android(eabi)?-ar$',
- build_ninja_content,
- re.MULTILINE)
- if match:
- return 'android'
+ # For GYP with Ninja, look for the appearance of “linux-android” in the path
+ # to ar. This path is configured by gyp_crashpad_android.py.
+ build_ninja_path = os.path.join(binary_dir, 'build.ninja')
+ if os.path.exists(build_ninja_path):
+ with open(build_ninja_path) as build_ninja_file:
+ build_ninja_content = build_ninja_file.read()
+ match = re.search('-linux-android(eabi)?-ar$', build_ninja_content,
+ re.MULTILINE)
+ if match:
+ return 'android'
- return None
+ return None
def _EnableVTProcessingOnWindowsConsole():
- """Enables virtual terminal processing for ANSI/VT100-style escape sequences
- on a Windows console attached to standard output. Returns True on success.
- Returns False if standard output is not a console or if virtual terminal
- processing is not supported. The feature was introduced in Windows 10.
- """
+ """Enables virtual terminal processing for ANSI/VT100-style escape sequences
+ on a Windows console attached to standard output. Returns True on success.
+ Returns False if standard output is not a console or if virtual terminal
+ processing is not supported. The feature was introduced in Windows 10.
+ """
- import pywintypes
- import win32console
- import winerror
+ import pywintypes
+ import win32console
+ import winerror
- stdout_console = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)
- try:
- console_mode = stdout_console.GetConsoleMode()
- except pywintypes.error as e:
- if e.winerror == winerror.ERROR_INVALID_HANDLE:
- # Standard output is not a console.
- return False
- raise
+ stdout_console = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)
+ try:
+ console_mode = stdout_console.GetConsoleMode()
+ except pywintypes.error as e:
+ if e.winerror == winerror.ERROR_INVALID_HANDLE:
+ # Standard output is not a console.
+ return False
+ raise
- try:
- # From <wincon.h>. This would be
- # win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to be
- # defined there.
- ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
+ try:
+ # From <wincon.h>. This would be
+ # win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to
+ # be defined there.
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
- stdout_console.SetConsoleMode(console_mode |
- ENABLE_VIRTUAL_TERMINAL_PROCESSING)
- except pywintypes.error as e:
- if e.winerror == winerror.ERROR_INVALID_PARAMETER:
- # ANSI/VT100-style escape sequence processing isn’t supported before
- # Windows 10.
- return False
- raise
+ stdout_console.SetConsoleMode(console_mode |
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING)
+ except pywintypes.error as e:
+ if e.winerror == winerror.ERROR_INVALID_PARAMETER:
+ # ANSI/VT100-style escape sequence processing isn’t supported before
+ # Windows 10.
+ return False
+ raise
- return True
+ return True
def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
- local_test_path = os.path.join(binary_dir, test)
- MAYBE_UNSUPPORTED_TESTS = (
- 'crashpad_client_test',
- 'crashpad_handler_test',
- 'crashpad_minidump_test',
- 'crashpad_snapshot_test',
- )
- if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS:
- print('This test is not present and may not be supported, skipping')
- return
+ local_test_path = os.path.join(binary_dir, test)
+ MAYBE_UNSUPPORTED_TESTS = (
+ 'crashpad_client_test',
+ 'crashpad_handler_test',
+ 'crashpad_minidump_test',
+ 'crashpad_snapshot_test',
+ )
+ if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS:
+ print('This test is not present and may not be supported, skipping')
+ return
- def _adb(*args):
- # Flush all of this script’s own buffered stdout output before running adb,
- # which will likely produce its own output on stdout.
- sys.stdout.flush()
+ def _adb(*args):
+ # Flush all of this script’s own buffered stdout output before running
+ # adb, which will likely produce its own output on stdout.
+ sys.stdout.flush()
- adb_command = ['adb', '-s', android_device]
- adb_command.extend(args)
- subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST)
+ adb_command = ['adb', '-s', android_device]
+ adb_command.extend(args)
+ subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST)
- def _adb_push(sources, destination):
- args = list(sources)
- args.append(destination)
- _adb('push', *args)
+ def _adb_push(sources, destination):
+ args = list(sources)
+ args.append(destination)
+ _adb('push', *args)
- def _adb_shell(command_args, env={}):
- # Build a command to execute via “sh -c” instead of invoking it directly.
- # Here’s why:
- #
- # /system/bin/env isn’t normally present prior to Android 6.0 (M), where
- # toybox was introduced (Android platform/manifest 9a2c01e8450b). Instead,
- # set environment variables by using the shell’s internal “export” command.
- #
- # adbd prior to Android 7.0 (N), and the adb client prior to SDK
- # platform-tools version 24, don’t know how to communicate a shell command’s
- # exit status. This was added in Android platform/system/core 606835ae5c4b).
- # With older adb servers and clients, adb will “exit 0” indicating success
- # even if the command failed on the device. This makes
- # subprocess.check_call() semantics difficult to implement directly. As a
- # workaround, have the device send the command’s exit status over stdout and
- # pick it back up in this function.
- #
- # Both workarounds are implemented by giving the device a simple script,
- # which adbd will run as an “sh -c” argument.
- adb_command = ['adb', '-s', android_device, 'shell']
- script_commands = []
- for k, v in env.items():
- script_commands.append('export %s=%s' % (pipes.quote(k), pipes.quote(v)))
- script_commands.extend([
- ' '.join(pipes.quote(x) for x in command_args),
- 'status=${?}',
- 'echo "status=${status}"',
- 'exit ${status}'])
- adb_command.append('; '.join(script_commands))
- child = subprocess.Popen(adb_command,
- shell=IS_WINDOWS_HOST,
- stdin=open(os.devnull),
- stdout=subprocess.PIPE)
+ def _adb_shell(command_args, env={}):
+ # Build a command to execute via “sh -c” instead of invoking it
+ # directly. Here’s why:
+ #
+ # /system/bin/env isn’t normally present prior to Android 6.0 (M), where
+ # toybox was introduced (Android platform/manifest 9a2c01e8450b).
+ # Instead, set environment variables by using the shell’s internal
+ # “export” command.
+ #
+ # adbd prior to Android 7.0 (N), and the adb client prior to SDK
+ # platform-tools version 24, don’t know how to communicate a shell
+ # command’s exit status. This was added in Android platform/system/core
+ # 606835ae5c4b). With older adb servers and clients, adb will “exit 0”
+ # indicating success even if the command failed on the device. This
+ # makes subprocess.check_call() semantics difficult to implement
+ # directly. As a workaround, have the device send the command’s exit
+ # status over stdout and pick it back up in this function.
+ #
+ # Both workarounds are implemented by giving the device a simple script,
+ # which adbd will run as an “sh -c” argument.
+ adb_command = ['adb', '-s', android_device, 'shell']
+ script_commands = []
+ for k, v in env.items():
+ script_commands.append('export %s=%s' %
+ (pipes.quote(k), pipes.quote(v)))
+ script_commands.extend([
+ ' '.join(pipes.quote(x) for x in command_args), 'status=${?}',
+ 'echo "status=${status}"', 'exit ${status}'
+ ])
+ adb_command.append('; '.join(script_commands))
+ child = subprocess.Popen(adb_command,
+ shell=IS_WINDOWS_HOST,
+ stdin=open(os.devnull),
+ stdout=subprocess.PIPE)
- FINAL_LINE_RE = re.compile('status=(\d+)$')
- final_line = None
- while True:
- # Use readline so that the test output appears “live” when running.
- data = child.stdout.readline().decode('utf-8')
- if data == '':
- break
- if final_line is not None:
- # It wasn’t really the final line.
- print(final_line, end='')
+ FINAL_LINE_RE = re.compile('status=(\d+)$')
final_line = None
- if FINAL_LINE_RE.match(data.rstrip()):
- final_line = data
- else:
- print(data, end='')
+ while True:
+ # Use readline so that the test output appears “live” when running.
+ data = child.stdout.readline().decode('utf-8')
+ if data == '':
+ break
+ if final_line is not None:
+ # It wasn’t really the final line.
+ print(final_line, end='')
+ final_line = None
+ if FINAL_LINE_RE.match(data.rstrip()):
+ final_line = data
+ else:
+ print(data, end='')
- if final_line is None:
- # Maybe there was some stderr output after the end of stdout. Old versions
- # of adb, prior to when the exit status could be communicated, smush the
- # two together.
- raise subprocess.CalledProcessError(-1, adb_command)
- status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1))
- if status != 0:
- raise subprocess.CalledProcessError(status, adb_command)
+ if final_line is None:
+ # Maybe there was some stderr output after the end of stdout. Old
+ # versions of adb, prior to when the exit status could be
+ # communicated, smush the two together.
+ raise subprocess.CalledProcessError(-1, adb_command)
+ status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1))
+ if status != 0:
+ raise subprocess.CalledProcessError(status, adb_command)
- child.wait()
- if child.returncode != 0:
- raise subprocess.CalledProcessError(subprocess.returncode, adb_command)
+ child.wait()
+ if child.returncode != 0:
+ raise subprocess.CalledProcessError(subprocess.returncode,
+ adb_command)
- # /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where
- # toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it with
- # a host-generated name. This won’t retry if the name is in use, but with 122
- # bits of randomness, it should be OK. This uses “mkdir” instead of “mkdir -p”
- # because the latter will not indicate failure if the directory already
- # exists.
- device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex)
- _adb_shell(['mkdir', device_temp_dir])
+ # /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where
+ # toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it
+ # with a host-generated name. This won’t retry if the name is in use, but
+ # with 122 bits of randomness, it should be OK. This uses “mkdir” instead of
+ # “mkdir -p”because the latter will not indicate failure if the directory
+ # already exists.
+ device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex)
+ _adb_shell(['mkdir', device_temp_dir])
- try:
- # Specify test dependencies that must be pushed to the device. This could be
- # determined automatically in a GN build, following the example used for
- # Fuchsia. Since nothing like that exists for GYP, hard-code it for
- # supported tests.
- test_build_artifacts = [test]
- test_data = ['test/test_paths_test_data_root.txt']
+ try:
+ # Specify test dependencies that must be pushed to the device. This
+ # could be determined automatically in a GN build, following the example
+ # used for Fuchsia. Since nothing like that exists for GYP, hard-code it
+ # for supported tests.
+ test_build_artifacts = [test, 'crashpad_handler']
+ test_data = ['test/test_paths_test_data_root.txt']
- if test == 'crashpad_test_test':
- test_build_artifacts.append(
- 'crashpad_test_test_multiprocess_exec_test_child')
- elif test == 'crashpad_util_test':
- test_data.append('util/net/testdata/')
+ if test == 'crashpad_test_test':
+ test_build_artifacts.append(
+ 'crashpad_test_test_multiprocess_exec_test_child')
+ elif test == 'crashpad_util_test':
+ test_data.append('util/net/testdata/')
- # Establish the directory structure on the device.
- device_out_dir = posixpath.join(device_temp_dir, 'out')
- device_mkdirs = [device_out_dir]
- for source_path in test_data:
- # A trailing slash could reasonably mean to copy an entire directory, but
- # will interfere with what’s needed from the path split. All parent
- # directories of any source_path need to be be represented in
- # device_mkdirs, but it’s important that no source_path itself wind up in
- # device_mkdirs, even if source_path names a directory, because that would
- # cause the “adb push” of the directory below to behave incorrectly.
- if source_path.endswith(posixpath.sep):
- source_path = source_path[:-1]
+ # Establish the directory structure on the device.
+ device_out_dir = posixpath.join(device_temp_dir, 'out')
+ device_mkdirs = [device_out_dir]
+ for source_path in test_data:
+ # A trailing slash could reasonably mean to copy an entire
+ # directory, but will interfere with what’s needed from the path
+ # split. All parent directories of any source_path need to be be
+ # represented in device_mkdirs, but it’s important that no
+ # source_path itself wind up in device_mkdirs, even if source_path
+ # names a directory, because that would cause the “adb push” of the
+ # directory below to behave incorrectly.
+ if source_path.endswith(posixpath.sep):
+ source_path = source_path[:-1]
- device_source_path = posixpath.join(device_temp_dir, source_path)
- device_mkdir = posixpath.split(device_source_path)[0]
- if device_mkdir not in device_mkdirs:
- device_mkdirs.append(device_mkdir)
- adb_mkdir_command = ['mkdir', '-p']
- adb_mkdir_command.extend(device_mkdirs)
- _adb_shell(adb_mkdir_command)
+ device_source_path = posixpath.join(device_temp_dir, source_path)
+ device_mkdir = posixpath.split(device_source_path)[0]
+ if device_mkdir not in device_mkdirs:
+ device_mkdirs.append(device_mkdir)
+ adb_mkdir_command = ['mkdir', '-p']
+ adb_mkdir_command.extend(device_mkdirs)
+ _adb_shell(adb_mkdir_command)
- # Push the test binary and any other build output to the device.
- local_test_build_artifacts = []
- for artifact in test_build_artifacts:
- local_test_build_artifacts.append(os.path.join(binary_dir, artifact))
- _adb_push(local_test_build_artifacts, device_out_dir)
+ # Push the test binary and any other build output to the device.
+ local_test_build_artifacts = []
+ for artifact in test_build_artifacts:
+ local_test_build_artifacts.append(os.path.join(
+ binary_dir, artifact))
+ _adb_push(local_test_build_artifacts, device_out_dir)
- # Push test data to the device.
- for source_path in test_data:
- _adb_push([os.path.join(CRASHPAD_DIR, source_path)],
- posixpath.join(device_temp_dir, source_path))
+ # Push test data to the device.
+ for source_path in test_data:
+ _adb_push([os.path.join(CRASHPAD_DIR, source_path)],
+ posixpath.join(device_temp_dir, source_path))
- # Run the test on the device. Pass the test data root in the environment.
- #
- # Because the test will not run with its standard output attached to a
- # pseudo-terminal device, gtest will not normally enable colored output, so
- # mimic gtest’s own logic for deciding whether to enable color by checking
- # this script’s own standard output connection. The whitelist of TERM values
- # comes from gtest googletest/src/gtest.cc
- # testing::internal::ShouldUseColor().
- env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir}
- gtest_color = os.environ.get('GTEST_COLOR')
- if gtest_color in ('auto', None):
- if (sys.stdout.isatty() and
- (os.environ.get('TERM') in
- ('xterm', 'xterm-color', 'xterm-256color', 'screen',
- 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode',
- 'rxvt-unicode-256color', 'linux', 'cygwin') or
- (IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))):
- gtest_color = 'yes'
- else:
- gtest_color = 'no'
- env['GTEST_COLOR'] = gtest_color
- _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, env)
- finally:
- _adb_shell(['rm', '-rf', device_temp_dir])
+ # Run the test on the device. Pass the test data root in the
+ # environment.
+ #
+ # Because the test will not run with its standard output attached to a
+ # pseudo-terminal device, gtest will not normally enable colored output,
+ # so mimic gtest’s own logic for deciding whether to enable color by
+ # checking this script’s own standard output connection. The whitelist
+ # of TERM values comes from gtest googletest/src/gtest.cc
+ # testing::internal::ShouldUseColor().
+ env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir}
+ gtest_color = os.environ.get('GTEST_COLOR')
+ if gtest_color in ('auto', None):
+ if (sys.stdout.isatty() and
+ (os.environ.get('TERM')
+ in ('xterm', 'xterm-color', 'xterm-256color', 'screen',
+ 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode',
+ 'rxvt-unicode-256color', 'linux', 'cygwin') or
+ (IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))):
+ gtest_color = 'yes'
+ else:
+ gtest_color = 'no'
+ env['GTEST_COLOR'] = gtest_color
+ _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line,
+ env)
+ finally:
+ _adb_shell(['rm', '-rf', device_temp_dir])
def _GetFuchsiaSDKRoot():
- arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
- return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch)
+ arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
+ return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch)
def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests):
- """Ensures a <binary_dir>/<test>.runtime_deps file exists for each test."""
- targets_file = os.path.join(binary_dir, 'targets.txt')
- with open(targets_file, 'wb') as f:
- f.write('//:' + '\n//:'.join(tests) + '\n')
- gn_path = _FindGNFromBinaryDir(binary_dir)
- subprocess.check_call(
- [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir,
- '--runtime-deps-list-file=' + targets_file])
+ """Ensures a <binary_dir>/<test>.runtime_deps file exists for each test."""
+ targets_file = os.path.join(binary_dir, 'targets.txt')
+ with open(targets_file, 'wb') as f:
+ f.write('//:' + '\n//:'.join(tests) + '\n')
+ gn_path = _FindGNFromBinaryDir(binary_dir)
+ subprocess.check_call([
+ gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir,
+ '--runtime-deps-list-file=' + targets_file
+ ])
- # Run again so that --runtime-deps-list-file isn't in the regen rule. See
- # https://crbug.com/814816.
- subprocess.check_call(
- [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir])
+ # Run again so that --runtime-deps-list-file isn't in the regen rule. See
+ # https://crbug.com/814816.
+ subprocess.check_call(
+ [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir])
def _HandleOutputFromFuchsiaLogListener(process, done_message):
- """Pass through the output from |process| (which should be an instance of
- Fuchsia's loglistener) until a special termination |done_message| is
- encountered.
+ """Pass through the output from |process| (which should be an instance of
+ Fuchsia's loglistener) until a special termination |done_message| is
+ encountered.
- Also attempts to determine if any tests failed by inspecting the log output,
- and returns False if there were failures.
- """
- success = True
- while True:
- line = process.stdout.readline().rstrip()
- if 'FAILED TEST' in line:
- success = False
- elif done_message in line and 'echo ' not in line:
- break
- print(line)
- return success
+ Also attempts to determine if any tests failed by inspecting the log output,
+ and returns False if there were failures.
+ """
+ success = True
+ while True:
+ line = process.stdout.readline().rstrip()
+ if 'FAILED TEST' in line:
+ success = False
+ elif done_message in line and 'echo ' not in line:
+ break
+ print(line)
+ return success
def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
- """Runs the given Fuchsia |test| executable on the given |device_name|. The
- device must already be booted.
+ """Runs the given Fuchsia |test| executable on the given |device_name|. The
+ device must already be booted.
- Copies the executable and its runtime dependencies as specified by GN to the
- target in /tmp using `netcp`, runs the binary on the target, and logs output
- back to stdout on this machine via `loglistener`.
- """
- sdk_root = _GetFuchsiaSDKRoot()
-
- # Run loglistener and filter the output to know when the test is done.
- loglistener_process = subprocess.Popen(
- [os.path.join(sdk_root, 'tools', 'loglistener'), device_name],
- stdout=subprocess.PIPE, stdin=open(os.devnull), stderr=open(os.devnull))
-
- runtime_deps_file = os.path.join(binary_dir, test + '.runtime_deps')
- with open(runtime_deps_file, 'rb') as f:
- runtime_deps = f.read().splitlines()
-
- def netruncmd(*args):
- """Runs a list of commands on the target device. Each command is escaped
- by using pipes.quote(), and then each command is chained by shell ';'.
+ Copies the executable and its runtime dependencies as specified by GN to the
+ target in /tmp using `netcp`, runs the binary on the target, and logs output
+ back to stdout on this machine via `loglistener`.
"""
- netruncmd_path = os.path.join(sdk_root, 'tools', 'netruncmd')
- final_args = ' ; '.join(' '.join(pipes.quote(x) for x in command)
- for command in args)
- subprocess.check_call([netruncmd_path, device_name, final_args])
+ sdk_root = _GetFuchsiaSDKRoot()
- try:
- unique_id = uuid.uuid4().hex
- test_root = '/tmp/%s_%s' % (test, unique_id)
- tmp_root = test_root + '/tmp'
- staging_root = test_root + '/pkg'
+ # Run loglistener and filter the output to know when the test is done.
+ loglistener_process = subprocess.Popen(
+ [os.path.join(sdk_root, 'tools', 'loglistener'), device_name],
+ stdout=subprocess.PIPE,
+ stdin=open(os.devnull),
+ stderr=open(os.devnull))
- # Make a staging directory tree on the target.
- directories_to_create = [tmp_root,
- '%s/bin' % staging_root,
- '%s/assets' % staging_root]
- netruncmd(['mkdir', '-p'] + directories_to_create)
+ runtime_deps_file = os.path.join(binary_dir, test + '.runtime_deps')
+ with open(runtime_deps_file, 'rb') as f:
+ runtime_deps = f.read().splitlines()
- def netcp(local_path):
- """Uses `netcp` to copy a file or directory to the device. Files located
- inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets.
- .so files are stored somewhere completely different, into /boot/lib (!).
- This is because the loader service does not yet correctly handle the
- namespace in which the caller is being run, and so can only load .so files
- from a couple hardcoded locations, the only writable one of which is
- /boot/lib, so we copy all .so files there. This bug is filed upstream as
- ZX-1619.
- """
- in_binary_dir = local_path.startswith(binary_dir + '/')
- if in_binary_dir:
- if local_path.endswith('.so'):
- target_path = os.path.join(
- '/boot/lib', local_path[len(binary_dir)+1:])
+ def netruncmd(*args):
+ """Runs a list of commands on the target device. Each command is escaped
+ by using pipes.quote(), and then each command is chained by shell ';'.
+ """
+ netruncmd_path = os.path.join(sdk_root, 'tools', 'netruncmd')
+ final_args = ' ; '.join(
+ ' '.join(pipes.quote(x) for x in command) for command in args)
+ subprocess.check_call([netruncmd_path, device_name, final_args])
+
+ try:
+ unique_id = uuid.uuid4().hex
+ test_root = '/tmp/%s_%s' % (test, unique_id)
+ tmp_root = test_root + '/tmp'
+ staging_root = test_root + '/pkg'
+
+ # Make a staging directory tree on the target.
+ directories_to_create = [
+ tmp_root,
+ '%s/bin' % staging_root,
+ '%s/assets' % staging_root
+ ]
+ netruncmd(['mkdir', '-p'] + directories_to_create)
+
+ def netcp(local_path):
+ """Uses `netcp` to copy a file or directory to the device. Files
+ located inside the build dir are stored to /pkg/bin, otherwise to
+ /pkg/assets. .so files are stored somewhere completely different,
+ into /boot/lib (!). This is because the loader service does not yet
+ correctly handle the namespace in which the caller is being run, and
+ so can only load .so files from a couple hardcoded locations, the
+ only writable one of which is /boot/lib, so we copy all .so files
+ there. This bug is filed upstream as ZX-1619.
+ """
+ in_binary_dir = local_path.startswith(binary_dir + '/')
+ if in_binary_dir:
+ if local_path.endswith('.so'):
+ target_path = os.path.join('/boot/lib',
+ local_path[len(binary_dir) + 1:])
+ else:
+ target_path = os.path.join(staging_root, 'bin',
+ local_path[len(binary_dir) + 1:])
+ else:
+ relative_path = os.path.relpath(local_path, CRASHPAD_DIR)
+ target_path = os.path.join(staging_root, 'assets',
+ relative_path)
+ netcp_path = os.path.join(sdk_root, 'tools', 'netcp')
+ subprocess.check_call(
+ [netcp_path, local_path, device_name + ':' + target_path],
+ stderr=open(os.devnull))
+
+ # Copy runtime deps into the staging tree.
+ for dep in runtime_deps:
+ local_path = os.path.normpath(os.path.join(binary_dir, dep))
+ if os.path.isdir(local_path):
+ for root, dirs, files in os.walk(local_path):
+ for f in files:
+ netcp(os.path.join(root, f))
+ else:
+ netcp(local_path)
+
+ done_message = 'TERMINATED: ' + unique_id
+ namespace_command = [
+ 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root,
+ '/svc=/svc', '--replace-child-argv0=/pkg/bin/' + test, '--',
+ staging_root + '/bin/' + test
+ ] + extra_command_line
+ netruncmd(namespace_command, ['echo', done_message])
+
+ success = _HandleOutputFromFuchsiaLogListener(loglistener_process,
+ done_message)
+ if not success:
+ raise subprocess.CalledProcessError(1, test)
+ finally:
+ netruncmd(['rm', '-rf', test_root])
+
+
+def _RunOnIOSTarget(binary_dir, test, is_xcuitest=False):
+ """Runs the given iOS |test| app on iPhone 8 with the default OS version."""
+
+ def xctest(binary_dir, test):
+ """Returns a dict containing the xctestrun data needed to run an
+ XCTest-based test app."""
+ test_path = os.path.join(CRASHPAD_DIR, binary_dir)
+ module_data = {
+ 'TestBundlePath': os.path.join(test_path, test + '_module.xctest'),
+ 'TestHostPath': os.path.join(test_path, test + '.app'),
+ 'TestingEnvironmentVariables': {
+ 'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:',
+ 'DYLD_INSERT_LIBRARIES':
+ ('__PLATFORMS__/iPhoneSimulator.platform/Developer/'
+ 'usr/lib/libXCTestBundleInject.dylib'),
+ 'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator',
+ 'IDEiPhoneInternalTestBundleName': test + '.app',
+ 'XCInjectBundleInto': '__TESTHOST__/' + test,
+ }
+ }
+ return {test: module_data}
+
+ def xcuitest(binary_dir, test):
+ """Returns a dict containing the xctestrun data needed to run an
+ XCUITest-based test app."""
+
+ test_path = os.path.join(CRASHPAD_DIR, binary_dir)
+ runner_path = os.path.join(test_path, test + '_module-Runner.app')
+ bundle_path = os.path.join(runner_path, 'PlugIns',
+ test + '_module.xctest')
+ target_app_path = os.path.join(test_path, test + '.app')
+ module_data = {
+ 'IsUITestBundle': True,
+ 'IsXCTRunnerHostedTestBundle': True,
+ 'TestBundlePath': bundle_path,
+ 'TestHostPath': runner_path,
+ 'UITargetAppPath': target_app_path,
+ 'DependentProductPaths': [
+ bundle_path, runner_path, target_app_path
+ ],
+ 'TestingEnvironmentVariables': {
+ 'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:',
+ 'DYLD_INSERT_LIBRARIES':
+ ('__PLATFORMS__/iPhoneSimulator.platform/Developer/'
+ 'usr/lib/libXCTestBundleInject.dylib'),
+ 'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator',
+ 'XCInjectBundleInto': '__TESTHOST__/' + test + '_module-Runner',
+ },
+ }
+ return {test: module_data}
+
+ with tempfile.NamedTemporaryFile() as f:
+ import plistlib
+
+ xctestrun_path = f.name
+ print(xctestrun_path)
+ if is_xcuitest:
+ plistlib.writePlist(xcuitest(binary_dir, test), xctestrun_path)
else:
- target_path = os.path.join(
- staging_root, 'bin', local_path[len(binary_dir)+1:])
- else:
- relative_path = os.path.relpath(local_path, CRASHPAD_DIR)
- target_path = os.path.join(staging_root, 'assets', relative_path)
- netcp_path = os.path.join(sdk_root, 'tools', 'netcp')
- subprocess.check_call([netcp_path, local_path,
- device_name + ':' + target_path],
- stderr=open(os.devnull))
+ plistlib.writePlist(xctest(binary_dir, test), xctestrun_path)
- # Copy runtime deps into the staging tree.
- for dep in runtime_deps:
- local_path = os.path.normpath(os.path.join(binary_dir, dep))
- if os.path.isdir(local_path):
- for root, dirs, files in os.walk(local_path):
- for f in files:
- netcp(os.path.join(root, f))
- else:
- netcp(local_path)
-
- done_message = 'TERMINATED: ' + unique_id
- namespace_command = [
- 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '/svc=/svc',
- '--replace-child-argv0=/pkg/bin/' + test, '--',
- staging_root + '/bin/' + test] + extra_command_line
- netruncmd(namespace_command, ['echo', done_message])
-
- success = _HandleOutputFromFuchsiaLogListener(
- loglistener_process, done_message)
- if not success:
- raise subprocess.CalledProcessError(1, test)
- finally:
- netruncmd(['rm', '-rf', test_root])
+ subprocess.check_call([
+ 'xcodebuild', 'test-without-building', '-xctestrun', xctestrun_path,
+ '-destination', 'platform=iOS Simulator,name=iPhone 8'
+ ])
# This script is primarily used from the waterfall so that the list of tests
# that are run is maintained in-tree, rather than in a separate infrastructure
# location in the recipe.
def main(args):
- parser = argparse.ArgumentParser(description='Run Crashpad unittests.')
- parser.add_argument('binary_dir', help='Root of build dir')
- parser.add_argument('test', nargs='*', help='Specific test(s) to run.')
- parser.add_argument('--gtest_filter',
- help='GTest filter applied to GTest binary runs.')
- args = parser.parse_args()
+ parser = argparse.ArgumentParser(description='Run Crashpad unittests.')
+ parser.add_argument('binary_dir', help='Root of build dir')
+ parser.add_argument('test', nargs='*', help='Specific test(s) to run.')
+ parser.add_argument('--gtest_filter',
+ help='GTest filter applied to GTest binary runs.')
+ args = parser.parse_args()
- # Tell 64-bit Windows tests where to find 32-bit test executables, for
- # cross-bitted testing. This relies on the fact that the GYP build by default
- # uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64 for the
- # 64-bit build. This is not a universally valid assumption, and if it’s not
- # met, 64-bit tests that require 32-bit build output will disable themselves
- # dynamically.
- if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and
- 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ):
- binary_dir_32 = args.binary_dir[:-4]
- if os.path.isdir(binary_dir_32):
- os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32
+ # Tell 64-bit Windows tests where to find 32-bit test executables, for
+ # cross-bitted testing. This relies on the fact that the GYP build by
+ # default uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64
+ # for the 64-bit build. This is not a universally valid assumption, and if
+ # it’s not met, 64-bit tests that require 32-bit build output will disable
+ # themselves dynamically.
+ if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and
+ 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ):
+ binary_dir_32 = args.binary_dir[:-4]
+ if os.path.isdir(binary_dir_32):
+ os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32
- target_os = _BinaryDirTargetOS(args.binary_dir)
- is_android = target_os == 'android'
- is_fuchsia = target_os == 'fuchsia'
+ target_os = _BinaryDirTargetOS(args.binary_dir)
+ is_android = target_os == 'android'
+ is_fuchsia = target_os == 'fuchsia'
+ is_ios = target_os == 'ios'
- tests = [
- 'crashpad_client_test',
- 'crashpad_handler_test',
- 'crashpad_minidump_test',
- 'crashpad_snapshot_test',
- 'crashpad_test_test',
- 'crashpad_util_test',
- ]
+ tests = [
+ 'crashpad_client_test',
+ 'crashpad_handler_test',
+ 'crashpad_minidump_test',
+ 'crashpad_snapshot_test',
+ 'crashpad_test_test',
+ 'crashpad_util_test',
+ ]
- if is_android:
- android_device = os.environ.get('ANDROID_DEVICE')
- if not android_device:
- adb_devices = subprocess.check_output(['adb', 'devices'],
- shell=IS_WINDOWS_HOST)
- devices = []
- for line in adb_devices.splitlines():
- line = line.decode('utf-8')
- if (line == 'List of devices attached' or
- re.match('^\* daemon .+ \*$', line) or
- line == ''):
- continue
- (device, ignore) = line.split('\t')
- devices.append(device)
- if len(devices) != 1:
- print("Please set ANDROID_DEVICE to your device's id", file=sys.stderr)
- return 2
- android_device = devices[0]
- print('Using autodetected Android device:', android_device)
- elif is_fuchsia:
- zircon_nodename = os.environ.get('ZIRCON_NODENAME')
- if not zircon_nodename:
- netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls')
- popen = subprocess.Popen([netls, '--nowait'], stdout=subprocess.PIPE)
- devices = popen.communicate()[0].splitlines()
- if popen.returncode != 0 or len(devices) != 1:
- print("Please set ZIRCON_NODENAME to your device's hostname",
- file=sys.stderr)
- return 2
- zircon_nodename = devices[0].strip().split()[1]
- print('Using autodetected Fuchsia device:', zircon_nodename)
- _GenerateFuchsiaRuntimeDepsFiles(
- args.binary_dir, [t for t in tests if not t.endswith('.py')])
- elif IS_WINDOWS_HOST:
- tests.append('snapshot/win/end_to_end_test.py')
+ if is_android:
+ android_device = os.environ.get('ANDROID_DEVICE')
+ if not android_device:
+ adb_devices = subprocess.check_output(['adb', 'devices'],
+ shell=IS_WINDOWS_HOST)
+ devices = []
+ for line in adb_devices.splitlines():
+ line = line.decode('utf-8')
+ if (line == 'List of devices attached' or
+ re.match('^\* daemon .+ \*$', line) or line == ''):
+ continue
+ (device, ignore) = line.split('\t')
+ devices.append(device)
+ if len(devices) != 1:
+ print("Please set ANDROID_DEVICE to your device's id",
+ file=sys.stderr)
+ return 2
+ android_device = devices[0]
+ print('Using autodetected Android device:', android_device)
+ elif is_fuchsia:
+ zircon_nodename = os.environ.get('ZIRCON_NODENAME')
+ if not zircon_nodename:
+ netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls')
+ popen = subprocess.Popen([netls, '--nowait'],
+ stdout=subprocess.PIPE)
+ devices = popen.communicate()[0].splitlines()
+ if popen.returncode != 0 or len(devices) != 1:
+ print("Please set ZIRCON_NODENAME to your device's hostname",
+ file=sys.stderr)
+ return 2
+ zircon_nodename = devices[0].strip().split()[1]
+ print('Using autodetected Fuchsia device:', zircon_nodename)
+ _GenerateFuchsiaRuntimeDepsFiles(
+ args.binary_dir, [t for t in tests if not t.endswith('.py')])
+ elif is_ios:
+ tests.append('ios_crash_xcuitests')
+ elif IS_WINDOWS_HOST:
+ tests.append('snapshot/win/end_to_end_test.py')
- if args.test:
- for t in args.test:
- if t not in tests:
- print('Unrecognized test:', t, file=sys.stderr)
- return 3
- tests = args.test
+ if args.test:
+ for t in args.test:
+ if t not in tests:
+ print('Unrecognized test:', t, file=sys.stderr)
+ return 3
+ tests = args.test
- for test in tests:
- print('-' * 80)
- print(test)
- print('-' * 80)
- if test.endswith('.py'):
- subprocess.check_call(
- [sys.executable, os.path.join(CRASHPAD_DIR, test), args.binary_dir])
- else:
- extra_command_line = []
- if args.gtest_filter:
- extra_command_line.append('--gtest_filter=' + args.gtest_filter)
- if is_android:
- _RunOnAndroidTarget(args.binary_dir, test, android_device,
- extra_command_line)
- elif is_fuchsia:
- _RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename,
- extra_command_line)
- else:
- subprocess.check_call([os.path.join(args.binary_dir, test)] +
- extra_command_line)
+ for test in tests:
+ print('-' * 80)
+ print(test)
+ print('-' * 80)
+ if test.endswith('.py'):
+ subprocess.check_call([
+ sys.executable,
+ os.path.join(CRASHPAD_DIR, test), args.binary_dir
+ ])
+ else:
+ extra_command_line = []
+ if args.gtest_filter:
+ extra_command_line.append('--gtest_filter=' + args.gtest_filter)
+ if is_android:
+ _RunOnAndroidTarget(args.binary_dir, test, android_device,
+ extra_command_line)
+ elif is_fuchsia:
+ _RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename,
+ extra_command_line)
+ elif is_ios:
+ _RunOnIOSTarget(args.binary_dir,
+ test,
+ is_xcuitest=test.startswith('ios'))
+ else:
+ subprocess.check_call([os.path.join(args.binary_dir, test)] +
+ extra_command_line)
- return 0
+ return 0
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/build/test.gni b/build/test.gni
index f46520b..01fd7e7 100644
--- a/build/test.gni
+++ b/build/test.gni
@@ -18,9 +18,32 @@
import("//testing/test.gni")
} else {
template("test") {
- executable(target_name) {
- testonly = true
- forward_variables_from(invoker, "*")
+ if (crashpad_is_ios) {
+ import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni")
+
+ _launch_image_bundle_target = target_name + "_launch_image"
+ bundle_data(_launch_image_bundle_target) {
+ forward_variables_from(invoker, [ "testonly" ])
+ sources = [ "//build/ios/Default.png" ]
+ outputs = [ "{{bundle_contents_dir}}/{{source_file_part}}" ]
+ }
+
+ ios_xctest_test(target_name) {
+ testonly = true
+ xctest_module_target = "//test/ios:google_test_runner"
+ info_plist = "//build/ios/Unittest-Info.plist"
+ extra_substitutions = [ "GTEST_BUNDLE_ID_SUFFIX=$target_name" ]
+ forward_variables_from(invoker, "*")
+ if (!defined(deps)) {
+ deps = []
+ }
+ deps += [ ":$_launch_image_bundle_target" ]
+ }
+ } else {
+ executable(target_name) {
+ testonly = true
+ forward_variables_from(invoker, "*")
+ }
}
}
}
diff --git a/client/BUILD.gn b/client/BUILD.gn
index a074b50..b748c3f 100644
--- a/client/BUILD.gn
+++ b/client/BUILD.gn
@@ -43,6 +43,13 @@
]
}
+ if (crashpad_is_ios) {
+ sources += [
+ "crash_report_database_mac.mm",
+ "crashpad_client_ios.cc",
+ ]
+ }
+
if (crashpad_is_linux || crashpad_is_android) {
set_sources_assignment_filter([])
sources += [
@@ -77,22 +84,36 @@
public_configs = [ "..:crashpad_config" ]
- deps = [
- "../compat",
+ public_deps = [
"../third_party/mini_chromium:base",
"../util",
]
+ deps = []
+
if (crashpad_is_win) {
libs = [ "rpcrt4.lib" ]
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
}
- if (crashpad_is_fuchsia && crashpad_is_in_fuchsia) {
+ # TODO(justincohen): Temporary dependency to bring up the iOS client.
+ if (crashpad_is_ios) {
deps += [
- "//zircon/public/lib/fdio",
+ "../minidump",
+ "../snapshot",
]
}
+
+ if (crashpad_is_linux || crashpad_is_android) {
+ deps += [ "../third_party/lss" ]
+ }
+
+ if (crashpad_is_fuchsia) {
+ deps += [ "../third_party/fuchsia" ]
+ if (crashpad_is_in_fuchsia) {
+ deps += [ "//zircon/public/lib/fdio" ]
+ }
+ }
}
source_set("client_test") {
@@ -116,6 +137,17 @@
sources += [ "crashpad_client_win_test.cc" ]
}
+ if (crashpad_is_ios) {
+ sources += [ "crashpad_client_ios_test.mm" ]
+ sources -= [
+ "annotation_list_test.cc",
+ "annotation_test.cc",
+ "crash_report_database_test.cc",
+ "prune_crash_reports_test.cc",
+ "settings_test.cc",
+ ]
+ }
+
if (crashpad_is_linux || crashpad_is_android) {
sources += [ "crashpad_client_linux_test.cc" ]
}
@@ -131,9 +163,9 @@
"../util",
]
- data_deps = [
- "../handler:crashpad_handler",
- ]
+ if (!crashpad_is_ios && !crashpad_is_fuchsia) {
+ data_deps = [ "../handler:crashpad_handler" ]
+ }
if (crashpad_is_win) {
data_deps += [ "../handler:crashpad_handler_console" ]
diff --git a/client/client.gyp b/client/client.gyp
index a23d0c8..dcb2d0e 100644
--- a/client/client.gyp
+++ b/client/client.gyp
@@ -23,6 +23,7 @@
'dependencies': [
'../compat/compat.gyp:crashpad_compat',
'../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../third_party/lss/lss.gyp:lss',
'../util/util.gyp:crashpad_util',
],
'include_dirs': [
diff --git a/client/client_argv_handling.cc b/client/client_argv_handling.cc
index 6933aac..742a000 100644
--- a/client/client_argv_handling.cc
+++ b/client/client_argv_handling.cc
@@ -61,14 +61,14 @@
return argv_strings;
}
-void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
- std::vector<const char*>* argv) {
- argv->clear();
- argv->reserve(argv_strings.size() + 1);
- for (const auto& arg : argv_strings) {
- argv->push_back(arg.c_str());
+void StringVectorToCStringVector(const std::vector<std::string>& strings,
+ std::vector<const char*>* c_strings) {
+ c_strings->clear();
+ c_strings->reserve(strings.size() + 1);
+ for (const auto& str : strings) {
+ c_strings->push_back(str.c_str());
}
- argv->push_back(nullptr);
+ c_strings->push_back(nullptr);
}
} // namespace crashpad
diff --git a/client/client_argv_handling.h b/client/client_argv_handling.h
index d380b1a..04c66d3 100644
--- a/client/client_argv_handling.h
+++ b/client/client_argv_handling.h
@@ -40,11 +40,11 @@
//! \brief Flattens a string vector into a const char* vector suitable for use
//! in an exec() call.
//!
-//! \param[in] argv_strings Arguments to be passed to child process, typically
-//! created by BuildHandlerArgvStrings().
-//! \param[out] argv argv suitable for starting the child process.
-void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
- std::vector<const char*>* argv);
+//! \param[in] strings A vector of string data. This vector must remain valid
+//! for the lifetime of \a c_strings.
+//! \param[out] c_strings A vector of pointers to the string data in \a strings.
+void StringVectorToCStringVector(const std::vector<std::string>& strings,
+ std::vector<const char*>* c_strings);
} // namespace crashpad
diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc
index d300a8f..eda0ef4 100644
--- a/client/crash_report_database.cc
+++ b/client/crash_report_database.cc
@@ -27,7 +27,8 @@
uploaded(false),
last_upload_attempt_time(0),
upload_attempts(0),
- upload_explicitly_requested(false) {}
+ upload_explicitly_requested(false),
+ total_size(0u) {}
CrashReportDatabase::NewReport::NewReport()
: writer_(std::make_unique<FileWriter>()),
@@ -64,6 +65,15 @@
return true;
}
+FileReaderInterface* CrashReportDatabase::NewReport::Reader() {
+ auto reader = std::make_unique<FileReader>();
+ if (!reader->Open(file_remover_.get())) {
+ return nullptr;
+ }
+ reader_ = std::move(reader);
+ return reader_.get();
+}
+
CrashReportDatabase::UploadReport::UploadReport()
: Report(),
reader_(std::make_unique<FileReader>()),
diff --git a/client/crash_report_database.h b/client/crash_report_database.h
index d115c74..cc4b8df 100644
--- a/client/crash_report_database.h
+++ b/client/crash_report_database.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
+#include <stdint.h>
#include <time.h>
#include <map>
@@ -98,6 +99,10 @@
//! Whether this crash report was explicitly requested by user to be
//! uploaded. This can be true only if report is in the 'pending' state.
bool upload_explicitly_requested;
+
+ //! The total size in bytes taken by the report, including any potential
+ //! attachments.
+ uint64_t total_size;
};
//! \brief A crash report that is in the process of being written.
@@ -108,9 +113,13 @@
NewReport();
~NewReport();
- //! An open FileWriter with which to write the report.
+ //! \brief An open FileWriter with which to write the report.
FileWriter* Writer() const { return writer_.get(); }
+ //! \brief Returns a FileReaderInterface to the report, or `nullptr` with a
+ //! message logged.
+ FileReaderInterface* Reader();
+
//! A unique identifier by which this report will always be known to the
//! database.
const UUID& ReportID() const { return uuid_; }
@@ -137,6 +146,7 @@
const base::FilePath::StringType& extension);
std::unique_ptr<FileWriter> writer_;
+ std::unique_ptr<FileReader> reader_;
ScopedRemoveFile file_remover_;
std::vector<std::unique_ptr<FileWriter>> attachment_writers_;
std::vector<ScopedRemoveFile> attachment_removers_;
@@ -163,7 +173,7 @@
//! This is not implemented on macOS or Windows.
std::map<std::string, FileReader*> GetAttachments() const {
return attachment_map_;
- };
+ }
private:
friend class CrashReportDatabase;
diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc
index 8e93237..aeaf2af 100644
--- a/client/crash_report_database_generic.cc
+++ b/client/crash_report_database_generic.cc
@@ -15,6 +15,7 @@
#include "client/crash_report_database.h"
#include <stdint.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
@@ -25,6 +26,7 @@
#include "util/file/directory_reader.h"
#include "util/file/filesystem.h"
#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/memory_sanitizer.h"
namespace crashpad {
@@ -160,6 +162,34 @@
DISALLOW_COPY_AND_ASSIGN(ScopedLockFile);
};
+off_t GetFileSize(const base::FilePath& filepath) {
+ struct stat statbuf;
+ if (stat(filepath.value().c_str(), &statbuf) == 0) {
+ return statbuf.st_size;
+ }
+ PLOG(ERROR) << "stat " << filepath.value();
+ return 0;
+}
+
+void AddAttachmentSize(const base::FilePath& attachments_dir, uint64_t* size) {
+ // Early return if the attachment directory does not exist.
+ if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
+ return;
+ }
+ DirectoryReader reader;
+ if (!reader.Open(attachments_dir)) {
+ return;
+ }
+ base::FilePath attachment_filename;
+ DirectoryReader::Result result;
+ while ((result = reader.NextFile(&attachment_filename)) ==
+ DirectoryReader::Result::kSuccess) {
+ const base::FilePath attachment_filepath(
+ attachments_dir.Append(attachment_filename));
+ *size += GetFileSize(attachment_filepath);
+ }
+}
+
} // namespace
class CrashReportDatabaseGeneric : public CrashReportDatabase {
@@ -253,7 +283,7 @@
void RemoveAttachmentsByUUID(const UUID& uuid);
// Reads the metadata for a report from path and returns it in report.
- static bool ReadMetadata(const base::FilePath& path, Report* report);
+ bool ReadMetadata(const base::FilePath& path, Report* report);
// Wraps ReadMetadata and removes the report from the database on failure.
bool CleaningReadMetadata(const base::FilePath& path, Report* report);
@@ -303,6 +333,9 @@
base::FilePath attachments_dir =
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
uuid);
+ if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
+ return;
+ }
DirectoryReader reader;
if (!reader.Open(attachments_dir)) {
return;
@@ -842,7 +875,6 @@
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
DirectoryReader reader;
if (!reader.Open(root_attachments_dir)) {
- LOG(ERROR) << "no attachments dir";
return;
}
@@ -883,6 +915,9 @@
void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
base::FilePath attachments_dir = AttachmentsPath(uuid);
+ if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
+ return;
+ }
DirectoryReader reader;
if (!reader.Open(attachments_dir)) {
return;
@@ -899,7 +934,6 @@
LoggingRemoveDirectory(attachments_dir);
}
-// static
bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
Report* report) {
const base::FilePath metadata_path(
@@ -910,7 +944,8 @@
return false;
}
- if (!report->uuid.InitializeFromString(
+ UUID uuid;
+ if (!uuid.InitializeFromString(
path.BaseName().RemoveFinalExtension().value())) {
LOG(ERROR) << "Couldn't interpret report uuid";
return false;
@@ -930,6 +965,12 @@
return false;
}
+ // Seed the total size with the main report size and then add the sizes of any
+ // potential attachments.
+ uint64_t total_size = GetFileSize(path);
+ AddAttachmentSize(AttachmentsPath(uuid), &total_size);
+
+ report->uuid = uuid;
report->upload_attempts = metadata.upload_attempts;
report->last_upload_attempt_time = metadata.last_upload_attempt_time;
report->creation_time = metadata.creation_time;
@@ -937,6 +978,7 @@
report->upload_explicitly_requested =
(metadata.attributes & kAttributeUploadExplicitlyRequested) != 0;
report->file_path = path;
+ report->total_size = total_size;
return true;
}
@@ -966,6 +1008,11 @@
}
ReportMetadata metadata;
+#if defined(MEMORY_SANITIZER)
+ // memset() + re-initialization is required to zero padding bytes for MSan.
+ memset(&metadata, 0, sizeof(metadata));
+#endif // defined(MEMORY_SANITIZER)
+ metadata = {};
metadata.creation_time = time(nullptr);
return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata));
@@ -986,6 +1033,11 @@
}
ReportMetadata metadata;
+#if defined(MEMORY_SANITIZER)
+ // memset() + re-initialization is required to zero padding bytes for MSan.
+ memset(&metadata, 0, sizeof(metadata));
+#endif // defined(MEMORY_SANITIZER)
+ metadata = {};
metadata.creation_time = report.creation_time;
metadata.last_upload_attempt_time = report.last_upload_attempt_time;
metadata.upload_attempts = report.upload_attempts;
diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm
index 3106dc2..1f8fec9 100644
--- a/client/crash_report_database_mac.mm
+++ b/client/crash_report_database_mac.mm
@@ -29,6 +29,7 @@
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/posix/eintr_wrapper.h"
#include "base/scoped_generic.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
@@ -279,7 +280,7 @@
}
// Create the three processing directories for the database.
- for (size_t i = 0; i < arraysize(kReportDirectories); ++i) {
+ for (size_t i = 0; i < base::size(kReportDirectories); ++i) {
if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
return false;
}
@@ -681,6 +682,14 @@
return false;
}
+ // There are no attachments on Mac so the total size is the main report size.
+ struct stat statbuf;
+ if (stat(path.value().c_str(), &statbuf) != 0) {
+ PLOG(ERROR) << "stat " << path.value();
+ return false;
+ }
+ report->total_size = statbuf.st_size;
+
return true;
}
diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc
index 2dbb4fc..20512a4 100644
--- a/client/crash_report_database_test.cc
+++ b/client/crash_report_database_test.cc
@@ -20,7 +20,6 @@
#include "test/errors.h"
#include "test/file.h"
#include "test/filesystem.h"
-#include "test/gtest_disabled.h"
#include "test/scoped_temp_dir.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
@@ -31,8 +30,7 @@
class CrashReportDatabaseTest : public testing::Test {
public:
- CrashReportDatabaseTest() {
- }
+ CrashReportDatabaseTest() {}
protected:
// testing::Test:
@@ -41,9 +39,7 @@
ASSERT_TRUE(db_);
}
- void ResetDatabase() {
- db_.reset();
- }
+ void ResetDatabase() { db_.reset(); }
CrashReportDatabase* db() { return db_.get(); }
base::FilePath path() const {
@@ -57,6 +53,12 @@
static constexpr char kTest[] = "test";
ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest)));
+ char contents[sizeof(kTest)];
+ FileReaderInterface* reader = new_report->Reader();
+ ASSERT_TRUE(reader->ReadExactly(contents, sizeof(contents)));
+ EXPECT_EQ(memcmp(contents, kTest, sizeof(contents)), 0);
+ EXPECT_EQ(reader->ReadExactly(contents, 1), 0);
+
UUID uuid;
EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid),
CrashReportDatabase::kNoError);
@@ -101,6 +103,7 @@
EXPECT_EQ(report.last_upload_attempt_time, 0);
EXPECT_EQ(report.upload_attempts, 0);
EXPECT_FALSE(report.upload_explicitly_requested);
+ EXPECT_GE(report.total_size, 0u);
}
void RelocateDatabase() {
@@ -673,7 +676,7 @@
TEST_F(CrashReportDatabaseTest, Attachments) {
#if defined(OS_MACOSX) || defined(OS_WIN)
// Attachments aren't supported on Mac and Windows yet.
- DISABLED_TEST();
+ GTEST_SKIP();
#else
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
@@ -719,7 +722,7 @@
TEST_F(CrashReportDatabaseTest, OrphanedAttachments) {
#if defined(OS_MACOSX) || defined(OS_WIN)
// Attachments aren't supported on Mac and Windows yet.
- DISABLED_TEST();
+ GTEST_SKIP();
#else
// TODO: This is using paths that are specific to the generic implementation
// and will need to be generalized for Mac and Windows.
@@ -835,6 +838,66 @@
}
#endif // !OS_MACOSX && !OS_WIN
+TEST_F(CrashReportDatabaseTest, TotalSize_MainReportOnly) {
+ std::unique_ptr<CrashReportDatabase::NewReport> new_report;
+ ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
+ CrashReportDatabase::kNoError);
+
+ // Main report.
+ static constexpr char main_report_data[] = "dlbvandslhb";
+ ASSERT_TRUE(
+ new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));
+
+ UUID uuid;
+ ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
+ CrashReportDatabase::kNoError);
+
+ CrashReportDatabase::Report report;
+ ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
+ CrashReportDatabase::kNoError);
+
+ EXPECT_EQ(report.total_size, sizeof(main_report_data));
+}
+
+TEST_F(CrashReportDatabaseTest, GetReportSize_RightSizeWithAttachments) {
+#if defined(OS_MACOSX) || defined(OS_WIN)
+ // Attachments aren't supported on Mac and Windows yet.
+ return;
+#else
+ std::unique_ptr<CrashReportDatabase::NewReport> new_report;
+ ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
+ CrashReportDatabase::kNoError);
+
+ // Main report.
+ static constexpr char main_report_data[] = "dlbvandslhb";
+ ASSERT_TRUE(
+ new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));
+
+ // First attachment.
+ FileWriter* attachment_1 = new_report->AddAttachment("my_attachment_1");
+ ASSERT_NE(attachment_1, nullptr);
+ static constexpr char attachment_1_data[] = "vKDnidhvbiudshoihbvdsoiuh nhh";
+ attachment_1->Write(attachment_1_data, sizeof(attachment_1_data));
+
+ // Second attachment.
+ FileWriter* attachment_2 = new_report->AddAttachment("my_attachment_2");
+ ASSERT_NE(attachment_2, nullptr);
+ static constexpr char attachment_2_data[] = "sgvsvgusiyguysigfkhpmo-[";
+ attachment_2->Write(attachment_2_data, sizeof(attachment_2_data));
+
+ UUID uuid;
+ ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
+ CrashReportDatabase::kNoError);
+
+ CrashReportDatabase::Report report;
+ ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
+ CrashReportDatabase::kNoError);
+ EXPECT_EQ(report.total_size,
+ sizeof(main_report_data) + sizeof(attachment_1_data) +
+ sizeof(attachment_2_data));
+#endif
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc
index 8967770..15137d0 100644
--- a/client/crash_report_database_win.cc
+++ b/client/crash_report_database_win.cc
@@ -458,7 +458,18 @@
LOG(ERROR) << "invalid string table index";
return;
}
- reports.push_back(ReportDisk(record, report_dir_, string_table));
+ ReportDisk report_disk(record, report_dir_, string_table);
+
+ // There are no attachments on Windows so the total size is the main
+ // report size.
+ struct _stati64 statbuf;
+ if (_wstat64(report_disk.file_path.value().c_str(), &statbuf) == 0) {
+ report_disk.total_size = statbuf.st_size;
+ } else {
+ LOG(ERROR) << "failed to stat report";
+ }
+
+ reports.push_back(report_disk);
}
}
reports_.swap(reports);
diff --git a/client/crashpad_client.h b/client/crashpad_client.h
index ae409d4..f5e02c9 100644
--- a/client/crashpad_client.h
+++ b/client/crashpad_client.h
@@ -16,6 +16,7 @@
#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -24,6 +25,7 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "build/build_config.h"
+#include "util/file/file_io.h"
#include "util/misc/capture_context.h"
#if defined(OS_MACOSX)
@@ -78,6 +80,10 @@
//! On Fuchsia, this method binds to the exception port of the current default
//! job, and starts a Crashpad handler to monitor that port.
//!
+ //! On Linux, this method starts a Crashpad handler, connected to this process
+ //! via an `AF_UNIX` socket pair and installs signal handlers to request crash
+ //! dumps on the client's socket end.
+ //!
//! \param[in] handler The path to a Crashpad handler executable.
//! \param[in] database The path to a Crashpad database. The handler will be
//! started with this path as its `--database` argument.
@@ -112,6 +118,199 @@
bool restartable,
bool asynchronous_start);
+#if defined(OS_ANDROID) || defined(OS_LINUX) || DOXYGEN
+ //! \brief Retrieve the socket and process ID for the handler.
+ //!
+ //! `StartHandler()` must have successfully been called before calling this
+ //! method.
+ //!
+ //! \param[out] sock The socket connected to the handler, if not `nullptr`.
+ //! \param[out] pid The handler's process ID, if not `nullptr`.
+ //! \return `true` on success. Otherwise `false` with a message logged.
+ static bool GetHandlerSocket(int* sock, pid_t* pid);
+
+ //! \brief Sets the socket to a presumably-running Crashpad handler process
+ //! which was started with StartHandler().
+ //!
+ //! This method installs a signal handler to request crash dumps on \a sock.
+ //!
+ //! \param[in] sock A socket connected to a Crashpad handler.
+ //! \param[in] pid The process ID of the handler, used to set the handler as
+ //! this process' ptracer. 0 indicates it is not necessary to set the
+ //! handler as this process' ptracer. -1 indicates that the handler's
+ //! process ID should be determined by communicating over the socket.
+ bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid);
+#endif // OS_ANDROID || OS_LINUX || DOXYGEN
+
+#if defined(OS_ANDROID) || DOXYGEN
+ //! \brief Installs a signal handler to execute `/system/bin/app_process` and
+ //! load a Java class in response to a crash.
+ //!
+ //! \param[in] class_name The fully qualified class name to load, which must
+ //! define a `main()` method to be invoked by `app_process`. Arguments
+ //! will be passed to this method as though it were the Crashpad handler.
+ //! This class is expected to load a native library defining
+ //! crashpad::HandlerMain() and pass the arguments to it.
+ //! \param[in] env A vector of environment variables of the form `var=value`
+ //! defining the environment in which to execute `app_process`. If this
+ //! value is `nullptr`, the application's environment at the time of the
+ //! crash will be used.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \param[in] metrics_dir The path to an already existing directory where
+ //! metrics files can be stored. The handler will be started with this
+ //! path as its `--metrics-dir` argument.
+ //! \param[in] url The URL of an upload server. The handler will be started
+ //! with this URL as its `--url` argument.
+ //! \param[in] annotations Process annotations to set in each crash report.
+ //! The handler will be started with an `--annotation` argument for each
+ //! element in this map.
+ //! \param[in] arguments Additional arguments to pass to the Crashpad handler.
+ //! Arguments passed in other parameters and arguments required to perform
+ //! the handshake are the responsibility of this method, and must not be
+ //! specified in this parameter.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool StartJavaHandlerAtCrash(
+ const std::string& class_name,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments);
+
+ //! \brief Executes `/system/bin/app_process` and loads a Java class.
+ //!
+ //! \param[in] class_name The fully qualified class name to load, which must
+ //! define a `main()` method to be invoked by `app_process`. Arguments
+ //! will be passed to this method as though it were the Crashpad handler.
+ //! This class is expected to load a native library defining
+ //! crashpad::HandlerMain() and pass the arguments to it.
+ //! \param[in] env A vector of environment variables of the form `var=value`
+ //! defining the environment in which to execute `app_process`. If this
+ //! value is `nullptr`, the application's current environment will be
+ //! used.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \param[in] metrics_dir The path to an already existing directory where
+ //! metrics files can be stored. The handler will be started with this
+ //! path as its `--metrics-dir` argument.
+ //! \param[in] url The URL of an upload server. The handler will be started
+ //! with this URL as its `--url` argument.
+ //! \param[in] annotations Process annotations to set in each crash report.
+ //! The handler will be started with an `--annotation` argument for each
+ //! element in this map.
+ //! \param[in] arguments Additional arguments to pass to the Crashpad handler.
+ //! Arguments passed in other parameters and arguments required to perform
+ //! the handshake are the responsibility of this method, and must not be
+ //! specified in this parameter.
+ //! \param[in] socket The server end of a socket pair. The client end should
+ //! be used with an ExceptionHandlerClient.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ static bool StartJavaHandlerForClient(
+ const std::string& class_name,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket);
+
+ //! \brief Installs a signal handler to start a Crashpad handler process by
+ //! loading it with `/system/bin/linker`.
+ //!
+ //! This method is only supported by Android Q+.
+ //!
+ //! \param[in] handler_trampoline The path to a Crashpad handler trampoline
+ //! executable, possibly located within an apk, e.g.
+ //! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
+ //! \param[in] handler_library The name of a library exporting the symbol
+ //! `CrashpadHandlerMain()`. The path to this library must be present in
+ //! `LD_LIBRARY_PATH`.
+ //! \param[in] is_64_bit `true` if \a handler_trampoline and \a
+ //! handler_library are 64-bit objects. They must have the same bitness.
+ //! \param[in] env A vector of environment variables of the form `var=value`
+ //! defining the environment in which to execute `app_process`. If this
+ //! value is `nullptr`, the application's environment at the time of the
+ //! crash will be used.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \param[in] metrics_dir The path to an already existing directory where
+ //! metrics files can be stored. The handler will be started with this
+ //! path as its `--metrics-dir` argument.
+ //! \param[in] url The URL of an upload server. The handler will be started
+ //! with this URL as its `--url` argument.
+ //! \param[in] annotations Process annotations to set in each crash report.
+ //! The handler will be started with an `--annotation` argument for each
+ //! element in this map.
+ //! \param[in] arguments Additional arguments to pass to the Crashpad handler.
+ //! Arguments passed in other parameters and arguments required to perform
+ //! the handshake are the responsibility of this method, and must not be
+ //! specified in this parameter.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool StartHandlerWithLinkerAtCrash(
+ const std::string& handler_trampoline,
+ const std::string& handler_library,
+ bool is_64_bit,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments);
+
+ //! \brief Starts a Crashpad handler process with an initial client by loading
+ //! it with `/system/bin/linker`.
+ //!
+ //! This method is only supported by Android Q+.
+ //!
+ //! \param[in] handler_trampoline The path to a Crashpad handler trampoline
+ //! executable, possibly located within an apk, e.g.
+ //! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
+ //! \param[in] handler_library The name of a library exporting the symbol
+ //! `CrashpadHandlerMain()`. The path to this library must be present in
+ //! `LD_LIBRARY_PATH`.
+ //! \param[in] is_64_bit `true` if \a handler_trampoline and \a
+ //! handler_library are 64-bit objects. They must have the same bitness.
+ //! \param[in] env A vector of environment variables of the form `var=value`
+ //! defining the environment in which to execute `app_process`. If this
+ //! value is `nullptr`, the application's current environment will be
+ //! used.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \param[in] metrics_dir The path to an already existing directory where
+ //! metrics files can be stored. The handler will be started with this
+ //! path as its `--metrics-dir` argument.
+ //! \param[in] url The URL of an upload server. The handler will be started
+ //! with this URL as its `--url` argument.
+ //! \param[in] annotations Process annotations to set in each crash report.
+ //! The handler will be started with an `--annotation` argument for each
+ //! element in this map.
+ //! \param[in] arguments Additional arguments to pass to the Crashpad handler.
+ //! Arguments passed in other parameters and arguments required to perform
+ //! the handshake are the responsibility of this method, and must not be
+ //! specified in this parameter.
+ //! \param[in] socket The server end of a socket pair. The client end should
+ //! be used with an ExceptionHandlerClient.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ static bool StartHandlerWithLinkerForClient(
+ const std::string& handler_trampoline,
+ const std::string& handler_library,
+ bool is_64_bit,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket);
+#endif // OS_ANDROID || DOXYGEN
+
#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
//! \brief Installs a signal handler to launch a handler process in reponse to
//! a crash.
@@ -135,7 +334,7 @@
//! specified in this parameter.
//!
//! \return `true` on success, `false` on failure with a message logged.
- static bool StartHandlerAtCrash(
+ bool StartHandlerAtCrash(
const base::FilePath& handler,
const base::FilePath& database,
const base::FilePath& metrics_dir,
@@ -188,6 +387,12 @@
//! CaptureContext() or similar.
static void DumpWithoutCrash(NativeCPUContext* context);
+ //! \brief Disables any installed crash handler, including any
+ //! FirstChanceHandler and crashes the current process.
+ //!
+ //! \param[in] message A message to be logged before crashing.
+ static void CrashWithoutDump(const std::string& message);
+
//! \brief The type for custom handlers installed by clients.
using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
@@ -209,8 +414,38 @@
//! \param[in] handler The custom crash signal handler to install.
static void SetFirstChanceExceptionHandler(FirstChanceHandler handler);
+ //! \brief Configures a set of signals that shouldn't have Crashpad signal
+ //! handlers installed.
+ //!
+ //! This method should be called before calling StartHandler(),
+ //! SetHandlerSocket(), or other methods that install Crashpad signal
+ //! handlers.
+ //!
+ //! \param[in] unhandled_signals The set of unhandled signals
+ void SetUnhandledSignals(const std::set<int>& unhandled_signals);
#endif // OS_LINUX || OS_ANDROID || DOXYGEN
+#if defined(OS_IOS) || DOXYGEN
+ //! \brief Configures the process to direct its crashes to the iOS in-process
+ //! Crashpad handler.
+ //!
+ //! This method is only defined on iOS.
+ //!
+ //! TODO(justincohen): This method will need to take database, metrics_dir,
+ //! url and annotations eventually.
+ void StartCrashpadInProcessHandler();
+
+ // TODO(justincohen): This method is purely for bringing up iOS interfaces.
+ //! \brief Requests that the handler capture a dump even though there hasn't
+ //! been a crash.
+ //!
+ //! A handler must have already been installed before calling this method.
+ //!
+ //! \param[in] context A NativeCPUContext, generally captured by
+ //! CaptureContext() or similar.
+ static void DumpWithoutCrash(NativeCPUContext* context);
+#endif
+
#if defined(OS_MACOSX) || DOXYGEN
//! \brief Sets the process’ crash handler to a Mach service registered with
//! the bootstrap server.
@@ -384,12 +619,24 @@
static void UseSystemDefaultHandler();
#endif
+#if defined(OS_CHROMEOS)
+ //! \brief Sets a timestamp on the signal handler to be passed on to
+ //! crashpad_handler and then eventually Chrome OS's crash_reporter.
+ //!
+ //! \note This method is used by clients that use `StartHandler()` to start
+ //! a handler and not by clients that use any other handler starting
+ //! methods.
+ static void SetCrashLoopBefore(uint64_t crash_loop_before_time);
+#endif
+
private:
#if defined(OS_MACOSX)
base::mac::ScopedMachSendRight exception_port_;
#elif defined(OS_WIN)
std::wstring ipc_pipe_;
ScopedKernelHANDLE handler_start_thread_;
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ std::set<int> unhandled_signals_;
#endif // OS_MACOSX
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc
index 0d1b65b..3da1f76 100644
--- a/client/crashpad_client_fuchsia.cc
+++ b/client/crashpad_client_fuchsia.cc
@@ -15,15 +15,15 @@
#include "client/crashpad_client.h"
#include <lib/fdio/spawn.h>
-#include <zircon/process.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/job.h>
+#include <lib/zx/process.h>
#include <zircon/processargs.h>
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/scoped_zx_handle.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/client_argv_handling.h"
-#include "util/fuchsia/system_exception_port_key.h"
namespace crashpad {
@@ -43,52 +43,44 @@
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia.
- zx_handle_t exception_port_raw;
- zx_status_t status = zx_port_create(0, &exception_port_raw);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_port_create";
- return false;
- }
- base::ScopedZxHandle exception_port(exception_port_raw);
-
- status = zx_task_bind_exception_port(
- zx_job_default(), exception_port.get(), kSystemExceptionPortKey, 0);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_task_bind_exception_port";
- return false;
- }
-
std::vector<std::string> argv_strings = BuildHandlerArgvStrings(
handler, database, metrics_dir, url, annotations, arguments);
std::vector<const char*> argv;
- ConvertArgvStrings(argv_strings, &argv);
+ StringVectorToCStringVector(argv_strings, &argv);
- // Follow the same protocol as devmgr and crashlogger in Zircon (that is,
- // process handle as handle 0, with type USER0, exception port handle as
- // handle 1, also with type PA_USER0) so that it's trivial to replace
- // crashlogger with crashpad_handler. The exception port is passed on, so
- // released here. Currently it is assumed that this process's default job
- // handle is the exception port that should be monitored. In the future, it
- // might be useful for this to be configurable by the client.
- constexpr size_t kActionCount = 2;
- fdio_spawn_action_t actions[] = {
- {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
- .h = {.id = PA_HND(PA_USER0, 0), .handle = ZX_HANDLE_INVALID}},
- {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
- .h = {.id = PA_HND(PA_USER0, 1), .handle = ZX_HANDLE_INVALID}},
- };
-
- status = zx_handle_duplicate(
- zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &actions[0].h.handle);
+ // Set up handles to send to the spawned process:
+ // 0. PA_USER0 job
+ // 1. PA_USER0 exception channel
+ //
+ // Currently it is assumed that this process's default job handle is the
+ // exception channel that should be monitored. In the future, it might be
+ // useful for this to be configurable by the client.
+ zx::job job;
+ zx_status_t status =
+ zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_handle_duplicate";
return false;
}
- actions[1].h.handle = exception_port.release();
+
+ zx::channel exception_channel;
+ status = job.create_exception_channel(0, &exception_channel);
+ if (status != ZX_OK) {
+ ZX_LOG(ERROR, status) << "zx_task_create_exception_channel";
+ return false;
+ }
+
+ constexpr size_t kActionCount = 2;
+ fdio_spawn_action_t actions[] = {
+ {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
+ .h = {.id = PA_HND(PA_USER0, 0), .handle = job.release()}},
+ {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
+ .h = {.id = PA_HND(PA_USER0, 1), .handle = exception_channel.release()}},
+ };
char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
- zx_handle_t child_raw;
+ zx::process child;
// TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL
// is useful during bringup, but should probably be made minimal for real
// usage.
@@ -99,9 +91,8 @@
nullptr,
kActionCount,
actions,
- &child_raw,
+ child.reset_and_get_address(),
error_message);
- base::ScopedZxHandle child(child_raw);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message;
return false;
diff --git a/client/crashpad_client_ios.cc b/client/crashpad_client_ios.cc
new file mode 100644
index 0000000..342e109
--- /dev/null
+++ b/client/crashpad_client_ios.cc
@@ -0,0 +1,228 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_client.h"
+
+#include <unistd.h>
+
+#include <ios>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/stl_util.h"
+#include "snapshot/ios/process_snapshot_ios.h"
+#include "util/ios/exception_processor.h"
+#include "util/ios/ios_system_data_collector.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/exception_ports.h"
+#include "util/mach/mach_extensions.h"
+#include "util/mach/mach_message.h"
+#include "util/mach/mach_message_server.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/posix/signals.h"
+#include "util/thread/thread.h"
+
+namespace crashpad {
+
+namespace {
+
+// A base class for signal handler and Mach exception server.
+class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
+ public:
+ static CrashHandler* Get() {
+ static CrashHandler* instance = new CrashHandler();
+ return instance;
+ }
+
+ void Initialize() {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ InstallMachExceptionHandler();
+ CHECK(Signals::InstallHandler(SIGABRT, CatchSignal, 0, &old_action_));
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ }
+
+ void DumpWithoutCrash(NativeCPUContext* context) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ mach_exception_data_type_t code[2] = {};
+ static constexpr int kSimulatedException = -1;
+ HandleMachException(MACH_EXCEPTION_CODES,
+ mach_thread_self(),
+ kSimulatedException,
+ code,
+ base::size(code),
+ MACHINE_THREAD_STATE,
+ reinterpret_cast<ConstThreadState>(context),
+ MACHINE_THREAD_STATE_COUNT);
+ }
+
+ private:
+ CrashHandler() = default;
+
+ void InstallMachExceptionHandler() {
+ exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ CHECK(exception_port_.is_valid());
+
+ kern_return_t kr = mach_port_insert_right(mach_task_self(),
+ exception_port_.get(),
+ exception_port_.get(),
+ MACH_MSG_TYPE_MAKE_SEND);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
+
+ // TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.
+ const exception_mask_t mask =
+ ExcMaskAll() &
+ ~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
+ EXC_MASK_RPC_ALERT | EXC_MASK_GUARD);
+ ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
+ exception_ports.GetExceptionPorts(mask, &original_handlers_);
+ exception_ports.SetExceptionPort(
+ mask,
+ exception_port_.get(),
+ EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
+ MACHINE_THREAD_STATE);
+
+ Start();
+ }
+
+ // Thread:
+
+ void ThreadMain() override {
+ UniversalMachExcServer universal_mach_exc_server(this);
+ while (true) {
+ mach_msg_return_t mr =
+ MachMessageServer::Run(&universal_mach_exc_server,
+ exception_port_.get(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kPersistent,
+ MachMessageServer::kReceiveLargeIgnore,
+ kMachMessageTimeoutWaitIndefinitely);
+ MACH_CHECK(mr == MACH_SEND_INVALID_DEST, mr) << "MachMessageServer::Run";
+ }
+ }
+
+ // UniversalMachExcServer::Interface:
+
+ kern_return_t CatchMachException(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ const mach_msg_trailer_t* trailer,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ // TODO(justincohen): Forward exceptions to original_handlers_ with
+ // UniversalExceptionRaise.
+
+ // iOS shouldn't have any child processes, but just in case, those will
+ // inherit the task exception ports, and this process isn’t prepared to
+ // handle them
+ if (task != mach_task_self()) {
+ LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
+ << mach_task_self();
+ return KERN_FAILURE;
+ }
+
+ HandleMachException(behavior,
+ thread,
+ exception,
+ code,
+ code_count,
+ *flavor,
+ old_state,
+ old_state_count);
+
+ // Respond with KERN_FAILURE so the system will continue to handle this
+ // exception as a crash.
+ return KERN_FAILURE;
+ }
+
+ void HandleMachException(exception_behavior_t behavior,
+ thread_t thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count) {
+ // TODO(justincohen): This is incomplete.
+ ProcessSnapshotIOS process_snapshot;
+ process_snapshot.Initialize(system_data_);
+ process_snapshot.SetExceptionFromMachException(behavior,
+ thread,
+ exception,
+ code,
+ code_count,
+ flavor,
+ old_state,
+ old_state_count);
+ }
+
+ // The signal handler installed at OS-level.
+ static void CatchSignal(int signo, siginfo_t* siginfo, void* context) {
+ Get()->HandleAndReraiseSignal(
+ signo, siginfo, reinterpret_cast<ucontext_t*>(context));
+ }
+
+ void HandleAndReraiseSignal(int signo,
+ siginfo_t* siginfo,
+ ucontext_t* context) {
+ // TODO(justincohen): This is incomplete.
+ ProcessSnapshotIOS process_snapshot;
+ process_snapshot.Initialize(system_data_);
+ process_snapshot.SetExceptionFromSignal(siginfo, context);
+
+ // Always call system handler.
+ Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
+ }
+
+ base::mac::ScopedMachReceiveRight exception_port_;
+ ExceptionPorts::ExceptionHandlerVector original_handlers_;
+ struct sigaction old_action_ = {};
+ IOSSystemDataCollector system_data_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashHandler);
+};
+
+} // namespace
+
+CrashpadClient::CrashpadClient() {}
+
+CrashpadClient::~CrashpadClient() {}
+
+void CrashpadClient::StartCrashpadInProcessHandler() {
+ InstallObjcExceptionPreprocessor();
+
+ CrashHandler* crash_handler = CrashHandler::Get();
+ DCHECK(crash_handler);
+ crash_handler->Initialize();
+}
+
+// static
+void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
+ CrashHandler* crash_handler = CrashHandler::Get();
+ DCHECK(crash_handler);
+ crash_handler->DumpWithoutCrash(context);
+}
+
+} // namespace crashpad
diff --git a/client/crashpad_client_ios_test.mm b/client/crashpad_client_ios_test.mm
new file mode 100644
index 0000000..002fddb
--- /dev/null
+++ b/client/crashpad_client_ios_test.mm
@@ -0,0 +1,73 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "client/crashpad_client.h"
+
+#import <Foundation/Foundation.h>
+
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using CrashpadIOSClient = PlatformTest;
+
+TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
+ CrashpadClient client;
+ client.StartCrashpadInProcessHandler();
+
+ NativeCPUContext context;
+#if defined(ARCH_CPU_X86_64)
+ CaptureContext(&context);
+#elif defined(ARCH_CPU_ARM64)
+ // TODO(justincohen): Implement CaptureContext for ARM64.
+ mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
+ kern_return_t kr =
+ thread_get_state(mach_thread_self(),
+ MACHINE_THREAD_STATE,
+ reinterpret_cast<thread_state_t>(&context),
+ &thread_state_count);
+ ASSERT_EQ(kr, KERN_SUCCESS);
+#endif
+ client.DumpWithoutCrash(&context);
+}
+
+// This test is covered by a similar XCUITest, but for development purposes
+// it's sometimes easier and faster to run as a gtest. However, there's no
+// way to correctly run this as a gtest. Leave the test here, disabled, for use
+// during development only.
+TEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) {
+ CrashpadClient client;
+ client.StartCrashpadInProcessHandler();
+ [NSException raise:@"GtestNSException" format:@"ThrowException"];
+}
+
+// This test is covered by a similar XCUITest, but for development purposes
+// it's sometimes easier and faster to run as a gtest. However, there's no
+// way to correctly run this as a gtest. Leave the test here, disabled, for use
+// during development only.
+TEST_F(CrashpadIOSClient, DISABLED_ThrowException) {
+ CrashpadClient client;
+ client.StartCrashpadInProcessHandler();
+ std::vector<int> empty_vector;
+ empty_vector.at(42);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc
index 54fe268..98c3d09 100644
--- a/client/crashpad_client_linux.cc
+++ b/client/crashpad_client_linux.cc
@@ -16,6 +16,7 @@
#include <fcntl.h>
#include <stdlib.h>
+#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -25,10 +26,14 @@
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/client_argv_handling.h"
+#include "third_party/lss/lss.h"
#include "util/file/file_io.h"
+#include "util/file/filesystem.h"
#include "util/linux/exception_handler_client.h"
#include "util/linux/exception_information.h"
+#include "util/linux/scoped_pr_set_dumpable.h"
#include "util/linux/scoped_pr_set_ptracer.h"
+#include "util/linux/socket.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/double_fork_and_exec.h"
#include "util/posix/signals.h"
@@ -41,51 +46,103 @@
return base::StringPrintf("--%s=%d", name.c_str(), value);
}
-std::string FormatArgumentAddress(const std::string& name, void* addr) {
+std::string FormatArgumentAddress(const std::string& name, const void* addr) {
return base::StringPrintf("--%s=%p", name.c_str(), addr);
}
+#if defined(OS_ANDROID)
+
+std::vector<std::string> BuildAppProcessArgs(
+ const std::string& class_name,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket) {
+#if defined(ARCH_CPU_64_BITS)
+ static constexpr char kAppProcess[] = "/system/bin/app_process64";
+#else
+ static constexpr char kAppProcess[] = "/system/bin/app_process32";
+#endif
+
+ std::vector<std::string> argv;
+ argv.push_back(kAppProcess);
+ argv.push_back("/system/bin");
+ argv.push_back("--application");
+ argv.push_back(class_name);
+
+ std::vector<std::string> handler_argv =
+ BuildHandlerArgvStrings(base::FilePath(kAppProcess),
+ database,
+ metrics_dir,
+ url,
+ annotations,
+ arguments);
+
+ if (socket != kInvalidFileHandle) {
+ handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
+ }
+
+ argv.insert(argv.end(), handler_argv.begin(), handler_argv.end());
+ return argv;
+}
+
+std::vector<std::string> BuildArgsToLaunchWithLinker(
+ const std::string& handler_trampoline,
+ const std::string& handler_library,
+ bool is_64_bit,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket) {
+ std::vector<std::string> argv;
+ if (is_64_bit) {
+ argv.push_back("/system/bin/linker64");
+ } else {
+ argv.push_back("/system/bin/linker");
+ }
+ argv.push_back(handler_trampoline);
+ argv.push_back(handler_library);
+
+ std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
+ base::FilePath(), database, metrics_dir, url, annotations, arguments);
+
+ if (socket != kInvalidFileHandle) {
+ handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
+ }
+
+ argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
+ return argv;
+}
+
+#endif // OS_ANDROID
+
+// A base class for Crashpad signal handler implementations.
class SignalHandler {
public:
- virtual void HandleCrashFatal(int signo,
- siginfo_t* siginfo,
- void* context) = 0;
- virtual bool HandleCrashNonFatal(int signo,
- siginfo_t* siginfo,
- void* context) = 0;
+ // Returns the currently installed signal hander. May be `nullptr` if no
+ // handler has been installed.
+ static SignalHandler* Get() { return handler_; }
+
+ // Disables any installed Crashpad signal handler for the calling thread. If a
+ // crash signal is received, any previously installed (non-Crashpad) signal
+ // handler will be restored and the signal reraised.
+ static void DisableForThread() { disabled_for_thread_ = true; }
void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
first_chance_handler_ = handler;
}
- protected:
- SignalHandler() = default;
- ~SignalHandler() = default;
+ // The base implementation for all signal handlers, suitable for calling
+ // directly to simulate signal delivery.
+ bool HandleCrash(int signo, siginfo_t* siginfo, void* context) {
+ if (disabled_for_thread_) {
+ return false;
+ }
- CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
-};
-
-// Launches a single use handler to snapshot this process.
-class LaunchAtCrashHandler : public SignalHandler {
- public:
- static LaunchAtCrashHandler* Get() {
- static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
- return instance;
- }
-
- bool Initialize(std::vector<std::string>* argv_in) {
- argv_strings_.swap(*argv_in);
-
- argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
- &exception_information_));
-
- ConvertArgvStrings(argv_strings_, &argv_);
- return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);
- }
-
- bool HandleCrashNonFatal(int signo,
- siginfo_t* siginfo,
- void* context) override {
if (first_chance_handler_ &&
first_chance_handler_(
signo, siginfo, static_cast<ucontext_t*>(context))) {
@@ -98,29 +155,100 @@
exception_information_.context_address =
FromPointerCast<decltype(exception_information_.context_address)>(
context);
- exception_information_.thread_id = syscall(SYS_gettid);
+ exception_information_.thread_id = sys_gettid();
- ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false);
+ ScopedPrSetDumpable set_dumpable(false);
+ HandleCrashImpl();
+ return false;
+ }
+
+ protected:
+ SignalHandler() = default;
+
+ bool Install(const std::set<int>* unhandled_signals) {
+ DCHECK(!handler_);
+ handler_ = this;
+ return Signals::InstallCrashHandlers(
+ HandleOrReraiseSignal, 0, &old_actions_, unhandled_signals);
+ }
+
+ const ExceptionInformation& GetExceptionInfo() {
+ return exception_information_;
+ }
+
+ virtual void HandleCrashImpl() = 0;
+
+ private:
+ // The signal handler installed at OS-level.
+ static void HandleOrReraiseSignal(int signo,
+ siginfo_t* siginfo,
+ void* context) {
+ if (handler_->HandleCrash(signo, siginfo, context)) {
+ return;
+ }
+ Signals::RestoreHandlerAndReraiseSignalOnReturn(
+ siginfo, handler_->old_actions_.ActionForSignal(signo));
+ }
+
+ Signals::OldActions old_actions_ = {};
+ ExceptionInformation exception_information_ = {};
+ CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
+
+ static SignalHandler* handler_;
+
+ static thread_local bool disabled_for_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(SignalHandler);
+};
+SignalHandler* SignalHandler::handler_ = nullptr;
+thread_local bool SignalHandler::disabled_for_thread_ = false;
+
+// Launches a single use handler to snapshot this process.
+class LaunchAtCrashHandler : public SignalHandler {
+ public:
+ static LaunchAtCrashHandler* Get() {
+ static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
+ return instance;
+ }
+
+ bool Initialize(std::vector<std::string>* argv_in,
+ const std::vector<std::string>* envp,
+ const std::set<int>* unhandled_signals) {
+ argv_strings_.swap(*argv_in);
+
+ if (envp) {
+ envp_strings_ = *envp;
+ StringVectorToCStringVector(envp_strings_, &envp_);
+ set_envp_ = true;
+ }
+
+ argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
+ &GetExceptionInfo()));
+
+ StringVectorToCStringVector(argv_strings_, &argv_);
+ return Install(unhandled_signals);
+ }
+
+ void HandleCrashImpl() override {
+ ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false);
pid_t pid = fork();
if (pid < 0) {
- return false;
+ return;
}
if (pid == 0) {
- execv(argv_[0], const_cast<char* const*>(argv_.data()));
+ if (set_envp_) {
+ execve(argv_[0],
+ const_cast<char* const*>(argv_.data()),
+ const_cast<char* const*>(envp_.data()));
+ } else {
+ execv(argv_[0], const_cast<char* const*>(argv_.data()));
+ }
_exit(EXIT_FAILURE);
}
int status;
waitpid(pid, &status, 0);
- return false;
- }
-
- void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override {
- if (HandleCrashNonFatal(signo, siginfo, context)) {
- return;
- }
- Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
private:
@@ -128,22 +256,107 @@
~LaunchAtCrashHandler() = delete;
- static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
- auto state = Get();
- state->HandleCrashFatal(signo, siginfo, context);
- }
-
std::vector<std::string> argv_strings_;
std::vector<const char*> argv_;
- ExceptionInformation exception_information_;
+ std::vector<std::string> envp_strings_;
+ std::vector<const char*> envp_;
+ bool set_envp_ = false;
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
};
-// A pointer to the currently installed crash signal handler. This allows
-// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
-// using the currently configured crash handling strategy.
-static SignalHandler* g_crash_handler;
+class RequestCrashDumpHandler : public SignalHandler {
+ public:
+ static RequestCrashDumpHandler* Get() {
+ static RequestCrashDumpHandler* instance = new RequestCrashDumpHandler();
+ return instance;
+ }
+
+ // pid < 0 indicates the handler pid should be determined by communicating
+ // over the socket.
+ // pid == 0 indicates it is not necessary to set the handler as this process'
+ // ptracer. e.g. if the handler has CAP_SYS_PTRACE or if this process is in a
+ // user namespace and the handler's uid matches the uid of the process that
+ // created the namespace.
+ // pid > 0 directly indicates what the handler's pid is expected to be, so
+ // retrieving this information from the handler is not necessary.
+ bool Initialize(ScopedFileHandle sock,
+ pid_t pid,
+ const std::set<int>* unhandled_signals) {
+ ExceptionHandlerClient client(sock.get(), true);
+ if (pid < 0) {
+ ucred creds;
+ if (!client.GetHandlerCredentials(&creds)) {
+ return false;
+ }
+ pid = creds.pid;
+ }
+ if (pid > 0 && prctl(PR_SET_PTRACER, pid, 0, 0, 0) != 0) {
+ PLOG(WARNING) << "prctl";
+ // TODO(jperaza): If this call to set the ptracer failed, it might be
+ // possible to try again just before a dump request, in case the
+ // environment has changed. Revisit ExceptionHandlerClient::SetPtracer()
+ // and consider saving the result of this call in ExceptionHandlerClient
+ // or as a member in this signal handler. ExceptionHandlerClient hasn't
+ // been responsible for maintaining state and a new ExceptionHandlerClient
+ // has been constructed as a local whenever a client needs to communicate
+ // with the handler. ExceptionHandlerClient lifetimes and ownership will
+ // need to be reconsidered if it becomes responsible for state.
+ }
+ sock_to_handler_.reset(sock.release());
+ handler_pid_ = pid;
+ return Install(unhandled_signals);
+ }
+
+ bool GetHandlerSocket(int* sock, pid_t* pid) {
+ if (!sock_to_handler_.is_valid()) {
+ return false;
+ }
+ if (sock) {
+ *sock = sock_to_handler_.get();
+ }
+ if (pid) {
+ *pid = handler_pid_;
+ }
+ return true;
+ }
+
+ void HandleCrashImpl() override {
+ ExceptionHandlerProtocol::ClientInformation info = {};
+ info.exception_information_address =
+ FromPointerCast<VMAddress>(&GetExceptionInfo());
+#if defined(OS_CHROMEOS)
+ info.crash_loop_before_time = crash_loop_before_time_;
+#endif
+
+ ExceptionHandlerClient client(sock_to_handler_.get(), true);
+ client.RequestCrashDump(info);
+ }
+
+#if defined(OS_CHROMEOS)
+ void SetCrashLoopBefore(uint64_t crash_loop_before_time) {
+ crash_loop_before_time_ = crash_loop_before_time;
+ }
+#endif
+
+ private:
+ RequestCrashDumpHandler() = default;
+
+ ~RequestCrashDumpHandler() = delete;
+
+ ScopedFileHandle sock_to_handler_;
+ pid_t handler_pid_ = -1;
+
+#if defined(OS_CHROMEOS)
+ // An optional UNIX timestamp passed to us from Chrome.
+ // This will pass to crashpad_handler and then to Chrome OS crash_reporter.
+ // This should really be a time_t, but it's basically an opaque value (we
+ // don't anything with it except pass it along).
+ uint64_t crash_loop_before_time_ = 0;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(RequestCrashDumpHandler);
+};
} // namespace
@@ -160,14 +373,134 @@
const std::vector<std::string>& arguments,
bool restartable,
bool asynchronous_start) {
- // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
- // supports accepting new connections.
- // https://crashpad.chromium.org/bug/30
- NOTREACHED();
- return false;
+ DCHECK(!asynchronous_start);
+
+ ScopedFileHandle client_sock, handler_sock;
+ if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,
+ &handler_sock)) {
+ return false;
+ }
+
+ std::vector<std::string> argv = BuildHandlerArgvStrings(
+ handler, database, metrics_dir, url, annotations, arguments);
+
+ argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
+ argv.push_back("--shared-client-connection");
+ if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
+ return false;
+ }
+
+ pid_t handler_pid = -1;
+ if (!IsRegularFile(base::FilePath("/proc/sys/kernel/yama/ptrace_scope"))) {
+ handler_pid = 0;
+ }
+
+ auto signal_handler = RequestCrashDumpHandler::Get();
+ return signal_handler->Initialize(
+ std::move(client_sock), handler_pid, &unhandled_signals_);
+}
+
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+// static
+bool CrashpadClient::GetHandlerSocket(int* sock, pid_t* pid) {
+ auto signal_handler = RequestCrashDumpHandler::Get();
+ return signal_handler->GetHandlerSocket(sock, pid);
+}
+
+bool CrashpadClient::SetHandlerSocket(ScopedFileHandle sock, pid_t pid) {
+ auto signal_handler = RequestCrashDumpHandler::Get();
+ return signal_handler->Initialize(std::move(sock), pid, &unhandled_signals_);
+}
+#endif // OS_ANDROID || OS_LINUX
+
+#if defined(OS_ANDROID)
+
+bool CrashpadClient::StartJavaHandlerAtCrash(
+ const std::string& class_name,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments) {
+ std::vector<std::string> argv = BuildAppProcessArgs(class_name,
+ database,
+ metrics_dir,
+ url,
+ annotations,
+ arguments,
+ kInvalidFileHandle);
+
+ auto signal_handler = LaunchAtCrashHandler::Get();
+ return signal_handler->Initialize(&argv, env, &unhandled_signals_);
}
// static
+bool CrashpadClient::StartJavaHandlerForClient(
+ const std::string& class_name,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket) {
+ std::vector<std::string> argv = BuildAppProcessArgs(
+ class_name, database, metrics_dir, url, annotations, arguments, socket);
+ return DoubleForkAndExec(argv, env, socket, false, nullptr);
+}
+
+bool CrashpadClient::StartHandlerWithLinkerAtCrash(
+ const std::string& handler_trampoline,
+ const std::string& handler_library,
+ bool is_64_bit,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments) {
+ std::vector<std::string> argv =
+ BuildArgsToLaunchWithLinker(handler_trampoline,
+ handler_library,
+ is_64_bit,
+ database,
+ metrics_dir,
+ url,
+ annotations,
+ arguments,
+ kInvalidFileHandle);
+ auto signal_handler = LaunchAtCrashHandler::Get();
+ return signal_handler->Initialize(&argv, env, &unhandled_signals_);
+}
+
+// static
+bool CrashpadClient::StartHandlerWithLinkerForClient(
+ const std::string& handler_trampoline,
+ const std::string& handler_library,
+ bool is_64_bit,
+ const std::vector<std::string>* env,
+ const base::FilePath& database,
+ const base::FilePath& metrics_dir,
+ const std::string& url,
+ const std::map<std::string, std::string>& annotations,
+ const std::vector<std::string>& arguments,
+ int socket) {
+ std::vector<std::string> argv =
+ BuildArgsToLaunchWithLinker(handler_trampoline,
+ handler_library,
+ is_64_bit,
+ database,
+ metrics_dir,
+ url,
+ annotations,
+ arguments,
+ socket);
+ return DoubleForkAndExec(argv, env, socket, false, nullptr);
+}
+
+#endif
+
bool CrashpadClient::StartHandlerAtCrash(
const base::FilePath& handler,
const base::FilePath& database,
@@ -179,12 +512,7 @@
handler, database, metrics_dir, url, annotations, arguments);
auto signal_handler = LaunchAtCrashHandler::Get();
- if (signal_handler->Initialize(&argv)) {
- DCHECK(!g_crash_handler);
- g_crash_handler = signal_handler;
- return true;
- }
- return false;
+ return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
}
// static
@@ -199,14 +527,17 @@
std::vector<std::string> argv = BuildHandlerArgvStrings(
handler, database, metrics_dir, url, annotations, arguments);
- argv.push_back(FormatArgumentInt("initial-client", socket));
+ argv.push_back(FormatArgumentInt("initial-client-fd", socket));
- return DoubleForkAndExec(argv, socket, true, nullptr);
+ return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
}
// static
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
- DCHECK(g_crash_handler);
+ if (!SignalHandler::Get()) {
+ DLOG(ERROR) << "Crashpad isn't enabled";
+ return;
+ }
#if defined(ARCH_CPU_ARMEL)
memset(context->uc_regspace, 0, sizeof(context->uc_regspace));
@@ -220,15 +551,34 @@
siginfo.si_signo = Signals::kSimulatedSigno;
siginfo.si_errno = 0;
siginfo.si_code = 0;
- g_crash_handler->HandleCrashNonFatal(
+ SignalHandler::Get()->HandleCrash(
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
}
// static
+void CrashpadClient::CrashWithoutDump(const std::string& message) {
+ SignalHandler::DisableForThread();
+ LOG(FATAL) << message;
+}
+
+// static
void CrashpadClient::SetFirstChanceExceptionHandler(
FirstChanceHandler handler) {
- DCHECK(g_crash_handler);
- g_crash_handler->SetFirstChanceHandler(handler);
+ DCHECK(SignalHandler::Get());
+ SignalHandler::Get()->SetFirstChanceHandler(handler);
}
+void CrashpadClient::SetUnhandledSignals(const std::set<int>& signals) {
+ DCHECK(!SignalHandler::Get());
+ unhandled_signals_ = signals;
+}
+
+#if defined(OS_CHROMEOS)
+// static
+void CrashpadClient::SetCrashLoopBefore(uint64_t crash_loop_before_time) {
+ auto request_crash_dump_handler = RequestCrashDumpHandler::Get();
+ request_crash_dump_handler->SetCrashLoopBefore(crash_loop_before_time);
+}
+#endif
+
} // namespace crashpad
diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc
index 4c5bf4d..2bfda5a 100644
--- a/client/crashpad_client_linux_test.cc
+++ b/client/crashpad_client_linux_test.cc
@@ -14,8 +14,8 @@
#include "client/crashpad_client.h"
+#include <dlfcn.h>
#include <stdlib.h>
-#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -37,76 +37,98 @@
#include "util/file/filesystem.h"
#include "util/linux/exception_handler_client.h"
#include "util/linux/exception_information.h"
+#include "util/linux/socket.h"
#include "util/misc/address_types.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/signals.h"
+#if defined(OS_ANDROID)
+#include <android/set_abort_message.h>
+#include "dlfcn_internal.h"
+
+// Normally this comes from set_abort_message.h, but only at API level 21.
+extern "C" void android_set_abort_message(const char* msg)
+ __attribute__((weak));
+#endif
+
namespace crashpad {
namespace test {
namespace {
+struct StartHandlerForSelfTestOptions {
+ bool start_handler_at_crash;
+ bool simulate_crash;
+ bool set_first_chance_handler;
+};
+
+class StartHandlerForSelfTest
+ : public testing::TestWithParam<std::tuple<bool, bool, bool>> {
+ public:
+ StartHandlerForSelfTest() = default;
+ ~StartHandlerForSelfTest() = default;
+
+ void SetUp() override {
+ std::tie(options_.start_handler_at_crash,
+ options_.simulate_crash,
+ options_.set_first_chance_handler) = GetParam();
+ }
+
+ const StartHandlerForSelfTestOptions& Options() const { return options_; }
+
+ private:
+ StartHandlerForSelfTestOptions options_;
+
+ DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfTest);
+};
+
bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
return true;
}
-TEST(CrashpadClient, SimulateCrash) {
- ScopedTempDir temp_dir;
-
- base::FilePath handler_path = TestPaths::Executable().DirName().Append(
- FILE_PATH_LITERAL("crashpad_handler"));
-
- crashpad::CrashpadClient client;
- ASSERT_TRUE(client.StartHandlerAtCrash(handler_path,
- base::FilePath(temp_dir.path()),
- base::FilePath(),
- "",
- std::map<std::string, std::string>(),
- std::vector<std::string>()));
-
- auto database =
- CrashReportDatabase::InitializeWithoutCreating(temp_dir.path());
- ASSERT_TRUE(database);
-
- {
- CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
-
- CRASHPAD_SIMULATE_CRASH();
-
- std::vector<CrashReportDatabase::Report> reports;
- ASSERT_EQ(database->GetPendingReports(&reports),
- CrashReportDatabase::kNoError);
- EXPECT_EQ(reports.size(), 0u);
-
- reports.clear();
- ASSERT_EQ(database->GetCompletedReports(&reports),
- CrashReportDatabase::kNoError);
- EXPECT_EQ(reports.size(), 0u);
- }
-
- {
- CrashpadClient::SetFirstChanceExceptionHandler(nullptr);
-
- CRASHPAD_SIMULATE_CRASH();
-
- std::vector<CrashReportDatabase::Report> reports;
- ASSERT_EQ(database->GetPendingReports(&reports),
- CrashReportDatabase::kNoError);
- EXPECT_EQ(reports.size(), 1u);
-
- reports.clear();
- ASSERT_EQ(database->GetCompletedReports(&reports),
- CrashReportDatabase::kNoError);
- EXPECT_EQ(reports.size(), 0u);
- }
+bool InstallHandler(CrashpadClient* client,
+ bool start_at_crash,
+ const base::FilePath& handler_path,
+ const base::FilePath& database_path) {
+ return start_at_crash
+ ? client->StartHandlerAtCrash(handler_path,
+ database_path,
+ base::FilePath(),
+ "",
+ std::map<std::string, std::string>(),
+ std::vector<std::string>())
+ : client->StartHandler(handler_path,
+ database_path,
+ base::FilePath(),
+ "",
+ std::map<std::string, std::string>(),
+ std::vector<std::string>(),
+ false,
+ false);
}
constexpr char kTestAnnotationName[] = "name_of_annotation";
constexpr char kTestAnnotationValue[] = "value_of_annotation";
+#if defined(OS_ANDROID)
+constexpr char kTestAbortMessage[] = "test abort message";
+#endif
+
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
ProcessSnapshotMinidump minidump_snapshot;
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
+#if defined(OS_ANDROID)
+ // This part of the test requires Q. The API level on Q devices will be 28
+ // until the API is finalized, so we can't check API level yet. For now, test
+ // for the presence of a libc symbol which was introduced in Q.
+ if (crashpad::internal::Dlsym(RTLD_DEFAULT, "android_fdsan_close_with_tag")) {
+ const auto& annotations = minidump_snapshot.AnnotationsSimpleMap();
+ auto abort_message = annotations.find("abort_message");
+ ASSERT_NE(annotations.end(), abort_message);
+ EXPECT_EQ(kTestAbortMessage, abort_message->second);
+ }
+#endif
+
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
if (static_cast<Annotation::Type>(annotation.type) !=
@@ -126,7 +148,7 @@
ADD_FAILURE();
}
-CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
+CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
VMSize temp_dir_length;
@@ -135,6 +157,9 @@
std::string temp_dir(temp_dir_length, '\0');
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
+ StartHandlerForSelfTestOptions options;
+ CheckedReadFileExactly(in, &options, sizeof(options));
+
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_handler"));
@@ -144,26 +169,41 @@
test_annotation.Set(kTestAnnotationValue);
crashpad::CrashpadClient client;
- if (!client.StartHandlerAtCrash(handler_path,
- base::FilePath(temp_dir),
- base::FilePath(),
- "",
- std::map<std::string, std::string>(),
- std::vector<std::string>())) {
+ if (!InstallHandler(&client,
+ options.start_handler_at_crash,
+ handler_path,
+ base::FilePath(temp_dir))) {
return EXIT_FAILURE;
}
+#if defined(OS_ANDROID)
+ if (android_set_abort_message) {
+ android_set_abort_message(kTestAbortMessage);
+ }
+#endif
+
+ if (options.simulate_crash) {
+ if (options.set_first_chance_handler) {
+ client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
+ }
+ CRASHPAD_SIMULATE_CRASH();
+ return EXIT_SUCCESS;
+ }
+
__builtin_trap();
NOTREACHED();
return EXIT_SUCCESS;
}
-class StartHandlerAtCrashTest : public MultiprocessExec {
+class StartHandlerForSelfInChildTest : public MultiprocessExec {
public:
- StartHandlerAtCrashTest() : MultiprocessExec() {
- SetChildTestMainFunction("StartHandlerAtCrashChild");
- SetExpectedChildTerminationBuiltinTrap();
+ StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
+ : MultiprocessExec(), options_(options) {
+ SetChildTestMainFunction("StartHandlerForSelfTestChild");
+ if (!options.simulate_crash) {
+ SetExpectedChildTerminationBuiltinTrap();
+ }
}
private:
@@ -174,6 +214,8 @@
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
ASSERT_TRUE(LoggingWriteFile(
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
+ ASSERT_TRUE(
+ LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
// Wait for child to finish.
CheckedReadFileAtEOF(ReadPipeHandle());
@@ -189,7 +231,11 @@
reports.clear();
ASSERT_EQ(database->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
- EXPECT_EQ(reports.size(), 1u);
+ ASSERT_EQ(reports.size(), options_.set_first_chance_handler ? 0u : 1u);
+
+ if (options_.set_first_chance_handler) {
+ return;
+ }
std::unique_ptr<const CrashReportDatabase::UploadReport> report;
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
@@ -197,14 +243,26 @@
ValidateDump(report.get());
}
- DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest);
+ StartHandlerForSelfTestOptions options_;
+
+ DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfInChildTest);
};
-TEST(CrashpadClient, StartHandlerAtCrash) {
- StartHandlerAtCrashTest test;
+TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
+ if (Options().set_first_chance_handler && !Options().simulate_crash) {
+ // TODO(jperaza): test first chance handlers with real crashes.
+ return;
+ }
+ StartHandlerForSelfInChildTest test(Options());
test.Run();
}
+INSTANTIATE_TEST_SUITE_P(StartHandlerForSelfTestSuite,
+ StartHandlerForSelfTest,
+ testing::Combine(testing::Bool(),
+ testing::Bool(),
+ testing::Bool()));
+
// Test state for starting the handler for another process.
class StartHandlerForClientTest {
public:
@@ -213,16 +271,8 @@
bool Initialize(bool sanitize) {
sanitize_ = sanitize;
-
- int socks[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) {
- PLOG(ERROR) << "socketpair";
- return false;
- }
- client_sock_.reset(socks[0]);
- server_sock_.reset(socks[1]);
-
- return true;
+ return UnixCredentialSocket::CreateCredentialSocketpair(&client_sock_,
+ &server_sock_);
}
bool StartHandlerOnDemand() {
@@ -298,7 +348,7 @@
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
auto state = Get();
- char c;
+ char c = 0;
CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c)));
ExceptionInformation exception_information;
@@ -310,7 +360,7 @@
context);
exception_information.thread_id = syscall(SYS_gettid);
- ClientInformation info;
+ ExceptionHandlerProtocol::ClientInformation info;
info.exception_information_address =
FromPointerCast<decltype(info.exception_information_address)>(
&exception_information);
@@ -324,7 +374,7 @@
FromPointerCast<VMAddress>(&sanitization_info);
}
- ExceptionHandlerClient handler_client(state->client_sock_);
+ ExceptionHandlerClient handler_client(state->client_sock_, false);
CHECK_EQ(handler_client.RequestCrashDump(info), 0);
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc
index 4a40820..2fa8729 100644
--- a/client/crashpad_client_mac.cc
+++ b/client/crashpad_client_mac.cc
@@ -26,6 +26,7 @@
#include "base/mac/mach_logging.h"
#include "base/strings/stringprintf.h"
#include "util/mac/mac_util.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
@@ -338,6 +339,7 @@
// this interface.
if (!DoubleForkAndExec(
argv,
+ nullptr,
server_write_fd.get(),
true,
restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {
diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc
index 8ceded3..4963f24 100644
--- a/client/crashpad_client_win.cc
+++ b/client/crashpad_client_win.cc
@@ -15,6 +15,7 @@
#include "client/crashpad_client.h"
#include <windows.h>
+
#include <signal.h>
#include <stdint.h>
#include <string.h>
@@ -35,10 +36,12 @@
#include "util/misc/random_string.h"
#include "util/win/address_types.h"
#include "util/win/command_line.h"
+#include "util/win/context_wrappers.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"
#include "util/win/handle.h"
#include "util/win/initial_client_data.h"
+#include "util/win/loader_lock.h"
#include "util/win/nt_internals.h"
#include "util/win/ntstatus_logging.h"
#include "util/win/process_info.h"
@@ -73,10 +76,10 @@
ExceptionInformation g_non_crash_exception_information;
enum class StartupState : int {
- kNotReady = 0, // This must be value 0 because it is the initial value of a
- // global AtomicWord.
+ kNotReady = 0, // This must be value 0 because it is the initial value of a
+ // global AtomicWord.
kSucceeded = 1, // The CreateProcess() for the handler succeeded.
- kFailed = 2, // The handler failed to start.
+ kFailed = 2, // The handler failed to start.
};
// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and
@@ -93,9 +96,8 @@
CRITICAL_SECTION g_critical_section_with_debug_info;
void SetHandlerStartupState(StartupState state) {
- DCHECK(state == StartupState::kSucceeded ||
- state == StartupState::kFailed);
- base::subtle::Acquire_Store(&g_handler_startup_state,
+ DCHECK(state == StartupState::kSucceeded || state == StartupState::kFailed);
+ base::subtle::Release_Store(&g_handler_startup_state,
static_cast<base::subtle::AtomicWord>(state));
}
@@ -103,7 +105,7 @@
// Wait until we know the handler has either succeeded or failed to start.
base::subtle::AtomicWord startup_state;
while (
- (startup_state = base::subtle::Release_Load(&g_handler_startup_state)) ==
+ (startup_state = base::subtle::Acquire_Load(&g_handler_startup_state)) ==
static_cast<int>(StartupState::kNotReady)) {
Sleep(1);
}
@@ -187,11 +189,7 @@
EXCEPTION_RECORD record = {};
record.ExceptionCode = STATUS_FATAL_APP_EXIT;
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
-#if defined(ARCH_CPU_64_BITS)
- record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
-#else
- record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
-#endif // ARCH_CPU_64_BITS
+ record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
EXCEPTION_POINTERS exception_pointers;
exception_pointers.ContextRecord = &context;
@@ -349,6 +347,8 @@
bool StartHandlerProcess(
std::unique_ptr<BackgroundHandlerStartThreadData> data) {
+ CHECK(!IsThreadInLoaderLock());
+
ScopedCallSetHandlerStartupState scoped_startup_state_caller;
std::wstring command_line;
@@ -476,17 +476,34 @@
}
}
+ // If the embedded crashpad handler is being started via an entry point in a
+ // DLL (the handler executable is rundll32.exe), then don't pass
+ // the application name to CreateProcess as this appears to generate an
+ // invalid command line where the first argument needed by rundll32 is not in
+ // the correct format as required in:
+ // https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface
+ const base::StringPiece16 kRunDll32Exe(L"rundll32.exe");
+ bool is_embedded_in_dll = false;
+ if (data->handler.value().size() >= kRunDll32Exe.size() &&
+ _wcsicmp(data->handler.value()
+ .substr(data->handler.value().size() - kRunDll32Exe.size())
+ .c_str(),
+ kRunDll32Exe.data()) == 0) {
+ is_embedded_in_dll = true;
+ }
+
PROCESS_INFORMATION process_info;
- rv = CreateProcess(data->handler.value().c_str(),
- &command_line[0],
- nullptr,
- nullptr,
- true,
- creation_flags,
- nullptr,
- nullptr,
- &startup_info.StartupInfo,
- &process_info);
+ rv = CreateProcess(
+ is_embedded_in_dll ? nullptr : data->handler.value().c_str(),
+ &command_line[0],
+ nullptr,
+ nullptr,
+ true,
+ creation_flags,
+ nullptr,
+ nullptr,
+ &startup_info.StartupInfo,
+ &process_info);
if (!rv) {
PLOG(ERROR) << "CreateProcess";
return false;
@@ -756,11 +773,7 @@
constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed;
EXCEPTION_RECORD record = {};
record.ExceptionCode = kSimulatedExceptionCode;
-#if defined(ARCH_CPU_64_BITS)
- record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
-#else
- record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
-#endif // ARCH_CPU_64_BITS
+ record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
exception_pointers.ExceptionRecord = &record;
diff --git a/client/crashpad_client_win_test.cc b/client/crashpad_client_win_test.cc
index 99778ba..b950699 100644
--- a/client/crashpad_client_win_test.cc
+++ b/client/crashpad_client_win_test.cc
@@ -17,7 +17,6 @@
#include <vector>
#include "base/files/file_path.h"
-#include "base/macros.h"
#include "base/logging.h"
#include "gtest/gtest.h"
#include "test/test_paths.h"
diff --git a/client/crashpad_info.h b/client/crashpad_info.h
index 5db9a6c..ed7b9c1 100644
--- a/client/crashpad_info.h
+++ b/client/crashpad_info.h
@@ -141,15 +141,15 @@
//!
//! When handling an exception, the Crashpad handler will scan all modules in
//! a process. The first one that has a CrashpadInfo structure populated with
- //! a value other than #kUnset for this field will dictate whether the handler
- //! is functional or not. If all modules with a CrashpadInfo structure specify
- //! #kUnset, the handler will be enabled. If disabled, the Crashpad handler
- //! will still run and receive exceptions, but will not take any action on an
- //! exception on its own behalf, except for the action necessary to determine
- //! that it has been disabled.
+ //! a value other than TriState::kUnset for this field will dictate whether
+ //! the handler is functional or not. If all modules with a CrashpadInfo
+ //! structure specify TriState::kUnset, the handler will be enabled. If
+ //! disabled, the Crashpad handler will still run and receive exceptions, but
+ //! will not take any action on an exception on its own behalf, except for the
+ //! action necessary to determine that it has been disabled.
//!
- //! The Crashpad handler should not normally be disabled. More commonly, it
- //! is appropriate to disable crash report upload by calling
+ //! The Crashpad handler should not normally be disabled. More commonly, it is
+ //! appropriate to disable crash report upload by calling
//! Settings::SetUploadsEnabled().
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
crashpad_handler_behavior_ = crashpad_handler_behavior;
@@ -160,15 +160,15 @@
//!
//! When handling an exception, the Crashpad handler will scan all modules in
//! a process. The first one that has a CrashpadInfo structure populated with
- //! a value other than #kUnset for this field will dictate whether the
- //! exception is forwarded to the system’s crash reporter. If all modules with
- //! a CrashpadInfo structure specify #kUnset, forwarding will be enabled.
- //! Unless disabled, forwarding may still occur if the Crashpad handler is
- //! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled,
- //! the Crashpad handler may choose not to forward all exceptions to the
- //! system’s crash reporter in cases where it has reason to believe that the
- //! system’s crash reporter would not normally have handled the exception in
- //! Crashpad’s absence.
+ //! a value other than TriState::kUnset for this field will dictate whether
+ //! the exception is forwarded to the system’s crash reporter. If all modules
+ //! with a CrashpadInfo structure specify TriState::kUnset, forwarding will be
+ //! enabled. Unless disabled, forwarding may still occur if the Crashpad
+ //! handler is disabled by SetCrashpadHandlerState(). Even when forwarding is
+ //! enabled, the Crashpad handler may choose not to forward all exceptions to
+ //! the system’s crash reporter in cases where it has reason to believe that
+ //! the system’s crash reporter would not normally have handled the exception
+ //! in Crashpad’s absence.
void set_system_crash_reporter_forwarding(
TriState system_crash_reporter_forwarding) {
system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;
@@ -179,8 +179,8 @@
//!
//! When handling an exception, the Crashpad handler will scan all modules in
//! a process. The first one that has a CrashpadInfo structure populated with
- //! a value other than #kUnset for this field will dictate whether the extra
- //! memory is captured.
+ //! a value other than TriState::kUnset for this field will dictate whether
+ //! the extra memory is captured.
//!
//! This causes Crashpad to include pages of data referenced by locals or
//! other stack memory. Turning this on can increase the size of the minidump
@@ -208,7 +208,7 @@
//! Note that streams will appear in the minidump in the reverse order to
//! which they are added.
//!
- //! TODO(scottmg) This is currently only supported on Windows.
+ //! TODO(scottmg) This is currently not supported on Mac.
//!
//! \param[in] stream_type The stream type identifier to use. This should be
//! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`
diff --git a/client/crashpad_info_note.S b/client/crashpad_info_note.S
index 4c29298..b13d864 100644
--- a/client/crashpad_info_note.S
+++ b/client/crashpad_info_note.S
@@ -26,16 +26,13 @@
#define NOTE_ALIGN 4
// This section must be "a"llocated so that it appears in the final binary at
- // runtime, and "w"ritable so that the relocation to CRASHPAD_INFO_SYMBOL can
- // be performed.
- .section .note.crashpad.info,"aw",%note
+ // runtime. The reference to CRASHPAD_INFO_SYMBOL uses an offset relative to
+ // this note to avoid making this note writable, which triggers a bug in GNU
+ // ld, or adding text relocations which require the target system to allow
+ // making text segments writable. https://crbug.com/crashpad/260.
+ .section .note.crashpad.info,"a",%note
.balign NOTE_ALIGN
- # .globl indicates that it's available to link against other .o files. .hidden
- # indicates that it will not appear in the executable's symbol table.
- .globl CRASHPAD_NOTE_REFERENCE
- .hidden CRASHPAD_NOTE_REFERENCE
- .type CRASHPAD_NOTE_REFERENCE, %object
-CRASHPAD_NOTE_REFERENCE:
+CRASHPAD_NOTE:
.long name_end - name // namesz
.long desc_end - desc // descsz
.long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type
@@ -45,15 +42,26 @@
.balign NOTE_ALIGN
desc:
#if defined(__LP64__)
- .quad CRASHPAD_INFO_SYMBOL
+ .quad CRASHPAD_INFO_SYMBOL - desc
#else
-#if defined(__LITTLE_ENDIAN__)
- .long CRASHPAD_INFO_SYMBOL
- .long 0
-#else
- .long 0
- .long CRASHPAD_INFO_SYMBOL
-#endif // __LITTLE_ENDIAN__
+ .long CRASHPAD_INFO_SYMBOL - desc
#endif // __LP64__
desc_end:
- .size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE
+ .size CRASHPAD_NOTE, .-CRASHPAD_NOTE
+
+ // CRASHPAD_NOTE can't be referenced directly by GetCrashpadInfo() because the
+ // relocation used to make the reference may require that the address be
+ // 8-byte aligned and notes must have 4-byte alignment.
+ .section .rodata,"a",%progbits
+ .balign 8
+ # .globl indicates that it's available to link against other .o files. .hidden
+ # indicates that it will not appear in the executable's symbol table.
+ .globl CRASHPAD_NOTE_REFERENCE
+ .hidden CRASHPAD_NOTE_REFERENCE
+ .type CRASHPAD_NOTE_REFERENCE, %object
+CRASHPAD_NOTE_REFERENCE:
+ // The value of this quad isn't important. It exists to reference
+ // CRASHPAD_NOTE, causing the linker to include the note into the binary
+ // linking Crashpad. The subtraction from |name| is a convenience to allow the
+ // value to be computed statically.
+ .quad name - CRASHPAD_NOTE
diff --git a/client/prune_crash_reports.cc b/client/prune_crash_reports.cc
index d045eb6..8eed18d 100644
--- a/client/prune_crash_reports.cc
+++ b/client/prune_crash_reports.cc
@@ -15,6 +15,7 @@
#include "client/prune_crash_reports.h"
#include <sys/stat.h>
+#include <stdint.h>
#include <algorithm>
#include <vector>
@@ -24,7 +25,7 @@
namespace crashpad {
-void PruneCrashReportDatabase(CrashReportDatabase* database,
+size_t PruneCrashReportDatabase(CrashReportDatabase* database,
PruneCondition* condition) {
std::vector<CrashReportDatabase::Report> all_reports;
CrashReportDatabase::OperationStatus status;
@@ -32,14 +33,14 @@
status = database->GetPendingReports(&all_reports);
if (status != CrashReportDatabase::kNoError) {
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports";
- return;
+ return 0;
}
std::vector<CrashReportDatabase::Report> completed_reports;
status = database->GetCompletedReports(&completed_reports);
if (status != CrashReportDatabase::kNoError) {
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports";
- return;
+ return 0;
}
all_reports.insert(all_reports.end(), completed_reports.begin(),
completed_reports.end());
@@ -50,16 +51,21 @@
return lhs.creation_time > rhs.creation_time;
});
+ size_t num_pruned = 0;
for (const auto& report : all_reports) {
if (condition->ShouldPruneReport(report)) {
status = database->DeleteReport(report.uuid);
if (status != CrashReportDatabase::kNoError) {
LOG(ERROR) << "Database Pruning: Failed to remove report "
<< report.uuid.ToString();
+ } else {
+ num_pruned++;
}
}
}
+ return num_pruned;
+
// TODO(rsesek): For databases that do not use a directory structure, it is
// possible for the metadata sidecar to become corrupted and thus leave
// orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66
@@ -96,19 +102,9 @@
bool DatabaseSizePruneCondition::ShouldPruneReport(
const CrashReportDatabase::Report& report) {
-#if defined(OS_POSIX)
- struct stat statbuf;
- if (stat(report.file_path.value().c_str(), &statbuf) == 0) {
-#elif defined(OS_WIN)
- struct _stati64 statbuf;
- if (_wstat64(report.file_path.value().c_str(), &statbuf) == 0) {
-#else
-#error "Not implemented"
-#endif
- // Round up fractional KB to the next 1-KB boundary.
- measured_size_in_kb_ +=
- static_cast<size_t>((statbuf.st_size + 1023) / 1024);
- }
+ // Round up fractional KB to the next 1-KB boundary.
+ measured_size_in_kb_ +=
+ static_cast<size_t>((report.total_size + 1023) / 1024);
return measured_size_in_kb_ > max_size_in_kb_;
}
diff --git a/client/prune_crash_reports.h b/client/prune_crash_reports.h
index 6dac5f3..07a7098 100644
--- a/client/prune_crash_reports.h
+++ b/client/prune_crash_reports.h
@@ -37,8 +37,10 @@
//! \param[in] database The database from which crash reports will be deleted.
//! \param[in] condition The condition against which all reports in the database
//! will be evaluated.
-void PruneCrashReportDatabase(CrashReportDatabase* database,
- PruneCondition* condition);
+//!
+//! \return The number of deleted crash reports.
+size_t PruneCrashReportDatabase(CrashReportDatabase* database,
+ PruneCondition* condition);
std::unique_ptr<PruneCondition> GetDefaultDatabasePruneCondition();
diff --git a/client/prune_crash_reports_test.cc b/client/prune_crash_reports_test.cc
index 01990d2..dbe8c0a 100644
--- a/client/prune_crash_reports_test.cc
+++ b/client/prune_crash_reports_test.cc
@@ -15,14 +15,15 @@
#include "client/prune_crash_reports.h"
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
+#include <random>
#include <string>
#include <vector>
#include "base/numerics/safe_conversions.h"
-#include "base/rand_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/scoped_temp_dir.h"
@@ -81,56 +82,49 @@
}
TEST(PruneCrashReports, SizeCondition) {
- ScopedTempDir temp_dir;
-
CrashReportDatabase::Report report_1k;
- report_1k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file1024"));
+ report_1k.total_size = 1024u;
CrashReportDatabase::Report report_3k;
- report_3k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file3072"));
+ report_3k.total_size = 1024u * 3u;
+ CrashReportDatabase::Report report_unset_size;
{
- ScopedFileHandle scoped_file_1k(
- LoggingOpenFileForWrite(report_1k.file_path,
- FileWriteMode::kCreateOrFail,
- FilePermissions::kOwnerOnly));
- ASSERT_TRUE(scoped_file_1k.is_valid());
-
- std::string string;
- for (int i = 0; i < 128; ++i)
- string.push_back(static_cast<char>(i));
-
- for (size_t i = 0; i < 1024; i += string.size()) {
- ASSERT_TRUE(LoggingWriteFile(scoped_file_1k.get(),
- string.c_str(), string.length()));
- }
-
- ScopedFileHandle scoped_file_3k(
- LoggingOpenFileForWrite(report_3k.file_path,
- FileWriteMode::kCreateOrFail,
- FilePermissions::kOwnerOnly));
- ASSERT_TRUE(scoped_file_3k.is_valid());
-
- for (size_t i = 0; i < 3072; i += string.size()) {
- ASSERT_TRUE(LoggingWriteFile(scoped_file_3k.get(),
- string.c_str(), string.length()));
- }
- }
-
- {
- DatabaseSizePruneCondition condition(1);
+ DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);
+ // |report_1k| should not be pruned as the cumulated size is not past 1kB
+ // yet.
EXPECT_FALSE(condition.ShouldPruneReport(report_1k));
- EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
- }
-
- {
- DatabaseSizePruneCondition condition(1);
+ // |report_3k| should be pruned as the cumulated size is now past 1kB.
EXPECT_TRUE(condition.ShouldPruneReport(report_3k));
}
{
- DatabaseSizePruneCondition condition(6);
+ DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);
+ // |report_3k| should be pruned as the cumulated size is already past 1kB.
+ EXPECT_TRUE(condition.ShouldPruneReport(report_3k));
+ }
+
+ {
+ DatabaseSizePruneCondition condition(/*max_size_in_kb=*/6);
+ // |report_3k| should not be pruned as the cumulated size is not past 6kB
+ // yet.
EXPECT_FALSE(condition.ShouldPruneReport(report_3k));
+ // |report_3k| should not be pruned as the cumulated size is not past 6kB
+ // yet.
EXPECT_FALSE(condition.ShouldPruneReport(report_3k));
+ // |report_1k| should be pruned as the cumulated size is now past 6kB.
+ EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
+ }
+
+ {
+ DatabaseSizePruneCondition condition(/*max_size_in_kb=*/0);
+ // |report_unset_size| should not be pruned as its size is 0, regardless of
+ // how many times we try to prune it.
+ EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
+ EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
+ EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
+ EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
+ EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
+ // |report_1k| should be pruned as the cumulated size is now past 0kB.
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
}
}
@@ -211,18 +205,16 @@
using ::testing::Return;
using ::testing::SetArgPointee;
+ const size_t kNumReports = 10;
std::vector<CrashReportDatabase::Report> reports;
- for (int i = 0; i < 10; ++i) {
+ for (size_t i = 0; i < kNumReports; ++i) {
CrashReportDatabase::Report temp;
- temp.uuid.data_1 = i;
- temp.creation_time = NDaysAgo(i * 10);
+ temp.uuid.data_1 = static_cast<uint32_t>(i);
+ temp.creation_time = NDaysAgo(static_cast<int>(i) * 10);
reports.push_back(temp);
}
- // The randomness from std::rand() is not, so use a better rand() instead.
- const auto random_generator = [](ptrdiff_t rand_max) {
- return base::RandInt(0, base::checked_cast<int>(rand_max) - 1);
- };
- std::random_shuffle(reports.begin(), reports.end(), random_generator);
+ std::mt19937 urng(std::random_device{}());
+ std::shuffle(reports.begin(), reports.end(), urng);
std::vector<CrashReportDatabase::Report> pending_reports(
reports.begin(), reports.begin() + 5);
std::vector<CrashReportDatabase::Report> completed_reports(
@@ -241,7 +233,7 @@
}
StaticCondition delete_all(true);
- PruneCrashReportDatabase(&db, &delete_all);
+ EXPECT_EQ(PruneCrashReportDatabase(&db, &delete_all), kNumReports);
}
} // namespace
diff --git a/client/settings.cc b/client/settings.cc
index 20bd258..0aa525f 100644
--- a/client/settings.cc
+++ b/client/settings.cc
@@ -63,7 +63,8 @@
CheckedCloseFile(handle_);
}
if (!lockfile_path_.empty()) {
- DCHECK(LoggingRemoveFile(lockfile_path_));
+ const bool success = LoggingRemoveFile(lockfile_path_);
+ DCHECK(success);
}
}
@@ -84,8 +85,8 @@
#endif // OS_FUCHSIA
struct Settings::Data {
- static const uint32_t kSettingsMagic = 'CPds';
- static const uint32_t kSettingsVersion = 1;
+ static constexpr uint32_t kSettingsMagic = 'CPds';
+ static constexpr uint32_t kSettingsVersion = 1;
enum Options : uint32_t {
kUploadsEnabled = 1 << 0,
@@ -225,10 +226,10 @@
FileHandle handle;
if (log_open_error) {
handle = LoggingOpenFileForReadAndWrite(
- file_path(), mode, FilePermissions::kWorldReadable);
+ file_path(), mode, FilePermissions::kOwnerOnly);
} else {
handle = OpenFileForReadAndWrite(
- file_path(), mode, FilePermissions::kWorldReadable);
+ file_path(), mode, FilePermissions::kOwnerOnly);
}
return MakeScopedLockedFileHandle(
diff --git a/client/settings.h b/client/settings.h
index a2b0c74..5761c6b 100644
--- a/client/settings.h
+++ b/client/settings.h
@@ -75,6 +75,10 @@
//!
//! The default value is `false`.
//!
+ //! \note
+ //! This setting is ignored if --use-cros-crash-reporter is present
+ //! (which it will be if invoked by Chrome on ChromeOS).
+ //!
//! \param[out] enabled Whether crash reports should be uploaded.
//!
//! \return On success, returns `true`, otherwise returns `false` with an
diff --git a/client/simple_string_dictionary_test.cc b/client/simple_string_dictionary_test.cc
index 7af51a2..cd23216 100644
--- a/client/simple_string_dictionary_test.cc
+++ b/client/simple_string_dictionary_test.cc
@@ -73,7 +73,7 @@
EXPECT_FALSE(dict.GetValueForKey("key3"));
// Remove by setting value to nullptr
- dict.SetKeyValue("key2", nullptr);
+ dict.SetKeyValue("key2", base::StringPiece(nullptr, 0));
// Now make sure it's not there anymore
EXPECT_FALSE(dict.GetValueForKey("key2"));
@@ -254,13 +254,14 @@
TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) {
TSimpleStringDictionary<4, 6, 6> map;
- ASSERT_DEATH_CHECK(map.SetKeyValue(nullptr, "hello"), "key");
+ ASSERT_DEATH_CHECK(map.SetKeyValue(base::StringPiece(nullptr, 0), "hello"),
+ "key");
}
TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) {
TSimpleStringDictionary<4, 6, 6> map;
map.SetKeyValue("hi", "there");
- ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key");
+ ASSERT_DEATH_CHECK(map.GetValueForKey(base::StringPiece(nullptr, 0)), "key");
EXPECT_STREQ("there", map.GetValueForKey("hi"));
}
diff --git a/client/simulate_crash_mac.cc b/client/simulate_crash_mac.cc
index 1e2d8e4..43d05b8 100644
--- a/client/simulate_crash_mac.cc
+++ b/client/simulate_crash_mac.cc
@@ -20,7 +20,7 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "util/mach/exc_client_variants.h"
@@ -191,7 +191,7 @@
base::mac::ScopedMachSendRight thread(mach_thread_self());
exception_type_t exception = kMachExceptionSimulated;
mach_exception_data_type_t codes[] = {0, 0};
- mach_msg_type_number_t code_count = arraysize(codes);
+ mach_msg_type_number_t code_count = base::size(codes);
// Look up the handler for EXC_CRASH exceptions in the same way that the
// kernel would: try a thread handler, then a task handler, and finally a host
@@ -213,7 +213,7 @@
bool success = false;
for (size_t target_type_index = 0;
- !success && target_type_index < arraysize(kTargetTypes);
+ !success && target_type_index < base::size(kTargetTypes);
++target_type_index) {
ExceptionPorts::ExceptionHandlerVector handlers;
ExceptionPorts exception_ports(kTargetTypes[target_type_index],
diff --git a/client/simulate_crash_mac_test.cc b/client/simulate_crash_mac_test.cc
index 1d63ff6..c2f4c20 100644
--- a/client/simulate_crash_mac_test.cc
+++ b/client/simulate_crash_mac_test.cc
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -342,15 +343,13 @@
#endif
};
- for (size_t target_index = 0;
- target_index < arraysize(kTargets);
+ for (size_t target_index = 0; target_index < base::size(kTargets);
++target_index) {
TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
SCOPED_TRACE(base::StringPrintf(
"target_index %zu, target %d", target_index, target));
- for (size_t behavior_index = 0;
- behavior_index < arraysize(kBehaviors);
+ for (size_t behavior_index = 0; behavior_index < base::size(kBehaviors);
++behavior_index) {
exception_behavior_t behavior = kBehaviors[behavior_index];
SCOPED_TRACE(base::StringPrintf(
@@ -364,8 +363,7 @@
target, behavior, THREAD_STATE_NONE);
test_simulate_crash_mac.Run();
} else {
- for (size_t flavor_index = 0;
- flavor_index < arraysize(kFlavors);
+ for (size_t flavor_index = 0; flavor_index < base::size(kFlavors);
++flavor_index) {
thread_state_flavor_t flavor = kFlavors[flavor_index];
SCOPED_TRACE(base::StringPrintf(
diff --git a/codereview.settings b/codereview.settings
index d863097..976f8e5 100644
--- a/codereview.settings
+++ b/codereview.settings
@@ -13,7 +13,6 @@
# limitations under the License.
GERRIT_HOST: True
-GERRIT_SQUASH_UPLOADS: True
CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
PROJECT: crashpad
diff --git a/compat/BUILD.gn b/compat/BUILD.gn
index 2bfe074..9c22acf 100644
--- a/compat/BUILD.gn
+++ b/compat/BUILD.gn
@@ -17,8 +17,14 @@
config("compat_config") {
include_dirs = []
- if (crashpad_is_mac) {
+ if (crashpad_is_mac || crashpad_is_ios) {
include_dirs += [ "mac" ]
+ } else {
+ include_dirs += [ "non_mac" ]
+ }
+
+ if (crashpad_is_ios) {
+ include_dirs += [ "ios" ]
}
if (crashpad_is_linux || crashpad_is_android) {
@@ -34,10 +40,14 @@
} else {
include_dirs += [ "non_win" ]
}
+
+ if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
+ include_dirs += [ "non_elf" ]
+ }
}
template("compat_target") {
- if (crashpad_is_mac) {
+ if (crashpad_is_mac || crashpad_is_ios) {
# There are no sources to compile, which doesn’t mix will with a
# static_library.
group(target_name) {
@@ -53,21 +63,39 @@
compat_target("compat") {
sources = []
- if (crashpad_is_mac) {
+ if (crashpad_is_mac || crashpad_is_ios) {
sources += [
"mac/AvailabilityMacros.h",
"mac/kern/exc_resource.h",
"mac/mach-o/loader.h",
+ "mac/mach/i386/thread_state.h",
"mac/mach/mach.h",
"mac/sys/resource.h",
]
} else {
- sources += [ "non_mac/mach/mach.h" ]
+ sources += [
+ "non_mac/mach-o/loader.h",
+ "non_mac/mach/mach.h",
+ "non_mac/mach/machine.h",
+ "non_mac/mach/vm_prot.h",
+ ]
+ }
+
+ if (crashpad_is_ios) {
+ sources += [
+ "ios/mach/exc.defs",
+ "ios/mach/mach_exc.defs",
+ "ios/mach/mach_types.defs",
+ "ios/mach/machine/machine_types.defs",
+ "ios/mach/std_types.defs",
+ ]
}
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/signal.h",
+ "linux/sys/mman.cc",
+ "linux/sys/mman.h",
"linux/sys/ptrace.h",
"linux/sys/user.h",
]
@@ -75,6 +103,8 @@
if (crashpad_is_android) {
sources += [
+ "android/android/api-level.cc",
+ "android/android/api-level.h",
"android/dlfcn_internal.cc",
"android/dlfcn_internal.h",
"android/elf.h",
@@ -114,6 +144,10 @@
]
}
+ if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
+ sources += [ "non_elf/elf.h" ]
+ }
+
public_configs = [
":compat_config",
"..:crashpad_config",
@@ -121,7 +155,15 @@
deps = []
+ if (!crashpad_is_mac) {
+ deps += [ "../third_party/xnu" ]
+ }
+
if (crashpad_is_win) {
deps += [ "../third_party/getopt" ]
}
+
+ if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
+ deps += [ "../third_party/glibc" ]
+ }
}
diff --git a/compat/android/android/api-level.cc b/compat/android/android/api-level.cc
new file mode 100644
index 0000000..c29ec1a
--- /dev/null
+++ b/compat/android/android/api-level.cc
@@ -0,0 +1,50 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android/api-level.h>
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/system_properties.h>
+
+#include "dlfcn_internal.h"
+
+#if __NDK_MAJOR__ < 20
+
+extern "C" {
+
+int android_get_device_api_level() {
+ using FuncType = int (*)();
+ static const FuncType bionic_get_device_api_level =
+ reinterpret_cast<FuncType>(
+ crashpad::internal::Dlsym(RTLD_NEXT, "android_get_device_api_level"));
+
+ if (bionic_get_device_api_level) {
+ return bionic_get_device_api_level();
+ }
+
+ char api_string[PROP_VALUE_MAX];
+ int length = __system_property_get("ro.build.version.sdk", api_string);
+ if (length <= 0) {
+ return -1;
+ }
+
+ int api_level = atoi(api_string);
+ return api_level > 0 ? api_level : -1;
+}
+
+} // extern "C"
+
+#endif // __NDK_MAJOR__ < 20
diff --git a/compat/android/android/api-level.h b/compat/android/android/api-level.h
new file mode 100644
index 0000000..bfff9af
--- /dev/null
+++ b/compat/android/android/api-level.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
+#define CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
+
+#include_next <android/api-level.h>
+#include <android/ndk-version.h>
+
+#include <sys/cdefs.h>
+
+#if __NDK_MAJOR__ < 20
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Returns the API level of the device or -1 if it can't be determined. This
+// function is provided by NDK r20.
+int android_get_device_api_level();
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __NDK_MAJOR__ < 20
+
+#endif // CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
diff --git a/compat/android/sys/mman.cc b/compat/android/sys/mman.cc
index 4c29a9d..d890d84 100644
--- a/compat/android/sys/mman.cc
+++ b/compat/android/sys/mman.cc
@@ -35,7 +35,7 @@
namespace {
template <typename T>
-T Align(T value, uint8_t alignment) {
+T Align(T value, size_t alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
diff --git a/compat/compat.gyp b/compat/compat.gyp
index 1229ee0..6294d1f 100644
--- a/compat/compat.gyp
+++ b/compat/compat.gyp
@@ -20,6 +20,8 @@
{
'target_name': 'crashpad_compat',
'sources': [
+ 'android/android/api-level.cc',
+ 'android/android/api-level.h',
'android/dlfcn_internal.cc',
'android/dlfcn_internal.h',
'android/elf.h',
diff --git a/util/net/http_transport_none.cc b/compat/ios/mach/exc.defs
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/ios/mach/exc.defs
index 1c08c1f..d1648e9 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/ios/mach/exc.defs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
+#define CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
-#include "base/logging.h"
+#include "third_party/xnu/osfmk/mach/exc.defs"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
diff --git a/util/net/http_transport_none.cc b/compat/ios/mach/mach_exc.defs
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/ios/mach/mach_exc.defs
index 1c08c1f..c562128 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/ios/mach/mach_exc.defs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
+#define CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
-#include "base/logging.h"
+#include "third_party/xnu/osfmk/mach/mach_exc.defs"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
diff --git a/util/net/http_transport_none.cc b/compat/ios/mach/mach_types.defs
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/ios/mach/mach_types.defs
index 1c08c1f..dc18b8e 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/ios/mach/mach_types.defs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
+#define CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
-#include "base/logging.h"
+#include "third_party/xnu/osfmk/mach/mach_types.defs"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
diff --git a/util/net/http_transport_none.cc b/compat/ios/mach/machine/machine_types.defs
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/ios/mach/machine/machine_types.defs
index 1c08c1f..e906466 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/ios/mach/machine/machine_types.defs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
+#define CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
-#include "base/logging.h"
+#include "third_party/xnu/osfmk/mach/machine/machine_types.defs"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
diff --git a/util/net/http_transport_none.cc b/compat/ios/mach/std_types.defs
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/ios/mach/std_types.defs
index 1c08c1f..e49c6e4 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/ios/mach/std_types.defs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
+#define CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
-#include "base/logging.h"
+#include "third_party/xnu/osfmk/mach/std_types.defs"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
diff --git a/compat/linux/sys/mman.cc b/compat/linux/sys/mman.cc
new file mode 100644
index 0000000..12aaa2c
--- /dev/null
+++ b/compat/linux/sys/mman.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if defined(__GLIBC__)
+
+extern "C" {
+
+int memfd_create(const char* name, unsigned int flags) {
+ using MemfdCreateType = int (*)(const char*, int);
+ static const MemfdCreateType next_memfd_create =
+ reinterpret_cast<MemfdCreateType>(dlsym(RTLD_NEXT, "memfd_create"));
+ return next_memfd_create ? next_memfd_create(name, flags)
+ : syscall(SYS_memfd_create, name, flags);
+}
+
+} // extern "C"
+
+#endif // __GLIBC__
diff --git a/compat/linux/sys/mman.h b/compat/linux/sys/mman.h
new file mode 100644
index 0000000..61c55d7
--- /dev/null
+++ b/compat/linux/sys/mman.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
+#define CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
+
+#include_next <sys/mman.h>
+
+#include <features.h>
+
+// There's no memfd_create() wrapper before glibc 2.27.
+// This can't select for glibc < 2.27 because linux-chromeos-rel bots build this
+// code using a sysroot which has glibc 2.27, but then run it on Ubuntu 16.04,
+// which doesn't.
+#if defined(__GLIBC__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int memfd_create(const char* name, unsigned int flags);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __GLIBC__
+
+#endif // CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h
index c5e37a0..f8be372 100644
--- a/compat/linux/sys/ptrace.h
+++ b/compat/linux/sys/ptrace.h
@@ -26,7 +26,8 @@
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(25);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
-#elif defined(__arm__) || defined(__aarch64__)
+// https://bugs.chromium.org/p/chromium/issues/detail?id=873168
+#elif defined(__arm__) || (defined(__aarch64__) && __GLIBC_PREREQ(2,28))
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(22);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
diff --git a/compat/mac/AvailabilityMacros.h b/compat/mac/AvailabilityMacros.h
index f105f94..a83d2a4 100644
--- a/compat/mac/AvailabilityMacros.h
+++ b/compat/mac/AvailabilityMacros.h
@@ -59,4 +59,16 @@
#define MAC_OS_X_VERSION_10_13 101300
#endif
+// 10.14 SDK
+
+#ifndef MAC_OS_X_VERSION_10_14
+#define MAC_OS_X_VERSION_10_14 101400
+#endif
+
+// 10.15 SDK
+
+#ifndef MAC_OS_X_VERSION_10_15
+#define MAC_OS_X_VERSION_10_15 101500
+#endif
+
#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
diff --git a/util/net/http_transport_none.cc b/compat/non_elf/elf.h
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/non_elf/elf.h
index 1c08c1f..1245f16 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/non_elf/elf.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_NON_ELF_ELF_H_
+#define CRASHPAD_COMPAT_NON_ELF_ELF_H_
-#include "base/logging.h"
+#include "third_party/glibc/elf/elf.h"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_NON_ELF_ELF_H_
diff --git a/util/net/http_transport_none.cc b/compat/non_mac/mach-o/loader.h
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/non_mac/mach-o/loader.h
index 1c08c1f..98a8b5f 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/non_mac/mach-o/loader.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
+#define CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
-#include "base/logging.h"
+#include "third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h"
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
diff --git a/util/net/http_transport_none.cc b/compat/non_mac/mach/machine.h
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/non_mac/mach/machine.h
index 1c08c1f..54d4742 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/non_mac/mach/machine.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
+#define CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
-#include "base/logging.h"
+typedef int cpu_type_t;
+typedef int cpu_subtype_t;
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
diff --git a/util/net/http_transport_none.cc b/compat/non_mac/mach/vm_prot.h
similarity index 61%
copy from util/net/http_transport_none.cc
copy to compat/non_mac/mach/vm_prot.h
index 1c08c1f..1200179 100644
--- a/util/net/http_transport_none.cc
+++ b/compat/non_mac/mach/vm_prot.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
+#define CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
-#include "base/logging.h"
+typedef int vm_prot_t;
-namespace crashpad {
-
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h
index 5ce88b8..ded912e 100644
--- a/compat/non_win/dbghelp.h
+++ b/compat/non_win/dbghelp.h
@@ -214,7 +214,7 @@
//! `cpuid 0x80000001` `edx`.
//!
//! This field is only valid if #VendorId identifies the CPU vendor as
- //! “AuthenticAMD”.
+ //! “AuthenticAMD” or "HygonGenuine".
uint32_t AMDExtendedCpuFeatures;
} X86CpuInfo;
diff --git a/compat/win/sys/types.h b/compat/win/sys/types.h
index fcded87..e8fae88 100644
--- a/compat/win/sys/types.h
+++ b/compat/win/sys/types.h
@@ -20,6 +20,4 @@
#include <stdint.h>
-typedef unsigned int pid_t;
-
#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
diff --git a/doc/developing.md b/doc/developing.md
index 84d1cf7..431ca1d 100644
--- a/doc/developing.md
+++ b/doc/developing.md
@@ -106,68 +106,67 @@
[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s
no need to install them separately.
+#### Fuchsia
+
+In order to instruct gclient to download the Fuchsia SDK, you need to add the
+following to `~/crashpad/.gclient`.
+
+```
+target_os=["fuchsia"]
+```
+
+If you're using this tree to develop for multiple targets, you can also add
+other entries to the the list (e.g. `target_os=["fuchsia", "mac"]`).
+
+#### Optional Linux Configs
+
+To pull and use Crashpad's version of clang and sysroot, make the following
+changes.
+
+Add the following to `~/crashpad/.gclient`.
+```
+"custom_vars": { "pull_linux_clang": True },
+```
+Add these args to `out/Default/args.gn`.
+```
+clang_path = "//third_party/linux/clang/linux-amd64"
+target_sysroot = "//third_party/linux/sysroot"
+```
+
### Android
-Crashpad’s Android port is in its early stages. This build relies on
-cross-compilation. It’s possible to develop Crashpad for Android on any platform
-that the [Android NDK (Native Development
-Kit)](https://developer.android.com/ndk/) runs on.
+This build relies on cross-compilation. It’s possible to develop Crashpad for
+Android on any platform that the [Android NDK (Native Development Kit)]
+(https://developer.android.com/ndk/) runs on.
If it’s not already present on your system, [download the NDK package for your
system](https://developer.android.com/ndk/downloads/) and expand it to a
suitable location. These instructions assume that it’s been expanded to
-`~/android-ndk-r16`.
-
-To build Crashpad, portions of the NDK must be reassembled into a [standalone
-toolchain](https://developer.android.com/ndk/guides/standalone_toolchain.html).
-This is a repackaged subset of the NDK suitable for cross-compiling for a single
-Android architecture (such as `arm`, `arm64`, `x86`, and `x86_64`) targeting a
-specific [Android API
-level](https://source.android.com/source/build-numbers.html). The standalone
-toolchain only needs to be built from the NDK one time for each set of options
-desired. To build a standalone toolchain targeting 64-bit ARM and API level 21
-(Android 5.0 “Lollipop”), run:
-
-```
-$ cd ~
-$ python android-ndk-r16/build/tools/make_standalone_toolchain.py \
- --arch=arm64 --api=21 --install-dir=android-ndk-r16_arm64_api21
-```
+`~/android-ndk-r20`.
Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for
32-bit platforms. See Chrome’s
[`build/config/android/config.gni`](https://chromium.googlesource.com/chromium/src/+/master/build/config/android/config.gni)
which sets `_android_api_level` and `_android64_api_level`.
-To configure a Crashpad build for Android using the standalone toolchain
-assembled above, use `gyp_crashpad_android.py`. This script is a wrapper for
-`gyp_crashpad.py` that sets several environment variables directing the build to
-the standalone toolchain, and several GYP options to identify an Android build.
-This must be done after any `gclient sync`, or instead of any `gclient runhooks`
-operation.
+To configure a Crashpad build for Android use `gyp_crashpad_android.py`. This
+script is a wrapper for `gyp_crashpad.py` that sets several environment
+variables directing the build to the toolchain, and several GYP options to
+identify an Android build. This must be done after any `gclient sync`, or
+instead of any `gclient runhooks` operation.
```
$ cd ~/crashpad/crashpad
-$ python build/gyp_crashpad_android.py \
- --ndk ~/android-ndk-r16_arm64_api21 \
- --generator-output out/android_arm64_api21
+python build/gyp_crashpad_android.py \
+ --ndk ~/usr/lib/android-ndk-r20 --arch arm64 --api-level 21 \
+ --generator-output=out/android_arm64_api21 \
```
-`gyp_crashpad_android.py` detects the build type based on the characteristics of
-the standalone toolchain given in its `--ndk` argument.
-
-`gyp_crashpad_android.py` sets the build up to use Clang by default. It’s also
-possible to use GCC by providing the `--compiler=gcc` argument to
-`gyp_crashpad_android.py`.
-
-The Android port is incomplete, but targets known to be working include
-`crashpad_test`, `crashpad_util`, and their tests. This list will grow over
-time. To build, direct `ninja` to the specific `out` directory chosen by the
+To build, direct `ninja` to the specific `out` directory chosen by the
`--generator-output` argument to `gyp_crashpad_android.py`.
```
-$ ninja -C out/android_arm64_api21/out/Debug \
- crashpad_test_test crashpad_util_test
+$ ninja -C out/android_arm64_api21/out/Debug all
```
## Testing
@@ -193,6 +192,13 @@
$ python build/run_tests.py out/Debug
```
+To run a subset of the tests, use the --gtest\_filter flag, e.g., to run all the
+tests for MinidumpStringWriter:
+
+```sh
+$ python build/run_tests.py out/Debug --gtest_filter MinidumpStringWriter*
+```
+
### Windows
On Windows, `end_to_end_test.py` requires the CDB debugger, installed with
@@ -220,6 +226,25 @@
location on the detected or selected device, run them, and clean up after itself
when done.
+### Fuchsia
+
+To test on Fuchsia, you need a connected device running Fuchsia and then run:
+
+```sh
+$ gn gen out/fuchsia --args='target_os="fuchsia" target_cpu="x64" is_debug=true'
+$ ninja -C out/fuchsia
+$ python build/run_tests.py out/fuchsia
+```
+
+If you have multiple devices running, you will need to specify which device you
+want using their hostname, for instance:
+
+```sh
+$ export ZIRCON_NODENAME=scare-brook-skip-dried; \
+ python build/run_tests.py out/fuchsia; \
+ unset ZIRCON_NODENAME
+```
+
## Contributing
Crashpad’s contribution process is very similar to [Chromium’s contribution
@@ -303,7 +328,7 @@
## Buildbot
-The [Crashpad Buildbot](https://build.chromium.org/p/client.crashpad/) performs
-automated builds and tests of Crashpad. Before checking out or updating the
-Crashpad source code, and after checking in a new change, it is prudent to check
-the Buildbot to ensure that “the tree is green.”
+The [Crashpad Buildbot](https://ci.chromium.org/p/crashpad/g/main/console)
+performs automated builds and tests of Crashpad. Before checking out or updating
+the Crashpad source code, and after checking in a new change, it is prudent to
+check the Buildbot to ensure that “the tree is green.”
diff --git a/doc/overview_design.md b/doc/overview_design.md
index 07a7d65..9d0f0ed 100644
--- a/doc/overview_design.md
+++ b/doc/overview_design.md
@@ -240,7 +240,24 @@
#### Linux/Android
-TODO(mmentovai): describe this. See this preliminary doc.
+On Linux, a registration is a connected socket pair between a client process and
+the Crashpad handler. This socket pair may be private or shared among many
+client processes.
+
+##### Private Connections
+
+Private connections are the default registration mode when starting the handler
+process in response to a crash or on behalf of another client. This mode is
+required to use a ptrace broker, which is in turn required to trace Android
+isolated processes.
+
+##### Shared Connections
+
+Shared connections are the default mode when using a long-lived handler. The
+same connected socket pair may be shared among any number of clients. The socket
+pair is created by the first process to start the handler at which point the
+client socket end may be shared with other clients by any convenient means (e.g.
+inheritance).
### Capturing Exceptions
@@ -290,8 +307,16 @@
#### Linux/Android
-TODO(mmentovai): describe this. See [this preliminary
-doc.](https://goto.google.com/crashpad-android-dd)
+On Linux, exceptions are dispatched as signals to the crashing thread. Crashpad
+signal handlers will send a message over the socket to the Crashpad handler
+notifying it of the crash and the location of exception information to be read
+from the crashing process. When using a shared socket connection, communication
+is entirely one-way. The client sends its dump request to the handler and then
+waits until the handler responds with a SIGCONT or a timeout occurs. When using
+a private socket connection, the handler may respond over the socket to
+communicate with a ptrace broker process. The broker is forked from the crashing
+process, executes ptrace requests against the crashing process, and sends the
+information over the socket to the handler.
### The CrashpadInfo structure
diff --git a/doc/status.md b/doc/status.md
index dcb6e62..3948b44 100644
--- a/doc/status.md
+++ b/doc/status.md
@@ -18,24 +18,27 @@
## Completed
-Crashpad currently consists of a crash-reporting client and some related tools
-for macOS and Windows. The core client work for both platforms is substantially
-complete. Crashpad became the crash reporter client for
-[Chromium](https://www.chromium.org/Home) on macOS as of [March
+Crashpad has complete crash-reporting clients and some related tools for macOS,
+Windows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became
+the crash reporter client for [Chromium](https://www.chromium.org/Home) on macOS
+as of [March
2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089),
-and on Windows as of [November
-2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c).
+Windows as of [November
+2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c),
+and Android as of [January
+2019](https://chromium.googlesource.com/chromium/src/+/f890e4b5495ab693d2d37aec3c378239946154f7).
+
## In Progress
-Initial work on a Crashpad client for
-[Android](https://crashpad.chromium.org/bug/30) has begun. This is currently in
-the early implementation phase.
+Chromium is transitioning to Crashpad for [Chromium OS and Desktop
+Linux](https://crbug.com/942279).
+
+Work has begun on a Crashpad client for
+[iOS](https://crashpad.chromium.org/bug/31).
## Future
-There are plans to bring Crashpad clients to other operating systems in the
-future, including a more generic non-Android Linux implementation. There are
-also plans to implement a [crash report
+There are also plans to implement a [crash report
processor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No
timeline for completing this work has been set yet.
diff --git a/doc/support/crashpad.doxy b/doc/support/crashpad.doxy
index 2f3e2bb..ca1ea25 100644
--- a/doc/support/crashpad.doxy
+++ b/doc/support/crashpad.doxy
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.14
+# Doxyfile 1.8.18
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -17,10 +17,10 @@
# Project related configuration options
#---------------------------------------------------------------------------
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all text
-# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
-# built into libc) for the transcoding. See
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
@@ -93,6 +93,14 @@
OUTPUT_LANGUAGE = English
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION = None
+
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
@@ -181,6 +189,16 @@
JAVADOC_AUTOBRIEF = NO
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER = NO
+
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@@ -230,15 +248,13 @@
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines (in the resulting output). You can put ^^ in the value part of an
# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -267,17 +283,26 @@
OPTIMIZE_OUTPUT_VHDL = NO
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE = NO
+
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
-# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
-# Fortran. In the later case the parser tries to guess whether the code is fixed
-# or free formatted code, this is the default for Fortran type files), VHDL. For
-# instance to make doxygen treat .inc files as Fortran files (default is PHP),
-# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
@@ -288,7 +313,7 @@
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
@@ -300,7 +325,7 @@
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
-# Minimum value: 0, maximum value: 99, default value: 0.
+# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
@@ -436,6 +461,12 @@
EXTRACT_PRIVATE = NO
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL = NO
+
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@@ -490,8 +521,8 @@
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@@ -514,7 +545,7 @@
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
+# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -746,7 +777,8 @@
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
-# parameter documentation, but not about the absence of documentation.
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
WARN_NO_PARAMDOC = NO
@@ -805,8 +837,10 @@
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
+# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cc \
@@ -968,7 +1002,7 @@
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
+# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
@@ -1005,7 +1039,7 @@
#
# To use it do the following:
# - Install the latest version of global
-# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
@@ -1183,9 +1217,9 @@
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
+# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
+# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1215,13 +1249,13 @@
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/tools/xcode/), introduced with
-# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
-# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1260,7 +1294,7 @@
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
@@ -1336,7 +1370,7 @@
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1344,7 +1378,8 @@
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1352,21 +1387,23 @@
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
-# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@@ -1450,6 +1487,17 @@
EXT_LINKS_IN_WINDOW = NO
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png The default and svg Looks nicer but requires the
+# pdf2svg tool.
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1470,8 +1518,14 @@
FORMULA_TRANSPARENT = YES
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1499,7 +1553,7 @@
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
@@ -1541,7 +1595,7 @@
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1625,21 +1679,35 @@
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked.
#
-# Note that when enabling USE_PDFLATEX this option is only used for generating
-# bitmaps for formulas in the HTML output, but not in the Makefile that is
-# written to the output directory.
-# The default file is: latex.
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
# The default file is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD = makeindex
+
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some
# trees in general.
@@ -1774,6 +1842,14 @@
LATEX_TIMESTAMP = NO
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY =
+
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
@@ -1813,9 +1889,9 @@
RTF_HYPERLINKS = NO
-# Load stylesheet definitions from file. Syntax is similar to doxygen's config
-# file, i.e. a series of assignments. You only have to provide replacements,
-# missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
#
# See also section "Doxygen usage" for information on how to generate the
# default style sheet that doxygen normally uses.
@@ -1824,8 +1900,8 @@
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
-# similar to doxygen's config file. A template extensions file can be generated
-# using doxygen -e rtf extensionFile.
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
@@ -1911,6 +1987,13 @@
XML_PROGRAMLISTING = YES
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
@@ -2116,12 +2199,6 @@
EXTERNAL_PAGES = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/perl
-
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@@ -2135,15 +2212,6 @@
CLASS_DIAGRAMS = YES
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
diff --git a/doc/support/generate_doxygen.py b/doc/support/generate_doxygen.py
index a93028d..577b51e 100755
--- a/doc/support/generate_doxygen.py
+++ b/doc/support/generate_doxygen.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# coding: utf-8
# Copyright 2017 The Crashpad Authors. All rights reserved.
#
@@ -24,26 +23,26 @@
def main(args):
- script_dir = os.path.dirname(__file__)
- crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir)
+ script_dir = os.path.dirname(__file__)
+ crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir)
- # Run from the Crashpad project root directory.
- os.chdir(crashpad_dir)
+ # Run from the Crashpad project root directory.
+ os.chdir(crashpad_dir)
- output_dir = os.path.join('out', 'doc', 'doxygen')
+ output_dir = os.path.join('out', 'doc', 'doxygen')
- if os.path.isdir(output_dir) and not os.path.islink(output_dir):
- shutil.rmtree(output_dir)
- elif os.path.exists(output_dir):
- os.unlink(output_dir)
+ if os.path.isdir(output_dir) and not os.path.islink(output_dir):
+ shutil.rmtree(output_dir)
+ elif os.path.exists(output_dir):
+ os.unlink(output_dir)
- os.makedirs(output_dir, 0o755)
+ os.makedirs(output_dir, 0o755)
- doxy_file = os.path.join('doc', 'support', 'crashpad.doxy')
- subprocess.check_call(['doxygen', doxy_file])
+ doxy_file = os.path.join('doc', 'support', 'crashpad.doxy')
+ subprocess.check_call(['doxygen', doxy_file])
- return 0
+ return 0
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/handler/BUILD.gn b/handler/BUILD.gn
index 2eae30b..17c4cbd 100644
--- a/handler/BUILD.gn
+++ b/handler/BUILD.gn
@@ -42,6 +42,8 @@
if (crashpad_is_linux || crashpad_is_android) {
set_sources_assignment_filter([])
sources += [
+ "linux/capture_snapshot.cc",
+ "linux/capture_snapshot.h",
"linux/crash_report_exception_handler.cc",
"linux/crash_report_exception_handler.h",
"linux/exception_handler_server.cc",
@@ -49,6 +51,13 @@
]
}
+ if (crashpad_is_linux) {
+ sources += [
+ "linux/cros_crash_report_exception_handler.cc",
+ "linux/cros_crash_report_exception_handler.h",
+ ]
+ }
+
if (crashpad_is_win) {
sources += [
"win/crash_report_exception_handler.cc",
@@ -56,25 +65,18 @@
]
}
- if (crashpad_is_fuchsia) {
- sources += [
- "fuchsia/crash_report_exception_handler.cc",
- "fuchsia/crash_report_exception_handler.h",
- "fuchsia/exception_handler_server.cc",
- "fuchsia/exception_handler_server.h",
- ]
- }
-
public_configs = [ "..:crashpad_config" ]
- deps = [
+ public_deps = [
"../client",
- "../compat",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+
+ deps = [
"../minidump",
"../snapshot",
- "../third_party/mini_chromium:base",
"../tools:tool_support",
- "../util",
]
if (crashpad_is_win) {
@@ -82,12 +84,20 @@
}
}
+if (crashpad_is_android) {
+ # CrashpadHandlerMain is defined in a separate target so that it can be
+ # overriden by implementers
+ source_set("crashpad_handler_main") {
+ sources = [ "crashpad_handler_main.cc" ]
+
+ deps = [ ":handler" ]
+ }
+}
+
source_set("handler_test") {
testonly = true
- sources = [
- "minidump_to_upload_parameters_test.cc",
- ]
+ sources = [ "minidump_to_upload_parameters_test.cc" ]
if (crashpad_is_linux || crashpad_is_android) {
sources += [ "linux/exception_handler_server_test.cc" ]
@@ -110,6 +120,8 @@
]
if (crashpad_is_win) {
+ deps += [ "../minidump:test_support" ]
+
data_deps = [
":crashpad_handler_test_extended_handler",
":fake_handler_that_crashes_at_startup",
@@ -117,45 +129,28 @@
}
}
-crashpad_executable("crashpad_handler") {
- sources = [
- "main.cc",
- ]
+if (!crashpad_is_ios) {
+ crashpad_executable("crashpad_handler") {
+ sources = [ "main.cc" ]
- deps = [
- ":handler",
- "../build:default_exe_manifest_win",
- "../compat",
- "../third_party/mini_chromium:base",
- ]
+ deps = [
+ ":handler",
+ "../build:default_exe_manifest_win",
+ "../compat",
+ "../third_party/mini_chromium:base",
+ "../tools:tool_support",
+ ]
- if (crashpad_is_mac && crashpad_is_in_chromium) {
- if (is_component_build) {
- ldflags = [
- # The handler is in
- # Chromium.app/Contents/Versions/X/Chromium Framework.framework/Versions/A/Helpers/
- # so set rpath up to the base.
- "-rpath",
- "@loader_path/../../../../../../../..",
-
- # The handler is also in
- # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Helpers/
- # so set the rpath for that too.
- "-rpath",
- "@loader_path/../../../../..",
- ]
- }
- }
-
- if (crashpad_is_win) {
- if (crashpad_is_in_chromium) {
- remove_configs = [ "//build/config/win:console" ]
- configs = [ "//build/config/win:windowed" ]
- } else {
- remove_configs =
- [ "//third_party/mini_chromium/mini_chromium/build:win_console" ]
- configs =
- [ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
+ if (crashpad_is_win) {
+ if (crashpad_is_in_chromium || crashpad_is_in_dart) {
+ remove_configs = [ "//build/config/win:console" ]
+ configs = [ "//build/config/win:windowed" ]
+ } else {
+ remove_configs =
+ [ "//third_party/mini_chromium/mini_chromium/build:win_console" ]
+ configs =
+ [ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
+ }
}
}
}
@@ -166,42 +161,47 @@
# handler executable an acceptable name.
if (crashpad_is_android) {
copy("crashpad_handler_named_as_so") {
+ deps = [ ":crashpad_handler" ]
+
+ sources = [ "$root_out_dir/crashpad_handler" ]
+
+ outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
+ }
+
+ crashpad_executable("crashpad_handler_trampoline") {
+ set_sources_assignment_filter([])
+ output_name = "libcrashpad_handler_trampoline.so"
+
+ sources = [ "linux/handler_trampoline.cc" ]
+
+ ldflags = [ "-llog" ]
+
+ if (crashpad_is_in_chromium) {
+ no_default_deps = true
+ }
+ }
+}
+
+if (!crashpad_is_ios) {
+ crashpad_executable("crashpad_handler_test_extended_handler") {
+ testonly = true
+
+ sources = [ "crashpad_handler_test_extended_handler.cc" ]
+
deps = [
- ":crashpad_handler",
- ]
-
- sources = [
- "$root_out_dir/crashpad_handler",
- ]
-
- outputs = [
- "$root_out_dir/libcrashpad_handler.so",
+ ":handler",
+ "../build:default_exe_manifest_win",
+ "../compat",
+ "../minidump:test_support",
+ "../third_party/mini_chromium:base",
+ "../tools:tool_support",
]
}
}
-crashpad_executable("crashpad_handler_test_extended_handler") {
- testonly = true
-
- sources = [
- "crashpad_handler_test_extended_handler.cc",
- ]
-
- deps = [
- ":handler",
- "../build:default_exe_manifest_win",
- "../compat",
- "../minidump:test_support",
- "../third_party/mini_chromium:base",
- "../tools:tool_support",
- ]
-}
-
if (crashpad_is_win) {
crashpad_executable("crashpad_handler_com") {
- sources = [
- "main.cc",
- ]
+ sources = [ "main.cc" ]
# Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by
# having this target produce crashpad_handler_com.com. Don’t use this target
@@ -213,27 +213,20 @@
"../build:default_exe_manifest_win",
"../compat",
"../third_party/mini_chromium:base",
+ "../tools:tool_support",
]
}
copy("crashpad_handler_console") {
- deps = [
- ":crashpad_handler_com",
- ]
- sources = [
- "$root_out_dir/crashpad_handler_com.com",
- ]
- outputs = [
- "$root_out_dir/crashpad_handler.com",
- ]
+ deps = [ ":crashpad_handler_com" ]
+ sources = [ "$root_out_dir/crashpad_handler_com.com" ]
+ outputs = [ "$root_out_dir/crashpad_handler.com" ]
}
crashpad_executable("crash_other_program") {
testonly = true
- sources = [
- "win/crash_other_program.cc",
- ]
+ sources = [ "win/crash_other_program.cc" ]
deps = [
"../client",
@@ -246,9 +239,7 @@
crashpad_executable("crashy_program") {
testonly = true
- sources = [
- "win/crashy_test_program.cc",
- ]
+ sources = [ "win/crashy_test_program.cc" ]
deps = [
"../client",
@@ -259,9 +250,7 @@
crashpad_executable("crashy_signal") {
testonly = true
- sources = [
- "win/crashy_signal.cc",
- ]
+ sources = [ "win/crashy_signal.cc" ]
cflags = [ "/wd4702" ] # Unreachable code.
@@ -274,17 +263,13 @@
crashpad_executable("fake_handler_that_crashes_at_startup") {
testonly = true
- sources = [
- "win/fake_handler_that_crashes_at_startup.cc",
- ]
+ sources = [ "win/fake_handler_that_crashes_at_startup.cc" ]
}
crashpad_executable("hanging_program") {
testonly = true
- sources = [
- "win/hanging_program.cc",
- ]
+ sources = [ "win/hanging_program.cc" ]
deps = [
"../client",
@@ -295,17 +280,13 @@
crashpad_loadable_module("loader_lock_dll") {
testonly = true
- sources = [
- "win/loader_lock_dll.cc",
- ]
+ sources = [ "win/loader_lock_dll.cc" ]
}
crashpad_executable("self_destroying_program") {
testonly = true
- sources = [
- "win/self_destroying_test_program.cc",
- ]
+ sources = [ "win/self_destroying_test_program.cc" ]
deps = [
"../client",
@@ -320,9 +301,7 @@
crashpad_executable("crashy_z7_loader") {
testonly = true
- sources = [
- "win/crashy_test_z7_loader.cc",
- ]
+ sources = [ "win/crashy_test_z7_loader.cc" ]
deps = [
"../client",
diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc
index 205d860..e144bdd 100644
--- a/handler/crash_report_upload_thread.cc
+++ b/handler/crash_report_upload_thread.cc
@@ -246,12 +246,6 @@
CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
const CrashReportDatabase::UploadReport* report,
std::string* response_body) {
-#if defined(OS_ANDROID)
- // TODO(jperaza): This method can be enabled on Android after HTTPTransport is
- // implemented and Crashpad takes over upload responsibilty on Android.
- NOTREACHED();
- return UploadResult::kPermanentFailure;
-#else
std::map<std::string, std::string> parameters;
FileReader* reader = report->Reader();
@@ -338,7 +332,6 @@
}
return UploadResult::kSuccess;
-#endif // OS_ANDROID
}
void CrashReportUploadThread::DoWork(const WorkerThread* thread) {
diff --git a/handler/crashpad_handler.md b/handler/crashpad_handler.md
index 003ee2e..6d055ed 100644
--- a/handler/crashpad_handler.md
+++ b/handler/crashpad_handler.md
@@ -121,13 +121,6 @@
Either this option or **--mach-service**, but not both, is required. This
option is only valid on macOS.
- * **--no-identify-client-via-url**
-
- Do not add client-identifying fields to the URL. By default, `"prod"`,
- `"ver"`, and `"guid"` annotations are added to the upload URL as name-value
- pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
- option disables that behavior.
-
* **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section*
Register the initial client using the inherited handles and data provided.
@@ -141,6 +134,13 @@
client to register, and exits when all clients have exited, after waiting for
any uploads in progress to complete.
+ * **--initial-client-fd**=_FD_
+
+ Wait for client requests on _FD_. Either this option or
+ **--trace-parent-with-exception**, but not both, is required. The handler
+ exits when all client connections have been closed. This option is only valid
+ on Linux platforms.
+
* **--mach-service**=_SERVICE_
Check in with the bootstrap server under the name _SERVICE_. Either this
@@ -198,6 +198,13 @@
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
be `--monitor-self`.
+* **--no-identify-client-via-url**
+
+ Do not add client-identifying fields to the URL. By default, `"prod"`,
+ `"ver"`, and `"guid"` annotations are added to the upload URL as name-value
+ pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
+ option disables that behavior.
+
* **--no-periodic-tasks**
Do not scan for new pending crash reports or prune the crash report database.
@@ -226,6 +233,12 @@
for use with collection servers that don’t accept uploads compressed in this
way.
+ * **--no-write-minidump-to-database**
+
+ Do not write the minidump to database. Normally, the minidump is written to
+ database for upload. Use this option with **--write-minidump-to-log** to
+ only write the minidump to log. This option is only available to Android.
+
* **--pipe-name**=_PIPE_
Listen on the given pipe name for connections from clients. _PIPE_ must be of
@@ -245,17 +258,24 @@
parent process. This option is only valid on macOS. Use of this option is
discouraged. It should not be used absent extraordinary circumstances.
+ * **--sanitization-information**=_SANITIZATION-INFORMATION-ADDRESS_
+
+ Provides sanitization settings in a SanitizationInformation struct at
+ _SANITIZATION-INFORMATION-ADDRESS_. This option requires
+ **--trace-parent-with-exception** and is only valid on Linux platforms.
+
+ * **--shared-client-connection**
+
+ Indicates that the file descriptor provided by **--initial-client-fd** is
+ shared among mulitple clients. Using a broker process is not supported for
+ clients using this option. This option is only valid on Linux platforms.
+
* **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_
Causes the handler process to trace its parent process and exit. The parent
process should have an ExceptionInformation struct at
- _EXCEPTION-INFORMATION-ADDRESS_.
-
- * **--initial-client-fd**=_FD_
-
- Starts the excetion handler server with an initial ExceptionHandlerClient
- connected on the socket _FD_. The server will exit when all connected client
- sockets have been closed.
+ _EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux
+ platforms.
* **--url**=_URL_
@@ -265,6 +285,19 @@
library, typically in response to a user requesting this behavior. If this
option is not specified, this program will behave as if uploads are disabled.
+ * **--use-cros-crash-reporter**
+
+ Causes crash reports to be passed via an in-memory file to
+ `/sbin/crash_reporter` instead of storing them in the database. The database
+ is still used for Crashpad settings. This option is only valid on Chromium
+ OS.
+
+* **--write-minidump-to-log**
+
+ Write the minidump to log. By default the minidump is only written to
+ database. Use this option with **--no-write-minidump-to-database** to only
+ write the minidump to log. This option is only available to Android.
+
* **--help**
Display help and exit.
diff --git a/util/net/http_transport_none.cc b/handler/crashpad_handler_main.cc
similarity index 66%
copy from util/net/http_transport_none.cc
copy to handler/crashpad_handler_main.cc
index 1c08c1f..b47488e 100644
--- a/util/net/http_transport_none.cc
+++ b/handler/crashpad_handler_main.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
-
-#include "base/logging.h"
+#include "handler/handler_main.h"
namespace crashpad {
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
+extern "C" {
+
+__attribute__((visibility("default"), used)) int CrashpadHandlerMain(
+ int argc,
+ char* argv[]) {
+ return HandlerMain(argc, argv, nullptr);
}
+} // extern "C"
+
} // namespace crashpad
diff --git a/handler/fuchsia/crash_report_exception_handler.cc b/handler/fuchsia/crash_report_exception_handler.cc
deleted file mode 100644
index 1da0985..0000000
--- a/handler/fuchsia/crash_report_exception_handler.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "handler/fuchsia/crash_report_exception_handler.h"
-
-#include <zircon/syscalls/exception.h>
-
-#include "base/fuchsia/fuchsia_logging.h"
-#include "client/settings.h"
-#include "minidump/minidump_file_writer.h"
-#include "minidump/minidump_user_extension_stream_data_source.h"
-#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
-#include "util/fuchsia/koid_utilities.h"
-#include "util/fuchsia/scoped_task_suspend.h"
-
-namespace crashpad {
-
-namespace {
-
-struct ScopedZxTaskResumeAfterException {
- ScopedZxTaskResumeAfterException(zx_handle_t thread) : thread_(thread) {}
- ~ScopedZxTaskResumeAfterException() {
- DCHECK_NE(thread_, ZX_HANDLE_INVALID);
- // Resuming with ZX_RESUME_TRY_NEXT chains to the next handler. In normal
- // operation, there won't be another beyond this one, which will result in
- // the kernel terminating the process.
- zx_status_t status =
- zx_task_resume(thread_, ZX_RESUME_EXCEPTION | ZX_RESUME_TRY_NEXT);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_task_resume";
- }
- }
-
- private:
- zx_handle_t thread_; // weak
-};
-
-} // namespace
-
-CrashReportExceptionHandler::CrashReportExceptionHandler(
- CrashReportDatabase* database,
- CrashReportUploadThread* upload_thread,
- const std::map<std::string, std::string>* process_annotations,
- const std::map<std::string, base::FilePath>* process_attachments,
- const UserStreamDataSources* user_stream_data_sources)
- : database_(database),
- upload_thread_(upload_thread),
- process_annotations_(process_annotations),
- process_attachments_(process_attachments),
- user_stream_data_sources_(user_stream_data_sources) {}
-
-CrashReportExceptionHandler::~CrashReportExceptionHandler() {}
-
-bool CrashReportExceptionHandler::HandleException(uint64_t process_id,
- uint64_t thread_id) {
- // TODO(scottmg): This function needs to be instrumented with metrics calls,
- // https://crashpad.chromium.org/bug/230.
-
- base::ScopedZxHandle process(GetProcessFromKoid(process_id));
- if (!process.is_valid()) {
- // There's no way to zx_task_resume() the thread if the process retrieval
- // fails. Assume that the process has been already killed, and bail.
- return false;
- }
-
- base::ScopedZxHandle thread(GetChildHandleByKoid(process.get(), thread_id));
- if (!thread.is_valid()) {
- return false;
- }
-
- return HandleExceptionHandles(process.get(), thread.get());
-}
-
-bool CrashReportExceptionHandler::HandleExceptionHandles(zx_handle_t process,
- zx_handle_t thread) {
- // Now that the thread has been successfully retrieved, it is possible to
- // correctly call zx_task_resume() to continue exception processing, even if
- // something else during this function fails.
- ScopedZxTaskResumeAfterException resume(thread);
-
- ProcessSnapshotFuchsia process_snapshot;
- if (!process_snapshot.Initialize(process)) {
- return false;
- }
-
- CrashpadInfoClientOptions client_options;
- process_snapshot.GetCrashpadOptions(&client_options);
-
- if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
- zx_exception_report_t report;
- zx_status_t status = zx_object_get_info(thread,
- ZX_INFO_THREAD_EXCEPTION_REPORT,
- &report,
- sizeof(report),
- nullptr,
- nullptr);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status)
- << "zx_object_get_info ZX_INFO_THREAD_EXCEPTION_REPORT";
- return false;
- }
-
- zx_koid_t thread_id = GetKoidForHandle(thread);
- if (!process_snapshot.InitializeException(thread_id, report)) {
- return false;
- }
-
- UUID client_id;
- Settings* const settings = database_->GetSettings();
- if (settings) {
- // If GetSettings() or GetClientID() fails, something else will log a
- // message and client_id will be left at its default value, all zeroes,
- // which is appropriate.
- settings->GetClientID(&client_id);
- }
-
- process_snapshot.SetClientID(client_id);
- process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
-
- std::unique_ptr<CrashReportDatabase::NewReport> new_report;
- CrashReportDatabase::OperationStatus database_status =
- database_->PrepareNewCrashReport(&new_report);
- if (database_status != CrashReportDatabase::kNoError) {
- return false;
- }
-
- process_snapshot.SetReportID(new_report->ReportID());
-
- MinidumpFileWriter minidump;
- minidump.InitializeFromSnapshot(&process_snapshot);
- AddUserExtensionStreams(
- user_stream_data_sources_, &process_snapshot, &minidump);
-
- if (!minidump.WriteEverything(new_report->Writer())) {
- return false;
- }
-
- if (process_attachments_) {
- // Note that attachments are read at this point each time rather than once
- // so that if the contents of the file has changed it will be re-read for
- // each upload (e.g. in the case of a log file).
- for (const auto& it : *process_attachments_) {
- FileWriter* writer = new_report->AddAttachment(it.first);
- if (writer) {
- std::string contents;
- if (!LoggingReadEntireFile(it.second, &contents)) {
- // Not being able to read the file isn't considered fatal, and
- // should not prevent the report from being processed.
- continue;
- }
- writer->Write(contents.data(), contents.size());
- }
- }
- }
-
- UUID uuid;
- database_status =
- database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
- if (database_status != CrashReportDatabase::kNoError) {
- return false;
- }
-
- if (upload_thread_) {
- upload_thread_->ReportPending(uuid);
- }
- }
-
- return true;
-}
-
-} // namespace crashpad
diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h
deleted file mode 100644
index 5043d7e..0000000
--- a/handler/fuchsia/crash_report_exception_handler.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
-#define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
-
-#include <stdint.h>
-#include <zircon/types.h>
-
-#include <map>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "client/crash_report_database.h"
-#include "handler/crash_report_upload_thread.h"
-#include "handler/user_stream_data_source.h"
-
-namespace crashpad {
-
-//! \brief An exception handler that writes crash reports for exception messages
-//! to a CrashReportDatabase.
-class CrashReportExceptionHandler {
- public:
- //! \brief Creates a new object that will store crash reports in \a database.
- //!
- //! \param[in] database The database to store crash reports in. Weak.
- //! \param[in] upload_thread The upload thread to notify when a new crash
- //! report is written into \a database.
- //! \param[in] process_annotations A map of annotations to insert as
- //! process-level annotations into each crash report that is written. Do
- //! not confuse this with module-level annotations, which are under the
- //! control of the crashing process, and are used to implement Chrome's
- //! "crash keys." Process-level annotations are those that are beyond the
- //! control of the crashing process, which must reliably be set even if
- //! the process crashes before it’s able to establish its own annotations.
- //! To interoperate with Breakpad servers, the recommended practice is to
- //! specify values for the `"prod"` and `"ver"` keys as process
- //! annotations.
- //! \param[in] process_attachments A map of file name keys to file paths to be
- //! included in the report. Each time a report is written, the file paths
- //! will be read in their entirety and included in the report using the
- //! file name key as the name in the http upload.
- //! \param[in] user_stream_data_sources Data sources to be used to extend
- //! crash reports. For each crash report that is written, the data sources
- //! are called in turn. These data sources may contribute additional
- //! minidump streams. `nullptr` if not required.
- CrashReportExceptionHandler(
- CrashReportDatabase* database,
- CrashReportUploadThread* upload_thread,
- const std::map<std::string, std::string>* process_annotations,
- const std::map<std::string, base::FilePath>* process_attachments,
- const UserStreamDataSources* user_stream_data_sources);
-
- ~CrashReportExceptionHandler();
-
- //! \brief Called when the exception handler server has caught an exception
- //! and wants a crash dump to be taken.
- //!
- //! This function is expected to call `zx_task_resume()` in order to complete
- //! handling of the exception.
- //!
- //! \param[in] process_id The koid of the process which sustained the
- //! exception.
- //! \param[in] thread_id The koid of the thread which sustained the exception.
- //! \return `true` on success, or `false` with an error logged.
- bool HandleException(uint64_t process_id, uint64_t thread_id);
-
- //! \brief Called when the exception handler server has caught an exception
- //! and wants a crash dump to be taken.
- //!
- //! This function is expected to call `zx_task_resume()` in order to complete
- //! handling of the exception.
- //!
- //! \param[in] process The handle to the process which sustained the
- //! exception.
- //! \param[in] thread The handle to the thread of \a process which sustained
- //! the exception.
- //! \return `true` on success, or `false` with an error logged.
- bool HandleExceptionHandles(zx_handle_t process, zx_handle_t thread);
-
- private:
- CrashReportDatabase* database_; // weak
- CrashReportUploadThread* upload_thread_; // weak
- const std::map<std::string, std::string>* process_annotations_; // weak
- const std::map<std::string, base::FilePath>* process_attachments_; // weak
- const UserStreamDataSources* user_stream_data_sources_; // weak
-
- DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
-};
-
-} // namespace crashpad
-
-#endif // CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/handler/fuchsia/exception_handler_server.cc b/handler/fuchsia/exception_handler_server.cc
deleted file mode 100644
index 63153d2..0000000
--- a/handler/fuchsia/exception_handler_server.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "handler/fuchsia/exception_handler_server.h"
-
-#include <zircon/syscalls/exception.h>
-#include <zircon/syscalls/port.h>
-
-#include <utility>
-
-#include "base/fuchsia/fuchsia_logging.h"
-#include "base/logging.h"
-#include "handler/fuchsia/crash_report_exception_handler.h"
-#include "util/fuchsia/system_exception_port_key.h"
-
-namespace crashpad {
-
-ExceptionHandlerServer::ExceptionHandlerServer(
- base::ScopedZxHandle root_job,
- base::ScopedZxHandle exception_port)
- : root_job_(std::move(root_job)),
- exception_port_(std::move(exception_port)) {}
-
-ExceptionHandlerServer::~ExceptionHandlerServer() = default;
-
-void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) {
- while (true) {
- zx_port_packet_t packet;
- zx_status_t status =
- zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_port_wait, aborting";
- return;
- }
-
- if (packet.key != kSystemExceptionPortKey) {
- LOG(ERROR) << "unexpected packet key, ignoring";
- continue;
- }
-
- bool result =
- handler->HandleException(packet.exception.pid, packet.exception.tid);
- if (!result) {
- LOG(ERROR) << "HandleException failed";
- }
- }
-}
-
-} // namespace crashpad
diff --git a/handler/fuchsia/exception_handler_server.h b/handler/fuchsia/exception_handler_server.h
deleted file mode 100644
index 184d148..0000000
--- a/handler/fuchsia/exception_handler_server.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
-#define CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
-
-#include "base/macros.h"
-#include "base/fuchsia/scoped_zx_handle.h"
-
-namespace crashpad {
-
-class CrashReportExceptionHandler;
-
-//! \brief Runs the main exception-handling server in Crashpad's handler
-//! process.
-class ExceptionHandlerServer {
- public:
- //! \brief Constructs an ExceptionHandlerServer object.
- //!
- //! \param[in] root_job The root of the tree of processes that will be handled
- //! by this server. It is assumed that \a exception_port is the exception
- //! port of this job.
- //! \param[in] exception_port The exception port that this server will
- //! monitor.
- ExceptionHandlerServer(base::ScopedZxHandle root_job,
- base::ScopedZxHandle exception_port);
- ~ExceptionHandlerServer();
-
- //! \brief Runs the exception-handling server.
- //!
- //! \param[in] handler The handler to which the exceptions are delegated when
- //! they are caught in Run(). Ownership is not transferred.
- void Run(CrashReportExceptionHandler* handler);
-
- private:
- base::ScopedZxHandle root_job_;
- base::ScopedZxHandle exception_port_;
-
- DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
-};
-
-} // namespace crashpad
-
-#endif // CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
diff --git a/handler/handler.gyp b/handler/handler.gyp
index 60a6f25..f8313a5 100644
--- a/handler/handler.gyp
+++ b/handler/handler.gyp
@@ -28,6 +28,7 @@
'../minidump/minidump.gyp:crashpad_minidump',
'../snapshot/snapshot.gyp:crashpad_snapshot',
'../third_party/mini_chromium/mini_chromium.gyp:base',
+ '../third_party/zlib/zlib.gyp:zlib',
'../tools/tools.gyp:crashpad_tool_support',
'../util/util.gyp:crashpad_util',
],
@@ -39,6 +40,8 @@
'crash_report_upload_thread.h',
'handler_main.cc',
'handler_main.h',
+ 'linux/capture_snapshot.cc',
+ 'linux/capture_snapshot.h',
'linux/crash_report_exception_handler.cc',
'linux/crash_report_exception_handler.h',
'linux/exception_handler_server.cc',
diff --git a/handler/handler_main.cc b/handler/handler_main.cc
index 412ee11..d56857f 100644
--- a/handler/handler_main.cc
+++ b/handler/handler_main.cc
@@ -56,6 +56,10 @@
#include "util/string/split_string.h"
#include "util/synchronization/semaphore.h"
+#if defined(OS_CHROMEOS)
+#include "handler/linux/cros_crash_report_exception_handler.h"
+#endif
+
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <unistd.h>
@@ -70,8 +74,8 @@
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "handler/mac/file_limit_annotation.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/child_port_handshake.h"
-#include "util/mach/mach_extensions.h"
#include "util/posix/close_stdio.h"
#include "util/posix/signals.h"
#elif defined(OS_WIN)
@@ -82,12 +86,6 @@
#include "util/win/handle.h"
#include "util/win/initial_client_data.h"
#include "util/win/session_end_watcher.h"
-#elif defined(OS_FUCHSIA)
-#include <zircon/process.h>
-#include <zircon/processargs.h>
-
-#include "handler/fuchsia/crash_report_exception_handler.h"
-#include "handler/fuchsia/exception_handler_server.h"
#elif defined(OS_LINUX)
#include "handler/linux/crash_report_exception_handler.h"
#include "handler/linux/exception_handler_server.h"
@@ -118,6 +116,9 @@
" Address_debug_critical_section\n"
" use precreated data to register initial client\n"
#endif // OS_WIN
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+" --initial-client-fd=FD a socket connected to a client.\n"
+#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX)
" --mach-service=SERVICE register SERVICE with the bootstrap server\n"
#endif // OS_MACOSX
@@ -133,6 +134,10 @@
" --no-periodic-tasks don't scan for new reports or prune the database\n"
" --no-rate-limit don't rate limit crash uploads\n"
" --no-upload-gzip don't use gzip compression when uploading\n"
+#if defined(OS_ANDROID)
+" --no-write-minidump-to-database\n"
+" don't write minidump to database\n"
+#endif // OS_ANDROID
#if defined(OS_WIN)
" --pipe-name=PIPE communicate with the client over PIPE\n"
#endif // OS_WIN
@@ -141,14 +146,31 @@
" reset the server's exception handler to default\n"
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
+" --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\n"
+" the address of a SanitizationInformation struct.\n"
+" --shared-client-connection the file descriptor provided by\n"
+" --initial-client-fd is shared among multiple\n"
+" clients\n"
" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
" request a dump for the handler's parent process\n"
-" --initial-client-fd=FD a socket connected to a client.\n"
-" --sanitization_information=SANITIZATION_INFORMATION_ADDRESS\n"
-" the address of a SanitizationInformation struct.\n"
#endif // OS_LINUX || OS_ANDROID
" --url=URL send crash reports to this Breakpad server URL,\n"
" only if uploads are enabled for the database\n"
+#if defined(OS_CHROMEOS)
+" --use-cros-crash-reporter\n"
+" pass crash reports to /sbin/crash_reporter\n"
+" instead of storing them in the database\n"
+" --minidump-dir-for-tests=TEST_MINIDUMP_DIR\n"
+" causes /sbin/crash_reporter to leave dumps in\n"
+" this directory instead of the normal location\n"
+" --always-allow-feedback\n"
+" pass the --always_allow_feedback flag to\n"
+" crash_reporter, thus skipping metrics consent\n"
+" checks\n"
+#endif // OS_CHROMEOS
+#if defined(OS_ANDROID)
+" --write-minidump-to-log write minidump to log\n"
+#endif // OS_ANDROID
" --help display this help and exit\n"
" --version output version information and exit\n",
me.value().c_str());
@@ -168,8 +190,13 @@
bool reset_own_crash_exception_port_to_system_default;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
VMAddress exception_information_address;
- int initial_client_fd;
VMAddress sanitization_information_address;
+ int initial_client_fd;
+ bool shared_client_connection;
+#if defined(OS_ANDROID)
+ bool write_minidump_to_log;
+ bool write_minidump_to_database;
+#endif // OS_ANDROID
#elif defined(OS_WIN)
std::string pipe_name;
InitialClientData initial_client_data;
@@ -179,6 +206,11 @@
bool periodic_tasks;
bool rate_limit;
bool upload_gzip;
+#if defined(OS_CHROMEOS)
+ bool use_cros_crash_reporter = false;
+ base::FilePath minidump_dir_for_tests;
+ bool always_allow_feedback = false;
+#endif // OS_CHROMEOS
};
// Splits |key_value| on '=' and inserts the resulting key and value into |map|.
@@ -238,8 +270,6 @@
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID)
-Signals::OldActions g_old_crash_signal_handlers;
-
void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
@@ -274,9 +304,7 @@
}
Metrics::HandlerCrashed(metrics_code);
- struct sigaction* old_action =
- g_old_crash_signal_handlers.ActionForSignal(sig);
- Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action);
+ Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
@@ -284,13 +312,13 @@
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
}
-#if defined(OS_MACOSX)
-
void ReinstallCrashHandler() {
// This is used to re-enable the metrics-recording crash handler after
// MonitorSelf() sets up a Crashpad exception handler. On macOS, the
// metrics-recording handler uses signals and the Crashpad handler uses Mach
// exceptions, so there’s nothing to re-enable.
+ // On Linux, the signal handler installed by StartHandler() restores the
+ // previously installed signal handler by default.
}
void InstallCrashHandler() {
@@ -300,6 +328,8 @@
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
}
+#if defined(OS_MACOSX)
+
struct ResetSIGTERMTraits {
static struct sigaction* InvalidValue() {
return nullptr;
@@ -324,21 +354,6 @@
g_exception_handler_server->Stop();
}
-#else
-
-void ReinstallCrashHandler() {
- // This is used to re-enable the metrics-recording crash handler after
- // MonitorSelf() sets up a Crashpad signal handler.
- Signals::InstallCrashHandlers(
- HandleCrashSignal, 0, &g_old_crash_signal_handlers);
-}
-
-void InstallCrashHandler() {
- ReinstallCrashHandler();
-
- Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
-}
-
#endif // OS_MACOSX
#elif defined(OS_WIN)
@@ -396,22 +411,6 @@
ALLOW_UNUSED_LOCAL(terminate_handler);
}
-#elif defined(OS_FUCHSIA)
-
-void InstallCrashHandler() {
- // There's nothing to do here. Crashes in this process will already be caught
- // here because this handler process is in the same job that has had its
- // exception port bound.
-
- // TODO(scottmg): This should collect metrics on handler crashes, at a
- // minimum. https://crashpad.chromium.org/bug/230.
-}
-
-void ReinstallCrashHandler() {
- // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196
- NOTREACHED();
-}
-
#endif // OS_MACOSX
void MonitorSelf(const Options& options) {
@@ -448,7 +447,7 @@
// instance of crashpad_handler to be writing metrics at a time, and it should
// be the primary instance.
CrashpadClient crashpad_client;
-#if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_ANDROID)
if (!crashpad_client.StartHandlerAtCrash(executable_path,
options.database,
base::FilePath(),
@@ -500,6 +499,12 @@
int HandlerMain(int argc,
char* argv[],
const UserStreamDataSources* user_stream_sources) {
+#if defined(OS_CHROMEOS)
+ if (freopen("/var/log/chrome/chrome", "a", stderr) == nullptr) {
+ PLOG(ERROR) << "Failed to redirect stderr to /var/log/chrome/chrome";
+ }
+#endif
+
InstallCrashHandler();
CallMetricsRecordNormalExit metrics_record_normal_exit;
@@ -518,6 +523,9 @@
#if defined(OS_WIN)
kOptionInitialClientData,
#endif // OS_WIN
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ kOptionInitialClientFD,
+#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX)
kOptionMachService,
#endif // OS_MACOSX
@@ -529,6 +537,9 @@
kOptionNoPeriodicTasks,
kOptionNoRateLimit,
kOptionNoUploadGzip,
+#if defined(OS_ANDROID)
+ kOptionNoWriteMinidumpToDatabase,
+#endif // OS_ANDROID
#if defined(OS_WIN)
kOptionPipeName,
#endif // OS_WIN
@@ -536,11 +547,19 @@
kOptionResetOwnCrashExceptionPortToSystemDefault,
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
- kOptionTraceParentWithException,
- kOptionInitialClientFD,
kOptionSanitizationInformation,
+ kOptionSharedClientConnection,
+ kOptionTraceParentWithException,
#endif
kOptionURL,
+#if defined(OS_CHROMEOS)
+ kOptionUseCrosCrashReporter,
+ kOptionMinidumpDirForTests,
+ kOptionAlwaysAllowFeedback,
+#endif // OS_CHROMEOS
+#if defined(OS_ANDROID)
+ kOptionWriteMinidumpToLog,
+#endif // OS_ANDROID
// Standard options.
kOptionHelp = -2,
@@ -559,6 +578,9 @@
nullptr,
kOptionInitialClientData},
#endif // OS_MACOSX
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
+#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX)
{"mach-service", required_argument, nullptr, kOptionMachService},
#endif // OS_MACOSX
@@ -579,6 +601,12 @@
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
+#if defined(OS_ANDROID)
+ {"no-write-minidump-to-database",
+ no_argument,
+ nullptr,
+ kOptionNoWriteMinidumpToDatabase},
+#endif // OS_ANDROID
#if defined(OS_WIN)
{"pipe-name", required_argument, nullptr, kOptionPipeName},
#endif // OS_WIN
@@ -589,17 +617,37 @@
kOptionResetOwnCrashExceptionPortToSystemDefault},
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
- {"trace-parent-with-exception",
- required_argument,
- nullptr,
- kOptionTraceParentWithException},
- {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
{"sanitization-information",
required_argument,
nullptr,
kOptionSanitizationInformation},
+ {"shared-client-connection",
+ no_argument,
+ nullptr,
+ kOptionSharedClientConnection},
+ {"trace-parent-with-exception",
+ required_argument,
+ nullptr,
+ kOptionTraceParentWithException},
#endif // OS_LINUX || OS_ANDROID
{"url", required_argument, nullptr, kOptionURL},
+#if defined(OS_CHROMEOS)
+ {"use-cros-crash-reporter",
+ no_argument,
+ nullptr,
+ kOptionUseCrosCrashReporter},
+ {"minidump-dir-for-tests",
+ required_argument,
+ nullptr,
+ kOptionMinidumpDirForTests},
+ {"always-allow-feedback",
+ no_argument,
+ nullptr,
+ kOptionAlwaysAllowFeedback},
+#endif // OS_CHROMEOS
+#if defined(OS_ANDROID)
+ {"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog},
+#endif // OS_ANDROID
{"help", no_argument, nullptr, kOptionHelp},
{"version", no_argument, nullptr, kOptionVersion},
{nullptr, 0, nullptr, 0},
@@ -610,13 +658,14 @@
options.handshake_fd = -1;
#endif
options.identify_client_via_url = true;
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ options.initial_client_fd = kInvalidFileHandle;
+#endif
options.periodic_tasks = true;
options.rate_limit = true;
options.upload_gzip = true;
-#if defined(OS_LINUX) || defined(OS_ANDROID)
- options.exception_information_address = 0;
- options.initial_client_fd = kInvalidFileHandle;
- options.sanitization_information_address = 0;
+#if defined(OS_ANDROID)
+ options.write_minidump_to_database = true;
#endif
int opt;
@@ -658,6 +707,15 @@
break;
}
#endif // OS_WIN
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ case kOptionInitialClientFD: {
+ if (!base::StringToInt(optarg, &options.initial_client_fd)) {
+ ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
+ return ExitFailure();
+ }
+ break;
+ }
+#endif // OS_ANDROID || OS_LINUX
case kOptionMetrics: {
options.metrics_dir = base::FilePath(
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
@@ -695,6 +753,12 @@
options.upload_gzip = false;
break;
}
+#if defined(OS_ANDROID)
+ case kOptionNoWriteMinidumpToDatabase: {
+ options.write_minidump_to_database = false;
+ break;
+ }
+#endif // OS_ANDROID
#if defined(OS_WIN)
case kOptionPipeName: {
options.pipe_name = optarg;
@@ -708,21 +772,6 @@
}
#endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID)
- case kOptionTraceParentWithException: {
- if (!StringToNumber(optarg, &options.exception_information_address)) {
- ToolSupport::UsageHint(
- me, "failed to parse --trace-parent-with-exception");
- return ExitFailure();
- }
- break;
- }
- case kOptionInitialClientFD: {
- if (!base::StringToInt(optarg, &options.initial_client_fd)) {
- ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
- return ExitFailure();
- }
- break;
- }
case kOptionSanitizationInformation: {
if (!StringToNumber(optarg,
&options.sanitization_information_address)) {
@@ -732,11 +781,44 @@
}
break;
}
+ case kOptionSharedClientConnection: {
+ options.shared_client_connection = true;
+ break;
+ }
+ case kOptionTraceParentWithException: {
+ if (!StringToNumber(optarg, &options.exception_information_address)) {
+ ToolSupport::UsageHint(
+ me, "failed to parse --trace-parent-with-exception");
+ return ExitFailure();
+ }
+ break;
+ }
#endif // OS_LINUX || OS_ANDROID
case kOptionURL: {
options.url = optarg;
break;
}
+#if defined(OS_CHROMEOS)
+ case kOptionUseCrosCrashReporter: {
+ options.use_cros_crash_reporter = true;
+ break;
+ }
+ case kOptionMinidumpDirForTests: {
+ options.minidump_dir_for_tests = base::FilePath(
+ ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
+ break;
+ }
+ case kOptionAlwaysAllowFeedback: {
+ options.always_allow_feedback = true;
+ break;
+ }
+#endif // OS_CHROMEOS
+#if defined(OS_ANDROID)
+ case kOptionWriteMinidumpToLog: {
+ options.write_minidump_to_log = true;
+ break;
+ }
+#endif // OS_ANDROID
case kOptionHelp: {
Usage(me);
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
@@ -781,7 +863,7 @@
if (!options.exception_information_address &&
options.initial_client_fd == kInvalidFileHandle) {
ToolSupport::UsageHint(
- me, "--trace-parent-with-exception or --initial_client_fd is required");
+ me, "--trace-parent-with-exception or --initial-client-fd is required");
return ExitFailure();
}
if (options.sanitization_information_address &&
@@ -791,6 +873,20 @@
"--sanitization_information requires --trace-parent-with-exception");
return ExitFailure();
}
+ if (options.shared_client_connection &&
+ options.initial_client_fd == kInvalidFileHandle) {
+ ToolSupport::UsageHint(
+ me, "--shared-client-connection requires --initial-client-fd");
+ return ExitFailure();
+ }
+#if defined(OS_ANDROID)
+ if (!options.write_minidump_to_log && !options.write_minidump_to_database) {
+ ToolSupport::UsageHint(me,
+ "--no_write_minidump_to_database is required to use "
+ "with --write_minidump_to_log.");
+ ExitFailure();
+ }
+#endif // OS_ANDROID
#endif // OS_MACOSX
if (options.database.empty()) {
@@ -856,24 +952,62 @@
upload_thread.Get()->Start();
}
- CrashReportExceptionHandler exception_handler(
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ std::unique_ptr<ExceptionHandlerServer::Delegate> exception_handler;
+#else
+ std::unique_ptr<CrashReportExceptionHandler> exception_handler;
+#endif
+
+#if defined(OS_CHROMEOS)
+ if (options.use_cros_crash_reporter) {
+ auto cros_handler = std::make_unique<CrosCrashReportExceptionHandler>(
+ database.get(),
+ &options.annotations,
+ user_stream_sources);
+
+ if (!options.minidump_dir_for_tests.empty()) {
+ cros_handler->SetDumpDir(options.minidump_dir_for_tests);
+ }
+
+ if (options.always_allow_feedback) {
+ cros_handler->SetAlwaysAllowFeedback();
+ }
+
+ exception_handler = std::move(cros_handler);
+ } else {
+ exception_handler = std::make_unique<CrashReportExceptionHandler>(
+ database.get(),
+ static_cast<CrashReportUploadThread*>(upload_thread.Get()),
+ &options.annotations,
+ true,
+ false,
+ user_stream_sources);
+ }
+#else
+ exception_handler = std::make_unique<CrashReportExceptionHandler>(
database.get(),
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
&options.annotations,
-#if defined(OS_FUCHSIA)
- // TODO(scottmg): Process level file attachments, and for all platforms.
- nullptr,
-#endif
+#if defined(OS_ANDROID)
+ options.write_minidump_to_database,
+ options.write_minidump_to_log,
+#endif // OS_ANDROID
+#if defined(OS_LINUX)
+ true,
+ false,
+#endif // OS_LINUX
user_stream_sources);
+#endif // OS_CHROMEOS
- #if defined(OS_LINUX) || defined(OS_ANDROID)
+#if defined(OS_LINUX) || defined(OS_ANDROID)
if (options.exception_information_address) {
- ClientInformation info;
+ ExceptionHandlerProtocol::ClientInformation info;
info.exception_information_address = options.exception_information_address;
info.sanitization_information_address =
options.sanitization_information_address;
- return exception_handler.HandleException(getppid(), info) ? EXIT_SUCCESS
- : ExitFailure();
+ return exception_handler->HandleException(getppid(), geteuid(), info)
+ ? EXIT_SUCCESS
+ : ExitFailure();
}
#endif // OS_LINUX || OS_ANDROID
@@ -937,27 +1071,6 @@
if (!options.pipe_name.empty()) {
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
}
-#elif defined(OS_FUCHSIA)
- // These handles are logically "moved" into these variables when retrieved by
- // zx_take_startup_handle(). Both are given to ExceptionHandlerServer which
- // owns them in this process. There is currently no "connect-later" mode on
- // Fuchsia, all the binding must be done by the client before starting
- // crashpad_handler.
- base::ScopedZxHandle root_job(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
- if (!root_job.is_valid()) {
- LOG(ERROR) << "no process handle passed in startup handle 0";
- return EXIT_FAILURE;
- }
-
- base::ScopedZxHandle exception_port(
- zx_take_startup_handle(PA_HND(PA_USER0, 1)));
- if (!exception_port.is_valid()) {
- LOG(ERROR) << "no exception port handle passed in startup handle 1";
- return EXIT_FAILURE;
- }
-
- ExceptionHandlerServer exception_handler_server(std::move(root_job),
- std::move(exception_port));
#elif defined(OS_LINUX) || defined(OS_ANDROID)
ExceptionHandlerServer exception_handler_server;
#endif // OS_MACOSX
@@ -978,17 +1091,18 @@
#if defined(OS_WIN)
if (options.initial_client_data.IsValid()) {
exception_handler_server.InitializeWithInheritedDataForInitialClient(
- options.initial_client_data, &exception_handler);
+ options.initial_client_data, exception_handler.get());
}
#elif defined(OS_LINUX) || defined(OS_ANDROID)
if (options.initial_client_fd == kInvalidFileHandle ||
- !exception_handler_server.InitializeWithClient(
- ScopedFileHandle(options.initial_client_fd))) {
+ !exception_handler_server.InitializeWithClient(
+ ScopedFileHandle(options.initial_client_fd),
+ options.shared_client_connection)) {
return ExitFailure();
}
#endif // OS_WIN
- exception_handler_server.Run(&exception_handler);
+ exception_handler_server.Run(exception_handler.get());
return EXIT_SUCCESS;
}
diff --git a/handler/handler_main.h b/handler/handler_main.h
index af01dbe..2526541 100644
--- a/handler/handler_main.h
+++ b/handler/handler_main.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
#define CRASHPAD_HANDLER_HANDLER_MAIN_H_
+#include "build/build_config.h"
#include "handler/user_stream_data_source.h"
namespace crashpad {
@@ -34,6 +35,16 @@
char* argv[],
const UserStreamDataSources* user_stream_sources);
+#if defined(OS_ANDROID)
+//! \brief The `main()` entry point for Android libraries.
+//!
+//! This symbol is the entry point for crashpad when it is dynamically loaded
+//! using /system/bin/linker.
+//!
+//! \sa CrashpadClient::StartHandlerWithLinkerAtCrash()
+extern "C" int CrashpadHandlerMain(int argc, char* argv[]);
+#endif
+
} // namespace crashpad
#endif // CRASHPAD_HANDLER_HANDLER_MAIN_H_
diff --git a/handler/linux/capture_snapshot.cc b/handler/linux/capture_snapshot.cc
new file mode 100644
index 0000000..0054029
--- /dev/null
+++ b/handler/linux/capture_snapshot.cc
@@ -0,0 +1,119 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/linux/capture_snapshot.h"
+
+#include <utility>
+
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/sanitized/sanitization_information.h"
+#include "util/misc/metrics.h"
+#include "util/misc/tri_state.h"
+
+namespace crashpad {
+
+bool CaptureSnapshot(
+ PtraceConnection* connection,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ const std::map<std::string, std::string>& process_annotations,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ std::unique_ptr<ProcessSnapshotLinux>* snapshot,
+ std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {
+ std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
+ new ProcessSnapshotLinux());
+ if (!process_snapshot->Initialize(connection)) {
+ Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
+ return false;
+ }
+
+ pid_t local_requesting_thread_id = -1;
+ if (requesting_thread_stack_address) {
+ local_requesting_thread_id = process_snapshot->FindThreadWithStackAddress(
+ requesting_thread_stack_address);
+ }
+
+ if (requesting_thread_id) {
+ *requesting_thread_id = local_requesting_thread_id;
+ }
+
+ if (!process_snapshot->InitializeException(info.exception_information_address,
+ local_requesting_thread_id)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kExceptionInitializationFailed);
+ return false;
+ }
+
+ Metrics::ExceptionCode(process_snapshot->Exception()->Exception());
+
+ CrashpadInfoClientOptions client_options;
+ process_snapshot->GetCrashpadOptions(&client_options);
+ if (client_options.crashpad_handler_behavior == TriState::kDisabled) {
+ return false;
+ }
+
+ for (auto& p : process_annotations) {
+ process_snapshot->AddAnnotation(p.first, p.second);
+ }
+
+ if (info.sanitization_information_address) {
+ SanitizationInformation sanitization_info;
+ ProcessMemoryRange range;
+ if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
+ !range.Read(info.sanitization_information_address,
+ sizeof(sanitization_info),
+ &sanitization_info)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kSanitizationInitializationFailed);
+ return false;
+ }
+
+ auto annotations_whitelist = std::make_unique<std::vector<std::string>>();
+ auto memory_range_whitelist =
+ std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
+ if (!ReadAnnotationsWhitelist(
+ range,
+ sanitization_info.annotations_whitelist_address,
+ annotations_whitelist.get()) ||
+ !ReadMemoryRangeWhitelist(
+ range,
+ sanitization_info.memory_range_whitelist_address,
+ memory_range_whitelist.get())) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kSanitizationInitializationFailed);
+ return false;
+ }
+
+ std::unique_ptr<ProcessSnapshotSanitized> sanitized(
+ new ProcessSnapshotSanitized());
+ if (!sanitized->Initialize(process_snapshot.get(),
+ sanitization_info.annotations_whitelist_address
+ ? std::move(annotations_whitelist)
+ : nullptr,
+ std::move(memory_range_whitelist),
+ sanitization_info.target_module_address,
+ sanitization_info.sanitize_stacks)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kSkippedDueToSanitization);
+ return false;
+ }
+ *sanitized_snapshot = std::move(sanitized);
+ }
+
+ *snapshot = std::move(process_snapshot);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/handler/linux/capture_snapshot.h b/handler/linux/capture_snapshot.h
new file mode 100644
index 0000000..78886dc
--- /dev/null
+++ b/handler/linux/capture_snapshot.h
@@ -0,0 +1,67 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
+#define CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
+
+#include <sys/types.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "snapshot/linux/process_snapshot_linux.h"
+#include "snapshot/sanitized/process_snapshot_sanitized.h"
+#include "util/linux/exception_handler_protocol.h"
+#include "util/linux/ptrace_connection.h"
+#include "util/misc/address_types.h"
+
+namespace crashpad {
+
+//! \brief Captures a snapshot of a client over \a connection.
+//!
+//! \param[in] connection A PtraceConnection to the client to snapshot.
+//! \param[in] info Information about the client configuring the snapshot.
+//! \param[in] process_annotations A map of annotations to insert as
+//! process-level annotations into the snapshot.
+//! \param[in] client_uid The client's user ID.
+//! \param[in] requesting_thread_stack_address An address on the stack of the
+//! thread requesting the snapshot. If \a info includes an exception
+//! address, the exception will be assigned to the thread whose stack
+//! address range contains this address. If 0, \a requesting_thread_id will
+//! be -1.
+//! \param[out] requesting_thread_id The thread ID of the thread corresponding
+//! to \a requesting_thread_stack_address. Set to -1 if the thread ID could
+//! not be determined. Optional.
+//! \param[out] process_snapshot A snapshot of the client process, valid if this
+//! function returns `true`.
+//! \param[out] sanitized_snapshot A sanitized snapshot of the client process,
+//! valid if this function returns `true` and sanitization was requested in
+//! \a info.
+//! \return `true` if \a process_snapshot was successfully created. A message
+//! will be logged on failure, but not if the snapshot was skipped because
+//! handling was disabled by CrashpadInfoClientOptions.
+bool CaptureSnapshot(
+ PtraceConnection* connection,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ const std::map<std::string, std::string>& process_annotations,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ std::unique_ptr<ProcessSnapshotLinux>* process_snapshot,
+ std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc
index 101c49f..29f6d0d 100644
--- a/handler/linux/crash_report_exception_handler.cc
+++ b/handler/linux/crash_report_exception_handler.cc
@@ -14,38 +14,75 @@
#include "handler/linux/crash_report_exception_handler.h"
-#include <vector>
+#include <memory>
+#include <utility>
#include "base/logging.h"
#include "client/settings.h"
+#include "handler/linux/capture_snapshot.h"
#include "minidump/minidump_file_writer.h"
-#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/linux/process_snapshot_linux.h"
#include "snapshot/sanitized/process_snapshot_sanitized.h"
-#include "snapshot/sanitized/sanitization_information.h"
+#include "util/file/file_reader.h"
+#include "util/file/output_stream_file_writer.h"
#include "util/linux/direct_ptrace_connection.h"
#include "util/linux/ptrace_client.h"
+#include "util/misc/implicit_cast.h"
#include "util/misc/metrics.h"
-#include "util/misc/tri_state.h"
#include "util/misc/uuid.h"
+#include "util/stream/base94_output_stream.h"
+#include "util/stream/log_output_stream.h"
+#include "util/stream/zlib_output_stream.h"
namespace crashpad {
+namespace {
+
+bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) {
+ ZlibOutputStream stream(ZlibOutputStream::Mode::kCompress,
+ std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kEncode,
+ std::make_unique<LogOutputStream>()));
+ FileOperationResult read_result;
+ do {
+ uint8_t buffer[4096];
+ read_result = file_reader->Read(buffer, sizeof(buffer));
+ if (read_result < 0)
+ return false;
+
+ if (read_result > 0 && (!stream.Write(buffer, read_result)))
+ return false;
+ } while (read_result > 0);
+ return stream.Flush();
+}
+
+} // namespace
+
CrashReportExceptionHandler::CrashReportExceptionHandler(
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations,
+ bool write_minidump_to_database,
+ bool write_minidump_to_log,
const UserStreamDataSources* user_stream_data_sources)
: database_(database),
upload_thread_(upload_thread),
process_annotations_(process_annotations),
- user_stream_data_sources_(user_stream_data_sources) {}
+ write_minidump_to_database_(write_minidump_to_database),
+ write_minidump_to_log_(write_minidump_to_log),
+ user_stream_data_sources_(user_stream_data_sources) {
+ DCHECK(write_minidump_to_database_ | write_minidump_to_log_);
+}
CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
bool CrashReportExceptionHandler::HandleException(
pid_t client_process_id,
- const ClientInformation& info) {
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id) {
Metrics::ExceptionEncountered();
DirectPtraceConnection connection;
@@ -55,13 +92,20 @@
return false;
}
- return HandleExceptionWithConnection(&connection, info);
+ return HandleExceptionWithConnection(&connection,
+ info,
+ client_uid,
+ requesting_thread_stack_address,
+ requesting_thread_id,
+ local_report_id);
}
bool CrashReportExceptionHandler::HandleExceptionWithBroker(
pid_t client_process_id,
- const ClientInformation& info,
- int broker_sock) {
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id) {
Metrics::ExceptionEncountered();
PtraceClient client;
@@ -71,123 +115,134 @@
return false;
}
- return HandleExceptionWithConnection(&client, info);
+ return HandleExceptionWithConnection(
+ &client, info, client_uid, 0, nullptr, local_report_id);
}
bool CrashReportExceptionHandler::HandleExceptionWithConnection(
PtraceConnection* connection,
- const ClientInformation& info) {
- ProcessSnapshotLinux process_snapshot;
- if (!process_snapshot.Initialize(connection)) {
- Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id) {
+ std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
+ std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
+ if (!CaptureSnapshot(connection,
+ info,
+ *process_annotations_,
+ client_uid,
+ requesting_thread_stack_address,
+ requesting_thread_id,
+ &process_snapshot,
+ &sanitized_snapshot)) {
return false;
}
- if (!process_snapshot.InitializeException(
- info.exception_information_address)) {
+ UUID client_id;
+ Settings* const settings = database_->GetSettings();
+ if (settings) {
+ // If GetSettings() or GetClientID() fails, something else will log a
+ // message and client_id will be left at its default value, all zeroes,
+ // which is appropriate.
+ settings->GetClientID(&client_id);
+ }
+ process_snapshot->SetClientID(client_id);
+
+ return write_minidump_to_database_
+ ? WriteMinidumpToDatabase(process_snapshot.get(),
+ sanitized_snapshot.get(),
+ write_minidump_to_log_,
+ local_report_id)
+ : WriteMinidumpToLog(process_snapshot.get(),
+ sanitized_snapshot.get());
+}
+
+bool CrashReportExceptionHandler::WriteMinidumpToDatabase(
+ ProcessSnapshotLinux* process_snapshot,
+ ProcessSnapshotSanitized* sanitized_snapshot,
+ bool write_minidump_to_log,
+ UUID* local_report_id) {
+ std::unique_ptr<CrashReportDatabase::NewReport> new_report;
+ CrashReportDatabase::OperationStatus database_status =
+ database_->PrepareNewCrashReport(&new_report);
+ if (database_status != CrashReportDatabase::kNoError) {
+ LOG(ERROR) << "PrepareNewCrashReport failed";
Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kExceptionInitializationFailed);
+ Metrics::CaptureResult::kPrepareNewCrashReportFailed);
return false;
}
- Metrics::ExceptionCode(process_snapshot.Exception()->Exception());
+ process_snapshot->SetReportID(new_report->ReportID());
- CrashpadInfoClientOptions client_options;
- process_snapshot.GetCrashpadOptions(&client_options);
- if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
- UUID client_id;
- Settings* const settings = database_->GetSettings();
- if (settings) {
- // If GetSettings() or GetClientID() fails, something else will log a
- // message and client_id will be left at its default value, all zeroes,
- // which is appropriate.
- settings->GetClientID(&client_id);
+ ProcessSnapshot* snapshot =
+ sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
+ : implicit_cast<ProcessSnapshot*>(process_snapshot);
+
+ MinidumpFileWriter minidump;
+ minidump.InitializeFromSnapshot(snapshot);
+ AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
+
+ if (!minidump.WriteEverything(new_report->Writer())) {
+ LOG(ERROR) << "WriteEverything failed";
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kMinidumpWriteFailed);
+ return false;
+ }
+
+ bool write_minidump_to_log_succeed = false;
+ if (write_minidump_to_log) {
+ if (auto* file_reader = new_report->Reader()) {
+ if (WriteMinidumpLogFromFile(file_reader))
+ write_minidump_to_log_succeed = true;
+ else
+ LOG(ERROR) << "WriteMinidumpLogFromFile failed";
}
+ }
- process_snapshot.SetClientID(client_id);
- process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
+ UUID uuid;
+ database_status =
+ database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
+ if (database_status != CrashReportDatabase::kNoError) {
+ LOG(ERROR) << "FinishedWritingCrashReport failed";
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
+ return false;
+ }
- std::unique_ptr<CrashReportDatabase::NewReport> new_report;
- CrashReportDatabase::OperationStatus database_status =
- database_->PrepareNewCrashReport(&new_report);
- if (database_status != CrashReportDatabase::kNoError) {
- LOG(ERROR) << "PrepareNewCrashReport failed";
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kPrepareNewCrashReportFailed);
- return false;
- }
+ if (upload_thread_) {
+ upload_thread_->ReportPending(uuid);
+ }
- process_snapshot.SetReportID(new_report->ReportID());
-
- ProcessSnapshot* snapshot = nullptr;
- ProcessSnapshotSanitized sanitized;
- std::vector<std::string> whitelist;
- if (info.sanitization_information_address) {
- SanitizationInformation sanitization_info;
- ProcessMemoryRange range;
- if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
- !range.Read(info.sanitization_information_address,
- sizeof(sanitization_info),
- &sanitization_info)) {
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kSanitizationInitializationFailed);
- return false;
- }
-
- if (sanitization_info.annotations_whitelist_address &&
- !ReadAnnotationsWhitelist(
- range,
- sanitization_info.annotations_whitelist_address,
- &whitelist)) {
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kSanitizationInitializationFailed);
- return false;
- }
-
- if (!sanitized.Initialize(&process_snapshot,
- sanitization_info.annotations_whitelist_address
- ? &whitelist
- : nullptr,
- sanitization_info.target_module_address,
- sanitization_info.sanitize_stacks)) {
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kSkippedDueToSanitization);
- return true;
- }
-
- snapshot = &sanitized;
- } else {
- snapshot = &process_snapshot;
- }
-
- MinidumpFileWriter minidump;
- minidump.InitializeFromSnapshot(snapshot);
- AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
-
- if (!minidump.WriteEverything(new_report->Writer())) {
- LOG(ERROR) << "WriteEverything failed";
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kMinidumpWriteFailed);
- return false;
- }
-
- UUID uuid;
- database_status =
- database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
- if (database_status != CrashReportDatabase::kNoError) {
- LOG(ERROR) << "FinishedWritingCrashReport failed";
- Metrics::ExceptionCaptureResult(
- Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
- return false;
- }
-
- if (upload_thread_) {
- upload_thread_->ReportPending(uuid);
- }
+ if (local_report_id != nullptr) {
+ *local_report_id = uuid;
}
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
- return true;
+
+ return write_minidump_to_log ? write_minidump_to_log_succeed : true;
+}
+
+bool CrashReportExceptionHandler::WriteMinidumpToLog(
+ ProcessSnapshotLinux* process_snapshot,
+ ProcessSnapshotSanitized* sanitized_snapshot) {
+ ProcessSnapshot* snapshot =
+ sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
+ : implicit_cast<ProcessSnapshot*>(process_snapshot);
+ MinidumpFileWriter minidump;
+ minidump.InitializeFromSnapshot(snapshot);
+ AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
+
+ OutputStreamFileWriter writer(std::make_unique<ZlibOutputStream>(
+ ZlibOutputStream::Mode::kCompress,
+ std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kEncode,
+ std::make_unique<LogOutputStream>())));
+ if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) {
+ LOG(ERROR) << "WriteMinidump failed";
+ return false;
+ }
+ return writer.Flush();
}
} // namespace crashpad
diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h
index 24d5995..83d07c0 100644
--- a/handler/linux/crash_report_exception_handler.h
+++ b/handler/linux/crash_report_exception_handler.h
@@ -26,9 +26,13 @@
#include "util/linux/exception_handler_protocol.h"
#include "util/linux/ptrace_connection.h"
#include "util/misc/address_types.h"
+#include "util/misc/uuid.h"
namespace crashpad {
+class ProcessSnapshotLinux;
+class ProcessSnapshotSanitized;
+
//! \brief An exception handler that writes crash reports for exceptions
//! to a CrashReportDatabase.
class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
@@ -49,6 +53,10 @@
//! To interoperate with Breakpad servers, the recommended practice is to
//! specify values for the `"prod"` and `"ver"` keys as process
//! annotations.
+ //! \param[in] write_minidump_to_database Whether the minidump shall be
+ //! written to database.
+ //! \param[in] write_minidump_to_log Whether the minidump shall be written to
+ //! log.
//! \param[in] user_stream_data_sources Data sources to be used to extend
//! crash reports. For each crash report that is written, the data sources
//! are called in turn. These data sources may contribute additional
@@ -57,26 +65,49 @@
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations,
+ bool write_minidump_to_database,
+ bool write_minidump_to_log,
const UserStreamDataSources* user_stream_data_sources);
- ~CrashReportExceptionHandler();
+ ~CrashReportExceptionHandler() override;
// ExceptionHandlerServer::Delegate:
bool HandleException(pid_t client_process_id,
- const ClientInformation& info) override;
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address = 0,
+ pid_t* requesting_thread_id = nullptr,
+ UUID* local_report_id = nullptr) override;
- bool HandleExceptionWithBroker(pid_t client_process_id,
- const ClientInformation& info,
- int broker_sock) override;
+ bool HandleExceptionWithBroker(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id = nullptr) override;
private:
- bool HandleExceptionWithConnection(PtraceConnection* connection,
- const ClientInformation& info);
+ bool HandleExceptionWithConnection(
+ PtraceConnection* connection,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id = nullptr);
+
+ bool WriteMinidumpToDatabase(ProcessSnapshotLinux* process_snapshot,
+ ProcessSnapshotSanitized* sanitized_snapshot,
+ bool write_minidump_to_log,
+ UUID* local_report_id);
+ bool WriteMinidumpToLog(ProcessSnapshotLinux* process_snapshot,
+ ProcessSnapshotSanitized* sanitized_snapshot);
CrashReportDatabase* database_; // weak
CrashReportUploadThread* upload_thread_; // weak
const std::map<std::string, std::string>* process_annotations_; // weak
+ bool write_minidump_to_database_;
+ bool write_minidump_to_log_;
const UserStreamDataSources* user_stream_data_sources_; // weak
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
diff --git a/handler/linux/cros_crash_report_exception_handler.cc b/handler/linux/cros_crash_report_exception_handler.cc
new file mode 100644
index 0000000..92bc93e
--- /dev/null
+++ b/handler/linux/cros_crash_report_exception_handler.cc
@@ -0,0 +1,286 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "handler/linux/cros_crash_report_exception_handler.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "client/settings.h"
+#include "handler/linux/capture_snapshot.h"
+#include "handler/minidump_to_upload_parameters.h"
+#include "minidump/minidump_file_writer.h"
+#include "snapshot/linux/process_snapshot_linux.h"
+#include "snapshot/minidump/process_snapshot_minidump.h"
+#include "snapshot/sanitized/process_snapshot_sanitized.h"
+#include "util/file/file_writer.h"
+#include "util/linux/direct_ptrace_connection.h"
+#include "util/linux/ptrace_client.h"
+#include "util/misc/metrics.h"
+#include "util/misc/uuid.h"
+#include "util/posix/double_fork_and_exec.h"
+
+namespace crashpad {
+
+namespace {
+
+// Returns the process name for a pid.
+const std::string GetProcessNameFromPid(pid_t pid) {
+ // Symlink to process binary is at /proc/###/exe.
+ std::string link_path = "/proc/" + std::to_string(pid) + "/exe";
+
+ constexpr int kMaxSize = 4096;
+ std::unique_ptr<char[]> buf(new char[kMaxSize]);
+ ssize_t size = readlink(link_path.c_str(), buf.get(), kMaxSize);
+ std::string result;
+ if (size < 0) {
+ PLOG(ERROR) << "Failed to readlink " << link_path;
+ } else {
+ result.assign(buf.get(), size);
+ size_t last_slash_pos = result.rfind('/');
+ if (last_slash_pos != std::string::npos) {
+ result = result.substr(last_slash_pos + 1);
+ }
+ }
+ return result;
+}
+
+bool WriteAnnotationsAndMinidump(
+ const std::map<std::string, std::string>& parameters,
+ MinidumpFileWriter& minidump,
+ FileWriter& file_writer) {
+ for (const auto& kv : parameters) {
+ if (kv.first.find(':') != std::string::npos) {
+ LOG(ERROR) << "Annotation key cannot have ':' in it " << kv.first;
+ return false;
+ }
+ if (!file_writer.Write(kv.first.c_str(), strlen(kv.first.c_str()))) {
+ return false;
+ }
+ if (!file_writer.Write(":", 1)) {
+ return false;
+ }
+ size_t value_size = strlen(kv.second.c_str());
+ std::string value_size_str = std::to_string(value_size);
+ if (!file_writer.Write(value_size_str.c_str(), value_size_str.size())) {
+ return false;
+ }
+ if (!file_writer.Write(":", 1)) {
+ return false;
+ }
+ if (!file_writer.Write(kv.second.c_str(), strlen(kv.second.c_str()))) {
+ return false;
+ }
+ }
+
+ static constexpr char kMinidumpName[] =
+ "upload_file_minidump\"; filename=\"dump\":";
+ if (!file_writer.Write(kMinidumpName, sizeof(kMinidumpName) - 1)) {
+ return false;
+ }
+ crashpad::FileOffset dump_size_start_offset = file_writer.Seek(0, SEEK_CUR);
+ if (dump_size_start_offset < 0) {
+ LOG(ERROR) << "Failed to get minidump size start offset";
+ return false;
+ }
+ static constexpr char kMinidumpLengthFilling[] = "00000000000000000000:";
+ if (!file_writer.Write(kMinidumpLengthFilling,
+ sizeof(kMinidumpLengthFilling) - 1)) {
+ return false;
+ }
+ crashpad::FileOffset dump_start_offset = file_writer.Seek(0, SEEK_CUR);
+ if (dump_start_offset < 0) {
+ LOG(ERROR) << "Failed to get minidump start offset";
+ return false;
+ }
+ if (!minidump.WriteEverything(&file_writer)) {
+ return false;
+ }
+ crashpad::FileOffset dump_end_offset = file_writer.Seek(0, SEEK_CUR);
+ if (dump_end_offset < 0) {
+ LOG(ERROR) << "Failed to get minidump end offset";
+ return false;
+ }
+
+ size_t dump_data_size = dump_end_offset - dump_start_offset;
+ std::string dump_data_size_str = std::to_string(dump_data_size);
+ file_writer.Seek(dump_size_start_offset + strlen(kMinidumpLengthFilling) - 1 -
+ dump_data_size_str.size(),
+ SEEK_SET);
+ if (!file_writer.Write(dump_data_size_str.c_str(),
+ dump_data_size_str.size())) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+CrosCrashReportExceptionHandler::CrosCrashReportExceptionHandler(
+ CrashReportDatabase* database,
+ const std::map<std::string, std::string>* process_annotations,
+ const UserStreamDataSources* user_stream_data_sources)
+ : database_(database),
+ process_annotations_(process_annotations),
+ user_stream_data_sources_(user_stream_data_sources),
+ always_allow_feedback_(false) {}
+
+CrosCrashReportExceptionHandler::~CrosCrashReportExceptionHandler() = default;
+
+bool CrosCrashReportExceptionHandler::HandleException(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id) {
+ Metrics::ExceptionEncountered();
+
+ DirectPtraceConnection connection;
+ if (!connection.Initialize(client_process_id)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kDirectPtraceFailed);
+ return false;
+ }
+
+ return HandleExceptionWithConnection(&connection,
+ info,
+ client_uid,
+ requesting_thread_stack_address,
+ requesting_thread_id,
+ local_report_id);
+}
+
+bool CrosCrashReportExceptionHandler::HandleExceptionWithBroker(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id) {
+ Metrics::ExceptionEncountered();
+
+ PtraceClient client;
+ if (!client.Initialize(broker_sock, client_process_id)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kBrokeredPtraceFailed);
+ return false;
+ }
+
+ return HandleExceptionWithConnection(
+ &client, info, client_uid, 0, nullptr, local_report_id);
+}
+
+bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
+ PtraceConnection* connection,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id) {
+ std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
+ std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
+ if (!CaptureSnapshot(connection,
+ info,
+ *process_annotations_,
+ client_uid,
+ requesting_thread_stack_address,
+ requesting_thread_id,
+ &process_snapshot,
+ &sanitized_snapshot)) {
+ return false;
+ }
+
+ UUID client_id;
+ Settings* const settings = database_->GetSettings();
+ if (settings) {
+ // If GetSettings() or GetClientID() fails, something else will log a
+ // message and client_id will be left at its default value, all zeroes,
+ // which is appropriate.
+ settings->GetClientID(&client_id);
+ }
+ process_snapshot->SetClientID(client_id);
+
+ UUID uuid;
+ uuid.InitializeWithNew();
+ process_snapshot->SetReportID(uuid);
+
+ ProcessSnapshot* snapshot =
+ sanitized_snapshot
+ ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get())
+ : implicit_cast<ProcessSnapshot*>(process_snapshot.get());
+
+ MinidumpFileWriter minidump;
+ minidump.InitializeFromSnapshot(snapshot);
+ AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
+
+ FileWriter file_writer;
+ if (!file_writer.OpenMemfd(base::FilePath("minidump"))) {
+ Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);
+ return false;
+ }
+
+ std::map<std::string, std::string> parameters =
+ BreakpadHTTPFormParametersFromMinidump(snapshot);
+ // Used to differentiate between breakpad and crashpad while the switch is
+ // ramping up.
+ parameters.emplace("crash_library", "crashpad");
+
+ if (!WriteAnnotationsAndMinidump(parameters, minidump, file_writer)) {
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kMinidumpWriteFailed);
+ return false;
+ }
+
+ // CrOS uses crash_reporter instead of Crashpad to report crashes.
+ // crash_reporter needs to know the pid and uid of the crashing process.
+ std::vector<std::string> argv({"/sbin/crash_reporter"});
+
+ argv.push_back("--chrome_memfd=" + std::to_string(file_writer.fd()));
+ argv.push_back("--pid=" + std::to_string(*requesting_thread_id));
+ argv.push_back("--uid=" + std::to_string(client_uid));
+ std::string process_name = GetProcessNameFromPid(*requesting_thread_id);
+ argv.push_back("--exe=" + (process_name.empty() ? "chrome" : process_name));
+
+ if (info.crash_loop_before_time != 0) {
+ argv.push_back("--crash_loop_before=" +
+ std::to_string(info.crash_loop_before_time));
+ }
+ if (!dump_dir_.empty()) {
+ argv.push_back("--chrome_dump_dir=" + dump_dir_.value());
+ }
+ if (always_allow_feedback_) {
+ argv.push_back("--always_allow_feedback");
+ }
+
+ if (!DoubleForkAndExec(argv,
+ nullptr /* envp */,
+ file_writer.fd() /* preserve_fd */,
+ false /* use_path */,
+ nullptr /* child_function */)) {
+ LOG(ERROR) << "DoubleForkAndExec failed";
+ Metrics::ExceptionCaptureResult(
+ Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
+ return false;
+ }
+
+ if (local_report_id != nullptr) {
+ *local_report_id = uuid;
+ }
+ LOG(INFO) << "Successfully wrote report " << uuid.ToString();
+
+ Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/handler/linux/cros_crash_report_exception_handler.h b/handler/linux/cros_crash_report_exception_handler.h
new file mode 100644
index 0000000..0b14544
--- /dev/null
+++ b/handler/linux/cros_crash_report_exception_handler.h
@@ -0,0 +1,101 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
+#define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "client/crash_report_database.h"
+#include "handler/linux/exception_handler_server.h"
+#include "handler/user_stream_data_source.h"
+#include "util/linux/exception_handler_protocol.h"
+#include "util/linux/ptrace_connection.h"
+#include "util/misc/address_types.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+
+//! \brief An exception handler that writes crash reports to the ChromeOS
+//! crash_reporter.
+class CrosCrashReportExceptionHandler
+ : public ExceptionHandlerServer::Delegate {
+ public:
+ //! \brief Creates a new object that will pass reports to
+ //! `/sbin/crash_reporter`.
+ //!
+ //! \param[in] database The database that supplies settings for this client.
+ //! This object does not write its reports to this database.
+ //! \param[in] process_annotations A map of annotations to insert as
+ //! process-level annotations into each crash report that is written. Do
+ //! not confuse this with module-level annotations, which are under the
+ //! control of the crashing process, and are used to implement Chrome’s
+ //! “crash keys.” Process-level annotations are those that are beyond the
+ //! control of the crashing process, which must reliably be set even if
+ //! the process crashes before it’s able to establish its own annotations.
+ //! To interoperate with Breakpad servers, the recommended practice is to
+ //! specify values for the `"prod"` and `"ver"` keys as process
+ //! annotations.
+ //! \param[in] user_stream_data_sources Data sources to be used to extend
+ //! crash reports. For each crash report that is written, the data sources
+ //! are called in turn. These data sources may contribute additional
+ //! minidump streams. `nullptr` if not required.
+ CrosCrashReportExceptionHandler(
+ CrashReportDatabase* database,
+ const std::map<std::string, std::string>* process_annotations,
+ const UserStreamDataSources* user_stream_data_sources);
+
+ ~CrosCrashReportExceptionHandler() override;
+
+ // ExceptionHandlerServer::Delegate:
+
+ bool HandleException(pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address = 0,
+ pid_t* requesting_thread_id = nullptr,
+ UUID* local_report_id = nullptr) override;
+
+ bool HandleExceptionWithBroker(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id = nullptr) override;
+
+ void SetDumpDir(const base::FilePath& dump_dir) { dump_dir_ = dump_dir; }
+ void SetAlwaysAllowFeedback() { always_allow_feedback_ = true; }
+ private:
+ bool HandleExceptionWithConnection(
+ PtraceConnection* connection,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ uid_t client_uid,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id,
+ UUID* local_report_id = nullptr);
+
+ CrashReportDatabase* database_; // weak
+ const std::map<std::string, std::string>* process_annotations_; // weak
+ const UserStreamDataSources* user_stream_data_sources_; // weak
+ base::FilePath dump_dir_;
+ bool always_allow_feedback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrosCrashReportExceptionHandler);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc
index 748bf6e..c9fbb0a 100644
--- a/handler/linux/exception_handler_server.cc
+++ b/handler/linux/exception_handler_server.cc
@@ -15,10 +15,11 @@
#include "handler/linux/exception_handler_server.h"
#include <errno.h>
-#include <sys/capability.h>
+#include <linux/capability.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -31,6 +32,8 @@
#include "build/build_config.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
+#include "util/linux/proc_task_reader.h"
+#include "util/linux/socket.h"
#include "util/misc/as_underlying_type.h"
namespace crashpad {
@@ -90,54 +93,96 @@
}
bool HaveCapSysPtrace() {
- struct __user_cap_header_struct cap_header = {};
- struct __user_cap_data_struct cap_data = {};
-
+ __user_cap_header_struct cap_header;
cap_header.pid = getpid();
+ cap_header.version = _LINUX_CAPABILITY_VERSION_3;
- if (capget(&cap_header, &cap_data) != 0) {
+ __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
+ if (syscall(SYS_capget, &cap_header, &cap_data) != 0) {
PLOG(ERROR) << "capget";
+ LOG_IF(ERROR, errno == EINVAL) << "cap_header.version " << std::hex
+ << cap_header.version;
return false;
}
- if (cap_header.version != _LINUX_CAPABILITY_VERSION_3) {
- LOG(ERROR) << "Unexpected capability version " << std::hex
- << cap_header.version;
- return false;
- }
-
- return (cap_data.effective & (1 << CAP_SYS_PTRACE)) != 0;
+ return (cap_data[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &
+ CAP_TO_MASK(CAP_SYS_PTRACE)) != 0;
}
-bool SendMessageToClient(int client_sock, ServerToClientMessage::Type type) {
- ServerToClientMessage message = {};
+bool SendMessageToClient(
+ int client_sock,
+ ExceptionHandlerProtocol::ServerToClientMessage::Type type) {
+ ExceptionHandlerProtocol::ServerToClientMessage message = {};
message.type = type;
- if (type == ServerToClientMessage::kTypeSetPtracer) {
+ if (type ==
+ ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer) {
message.pid = getpid();
}
return LoggingWriteFile(client_sock, &message, sizeof(message));
}
+int tgkill(pid_t pid, pid_t tid, int signo) {
+ return syscall(SYS_tgkill, pid, tid, signo);
+}
+
+void SendSIGCONT(pid_t pid, pid_t tid) {
+ if (tid > 0) {
+ if (tgkill(pid, tid, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {
+ PLOG(ERROR) << "tgkill";
+ }
+ return;
+ }
+
+ std::vector<pid_t> threads;
+ if (!ReadThreadIDs(pid, &threads)) {
+ return;
+ }
+ for (const auto& thread : threads) {
+ if (tgkill(pid, thread, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {
+ PLOG(ERROR) << "tgkill";
+ }
+ }
+}
+
+bool SendCredentials(int client_sock) {
+ ExceptionHandlerProtocol::ServerToClientMessage message = {};
+ message.type =
+ ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials;
+ return UnixCredentialSocket::SendMsg(
+ client_sock, &message, sizeof(message)) == 0;
+}
+
class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
public:
PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}
~PtraceStrategyDeciderImpl() = default;
- Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
+ Strategy ChooseStrategy(int sock,
+ bool multiple_clients,
+ const ucred& client_credentials) override {
+ if (client_credentials.pid <= 0) {
+ LOG(ERROR) << "invalid credentials";
+ return Strategy::kNoPtrace;
+ }
+
switch (GetPtraceScope()) {
case PtraceScope::kClassic:
- if (getuid() == client_credentials.uid) {
+ if (getuid() == client_credentials.uid || HaveCapSysPtrace()) {
return Strategy::kDirectPtrace;
}
- return TryForkingBroker(sock);
+ return multiple_clients ? Strategy::kNoPtrace : TryForkingBroker(sock);
case PtraceScope::kRestricted:
+ if (multiple_clients) {
+ return Strategy::kDirectPtrace;
+ }
if (!SendMessageToClient(sock,
- ServerToClientMessage::kTypeSetPtracer)) {
+ ExceptionHandlerProtocol::
+ ServerToClientMessage::kTypeSetPtracer)) {
return Strategy::kError;
}
- Errno status;
+ ExceptionHandlerProtocol::Errno status;
if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {
return Strategy::kError;
}
@@ -169,12 +214,13 @@
private:
static Strategy TryForkingBroker(int client_sock) {
- if (!SendMessageToClient(client_sock,
- ServerToClientMessage::kTypeForkBroker)) {
+ if (!SendMessageToClient(
+ client_sock,
+ ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker)) {
return Strategy::kError;
}
- Errno status;
+ ExceptionHandlerProtocol::Errno status;
if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) {
return Strategy::kError;
}
@@ -190,12 +236,6 @@
} // namespace
-struct ExceptionHandlerServer::Event {
- enum class Type { kShutdown, kClientMessage } type;
-
- ScopedFileHandle fd;
-};
-
ExceptionHandlerServer::ExceptionHandlerServer()
: clients_(),
shutdown_event_(),
@@ -211,7 +251,8 @@
strategy_decider_ = std::move(decider);
}
-bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
+bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock,
+ bool multiple_clients) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
@@ -239,7 +280,9 @@
return false;
}
- if (!InstallClientSocket(std::move(sock))) {
+ if (!InstallClientSocket(std::move(sock),
+ multiple_clients ? Event::Type::kSharedSocketMessage
+ : Event::Type::kClientMessage)) {
return false;
}
@@ -281,8 +324,8 @@
}
void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
- DCHECK_EQ(AsUnderlyingType(event->type),
- AsUnderlyingType(Event::Type::kClientMessage));
+ DCHECK_NE(AsUnderlyingType(event->type),
+ AsUnderlyingType(Event::Type::kShutdown));
if (event_type & EPOLLERR) {
LogSocketError(event->fd.get());
@@ -306,16 +349,30 @@
return;
}
-bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket) {
- int optval = 1;
+bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket,
+ Event::Type type) {
+ // The handler may not have permission to set SO_PASSCRED on the socket, but
+ // it doesn't need to if the client has already set it.
+ // https://bugs.chromium.org/p/crashpad/issues/detail?id=252
+ int optval;
socklen_t optlen = sizeof(optval);
- if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != 0) {
- PLOG(ERROR) << "setsockopt";
+ if (getsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, &optlen) !=
+ 0) {
+ PLOG(ERROR) << "getsockopt";
return false;
}
+ if (!optval) {
+ optval = 1;
+ optlen = sizeof(optval);
+ if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=
+ 0) {
+ PLOG(ERROR) << "setsockopt";
+ return false;
+ }
+ }
auto event = std::make_unique<Event>();
- event->type = Event::Type::kClientMessage;
+ event->type = type;
event->fd.reset(socket.release());
Event* eventp = event.get();
@@ -355,53 +412,24 @@
}
bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
- ClientToServerMessage message;
- iovec iov;
- iov.iov_base = &message;
- iov.iov_len = sizeof(message);
-
- msghdr msg;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- char cmsg_buf[CMSG_SPACE(sizeof(ucred))];
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = sizeof(cmsg_buf);
- msg.msg_flags = 0;
-
- int res = HANDLE_EINTR(recvmsg(event->fd.get(), &msg, 0));
- if (res < 0) {
- PLOG(ERROR) << "recvmsg";
- return false;
- }
- if (res == 0) {
- // The client had an orderly shutdown.
+ ExceptionHandlerProtocol::ClientToServerMessage message;
+ ucred creds;
+ if (!UnixCredentialSocket::RecvMsg(
+ event->fd.get(), &message, sizeof(message), &creds)) {
return false;
}
- if (msg.msg_name != nullptr || msg.msg_namelen != 0) {
- LOG(ERROR) << "unexpected msg name";
- return false;
- }
+ switch (message.type) {
+ case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials:
+ return SendCredentials(event->fd.get());
- if (msg.msg_iovlen != 1) {
- LOG(ERROR) << "unexpected iovlen";
- return false;
- }
-
- if (msg.msg_iov[0].iov_len != sizeof(ClientToServerMessage)) {
- LOG(ERROR) << "unexpected message size " << msg.msg_iov[0].iov_len;
- return false;
- }
- auto client_msg =
- reinterpret_cast<ClientToServerMessage*>(msg.msg_iov[0].iov_base);
-
- switch (client_msg->type) {
- case ClientToServerMessage::kCrashDumpRequest:
+ case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest:
return HandleCrashDumpRequest(
- msg, client_msg->client_info, event->fd.get());
+ creds,
+ message.client_info,
+ message.requesting_thread_stack_address,
+ event->fd.get(),
+ event->type == Event::Type::kSharedSocketMessage);
}
DCHECK(false);
@@ -410,53 +438,56 @@
}
bool ExceptionHandlerServer::HandleCrashDumpRequest(
- const msghdr& msg,
- const ClientInformation& client_info,
- int client_sock) {
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- if (cmsg == nullptr) {
- LOG(ERROR) << "missing credentials";
- return false;
- }
+ const ucred& creds,
+ const ExceptionHandlerProtocol::ClientInformation& client_info,
+ VMAddress requesting_thread_stack_address,
+ int client_sock,
+ bool multiple_clients) {
+ pid_t client_process_id = creds.pid;
+ pid_t requesting_thread_id = -1;
+ uid_t client_uid = creds.uid;
- if (cmsg->cmsg_level != SOL_SOCKET) {
- LOG(ERROR) << "unexpected cmsg_level " << cmsg->cmsg_level;
- return false;
- }
-
- if (cmsg->cmsg_type != SCM_CREDENTIALS) {
- LOG(ERROR) << "unexpected cmsg_type " << cmsg->cmsg_type;
- return false;
- }
-
- if (cmsg->cmsg_len != CMSG_LEN(sizeof(ucred))) {
- LOG(ERROR) << "unexpected cmsg_len " << cmsg->cmsg_len;
- return false;
- }
-
- ucred* client_credentials = reinterpret_cast<ucred*>(CMSG_DATA(cmsg));
- pid_t client_process_id = client_credentials->pid;
-
- switch (strategy_decider_->ChooseStrategy(client_sock, *client_credentials)) {
+ switch (
+ strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) {
case PtraceStrategyDecider::Strategy::kError:
+ if (multiple_clients) {
+ SendSIGCONT(client_process_id, requesting_thread_id);
+ }
return false;
case PtraceStrategyDecider::Strategy::kNoPtrace:
- return SendMessageToClient(client_sock,
- ServerToClientMessage::kTypeCrashDumpFailed);
+ if (multiple_clients) {
+ SendSIGCONT(client_process_id, requesting_thread_id);
+ return true;
+ }
+ return SendMessageToClient(
+ client_sock,
+ ExceptionHandlerProtocol::ServerToClientMessage::
+ kTypeCrashDumpFailed);
- case PtraceStrategyDecider::Strategy::kDirectPtrace:
- delegate_->HandleException(client_process_id, client_info);
+ case PtraceStrategyDecider::Strategy::kDirectPtrace: {
+ delegate_->HandleException(client_process_id,
+ client_uid,
+ client_info,
+ requesting_thread_stack_address,
+ &requesting_thread_id);
+ if (multiple_clients) {
+ SendSIGCONT(client_process_id, requesting_thread_id);
+ return true;
+ }
break;
+ }
case PtraceStrategyDecider::Strategy::kUseBroker:
+ DCHECK(!multiple_clients);
delegate_->HandleExceptionWithBroker(
- client_process_id, client_info, client_sock);
+ client_process_id, client_uid, client_info, client_sock);
break;
}
- return SendMessageToClient(client_sock,
- ServerToClientMessage::kTypeCrashDumpComplete);
+ return SendMessageToClient(
+ client_sock,
+ ExceptionHandlerProtocol::ServerToClientMessage::kTypeCrashDumpComplete);
}
} // namespace crashpad
diff --git a/handler/linux/exception_handler_server.h b/handler/linux/exception_handler_server.h
index 5f49182..ac430a4 100644
--- a/handler/linux/exception_handler_server.h
+++ b/handler/linux/exception_handler_server.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <sys/socket.h>
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -26,6 +27,7 @@
#include "util/linux/exception_handler_protocol.h"
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/uuid.h"
namespace crashpad {
@@ -53,9 +55,12 @@
//! \brief Chooses an appropriate `ptrace` strategy.
//!
//! \param[in] sock A socket conncted to a ExceptionHandlerClient.
+ //! \param[in] multiple_clients `true` if the socket is connected to multiple
+ //! clients. The broker is not supported in this configuration.
//! \param[in] client_credentials The credentials for the connected client.
//! \return the chosen #Strategy.
virtual Strategy ChooseStrategy(int sock,
+ bool multiple_clients,
const ucred& client_credentials) = 0;
protected:
@@ -71,24 +76,43 @@
//! \brief Called on receipt of a crash dump request from a client.
//!
//! \param[in] client_process_id The process ID of the crashing client.
+ //! \param[in] client_uid The user ID of the crashing client.
//! \param[in] info Information on the client.
+ //! \param[in] requesting_thread_stack_address Any address within the stack
+ //! range for the the thread that sent the crash dump request. Optional.
+ //! If unspecified or 0, \a requesting_thread_id will be -1.
+ //! \param[out] requesting_thread_id The thread ID of the thread which
+ //! requested the crash dump if not `nullptr`. Set to -1 if the thread
+ //! ID could not be determined. Optional.
+ //! \param[out] local_report_id The unique identifier for the report created
+ //! in the local report database. Optional.
//! \return `true` on success. `false` on failure with a message logged.
- virtual bool HandleException(pid_t client_process_id,
- const ClientInformation& info) = 0;
+ virtual bool HandleException(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address = 0,
+ pid_t* requesting_thread_id = nullptr,
+ UUID* local_report_id = nullptr) = 0;
//! \brief Called on the receipt of a crash dump request from a client for a
//! crash that should be mediated by a PtraceBroker.
//!
//! \param[in] client_process_id The process ID of the crashing client.
+ //! \param[in] client_uid The uid of the crashing client.
//! \param[in] info Information on the client.
//! \param[in] broker_sock A socket connected to the PtraceBroker.
+ //! \param[out] local_report_id The unique identifier for the report created
+ //! in the local report database. Optional.
//! \return `true` on success. `false` on failure with a message logged.
- virtual bool HandleExceptionWithBroker(pid_t client_process_id,
- const ClientInformation& info,
- int broker_sock) = 0;
+ virtual bool HandleExceptionWithBroker(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id = nullptr) = 0;
- protected:
- ~Delegate() {}
+ virtual ~Delegate() {}
};
ExceptionHandlerServer();
@@ -105,8 +129,11 @@
//! This method must be successfully called before Run().
//!
//! \param[in] sock A socket on which to receive client requests.
+ //! \param[in] multiple_clients `true` if this socket is used by multiple
+ //! clients. Using a broker process is not supported in this
+ //! configuration.
//! \return `true` on success. `false` on failure with a message logged.
- bool InitializeWithClient(ScopedFileHandle sock);
+ bool InitializeWithClient(ScopedFileHandle sock, bool multiple_clients);
//! \brief Runs the exception-handling server.
//!
@@ -126,22 +153,39 @@
void Stop();
private:
- struct Event;
+ struct Event {
+ enum class Type {
+ // Used by Stop() to shutdown the server.
+ kShutdown,
+
+ // A message from a client on a private socket connection.
+ kClientMessage,
+
+ // A message from a client on a shared socket connection.
+ kSharedSocketMessage
+ };
+
+ Type type;
+ ScopedFileHandle fd;
+ };
void HandleEvent(Event* event, uint32_t event_type);
- bool InstallClientSocket(ScopedFileHandle socket);
+ bool InstallClientSocket(ScopedFileHandle socket, Event::Type type);
bool UninstallClientSocket(Event* event);
bool ReceiveClientMessage(Event* event);
- bool HandleCrashDumpRequest(const msghdr& msg,
- const ClientInformation& client_info,
- int client_sock);
+ bool HandleCrashDumpRequest(
+ const ucred& creds,
+ const ExceptionHandlerProtocol::ClientInformation& client_info,
+ VMAddress requesting_thread_stack_address,
+ int client_sock,
+ bool multiple_clients);
std::unordered_map<int, std::unique_ptr<Event>> clients_;
std::unique_ptr<Event> shutdown_event_;
std::unique_ptr<PtraceStrategyDecider> strategy_decider_;
Delegate* delegate_;
ScopedFileHandle pollfd_;
- bool keep_running_;
+ std::atomic<bool> keep_running_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc
index eda14d3..7da5bab 100644
--- a/handler/linux/exception_handler_server_test.cc
+++ b/handler/linux/exception_handler_server_test.cc
@@ -18,16 +18,23 @@
#include <unistd.h>
#include "base/logging.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
+#include "snapshot/linux/process_snapshot_linux.h"
#include "test/errors.h"
#include "test/multiprocess.h"
#include "util/linux/direct_ptrace_connection.h"
#include "util/linux/exception_handler_client.h"
#include "util/linux/ptrace_client.h"
#include "util/linux/scoped_pr_set_ptracer.h"
+#include "util/misc/uuid.h"
#include "util/synchronization/semaphore.h"
#include "util/thread/thread.h"
+#if defined(OS_ANDROID)
+#include <android/api-level.h>
+#endif
+
namespace crashpad {
namespace test {
namespace {
@@ -101,7 +108,11 @@
}
bool HandleException(pid_t client_process_id,
- const ClientInformation& info) override {
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress requesting_thread_stack_address,
+ pid_t* requesting_thread_id = nullptr,
+ UUID* local_report_id = nullptr) override {
DirectPtraceConnection connection;
bool connected = connection.Initialize(client_process_id);
EXPECT_TRUE(connected);
@@ -109,12 +120,32 @@
last_exception_address_ = info.exception_information_address;
last_client_ = client_process_id;
sem_.Signal();
- return connected;
+ if (!connected) {
+ return false;
+ }
+
+ if (requesting_thread_id) {
+ if (requesting_thread_stack_address) {
+ ProcessSnapshotLinux process_snapshot;
+ if (!process_snapshot.Initialize(&connection)) {
+ ADD_FAILURE();
+ return false;
+ }
+ *requesting_thread_id = process_snapshot.FindThreadWithStackAddress(
+ requesting_thread_stack_address);
+ } else {
+ *requesting_thread_id = -1;
+ }
+ }
+ return true;
}
- bool HandleExceptionWithBroker(pid_t client_process_id,
- const ClientInformation& info,
- int broker_sock) override {
+ bool HandleExceptionWithBroker(
+ pid_t client_process_id,
+ uid_t client_uid,
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ int broker_sock,
+ UUID* local_report_id = nullptr) override {
PtraceClient client;
bool connected = client.Initialize(broker_sock, client_process_id);
EXPECT_TRUE(connected);
@@ -140,12 +171,15 @@
~MockPtraceStrategyDecider() {}
- Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
+ Strategy ChooseStrategy(int sock,
+ bool multiple_clients,
+ const ucred& client_credentials) override {
if (strategy_ == Strategy::kUseBroker) {
- ServerToClientMessage message = {};
- message.type = ServerToClientMessage::kTypeForkBroker;
+ ExceptionHandlerProtocol::ServerToClientMessage message = {};
+ message.type =
+ ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker;
- Errno status;
+ ExceptionHandlerProtocol::Errno status;
bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&
LoggingReadFileExactly(sock, &status, sizeof(status));
EXPECT_TRUE(result);
@@ -169,13 +203,14 @@
DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
};
-class ExceptionHandlerServerTest : public testing::Test {
+class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
public:
ExceptionHandlerServerTest()
: server_(),
delegate_(),
server_thread_(&server_, &delegate_),
- sock_to_handler_() {}
+ sock_to_handler_(),
+ use_multi_client_socket_(GetParam()) {}
~ExceptionHandlerServerTest() = default;
@@ -197,7 +232,7 @@
~CrashDumpTest() = default;
void MultiprocessParent() override {
- ClientInformation info;
+ ExceptionHandlerProtocol::ClientInformation info;
ASSERT_TRUE(
LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
@@ -216,9 +251,8 @@
void MultiprocessChild() override {
ASSERT_EQ(close(server_test_->sock_to_client_), 0);
- ClientInformation info;
+ ExceptionHandlerProtocol::ClientInformation info;
info.exception_information_address = 42;
-
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
// If the current ptrace_scope is restricted, the broker needs to be set
@@ -226,7 +260,8 @@
// ptracer allows the broker to inherit this condition.
ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true);
- ExceptionHandlerClient client(server_test_->SockToHandler());
+ ExceptionHandlerClient client(server_test_->SockToHandler(),
+ server_test_->use_multi_client_socket_);
ASSERT_EQ(client.RequestCrashDump(info), 0);
}
@@ -239,16 +274,18 @@
void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
bool succeeds) {
- ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
- ServerThread()->Start();
-
Server()->SetPtraceStrategyDecider(
std::make_unique<MockPtraceStrategyDecider>(strategy));
+ ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
+ ServerThread()->Start();
+
CrashDumpTest test(this, succeeds);
test.Run();
}
+ bool UsingMultiClientSocket() const { return use_multi_client_socket_; }
+
protected:
void SetUp() override {
int socks[2];
@@ -256,7 +293,8 @@
sock_to_handler_.reset(socks[0]);
sock_to_client_ = socks[1];
- ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1])));
+ ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]),
+ use_multi_client_socket_));
}
private:
@@ -265,36 +303,37 @@
RunServerThread server_thread_;
ScopedFileHandle sock_to_handler_;
int sock_to_client_;
+ bool use_multi_client_socket_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
};
-TEST_F(ExceptionHandlerServerTest, ShutdownWithNoClients) {
+TEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) {
ServerThread()->Start();
Hangup();
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
}
-TEST_F(ExceptionHandlerServerTest, StopWithClients) {
+TEST_P(ExceptionHandlerServerTest, StopWithClients) {
ServerThread()->Start();
Server()->Stop();
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
}
-TEST_F(ExceptionHandlerServerTest, StopBeforeRun) {
+TEST_P(ExceptionHandlerServerTest, StopBeforeRun) {
Server()->Stop();
ServerThread()->Start();
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
}
-TEST_F(ExceptionHandlerServerTest, MultipleStops) {
+TEST_P(ExceptionHandlerServerTest, MultipleStops) {
ServerThread()->Start();
Server()->Stop();
Server()->Stop();
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
}
-TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
+TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
ServerThread()->Start();
@@ -302,25 +341,35 @@
test.Run();
}
-TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
+TEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
false);
}
-TEST_F(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
+TEST_P(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
+ if (UsingMultiClientSocket()) {
+ // The broker is not supported with multiple clients connected on a single
+ // socket.
+ return;
+ }
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker,
true);
}
-TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
+TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
true);
}
-TEST_F(ExceptionHandlerServerTest, RequestCrashDumpError) {
+TEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) {
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
}
+INSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite,
+ ExceptionHandlerServerTest,
+ testing::Bool()
+);
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/handler/linux/handler_trampoline.cc b/handler/linux/handler_trampoline.cc
new file mode 100644
index 0000000..3453563
--- /dev/null
+++ b/handler/linux/handler_trampoline.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android/log.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+// The first argument passed to the trampoline is the name of the native library
+// exporting the symbol `CrashpadHandlerMain`. The remaining arguments are the
+// same as for `HandlerMain()`.
+int main(int argc, char* argv[]) {
+ static constexpr char kTag[] = "crashpad";
+
+ if (argc < 2) {
+ __android_log_print(ANDROID_LOG_FATAL, kTag, "usage: %s <path>", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL);
+ if (!handle) {
+ __android_log_print(ANDROID_LOG_FATAL, kTag, "dlopen: %s", dlerror());
+ return EXIT_FAILURE;
+ }
+
+ using MainType = int (*)(int, char*[]);
+ MainType crashpad_main =
+ reinterpret_cast<MainType>(dlsym(handle, "CrashpadHandlerMain"));
+ if (!crashpad_main) {
+ __android_log_print(ANDROID_LOG_FATAL, kTag, "dlsym: %s", dlerror());
+ return EXIT_FAILURE;
+ }
+
+ return crashpad_main(argc - 1, argv + 1);
+}
diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc
index 9919e95..cccf1e9 100644
--- a/handler/mac/crash_report_exception_handler.cc
+++ b/handler/mac/crash_report_exception_handler.cc
@@ -28,6 +28,7 @@
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/file/file_writer.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/exc_client_variants.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"
diff --git a/handler/mac/crash_report_exception_handler.h b/handler/mac/crash_report_exception_handler.h
index 0b44de6..b5a59e4 100644
--- a/handler/mac/crash_report_exception_handler.h
+++ b/handler/mac/crash_report_exception_handler.h
@@ -30,7 +30,8 @@
//! \brief An exception handler that writes crash reports for exception messages
//! to a CrashReportDatabase.
-class CrashReportExceptionHandler : public UniversalMachExcServer::Interface {
+class CrashReportExceptionHandler final
+ : public UniversalMachExcServer::Interface {
public:
//! \brief Creates a new object that will store crash reports in \a database.
//!
diff --git a/handler/mac/file_limit_annotation.cc b/handler/mac/file_limit_annotation.cc
index cf56eb8..a01e87d 100644
--- a/handler/mac/file_limit_annotation.cc
+++ b/handler/mac/file_limit_annotation.cc
@@ -24,7 +24,7 @@
#include <string>
#include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "client/simple_string_dictionary.h"
@@ -108,7 +108,7 @@
int mib[] = {CTL_KERN, KERN_MAXFILES};
size = sizeof(value);
std::string max_files = FormatFromSysctl(
- sysctl(mib, arraysize(mib), &value, &size, nullptr, 0), &value, &size);
+ sysctl(mib, base::size(mib), &value, &size, nullptr, 0), &value, &size);
std::string open_files = CountOpenFileDescriptors();
diff --git a/handler/minidump_to_upload_parameters.cc b/handler/minidump_to_upload_parameters.cc
index 03c6fe1..6b64081 100644
--- a/handler/minidump_to_upload_parameters.cc
+++ b/handler/minidump_to_upload_parameters.cc
@@ -17,6 +17,7 @@
#include "base/logging.h"
#include "client/annotation.h"
#include "snapshot/module_snapshot.h"
+#include "snapshot/process_snapshot.h"
#include "util/stdlib/map_insert.h"
namespace crashpad {
@@ -49,7 +50,7 @@
}
}
- for (std::string annotation : module->AnnotationsVector()) {
+ for (const std::string& annotation : module->AnnotationsVector()) {
list_annotations.append(annotation);
list_annotations.append("\n");
}
diff --git a/handler/minidump_to_upload_parameters.h b/handler/minidump_to_upload_parameters.h
index 41056f7..94d396f 100644
--- a/handler/minidump_to_upload_parameters.h
+++ b/handler/minidump_to_upload_parameters.h
@@ -18,10 +18,10 @@
#include <map>
#include <string>
-#include "snapshot/process_snapshot.h"
-
namespace crashpad {
+class ProcessSnapshot;
+
//! \brief Given a ProcessSnapshot, returns a map of key-value pairs to use as
//! HTTP form parameters for upload to a Breakpad crash report colleciton
//! server.
diff --git a/handler/win/.gitattributes b/handler/win/.gitattributes
new file mode 100644
index 0000000..6495e51
--- /dev/null
+++ b/handler/win/.gitattributes
@@ -0,0 +1,21 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This should be a .cc file, which would allow its attributes to be controlled
+# by the *.cc pattern in the root .gitattributes file, but it’s named with a
+# .cpp extension instead. This file needs to be built with VC++6, a vintage 1998
+# compiler, which might not understand .cc to mean C++. Rather than setting
+# attributes globally for .cpp files, which are undesirable (.cc should be used
+# in its place), provide a file-specific mapping here.
+/z7_test.cpp text eol=lf
diff --git a/handler/win/crash_report_exception_handler.h b/handler/win/crash_report_exception_handler.h
index c2781de..566f047 100644
--- a/handler/win/crash_report_exception_handler.h
+++ b/handler/win/crash_report_exception_handler.h
@@ -31,7 +31,8 @@
//! \brief An exception handler that writes crash reports for exception messages
//! to a CrashReportDatabase.
-class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
+class CrashReportExceptionHandler final
+ : public ExceptionHandlerServer::Delegate {
public:
//! \brief Creates a new object that will store crash reports in \a database.
//!
diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc
index a4f4797..9c8adbb 100644
--- a/handler/win/crashy_test_program.cc
+++ b/handler/win/crashy_test_program.cc
@@ -26,7 +26,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
@@ -83,11 +83,11 @@
// All of these allocations are leaked, we want to view them in windbg via
// !vprot.
void* reserve = VirtualAlloc(
- nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
+ nullptr, base::size(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);
- for (size_t i = 0; i < arraysize(kPageTypes); ++i) {
+ for (size_t i = 0; i < base::size(kPageTypes); ++i) {
void* result =
VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
kPageSize,
@@ -101,7 +101,7 @@
return 0;
}
-// Creates a suspended background thread, and sets EDI/RDI to point at
+// Creates a suspended background thread, and sets EDI/RDI/X17 to point at
// g_test_memory so we can confirm it's available in the minidump.
bool CreateThreadWithRegisterPointingToTestMemory() {
HANDLE thread = CreateThread(
@@ -121,6 +121,8 @@
context.Rdi = reinterpret_cast<DWORD64>(g_test_memory);
#elif defined(ARCH_CPU_X86)
context.Edi = reinterpret_cast<DWORD>(g_test_memory);
+#elif defined(ARCH_CPU_ARM64)
+ context.X17 = reinterpret_cast<DWORD64>(g_test_memory);
#endif
if (!SetThreadContext(thread, &context)) {
PLOG(ERROR) << "SetThreadContext";
diff --git a/handler/win/hanging_program.cc b/handler/win/hanging_program.cc
index 840c512..f4e5b98 100644
--- a/handler/win/hanging_program.cc
+++ b/handler/win/hanging_program.cc
@@ -17,7 +17,7 @@
#include "base/debug/alias.h"
#include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "client/crashpad_client.h"
@@ -123,8 +123,8 @@
fflush(stdout);
// This is not expected to return.
- DWORD count =
- WaitForMultipleObjects(arraysize(threads), threads, true, INFINITE);
+ DWORD count = WaitForMultipleObjects(
+ static_cast<DWORD>(base::size(threads)), threads, true, INFINITE);
if (count == WAIT_FAILED) {
PLOG(ERROR) << "WaitForMultipleObjects";
} else {
diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py
new file mode 100644
index 0000000..ca04433
--- /dev/null
+++ b/infra/config/PRESUBMIT.py
@@ -0,0 +1,23 @@
+# Copyright 2018 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return input_api.canned_checks.CheckChangedLUCIConfigs(
+ input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return input_api.canned_checks.CheckChangedLUCIConfigs(
+ input_api, output_api)
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
deleted file mode 100644
index 1fc3324..0000000
--- a/infra/config/cq.cfg
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2017 The Crashpad Authors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See https://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
-# documentation of this file format.
-
-version: 1
-cq_name: "crashpad"
-cq_status_url: "https://chromium-cq-status.appspot.com"
-
-# This is required for gerrit projects.
-git_repo_url: "https://chromium.googlesource.com/crashpad/crashpad"
-
-# Crashpad uses gerrit with no special options.
-gerrit {}
-
-verifiers {
- gerrit_cq_ability {
- committer_list: "project-crashpad-tryjob-access"
- dry_run_access_list: "project-crashpad-tryjob-access"
- }
- try_job {
- buckets {
- name: "master.client.crashpad"
- builders { name: "crashpad_try_win_dbg" }
- builders { name: "crashpad_try_win_rel" }
- # https://crbug.com/743139 - disabled until we can move these to swarming,
- # at which point we can just remove them.
- #builders { name: "crashpad_try_win_x86_dbg" }
- #builders { name: "crashpad_try_win_x86_rel" }
- }
- buckets {
- name: "luci.crashpad.try"
- builders { name: "crashpad_try_mac_dbg" }
- builders { name: "crashpad_try_mac_rel" }
- builders { name: "crashpad_try_win_dbg" experiment_percentage: 100 }
- builders { name: "crashpad_try_win_rel" experiment_percentage: 100 }
- builders { name: "crashpad_try_linux_dbg" }
- builders { name: "crashpad_try_linux_rel" }
- builders { name: "crashpad_try_fuchsia_arm64_dbg" }
- builders { name: "crashpad_try_fuchsia_arm64_rel" }
- builders { name: "crashpad_try_fuchsia_x64_dbg" }
- builders { name: "crashpad_try_fuchsia_x64_rel" }
- # https://crbug.com/743139 - disabled until we can move these to swarming,
- # at which point we can just remove them.
- #builders { name: "crashpad_try_win_x86_dbg" }
- #builders { name: "crashpad_try_win_x86_rel" }
- }
- }
-}
diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn
index 88a1081..4442585 100644
--- a/minidump/BUILD.gn
+++ b/minidump/BUILD.gn
@@ -20,15 +20,12 @@
"minidump_annotation_writer.h",
"minidump_byte_array_writer.cc",
"minidump_byte_array_writer.h",
- "minidump_context.h",
"minidump_context_writer.cc",
"minidump_context_writer.h",
"minidump_crashpad_info_writer.cc",
"minidump_crashpad_info_writer.h",
"minidump_exception_writer.cc",
"minidump_exception_writer.h",
- "minidump_extensions.cc",
- "minidump_extensions.h",
"minidump_file_writer.cc",
"minidump_file_writer.h",
"minidump_handle_writer.cc",
@@ -72,6 +69,7 @@
public_configs = [ "..:crashpad_config" ]
public_deps = [
+ ":format",
"../compat",
]
@@ -89,6 +87,25 @@
}
}
+# :format is the only part of minidump that snapshot may depend on.
+static_library("format") {
+ sources = [
+ "minidump_context.h",
+ "minidump_extensions.cc",
+ "minidump_extensions.h",
+ ]
+
+ public_configs = [ "..:crashpad_config" ]
+
+ public_deps = [ "../compat" ]
+
+ deps = [
+ "../snapshot:context",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+}
+
static_library("test_support") {
testonly = true
@@ -113,13 +130,14 @@
public_configs = [ "..:crashpad_config" ]
- public_deps = [
- ":minidump",
- ]
+ public_deps = [ ":minidump" ]
deps = [
+ "../snapshot:test_support",
+ "../test",
"../third_party/gtest:gtest",
"../third_party/mini_chromium:base",
+ "../util",
]
if (crashpad_is_win) {
@@ -154,6 +172,8 @@
"minidump_writable_test.cc",
]
+ configs += [ "../build:crashpad_is_in_fuchsia" ]
+
deps = [
":test_support",
"../snapshot:test_support",
@@ -166,4 +186,6 @@
if (crashpad_is_win) {
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
}
+
+ configs += [ "..:disable_ubsan" ]
}
diff --git a/minidump/minidump_annotation_writer_test.cc b/minidump/minidump_annotation_writer_test.cc
index 1641d1d..dc05cc6 100644
--- a/minidump/minidump_annotation_writer_test.cc
+++ b/minidump/minidump_annotation_writer_test.cc
@@ -16,7 +16,7 @@
#include <memory>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "minidump/minidump_extensions.h"
#include "minidump/test/minidump_byte_array_writer_test_util.h"
@@ -107,7 +107,7 @@
MinidumpAnnotationListWriter list_writer;
- for (size_t i = 0; i < arraysize(kNames); ++i) {
+ for (size_t i = 0; i < base::size(kNames); ++i) {
auto annotation = std::make_unique<MinidumpAnnotationWriter>();
annotation->InitializeWithData(kNames[i], kTypes[i], kValues[i]);
list_writer.AddObject(std::move(annotation));
diff --git a/minidump/minidump_byte_array_writer_test.cc b/minidump/minidump_byte_array_writer_test.cc
index f20ad35..e4cd526 100644
--- a/minidump/minidump_byte_array_writer_test.cc
+++ b/minidump/minidump_byte_array_writer_test.cc
@@ -17,6 +17,7 @@
#include <memory>
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "minidump/test/minidump_writable_test_util.h"
@@ -34,7 +35,7 @@
{},
};
- for (size_t i = 0; i < arraysize(kTests); ++i) {
+ for (size_t i = 0; i < base::size(kTests); ++i) {
SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i));
StringFile string_file;
@@ -66,7 +67,7 @@
{},
};
- for (size_t i = 0; i < arraysize(kTests); ++i) {
+ for (size_t i = 0; i < base::size(kTests); ++i) {
SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i));
crashpad::MinidumpByteArrayWriter writer;
diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h
index 6f51b5a..3a3e603 100644
--- a/minidump/minidump_context.h
+++ b/minidump/minidump_context.h
@@ -389,11 +389,16 @@
//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags.
enum MinidumpContextARM64Flags : uint32_t {
//! \brief Identifies the context structure as 64-bit ARM.
- kMinidumpContextARM64 = 0x80000000,
+ kMinidumpContextARM64 = 0x00400000,
+
+ //! \brief Indicates the validity of control registers.
+ //!
+ //! Registers `fp`, `lr`, `sp`, `pc`, and `cpsr`.
+ kMinidumpContextARM64Control = kMinidumpContextARM64 | 0x00000001,
//! \brief Indicates the validty of integer registers.
//!
- //! Registers `x0`-`x31`, `pc`, and `cpsr`.
+ //! Registers `x0`-`x28`.
kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002,
//! \brief Indicates the validity of fpsimd registers.
@@ -401,17 +406,37 @@
//! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid.
kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004,
+ //! \brief Indicates the validity of debug registers.
+ //!
+ //! `bcr`, `bvr`, `wcr`, and `wvr` are valid.
+ kMinidumpContextARM64Debug = kMinidumpContextARM64 | 0x00000008,
+
+ //! \brief Indicates the validity of control, integer and floating point
+ //! registers.
+ kMinidumpContextARM64Full = kMinidumpContextARM64Control |
+ kMinidumpContextARM64Integer |
+ kMinidumpContextARM64Fpsimd,
+
//! \brief Indicates the validity of all registers.
kMinidumpContextARM64All =
- kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd,
+ kMinidumpContextARM64Full | kMinidumpContextARM64Debug,
};
//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file.
struct MinidumpContextARM64 {
- uint64_t context_flags;
+ uint32_t context_flags;
- //! \brief General-purpose registers `x0`-`x30`.
- uint64_t regs[31];
+ //! \brief Current program status register.
+ uint32_t cpsr;
+
+ //! \brief General-purpose registers `x0`-`x28`.
+ uint64_t regs[29];
+
+ //! \brief Frame pointer or `x29`.
+ uint64_t fp;
+
+ //! \brief Link register or `x30`.
+ uint64_t lr;
//! \brief Stack pointer or `x31`.
uint64_t sp;
@@ -419,17 +444,20 @@
//! \brief Program counter.
uint64_t pc;
- //! \brief Current program status register.
- uint32_t cpsr;
-
- //! \brief Floating-point status register.
- uint32_t fpsr;
+ //! \brief NEON registers `v0`-`v31`.
+ uint128_struct fpsimd[32];
//! \brief Floating-point control register.
uint32_t fpcr;
- //! \brief NEON registers `v0`-`v31`.
- uint128_struct fpsimd[32];
+ //! \brief Floating-point status register.
+ uint32_t fpsr;
+
+ //! \brief Debug registers.
+ uint32_t bcr[8];
+ uint64_t bvr[8];
+ uint32_t wcr[2];
+ uint64_t wvr[2];
};
//! \brief 32bit MIPS-specifc flags for MinidumpContextMIPS::context_flags.
diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc
index 20adbf3..d7e53a4 100644
--- a/minidump/minidump_context_writer.cc
+++ b/minidump/minidump_context_writer.cc
@@ -321,26 +321,29 @@
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextARM64);
- context_.context_flags = kMinidumpContextARM64All;
+ context_.context_flags = kMinidumpContextARM64Full;
- static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
- "GPRs size mismatch");
+ static_assert(
+ sizeof(context_.regs) == sizeof(context_snapshot->regs) -
+ 2 * sizeof(context_snapshot->regs[0]),
+ "GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
+ context_.fp = context_snapshot->regs[29];
+ context_.lr = context_snapshot->regs[30];
context_.sp = context_snapshot->sp;
context_.pc = context_snapshot->pc;
+ context_.cpsr = context_snapshot->spsr;
- if (context_snapshot->pstate >
- std::numeric_limits<decltype(context_.cpsr)>::max()) {
- LOG(WARNING) << "pstate truncation";
- }
- context_.cpsr =
- static_cast<decltype(context_.cpsr)>(context_snapshot->pstate);
-
- context_.fpsr = context_snapshot->fpsr;
- context_.fpcr = context_snapshot->fpcr;
static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd),
"FPSIMD size mismatch");
memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd));
+ context_.fpcr = context_snapshot->fpcr;
+ context_.fpsr = context_snapshot->fpsr;
+
+ memset(context_.bcr, 0, sizeof(context_.bcr));
+ memset(context_.bvr, 0, sizeof(context_.bvr));
+ memset(context_.wcr, 0, sizeof(context_.wcr));
+ memset(context_.wvr, 0, sizeof(context_.wvr));
}
bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) {
diff --git a/minidump/minidump_exception_writer.cc b/minidump/minidump_exception_writer.cc
index d870de3..b0e1e62 100644
--- a/minidump/minidump_exception_writer.cc
+++ b/minidump/minidump_exception_writer.cc
@@ -21,7 +21,7 @@
#include "minidump/minidump_context_writer.h"
#include "snapshot/exception_snapshot.h"
#include "util/file/file_writer.h"
-#include "util/misc/arraysize_unsafe.h"
+#include "util/misc/arraysize.h"
namespace crashpad {
@@ -65,7 +65,7 @@
const size_t parameters = exception_information.size();
constexpr size_t kMaxParameters =
- ARRAYSIZE_UNSAFE(exception_.ExceptionRecord.ExceptionInformation);
+ ArraySize(exception_.ExceptionRecord.ExceptionInformation);
CHECK_LE(parameters, kMaxParameters);
exception_.ExceptionRecord.NumberParameters =
diff --git a/minidump/minidump_exception_writer_test.cc b/minidump/minidump_exception_writer_test.cc
index e4dc5fa..75adbee 100644
--- a/minidump/minidump_exception_writer_test.cc
+++ b/minidump/minidump_exception_writer_test.cc
@@ -17,6 +17,7 @@
#include <string>
#include <utility>
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "minidump/minidump_context.h"
#include "minidump/minidump_context_writer.h"
@@ -80,7 +81,7 @@
expected->ExceptionRecord.NumberParameters);
EXPECT_EQ(observed->ExceptionRecord.__unusedAlignment, 0u);
for (size_t index = 0;
- index < arraysize(observed->ExceptionRecord.ExceptionInformation);
+ index < base::size(observed->ExceptionRecord.ExceptionInformation);
++index) {
EXPECT_EQ(observed->ExceptionRecord.ExceptionInformation[index],
expected->ExceptionRecord.ExceptionInformation[index]);
diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h
index f3701dc..97276d5 100644
--- a/minidump/minidump_extensions.h
+++ b/minidump/minidump_extensions.h
@@ -25,14 +25,15 @@
#include "util/misc/pdb_structures.h"
#include "util/misc/uuid.h"
+#if defined(COMPILER_MSVC)
// C4200 is "nonstandard extension used : zero-sized array in struct/union".
// We would like to globally disable this warning, but unfortunately, the
// compiler is buggy and only supports disabling it with a pragma, so we can't
-// disable it with other silly warnings in build/common.gypi. See:
+// disable it with other silly warnings in the build files. See:
// https://connect.microsoft.com/VisualStudio/feedback/details/1114440
-MSVC_PUSH_DISABLE_WARNING(4200);
+#pragma warning(push)
+#pragma warning(disable: 4200)
-#if defined(COMPILER_MSVC)
#define PACKED
#pragma pack(push, 1)
#else
@@ -92,10 +93,18 @@
//! \sa MemoryInfoListStream
kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream,
+ //! \brief The last reserved minidump stream.
+ //!
+ //! \sa MemoryInfoListStream
+ kMinidumpStreamTypeLastReservedStream = LastReservedStream,
+
// 0x4350 = "CP"
//! \brief The stream type for MinidumpCrashpadInfo.
kMinidumpStreamTypeCrashpadInfo = 0x43500001,
+
+ //! \brief The last reserved crashpad stream.
+ kMinidumpStreamTypeCrashpadLastReservedStream = 0x4350ffff,
};
//! \brief A variable-length UTF-8-encoded string carried within a minidump
@@ -233,7 +242,7 @@
kMinidumpOSMacOSX = 0x8101,
//! \brief iOS, Darwin for mobile devices.
- kMinidumpOSiOS = 0x8102,
+ kMinidumpOSIOS = 0x8102,
//! \brief Linux, not including Android.
kMinidumpOSLinux = 0x8201,
@@ -255,7 +264,6 @@
kMinidumpOSUnknown = 0xffffffff,
};
-
//! \brief A list of ::RVA pointers.
struct ALIGNAS(4) PACKED MinidumpRVAList {
//! \brief The number of children present in the #children array.
@@ -490,11 +498,10 @@
#if defined(COMPILER_MSVC)
#pragma pack(pop)
+#pragma warning(pop) // C4200
#endif // COMPILER_MSVC
#undef PACKED
-MSVC_POP_WARNING(); // C4200
-
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_
diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc
index 7226545..6727a0d 100644
--- a/minidump/minidump_file_writer.cc
+++ b/minidump/minidump_file_writer.cc
@@ -211,17 +211,30 @@
}
bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) {
+ return WriteMinidump(file_writer, true);
+}
+
+bool MinidumpFileWriter::WriteMinidump(FileWriterInterface* file_writer,
+ bool allow_seek) {
DCHECK_EQ(state(), kStateMutable);
- FileOffset start_offset = file_writer->Seek(0, SEEK_CUR);
- if (start_offset < 0) {
- return false;
+ FileOffset start_offset = -1;
+ if (allow_seek) {
+ start_offset = file_writer->Seek(0, SEEK_CUR);
+ if (start_offset < 0) {
+ return false;
+ }
+ } else {
+ header_.Signature = MINIDUMP_SIGNATURE;
}
if (!MinidumpWritable::WriteEverything(file_writer)) {
return false;
}
+ if (!allow_seek)
+ return true;
+
FileOffset end_offset = file_writer->Seek(0, SEEK_CUR);
if (end_offset < 0) {
return false;
@@ -232,7 +245,7 @@
// it as a valid minidump file.
header_.Signature = MINIDUMP_SIGNATURE;
- if (file_writer->Seek(start_offset, SEEK_SET) != 0) {
+ if (file_writer->Seek(start_offset, SEEK_SET) < 0) {
return false;
}
diff --git a/minidump/minidump_file_writer.h b/minidump/minidump_file_writer.h
index ce2f1d7..0a1b064 100644
--- a/minidump/minidump_file_writer.h
+++ b/minidump/minidump_file_writer.h
@@ -134,6 +134,21 @@
//! mistaken for valid ones.
bool WriteEverything(FileWriterInterface* file_writer) override;
+ //! \brief Writes this object to a minidump file.
+ //!
+ //! Same as \a WriteEverything, but give the option to disable the seek. It
+ //! is typically used to write to stream backed \a FileWriterInterface which
+ //! doesn't support seek.
+ //!
+ //! \param[in] file_writer The file writer to receive the minidump file’s
+ //! content.
+ //!
+ //! \param[in] allow_seek Whether seek is allowed.
+ //!
+ //! \return `true` on success. `false` on failure, with an appropriate message
+ //! logged.
+ bool WriteMinidump(FileWriterInterface* file_writer, bool allow_seek);
+
protected:
// MinidumpWritable:
bool Freeze() override;
diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc
index 730da26..19eaae4 100644
--- a/minidump/minidump_file_writer_test.cc
+++ b/minidump/minidump_file_writer_test.cc
@@ -20,7 +20,8 @@
#include <string>
#include <utility>
-#include "base/compiler_specific.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "minidump/minidump_stream_writer.h"
#include "minidump/minidump_user_extension_stream_data_source.h"
@@ -35,7 +36,9 @@
#include "snapshot/test/test_system_snapshot.h"
#include "snapshot/test/test_thread_snapshot.h"
#include "test/gtest_death.h"
+#include "util/file/output_stream_file_writer.h"
#include "util/file/string_file.h"
+#include "util/stream/output_stream_interface.h"
namespace crashpad {
namespace test {
@@ -87,6 +90,22 @@
DISALLOW_COPY_AND_ASSIGN(TestStream);
};
+class StringFileOutputStream : public OutputStreamInterface {
+ public:
+ StringFileOutputStream() = default;
+ ~StringFileOutputStream() override = default;
+ bool Write(const uint8_t* data, size_t size) override {
+ return string_file_.Write(data, size);
+ }
+ bool Flush() override { return true; }
+ const StringFile& string_file() const { return string_file_; }
+
+ private:
+ StringFile string_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringFileOutputStream);
+};
+
TEST(MinidumpFileWriter, OneStream) {
MinidumpFileWriter minidump_file;
constexpr time_t kTimestamp = 0x155d2fb8;
@@ -134,7 +153,7 @@
minidump_file.SetTimestamp(kTimestamp);
static constexpr uint8_t kStreamData[] = "Hello World!";
- constexpr size_t kStreamSize = arraysize(kStreamData);
+ constexpr size_t kStreamSize = base::size(kStreamData);
constexpr MinidumpStreamType kStreamType =
static_cast<MinidumpStreamType>(0x4d);
@@ -392,21 +411,16 @@
}
TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) {
- // In a 32-bit environment, this will give a “timestamp out of range” warning,
+ // In a 32-bit environment, this will give a “timestamp out of range” warning,
// but the test should complete without failure.
constexpr uint32_t kSnapshotTime = 0xfd469ab8;
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconstant-conversion"
-#define DISABLED_WCONSTANT_CONVERSION
-#endif // __clang__
- MSVC_SUPPRESS_WARNING(4309); // Truncation of constant value.
- MSVC_SUPPRESS_WARNING(4838); // Narrowing conversion.
- constexpr timeval kSnapshotTimeval = {static_cast<time_t>(kSnapshotTime), 0};
-#if defined(DISABLED_WCONSTANT_CONVERSION)
-#pragma clang diagnostic pop
-#undef DISABLED_WCONSTANT_CONVERSION
-#endif // DISABLED_WCONSTANT_CONVERSION
+ constexpr timeval kSnapshotTimeval = {
+#ifdef OS_WIN
+ static_cast<long>(kSnapshotTime),
+#else
+ static_cast<time_t>(kSnapshotTime),
+#endif
+ 0};
TestProcessSnapshot process_snapshot;
process_snapshot.SetSnapshotTime(kSnapshotTimeval);
@@ -582,6 +596,51 @@
EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStream0Size), 0);
}
+TEST(MinidumpFileWriter, WriteMinidumpDisallowSeek) {
+ MinidumpFileWriter minidump_file;
+ constexpr time_t kTimestamp = 0x155d2fb8;
+ minidump_file.SetTimestamp(kTimestamp);
+
+ constexpr size_t kStreamSize = 5;
+ constexpr MinidumpStreamType kStreamType =
+ static_cast<MinidumpStreamType>(0x4d);
+ constexpr uint8_t kStreamValue = 0x5a;
+ auto stream =
+ std::make_unique<TestStream>(kStreamType, kStreamSize, kStreamValue);
+ ASSERT_TRUE(minidump_file.AddStream(std::move(stream)));
+
+ std::unique_ptr<StringFileOutputStream> string_file_output_stream =
+ std::make_unique<StringFileOutputStream>();
+ const StringFile& string_file = string_file_output_stream->string_file();
+ OutputStreamFileWriter output_stream(std::move(string_file_output_stream));
+ ASSERT_TRUE(minidump_file.WriteMinidump(&output_stream, false));
+ ASSERT_TRUE(output_stream.Flush());
+
+ constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
+ constexpr size_t kStreamOffset =
+ kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
+ constexpr size_t kFileSize = kStreamOffset + kStreamSize;
+
+ ASSERT_EQ(string_file.string().size(), kFileSize);
+
+ const MINIDUMP_DIRECTORY* directory;
+ const MINIDUMP_HEADER* header =
+ MinidumpHeaderAtStart(string_file.string(), &directory);
+ ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));
+ ASSERT_TRUE(directory);
+
+ EXPECT_EQ(directory[0].StreamType, kStreamType);
+ EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);
+ EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);
+
+ const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(
+ string_file.string(), directory[0].Location);
+ ASSERT_TRUE(stream_data);
+
+ std::string expected_stream(kStreamSize, kStreamValue);
+ EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0);
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/minidump/minidump_memory_writer_test.cc b/minidump/minidump_memory_writer_test.cc
index 60b7fa8..90287cb 100644
--- a/minidump/minidump_memory_writer_test.cc
+++ b/minidump/minidump_memory_writer_test.cc
@@ -17,6 +17,7 @@
#include <utility>
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "minidump/minidump_extensions.h"
@@ -340,7 +341,7 @@
TEST(MinidumpMemoryWriter, AddFromSnapshot) {
MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {};
- uint8_t values[arraysize(expect_memory_descriptors)] = {};
+ uint8_t values[base::size(expect_memory_descriptors)] = {};
expect_memory_descriptors[0].StartOfMemoryRange = 0;
expect_memory_descriptors[0].Memory.DataSize = 0x1000;
@@ -356,8 +357,7 @@
std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
std::vector<const MemorySnapshot*> memory_snapshots;
- for (size_t index = 0;
- index < arraysize(expect_memory_descriptors);
+ for (size_t index = 0; index < base::size(expect_memory_descriptors);
++index) {
memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
@@ -396,7 +396,7 @@
TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) {
MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {};
- uint8_t values[arraysize(expect_memory_descriptors)] = {};
+ uint8_t values[base::size(expect_memory_descriptors)] = {};
expect_memory_descriptors[0].StartOfMemoryRange = 0;
expect_memory_descriptors[0].Memory.DataSize = 1000;
diff --git a/minidump/minidump_misc_info_writer.cc b/minidump/minidump_misc_info_writer.cc
index d83ed23..a134076 100644
--- a/minidump/minidump_misc_info_writer.cc
+++ b/minidump/minidump_misc_info_writer.cc
@@ -18,6 +18,7 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
@@ -26,7 +27,6 @@
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
#include "util/file/file_writer.h"
-#include "util/misc/arraysize_unsafe.h"
#include "util/numeric/in_range_cast.h"
#include "util/numeric/safe_assignment.h"
@@ -302,7 +302,7 @@
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
misc_info_.TimeZone.StandardName,
- ARRAYSIZE_UNSAFE(misc_info_.TimeZone.StandardName),
+ base::size(misc_info_.TimeZone.StandardName),
standard_name);
misc_info_.TimeZone.StandardDate = standard_date;
@@ -310,7 +310,7 @@
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
misc_info_.TimeZone.DaylightName,
- ARRAYSIZE_UNSAFE(misc_info_.TimeZone.DaylightName),
+ base::size(misc_info_.TimeZone.DaylightName),
daylight_name);
misc_info_.TimeZone.DaylightDate = daylight_date;
@@ -327,12 +327,10 @@
misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING;
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
- misc_info_.BuildString,
- ARRAYSIZE_UNSAFE(misc_info_.BuildString),
- build_string);
+ misc_info_.BuildString, base::size(misc_info_.BuildString), build_string);
internal::MinidumpWriterUtil::AssignUTF8ToUTF16(
misc_info_.DbgBldStr,
- ARRAYSIZE_UNSAFE(misc_info_.DbgBldStr),
+ base::size(misc_info_.DbgBldStr),
debug_build_string);
}
diff --git a/minidump/minidump_misc_info_writer_test.cc b/minidump/minidump_misc_info_writer_test.cc
index 3c60ebc..bd92733 100644
--- a/minidump/minidump_misc_info_writer_test.cc
+++ b/minidump/minidump_misc_info_writer_test.cc
@@ -21,6 +21,7 @@
#include "base/compiler_specific.h"
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
@@ -31,7 +32,6 @@
#include "snapshot/test/test_process_snapshot.h"
#include "snapshot/test/test_system_snapshot.h"
#include "util/file/string_file.h"
-#include "util/misc/arraysize_unsafe.h"
#include "util/stdlib/strlcpy.h"
namespace crashpad {
@@ -127,7 +127,7 @@
SCOPED_TRACE("Standard");
ExpectNULPaddedString16Equal(expected->TimeZone.StandardName,
observed->TimeZone.StandardName,
- arraysize(expected->TimeZone.StandardName));
+ base::size(expected->TimeZone.StandardName));
ExpectSystemTimeEqual(&expected->TimeZone.StandardDate,
&observed->TimeZone.StandardDate);
EXPECT_EQ(observed->TimeZone.StandardBias, expected->TimeZone.StandardBias);
@@ -136,7 +136,7 @@
SCOPED_TRACE("Daylight");
ExpectNULPaddedString16Equal(expected->TimeZone.DaylightName,
observed->TimeZone.DaylightName,
- arraysize(expected->TimeZone.DaylightName));
+ base::size(expected->TimeZone.DaylightName));
ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate,
&observed->TimeZone.DaylightDate);
EXPECT_EQ(observed->TimeZone.DaylightBias, expected->TimeZone.DaylightBias);
@@ -154,13 +154,13 @@
SCOPED_TRACE("BuildString");
ExpectNULPaddedString16Equal(expected->BuildString,
observed->BuildString,
- arraysize(expected->BuildString));
+ base::size(expected->BuildString));
}
{
SCOPED_TRACE("DbgBldStr");
ExpectNULPaddedString16Equal(expected->DbgBldStr,
observed->DbgBldStr,
- arraysize(expected->DbgBldStr));
+ base::size(expected->DbgBldStr));
}
}
@@ -176,7 +176,7 @@
EXPECT_EQ(observed->XStateData.EnabledFeatures,
expected->XStateData.EnabledFeatures);
for (size_t feature_index = 0;
- feature_index < arraysize(observed->XStateData.Features);
+ feature_index < base::size(observed->XStateData.Features);
++feature_index) {
SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index));
EXPECT_EQ(observed->XStateData.Features[feature_index].Offset,
@@ -396,7 +396,7 @@
base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
c16lcpy(expected.TimeZone.StandardName,
standard_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.StandardName));
+ base::size(expected.TimeZone.StandardName));
memcpy(&expected.TimeZone.StandardDate,
&kStandardDate,
sizeof(expected.TimeZone.StandardDate));
@@ -404,7 +404,7 @@
base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
c16lcpy(expected.TimeZone.DaylightName,
daylight_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.DaylightName));
+ base::size(expected.TimeZone.DaylightName));
memcpy(&expected.TimeZone.DaylightDate,
&kDaylightDate,
sizeof(expected.TimeZone.DaylightDate));
@@ -424,10 +424,9 @@
constexpr int32_t kBias = 300;
MINIDUMP_MISC_INFO_N tmp;
ALLOW_UNUSED_LOCAL(tmp);
- std::string standard_name(ARRAYSIZE_UNSAFE(tmp.TimeZone.StandardName) + 1,
- 's');
+ std::string standard_name(base::size(tmp.TimeZone.StandardName) + 1, 's');
constexpr int32_t kStandardBias = 0;
- std::string daylight_name(ARRAYSIZE_UNSAFE(tmp.TimeZone.DaylightName), 'd');
+ std::string daylight_name(base::size(tmp.TimeZone.DaylightName), 'd');
constexpr int32_t kDaylightBias = -60;
// Test using kSystemTimeZero, because not all platforms will be able to
@@ -458,7 +457,7 @@
base::string16 standard_name_utf16 = base::UTF8ToUTF16(standard_name);
c16lcpy(expected.TimeZone.StandardName,
standard_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.StandardName));
+ base::size(expected.TimeZone.StandardName));
memcpy(&expected.TimeZone.StandardDate,
&kSystemTimeZero,
sizeof(expected.TimeZone.StandardDate));
@@ -466,7 +465,7 @@
base::string16 daylight_name_utf16 = base::UTF8ToUTF16(daylight_name);
c16lcpy(expected.TimeZone.DaylightName,
daylight_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.DaylightName));
+ base::size(expected.TimeZone.DaylightName));
memcpy(&expected.TimeZone.DaylightDate,
&kSystemTimeZero,
sizeof(expected.TimeZone.DaylightDate));
@@ -497,12 +496,12 @@
base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
c16lcpy(expected.BuildString,
build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.BuildString));
+ base::size(expected.BuildString));
base::string16 debug_build_string_utf16 =
base::UTF8ToUTF16(kDebugBuildString);
c16lcpy(expected.DbgBldStr,
debug_build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.DbgBldStr));
+ base::size(expected.DbgBldStr));
ExpectMiscInfoEqual(&expected, observed);
}
@@ -516,8 +515,8 @@
MINIDUMP_MISC_INFO_N tmp;
ALLOW_UNUSED_LOCAL(tmp);
- std::string build_string(ARRAYSIZE_UNSAFE(tmp.BuildString) + 1, 'B');
- std::string debug_build_string(ARRAYSIZE_UNSAFE(tmp.DbgBldStr), 'D');
+ std::string build_string(base::size(tmp.BuildString) + 1, 'B');
+ std::string debug_build_string(base::size(tmp.DbgBldStr), 'D');
misc_info_writer->SetBuildString(build_string, debug_build_string);
@@ -534,12 +533,12 @@
base::string16 build_string_utf16 = base::UTF8ToUTF16(build_string);
c16lcpy(expected.BuildString,
build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.BuildString));
+ base::size(expected.BuildString));
base::string16 debug_build_string_utf16 =
base::UTF8ToUTF16(debug_build_string);
c16lcpy(expected.DbgBldStr,
debug_build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.DbgBldStr));
+ base::size(expected.DbgBldStr));
ExpectMiscInfoEqual(&expected, observed);
}
@@ -679,7 +678,7 @@
base::string16 standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
c16lcpy(expected.TimeZone.StandardName,
standard_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.StandardName));
+ base::size(expected.TimeZone.StandardName));
memcpy(&expected.TimeZone.StandardDate,
&kSystemTimeZero,
sizeof(expected.TimeZone.StandardDate));
@@ -687,7 +686,7 @@
base::string16 daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
c16lcpy(expected.TimeZone.DaylightName,
daylight_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.TimeZone.DaylightName));
+ base::size(expected.TimeZone.DaylightName));
memcpy(&expected.TimeZone.DaylightDate,
&kSystemTimeZero,
sizeof(expected.TimeZone.DaylightDate));
@@ -695,12 +694,12 @@
base::string16 build_string_utf16 = base::UTF8ToUTF16(kBuildString);
c16lcpy(expected.BuildString,
build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.BuildString));
+ base::size(expected.BuildString));
base::string16 debug_build_string_utf16 =
base::UTF8ToUTF16(kDebugBuildString);
c16lcpy(expected.DbgBldStr,
debug_build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expected.DbgBldStr));
+ base::size(expected.DbgBldStr));
ExpectMiscInfoEqual(&expected, observed);
}
@@ -744,18 +743,18 @@
expect_misc_info.TimeZone.Bias = 300;
c16lcpy(expect_misc_info.TimeZone.StandardName,
standard_time_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expect_misc_info.TimeZone.StandardName));
+ base::size(expect_misc_info.TimeZone.StandardName));
expect_misc_info.TimeZone.StandardBias = 0;
c16lcpy(expect_misc_info.TimeZone.DaylightName,
daylight_time_name_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expect_misc_info.TimeZone.DaylightName));
+ base::size(expect_misc_info.TimeZone.DaylightName));
expect_misc_info.TimeZone.DaylightBias = -60;
c16lcpy(expect_misc_info.BuildString,
build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expect_misc_info.BuildString));
+ base::size(expect_misc_info.BuildString));
c16lcpy(expect_misc_info.DbgBldStr,
debug_build_string_utf16.c_str(),
- ARRAYSIZE_UNSAFE(expect_misc_info.DbgBldStr));
+ base::size(expect_misc_info.DbgBldStr));
const timeval kStartTime =
{ static_cast<time_t>(expect_misc_info.ProcessCreateTime), 0 };
diff --git a/minidump/minidump_module_crashpad_info_writer_test.cc b/minidump/minidump_module_crashpad_info_writer_test.cc
index 63a9338..ba4ab05 100644
--- a/minidump/minidump_module_crashpad_info_writer_test.cc
+++ b/minidump/minidump_module_crashpad_info_writer_test.cc
@@ -19,6 +19,7 @@
#include <utility>
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "minidump/minidump_annotation_writer.h"
#include "minidump/minidump_simple_string_dictionary_writer.h"
@@ -154,9 +155,9 @@
sizeof(MinidumpSimpleStringDictionaryEntry) +
sizeof(MinidumpAnnotationList) + 2 + // padding
sizeof(MinidumpAnnotation) + sizeof(MinidumpUTF8String) +
- arraysize(kEntry) + 2 + // padding
- sizeof(MinidumpUTF8String) + arraysize(kKey) +
- sizeof(MinidumpUTF8String) + arraysize(kValue) +
+ base::size(kEntry) + 2 + // padding
+ sizeof(MinidumpUTF8String) + base::size(kKey) +
+ sizeof(MinidumpUTF8String) + base::size(kValue) +
sizeof(MinidumpUTF8String) + annotation.name.size() + 1 +
sizeof(MinidumpByteArray) + annotation.value.size());
diff --git a/minidump/minidump_module_writer.cc b/minidump/minidump_module_writer.cc
index 305d37c..6e53352 100644
--- a/minidump/minidump_module_writer.cc
+++ b/minidump/minidump_module_writer.cc
@@ -31,8 +31,7 @@
namespace crashpad {
-MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {
-}
+MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {}
namespace internal {
@@ -45,8 +44,7 @@
template <typename CodeViewRecordType>
MinidumpModuleCodeViewRecordPDBLinkWriter<
- CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {
-}
+ CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {}
template <typename CodeViewRecordType>
size_t
@@ -82,8 +80,7 @@
CodeViewRecordPDB20>;
MinidumpModuleCodeViewRecordPDB20Writer::
- ~MinidumpModuleCodeViewRecordPDB20Writer() {
-}
+ ~MinidumpModuleCodeViewRecordPDB20Writer() {}
void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(
time_t timestamp,
@@ -100,8 +97,7 @@
CodeViewRecordPDB70>;
MinidumpModuleCodeViewRecordPDB70Writer::
- ~MinidumpModuleCodeViewRecordPDB70Writer() {
-}
+ ~MinidumpModuleCodeViewRecordPDB70Writer() {}
void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot(
const ModuleSnapshot* module_snapshot) {
@@ -115,15 +111,52 @@
SetUUIDAndAge(uuid, age);
}
+MinidumpModuleCodeViewRecordBuildIDWriter::
+ MinidumpModuleCodeViewRecordBuildIDWriter()
+ : MinidumpModuleCodeViewRecordWriter(), build_id_() {}
+
+MinidumpModuleCodeViewRecordBuildIDWriter::
+ ~MinidumpModuleCodeViewRecordBuildIDWriter() {}
+
+size_t MinidumpModuleCodeViewRecordBuildIDWriter::SizeOfObject() {
+ DCHECK_GE(state(), kStateFrozen);
+ return offsetof(CodeViewRecordBuildID, build_id) + build_id_.size();
+}
+
+void MinidumpModuleCodeViewRecordBuildIDWriter::SetBuildID(
+ const std::vector<uint8_t>& build_id) {
+ DCHECK_EQ(state(), kStateMutable);
+ build_id_ = build_id;
+}
+
+bool MinidumpModuleCodeViewRecordBuildIDWriter::WriteObject(
+ FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+
+ CodeViewRecordBuildID cv;
+ cv.signature = CodeViewRecordBuildID::kSignature;
+
+ WritableIoVec iov;
+ iov.iov_base = &cv;
+ iov.iov_len = offsetof(CodeViewRecordBuildID, build_id);
+ std::vector<WritableIoVec> iovecs(1, iov);
+
+ if (!build_id_.empty()) {
+ iov.iov_base = build_id_.data();
+ iov.iov_len = build_id_.size();
+ iovecs.push_back(iov);
+ }
+
+ return file_writer->WriteIoVec(&iovecs);
+}
+
MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter()
: internal::MinidumpWritable(),
image_debug_misc_(),
data_(),
- data_utf16_() {
-}
+ data_utf16_() {}
-MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {
-}
+MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {}
void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data,
bool utf16) {
@@ -203,8 +236,7 @@
module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION;
}
-MinidumpModuleWriter::~MinidumpModuleWriter() {
-}
+MinidumpModuleWriter::~MinidumpModuleWriter() {}
void MinidumpModuleWriter::InitializeFromSnapshot(
const ModuleSnapshot* module_snapshot) {
@@ -242,9 +274,21 @@
}
SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN);
- auto codeview_record =
- std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
- codeview_record->InitializeFromSnapshot(module_snapshot);
+ auto build_id = module_snapshot->BuildID();
+
+ std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record;
+ if (!build_id.empty()) {
+ auto cv_record_build_id =
+ std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();
+ cv_record_build_id->SetBuildID(build_id);
+ codeview_record = std::move(cv_record_build_id);
+ } else {
+ auto cv_record_pdb70 =
+ std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();
+ cv_record_pdb70->InitializeFromSnapshot(module_snapshot);
+ codeview_record = std::move(cv_record_pdb70);
+ }
+
SetCodeViewRecord(std::move(codeview_record));
}
@@ -372,11 +416,9 @@
}
MinidumpModuleListWriter::MinidumpModuleListWriter()
- : MinidumpStreamWriter(), modules_(), module_list_base_() {
-}
+ : MinidumpStreamWriter(), modules_(), module_list_base_() {}
-MinidumpModuleListWriter::~MinidumpModuleListWriter() {
-}
+MinidumpModuleListWriter::~MinidumpModuleListWriter() {}
void MinidumpModuleListWriter::InitializeFromSnapshot(
const std::vector<const ModuleSnapshot*>& module_snapshots) {
diff --git a/minidump/minidump_module_writer.h b/minidump/minidump_module_writer.h
index 555c411..b328bf0 100644
--- a/minidump/minidump_module_writer.h
+++ b/minidump/minidump_module_writer.h
@@ -88,7 +88,8 @@
//! \brief The writer for a CodeViewRecordPDB20 object in a minidump file.
//!
-//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
+//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer or
+//! MinidumpModuleCodeViewRecordBuildIDWriter instead.
class MinidumpModuleCodeViewRecordPDB20Writer final
: public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
CodeViewRecordPDB20> {
@@ -136,6 +137,26 @@
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB70Writer);
};
+//! \brief The writer for a CodeViewRecordBuildID object in a minidump file.
+class MinidumpModuleCodeViewRecordBuildIDWriter final
+ : public MinidumpModuleCodeViewRecordWriter {
+ public:
+ MinidumpModuleCodeViewRecordBuildIDWriter();
+ ~MinidumpModuleCodeViewRecordBuildIDWriter() override;
+
+ //! \brief Sets the build ID used for symbol lookup.
+ void SetBuildID(const std::vector<uint8_t>& build_id);
+
+ private:
+ // MinidumpWritable:
+ size_t SizeOfObject() override;
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ std::vector<uint8_t> build_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordBuildIDWriter);
+};
+
//! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file.
//!
//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc
index 0fddfe8..71db3ac 100644
--- a/minidump/minidump_module_writer_test.cc
+++ b/minidump/minidump_module_writer_test.cc
@@ -20,6 +20,7 @@
#include <utility>
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
@@ -264,6 +265,61 @@
expected_debug_utf16));
}
+// ExpectModuleWithBuildIDCv() is like ExpectModule( but expects the module to
+// have a BuildID CodeView Record.
+void ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected,
+ const MINIDUMP_MODULE* observed,
+ const std::string& file_contents,
+ const std::string& expected_module_name,
+ const std::vector<uint8_t>& expected_build_id) {
+ EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);
+ EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);
+ EXPECT_EQ(observed->CheckSum, expected->CheckSum);
+ EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);
+ EXPECT_EQ(observed->VersionInfo.dwSignature,
+ implicit_cast<uint32_t>(VS_FFI_SIGNATURE));
+ EXPECT_EQ(observed->VersionInfo.dwStrucVersion,
+ implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));
+ EXPECT_EQ(observed->VersionInfo.dwFileVersionMS,
+ expected->VersionInfo.dwFileVersionMS);
+ EXPECT_EQ(observed->VersionInfo.dwFileVersionLS,
+ expected->VersionInfo.dwFileVersionLS);
+ EXPECT_EQ(observed->VersionInfo.dwProductVersionMS,
+ expected->VersionInfo.dwProductVersionMS);
+ EXPECT_EQ(observed->VersionInfo.dwProductVersionLS,
+ expected->VersionInfo.dwProductVersionLS);
+ EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask,
+ expected->VersionInfo.dwFileFlagsMask);
+ EXPECT_EQ(observed->VersionInfo.dwFileFlags,
+ expected->VersionInfo.dwFileFlags);
+ EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS);
+ EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType);
+ EXPECT_EQ(observed->VersionInfo.dwFileSubtype,
+ expected->VersionInfo.dwFileSubtype);
+ EXPECT_EQ(observed->VersionInfo.dwFileDateMS,
+ expected->VersionInfo.dwFileDateMS);
+ EXPECT_EQ(observed->VersionInfo.dwFileDateLS,
+ expected->VersionInfo.dwFileDateLS);
+ EXPECT_EQ(observed->Reserved0, 0u);
+ EXPECT_EQ(observed->Reserved1, 0u);
+
+ EXPECT_NE(observed->ModuleNameRva, 0u);
+ base::string16 observed_module_name_utf16 =
+ MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
+ base::string16 expected_module_name_utf16 =
+ base::UTF8ToUTF16(expected_module_name);
+ EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);
+
+ const CodeViewRecordBuildID* codeview_build_id_record =
+ MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+ file_contents, observed->CvRecord);
+ ASSERT_TRUE(codeview_build_id_record);
+ EXPECT_EQ(memcmp(expected_build_id.data(),
+ &codeview_build_id_record->build_id,
+ expected_build_id.size()),
+ 0);
+}
+
TEST(MinidumpModuleWriter, EmptyModule) {
MinidumpFileWriter minidump_file_writer;
auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
@@ -324,9 +380,22 @@
constexpr uint32_t kFileType = VFT_DRV;
constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;
static constexpr char kPDBName[] = "statical.pdb";
- static constexpr uint8_t kPDBUUIDBytes[16] =
- {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
- 0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f};
+ static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe,
+ 0xdc,
+ 0xba,
+ 0x98,
+ 0x76,
+ 0x54,
+ 0x32,
+ 0x10,
+ 0x08,
+ 0x19,
+ 0x2a,
+ 0x3b,
+ 0x4c,
+ 0x5d,
+ 0x6e,
+ 0x7f};
UUID pdb_uuid;
pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);
constexpr uint32_t kPDBAge = 1;
@@ -470,6 +539,50 @@
kDebugUTF16));
}
+TEST(MinidumpModuleWriter, OneModule_CodeViewBuildID) {
+ // MinidumpModuleWriter.OneModule tested with a BuildID CodeView
+ MinidumpFileWriter minidump_file_writer;
+ auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();
+
+ static constexpr char kModuleName[] = "dinosaur";
+ static constexpr char kBuildID[] =
+ "averylonghashcodeormaybeitsjustrandomnumbershardtosay";
+
+ std::vector<uint8_t> build_id_data(kBuildID, kBuildID + 53);
+
+ auto module_writer = std::make_unique<MinidumpModuleWriter>();
+ module_writer->SetName(kModuleName);
+
+ auto codeview_build_id_writer =
+ std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();
+ codeview_build_id_writer->SetBuildID(build_id_data);
+ module_writer->SetCodeViewRecord(std::move(codeview_build_id_writer));
+
+ module_list_writer->AddModule(std::move(module_writer));
+ ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ ASSERT_GT(string_file.string().size(),
+ sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
+ sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
+
+ const MINIDUMP_MODULE_LIST* module_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetModuleListStream(string_file.string(), &module_list));
+
+ EXPECT_EQ(module_list->NumberOfModules, 1u);
+
+ MINIDUMP_MODULE expected = {};
+
+ ASSERT_NO_FATAL_FAILURE(ExpectModuleWithBuildIDCv(&expected,
+ &module_list->Modules[0],
+ string_file.string(),
+ kModuleName,
+ build_id_data));
+}
+
TEST(MinidumpModuleWriter, ThreeModules) {
// As good exercise, this test uses three modules, one with a PDB 7.0 link as
// its CodeView record, one with no CodeView record, and one with a PDB 2.0
@@ -481,9 +594,22 @@
constexpr uint64_t kModuleBase0 = 0x100101000;
constexpr uint32_t kModuleSize0 = 0xf000;
static constexpr char kPDBName0[] = "main";
- static constexpr uint8_t kPDBUUIDBytes0[16] =
- {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
- 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
+ static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0xee,
+ 0xff,
+ 0x00,
+ 0x11,
+ 0x22,
+ 0x33,
+ 0x44,
+ 0x55,
+ 0x66,
+ 0x77,
+ 0x88,
+ 0x99};
UUID pdb_uuid_0;
pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0);
constexpr uint32_t kPDBAge0 = 0;
@@ -650,10 +776,10 @@
TEST(MinidumpModuleWriter, InitializeFromSnapshot) {
MINIDUMP_MODULE expect_modules[3] = {};
- const char* module_paths[arraysize(expect_modules)] = {};
- const char* module_pdbs[arraysize(expect_modules)] = {};
- UUID uuids[arraysize(expect_modules)] = {};
- uint32_t ages[arraysize(expect_modules)] = {};
+ const char* module_paths[base::size(expect_modules)] = {};
+ const char* module_pdbs[base::size(expect_modules)] = {};
+ UUID uuids[base::size(expect_modules)] = {};
+ uint32_t ages[base::size(expect_modules)] = {};
expect_modules[0].BaseOfImage = 0x100101000;
expect_modules[0].SizeOfImage = 0xf000;
@@ -665,9 +791,22 @@
expect_modules[0].VersionInfo.dwFileType = VFT_APP;
module_paths[0] = "/usr/bin/true";
module_pdbs[0] = "true";
- static constexpr uint8_t kUUIDBytes0[16] =
- {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
+ static constexpr uint8_t kUUIDBytes0[16] = {0x00,
+ 0x11,
+ 0x22,
+ 0x33,
+ 0x44,
+ 0x55,
+ 0x66,
+ 0x77,
+ 0x88,
+ 0x99,
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0xee,
+ 0xff};
uuids[0].InitializeFromBytes(kUUIDBytes0);
ages[0] = 10;
@@ -681,9 +820,22 @@
expect_modules[1].VersionInfo.dwFileType = VFT_DLL;
module_paths[1] = "/usr/lib/libSystem.B.dylib";
module_pdbs[1] = "libSystem.B.dylib.pdb";
- static constexpr uint8_t kUUIDBytes1[16] =
- {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
+ static constexpr uint8_t kUUIDBytes1[16] = {0x00,
+ 0x01,
+ 0x02,
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07,
+ 0x08,
+ 0x09,
+ 0x0a,
+ 0x0b,
+ 0x0c,
+ 0x0d,
+ 0x0e,
+ 0x0f};
uuids[1].InitializeFromBytes(kUUIDBytes1);
ages[1] = 20;
@@ -697,15 +849,28 @@
expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN;
module_paths[2] = "/usr/lib/dyld";
module_pdbs[2] = "/usr/lib/dyld.pdb";
- static constexpr uint8_t kUUIDBytes2[16] =
- {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
- 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0};
+ static constexpr uint8_t kUUIDBytes2[16] = {0xff,
+ 0xfe,
+ 0xfd,
+ 0xfc,
+ 0xfb,
+ 0xfa,
+ 0xf9,
+ 0xf8,
+ 0xf7,
+ 0xf6,
+ 0xf5,
+ 0xf4,
+ 0xf3,
+ 0xf2,
+ 0xf1,
+ 0xf0};
uuids[2].InitializeFromBytes(kUUIDBytes2);
ages[2] = 30;
std::vector<std::unique_ptr<TestModuleSnapshot>> module_snapshots_owner;
std::vector<const ModuleSnapshot*> module_snapshots;
- for (size_t index = 0; index < arraysize(expect_modules); ++index) {
+ for (size_t index = 0; index < base::size(expect_modules); ++index) {
module_snapshots_owner.push_back(std::make_unique<TestModuleSnapshot>());
TestModuleSnapshot* module_snapshot = module_snapshots_owner.back().get();
InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot,
diff --git a/minidump/minidump_rva_list_writer_test.cc b/minidump/minidump_rva_list_writer_test.cc
index 3043287..3807432 100644
--- a/minidump/minidump_rva_list_writer_test.cc
+++ b/minidump/minidump_rva_list_writer_test.cc
@@ -17,6 +17,7 @@
#include <utility>
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "minidump/test/minidump_rva_list_test_util.h"
@@ -86,10 +87,10 @@
ASSERT_TRUE(list_writer.WriteEverything(&string_file));
const MinidumpRVAList* list =
- MinidumpRVAListAtStart(string_file.string(), arraysize(kValues));
+ MinidumpRVAListAtStart(string_file.string(), base::size(kValues));
ASSERT_TRUE(list);
- for (size_t index = 0; index < arraysize(kValues); ++index) {
+ for (size_t index = 0; index < base::size(kValues); ++index) {
SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(
diff --git a/minidump/minidump_string_writer_test.cc b/minidump/minidump_string_writer_test.cc
index 382baaf..ddb8c48 100644
--- a/minidump/minidump_string_writer_test.cc
+++ b/minidump/minidump_string_writer_test.cc
@@ -18,6 +18,7 @@
#include "base/compiler_specific.h"
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
@@ -66,15 +67,16 @@
{4, "\360\220\204\202", 2, {0xd800, 0xdd02}}, // 𐄂 (non-BMP)
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", input %s", index, kTestData[index].input_string));
// Make sure that the expected output string with its NUL terminator fits in
// the space provided.
- ASSERT_EQ(kTestData[index]
- .output_string[arraysize(kTestData[index].output_string) - 1],
- 0);
+ ASSERT_EQ(
+ kTestData[index]
+ .output_string[base::size(kTestData[index].output_string) - 1],
+ 0);
string_file.Reset();
crashpad::internal::MinidumpUTF16StringWriter string_writer;
@@ -100,6 +102,12 @@
}
}
+// Related tracking issues:
+// https://fuchsia.atlassian.net/browse/DX-487
+// https://bugs.chromium.org/p/chromium/issues/detail?id=872892
+// https://bugs.chromium.org/p/chromium/issues/detail?id=889582
+// TODO: Re-enable test once LUCI supports invalid UTF8 characters in test logs.
+#if !defined(CRASHPAD_IS_IN_FUCHSIA)
TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) {
StringFile string_file;
@@ -112,7 +120,7 @@
"\303\0\251", // NUL in middle of valid sequence
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", input %s", index, kTestData[index]));
string_file.Reset();
@@ -139,6 +147,7 @@
EXPECT_NE(output_string.find(0xfffd), base::string16::npos);
}
}
+#endif // !defined(CRASHPAD_IS_IN_FUCHSIA)
TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) {
StringFile string_file;
@@ -174,7 +183,7 @@
{4, "\360\220\204\202"}, // 𐄂 (non-BMP)
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", input %s", index, kTestData[index].string));
diff --git a/minidump/minidump_system_info_writer.cc b/minidump/minidump_system_info_writer.cc
index cc87d24..7b4b49d 100644
--- a/minidump/minidump_system_info_writer.cc
+++ b/minidump/minidump_system_info_writer.cc
@@ -17,10 +17,11 @@
#include <string.h>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "minidump/minidump_string_writer.h"
#include "snapshot/system_snapshot.h"
#include "util/file/file_writer.h"
-#include "util/misc/arraysize_unsafe.h"
+#include "util/misc/arraysize.h"
#include "util/misc/implicit_cast.h"
namespace crashpad {
@@ -150,7 +151,7 @@
SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(),
system_snapshot->CPUX86Features() & 0xffffffff);
- if (cpu_vendor == "AuthenticAMD") {
+ if (cpu_vendor == "AuthenticAMD" || cpu_vendor == "HygonGenuine") {
SetCPUX86AMDExtendedFeatures(
system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff);
}
@@ -175,6 +176,9 @@
case SystemSnapshot::kOperatingSystemFuchsia:
operating_system = kMinidumpOSFuchsia;
break;
+ case SystemSnapshot::kOperatingSystemIOS:
+ operating_system = kMinidumpOSIOS;
+ break;
default:
NOTREACHED();
operating_system = kMinidumpOSUnknown;
@@ -212,7 +216,7 @@
system_info_.ProcessorArchitecture ==
kMinidumpCPUArchitectureX86Win64);
- static_assert(ARRAYSIZE_UNSAFE(system_info_.Cpu.X86CpuInfo.VendorId) == 3,
+ static_assert(ArraySize(system_info_.Cpu.X86CpuInfo.VendorId) == 3,
"VendorId must have 3 elements");
system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx;
@@ -230,7 +234,7 @@
sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId),
"VendorId sizes must be equal");
- for (size_t index = 0; index < arraysize(registers); ++index) {
+ for (size_t index = 0; index < base::size(registers); ++index) {
memcpy(®isters[index],
&vendor[index * sizeof(*registers)],
sizeof(*registers));
@@ -270,9 +274,8 @@
system_info_.ProcessorArchitecture !=
kMinidumpCPUArchitectureX86Win64);
- static_assert(
- ARRAYSIZE_UNSAFE(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2,
- "ProcessorFeatures must have 2 elements");
+ static_assert(ArraySize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2,
+ "ProcessorFeatures must have 2 elements");
system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0;
system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1;
diff --git a/minidump/minidump_thread_id_map_test.cc b/minidump/minidump_thread_id_map_test.cc
index 7c6795a..548729e 100644
--- a/minidump/minidump_thread_id_map_test.cc
+++ b/minidump/minidump_thread_id_map_test.cc
@@ -19,6 +19,7 @@
#include <vector>
#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "snapshot/test/test_thread_snapshot.h"
@@ -38,7 +39,8 @@
// testing::Test:
void SetUp() override {
- for (size_t index = 0; index < arraysize(test_thread_snapshots_); ++index) {
+ for (size_t index = 0; index < base::size(test_thread_snapshots_);
+ ++index) {
thread_snapshots_.push_back(&test_thread_snapshots_[index]);
}
}
@@ -59,7 +61,7 @@
}
void SetThreadID(size_t index, uint64_t thread_id) {
- ASSERT_LT(index, arraysize(test_thread_snapshots_));
+ ASSERT_LT(index, base::size(test_thread_snapshots_));
test_thread_snapshots_[index].SetThreadID(thread_id);
}
diff --git a/minidump/minidump_thread_writer_test.cc b/minidump/minidump_thread_writer_test.cc
index e95c904..956d100 100644
--- a/minidump/minidump_thread_writer_test.cc
+++ b/minidump/minidump_thread_writer_test.cc
@@ -19,6 +19,7 @@
#include "base/compiler_specific.h"
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "minidump/minidump_context_writer.h"
@@ -522,10 +523,10 @@
void RunInitializeFromSnapshotTest(bool thread_id_collision) {
using MinidumpContextType = typename Traits::MinidumpContextType;
MINIDUMP_THREAD expect_threads[3] = {};
- uint64_t thread_ids[arraysize(expect_threads)] = {};
- uint8_t memory_values[arraysize(expect_threads)] = {};
- uint32_t context_seeds[arraysize(expect_threads)] = {};
- MINIDUMP_MEMORY_DESCRIPTOR tebs[arraysize(expect_threads)] = {};
+ uint64_t thread_ids[base::size(expect_threads)] = {};
+ uint8_t memory_values[base::size(expect_threads)] = {};
+ uint32_t context_seeds[base::size(expect_threads)] = {};
+ MINIDUMP_MEMORY_DESCRIPTOR tebs[base::size(expect_threads)] = {};
constexpr size_t kTebSize = 1024;
@@ -581,7 +582,7 @@
std::vector<std::unique_ptr<TestThreadSnapshot>> thread_snapshots_owner;
std::vector<const ThreadSnapshot*> thread_snapshots;
- for (size_t index = 0; index < arraysize(expect_threads); ++index) {
+ for (size_t index = 0; index < base::size(expect_threads); ++index) {
thread_snapshots_owner.push_back(std::make_unique<TestThreadSnapshot>());
TestThreadSnapshot* thread_snapshot = thread_snapshots_owner.back().get();
diff --git a/minidump/minidump_unloaded_module_writer.cc b/minidump/minidump_unloaded_module_writer.cc
index 855e196..c8a5f0f 100644
--- a/minidump/minidump_unloaded_module_writer.cc
+++ b/minidump/minidump_unloaded_module_writer.cc
@@ -123,7 +123,8 @@
DCHECK_EQ(state(), kStateMutable);
DCHECK(unloaded_modules_.empty());
- for (auto unloaded_module_snapshot : unloaded_module_snapshots) {
+ for (const UnloadedModuleSnapshot& unloaded_module_snapshot :
+ unloaded_module_snapshots) {
auto unloaded_module = std::make_unique<MinidumpUnloadedModuleWriter>();
unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot);
AddUnloadedModule(std::move(unloaded_module));
diff --git a/minidump/minidump_user_stream_writer.cc b/minidump/minidump_user_stream_writer.cc
index 139e827..8401b28 100644
--- a/minidump/minidump_user_stream_writer.cc
+++ b/minidump/minidump_user_stream_writer.cc
@@ -42,7 +42,7 @@
return snapshot_->Read(this);
}
- size_t GetSize() const override { return snapshot_ ? snapshot_->Size() : 0; };
+ size_t GetSize() const override { return snapshot_ ? snapshot_->Size() : 0; }
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
return writer_->Write(data, size);
diff --git a/minidump/minidump_writable.cc b/minidump/minidump_writable.cc
index d2b58f5..fa3e2f1 100644
--- a/minidump/minidump_writable.cc
+++ b/minidump/minidump_writable.cc
@@ -17,6 +17,7 @@
#include <stdint.h>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h"
@@ -244,7 +245,7 @@
// The number of elements in kZeroes must be at least one less than the
// maximum Alignment() ever encountered.
static constexpr uint8_t kZeroes[kMaximumAlignment - 1] = {};
- DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes));
+ DCHECK_LE(leading_pad_bytes_, base::size(kZeroes));
if (leading_pad_bytes_) {
if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) {
diff --git a/minidump/test/minidump_context_test_util.cc b/minidump/test/minidump_context_test_util.cc
index 28f9410..c94fa20 100644
--- a/minidump/test/minidump_context_test_util.cc
+++ b/minidump/test/minidump_context_test_util.cc
@@ -18,7 +18,7 @@
#include <sys/types.h>
#include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "snapshot/cpu_context.h"
@@ -128,7 +128,8 @@
context->ds = static_cast<uint16_t>(value++);
context->es = static_cast<uint16_t>(value++);
context->ss = static_cast<uint16_t>(value++);
- for (size_t index = 0; index < arraysize(context->vector_register); ++index) {
+ for (size_t index = 0; index < base::size(context->vector_register);
+ ++index) {
context->vector_register[index].lo = value++;
context->vector_register[index].hi = value++;
}
@@ -151,7 +152,7 @@
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(context->regs); ++index) {
+ for (size_t index = 0; index < base::size(context->regs); ++index) {
context->regs[index] = value++;
}
context->fp = value++;
@@ -162,7 +163,7 @@
context->pc = value++;
context->cpsr = value++;
- for (size_t index = 0; index < arraysize(context->vfp); ++index) {
+ for (size_t index = 0; index < base::size(context->vfp); ++index) {
context->vfp[index] = value++;
}
context->fpscr = value++;
@@ -176,18 +177,20 @@
return;
}
- context->context_flags = kMinidumpContextARM64All;
+ context->context_flags = kMinidumpContextARM64Full;
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(context->regs); ++index) {
+ for (size_t index = 0; index < base::size(context->regs); ++index) {
context->regs[index] = value++;
}
+ context->fp = value++;
+ context->lr = value++;
context->sp = value++;
context->pc = value++;
context->cpsr = value++;
- for (size_t index = 0; index < arraysize(context->fpsimd); ++index) {
+ for (size_t index = 0; index < base::size(context->fpsimd); ++index) {
context->fpsimd[index].lo = value++;
context->fpsimd[index].hi = value++;
}
@@ -207,7 +210,7 @@
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(context->regs); ++index) {
+ for (size_t index = 0; index < base::size(context->regs); ++index) {
context->regs[index] = value++;
}
@@ -218,7 +221,7 @@
context->status = value++;
context->cause = value++;
- for (size_t index = 0; index < arraysize(context->fpregs.fregs); ++index) {
+ for (size_t index = 0; index < base::size(context->fpregs.fregs); ++index) {
context->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
}
@@ -245,7 +248,7 @@
uint64_t value = seed;
- for (size_t index = 0; index < arraysize(context->regs); ++index) {
+ for (size_t index = 0; index < base::size(context->regs); ++index) {
context->regs[index] = value++;
}
@@ -256,7 +259,7 @@
context->status = value++;
context->cause = value++;
- for (size_t index = 0; index < arraysize(context->fpregs.dregs); ++index) {
+ for (size_t index = 0; index < base::size(context->fpregs.dregs); ++index) {
context->fpregs.dregs[index] = static_cast<double>(value++);
}
context->fpcsr = value++;
@@ -291,35 +294,33 @@
EXPECT_EQ(observed->reserved_3, expected->reserved_3);
EXPECT_EQ(observed->mxcsr, expected->mxcsr);
EXPECT_EQ(observed->mxcsr_mask, expected->mxcsr_mask);
- for (size_t st_mm_index = 0;
- st_mm_index < arraysize(expected->st_mm);
+ for (size_t st_mm_index = 0; st_mm_index < base::size(expected->st_mm);
++st_mm_index) {
SCOPED_TRACE(base::StringPrintf("st_mm_index %" PRIuS, st_mm_index));
EXPECT_EQ(BytesToHexString(observed->st_mm[st_mm_index].st,
- arraysize(observed->st_mm[st_mm_index].st)),
+ base::size(observed->st_mm[st_mm_index].st)),
BytesToHexString(expected->st_mm[st_mm_index].st,
- arraysize(expected->st_mm[st_mm_index].st)));
+ base::size(expected->st_mm[st_mm_index].st)));
EXPECT_EQ(
BytesToHexString(observed->st_mm[st_mm_index].st_reserved,
- arraysize(observed->st_mm[st_mm_index].st_reserved)),
+ base::size(observed->st_mm[st_mm_index].st_reserved)),
BytesToHexString(expected->st_mm[st_mm_index].st_reserved,
- arraysize(expected->st_mm[st_mm_index].st_reserved)));
+ base::size(expected->st_mm[st_mm_index].st_reserved)));
}
- for (size_t xmm_index = 0;
- xmm_index < arraysize(expected->xmm);
+ for (size_t xmm_index = 0; xmm_index < base::size(expected->xmm);
++xmm_index) {
EXPECT_EQ(BytesToHexString(observed->xmm[xmm_index],
- arraysize(observed->xmm[xmm_index])),
+ base::size(observed->xmm[xmm_index])),
BytesToHexString(expected->xmm[xmm_index],
- arraysize(expected->xmm[xmm_index])))
+ base::size(expected->xmm[xmm_index])))
<< "xmm_index " << xmm_index;
}
EXPECT_EQ(
- BytesToHexString(observed->reserved_4, arraysize(observed->reserved_4)),
- BytesToHexString(expected->reserved_4, arraysize(expected->reserved_4)));
+ BytesToHexString(observed->reserved_4, base::size(observed->reserved_4)),
+ BytesToHexString(expected->reserved_4, base::size(expected->reserved_4)));
EXPECT_EQ(
- BytesToHexString(observed->available, arraysize(observed->available)),
- BytesToHexString(expected->available, arraysize(expected->available)));
+ BytesToHexString(observed->available, base::size(observed->available)),
+ BytesToHexString(expected->available, base::size(expected->available)));
}
} // namespace
@@ -344,11 +345,11 @@
EXPECT_EQ(observed->fsave.fpu_cs, expected.fsave.fpu_cs);
EXPECT_EQ(observed->fsave.fpu_dp, expected.fsave.fpu_dp);
EXPECT_EQ(observed->fsave.fpu_ds, expected.fsave.fpu_ds);
- for (size_t index = 0; index < arraysize(expected.fsave.st); ++index) {
+ for (size_t index = 0; index < base::size(expected.fsave.st); ++index) {
EXPECT_EQ(BytesToHexString(observed->fsave.st[index],
- arraysize(observed->fsave.st[index])),
+ base::size(observed->fsave.st[index])),
BytesToHexString(expected.fsave.st[index],
- arraysize(expected.fsave.st[index])))
+ base::size(expected.fsave.st[index])))
<< "index " << index;
}
if (snapshot) {
@@ -447,7 +448,8 @@
ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);
- for (size_t index = 0; index < arraysize(expected.vector_register); ++index) {
+ for (size_t index = 0; index < base::size(expected.vector_register);
+ ++index) {
if (snapshot) {
EXPECT_EQ(observed->vector_register[index].lo, 0u) << "index " << index;
EXPECT_EQ(observed->vector_register[index].hi, 0u) << "index " << index;
@@ -487,7 +489,7 @@
EXPECT_EQ(observed->context_flags, expected.context_flags);
- for (size_t index = 0; index < arraysize(expected.regs); ++index) {
+ for (size_t index = 0; index < base::size(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
EXPECT_EQ(observed->fp, expected.fp);
@@ -498,10 +500,10 @@
EXPECT_EQ(observed->cpsr, expected.cpsr);
EXPECT_EQ(observed->fpscr, expected.fpscr);
- for (size_t index = 0; index < arraysize(expected.vfp); ++index) {
+ for (size_t index = 0; index < base::size(expected.vfp); ++index) {
EXPECT_EQ(observed->vfp[index], expected.vfp[index]);
}
- for (size_t index = 0; index < arraysize(expected.extra); ++index) {
+ for (size_t index = 0; index < base::size(expected.extra); ++index) {
EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]);
}
}
@@ -514,14 +516,14 @@
EXPECT_EQ(observed->context_flags, expected.context_flags);
- for (size_t index = 0; index < arraysize(expected.regs); ++index) {
+ for (size_t index = 0; index < base::size(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
EXPECT_EQ(observed->cpsr, expected.cpsr);
EXPECT_EQ(observed->fpsr, expected.fpsr);
EXPECT_EQ(observed->fpcr, expected.fpcr);
- for (size_t index = 0; index < arraysize(expected.fpsimd); ++index) {
+ for (size_t index = 0; index < base::size(expected.fpsimd); ++index) {
EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo);
EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi);
}
@@ -535,7 +537,7 @@
EXPECT_EQ(observed->context_flags, expected.context_flags);
- for (size_t index = 0; index < arraysize(expected.regs); ++index) {
+ for (size_t index = 0; index < base::size(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
@@ -546,7 +548,7 @@
EXPECT_EQ(observed->status, expected.status);
EXPECT_EQ(observed->cause, expected.cause);
- for (size_t index = 0; index < arraysize(expected.fpregs.fregs); ++index) {
+ for (size_t index = 0; index < base::size(expected.fpregs.fregs); ++index) {
EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs,
expected.fpregs.fregs[index]._fp_fregs);
}
@@ -568,7 +570,7 @@
EXPECT_EQ(observed->context_flags, expected.context_flags);
- for (size_t index = 0; index < arraysize(expected.regs); ++index) {
+ for (size_t index = 0; index < base::size(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
@@ -579,7 +581,7 @@
EXPECT_EQ(observed->status, expected.status);
EXPECT_EQ(observed->cause, expected.cause);
- for (size_t index = 0; index < arraysize(expected.fpregs.dregs); ++index) {
+ for (size_t index = 0; index < base::size(expected.fpregs.dregs); ++index) {
EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]);
}
EXPECT_EQ(observed->fpcsr, expected.fpcsr);
diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc
index 1a832bf..db07b36 100644
--- a/minidump/test/minidump_writable_test_util.cc
+++ b/minidump/test/minidump_writable_test_util.cc
@@ -210,17 +210,13 @@
struct MinidumpModuleCrashpadInfoListTraits {
using ListType = MinidumpModuleCrashpadInfoList;
enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) };
- static size_t ElementCount(const ListType* list) {
- return list->count;
- }
+ static size_t ElementCount(const ListType* list) { return list->count; }
};
struct MinidumpSimpleStringDictionaryListTraits {
using ListType = MinidumpSimpleStringDictionary;
enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) };
- static size_t ElementCount(const ListType* list) {
- return list->count;
- }
+ static size_t ElementCount(const ListType* list) { return list->count; }
};
struct MinidumpAnnotationListObjectsTraits {
@@ -253,17 +249,19 @@
} // namespace
template <>
-const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MEMORY_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MEMORY_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpMemoryListTraits>(
file_contents, location);
}
template <>
-const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MODULE_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MODULE_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpModuleListTraits>(
file_contents, location);
}
@@ -278,25 +276,28 @@
}
template <>
-const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_THREAD_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_THREAD_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpThreadListTraits>(
file_contents, location);
}
template <>
-const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_HANDLE_DATA_STREAM*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(
file_contents, location);
}
template <>
-const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const MINIDUMP_MEMORY_INFO_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>(
file_contents, location);
}
@@ -357,28 +358,51 @@
} // namespace
template <>
-const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
- CodeViewRecordPDB20>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const CodeViewRecordPDB20*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,
location);
}
template <>
-const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
- CodeViewRecordPDB70>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+const CodeViewRecordPDB70*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,
location);
}
-TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)
- : MinidumpWritable(),
- value_(value) {
+template <>
+const CodeViewRecordBuildID*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location) {
+ const CodeViewRecordBuildID* cv =
+ reinterpret_cast<const CodeViewRecordBuildID*>(
+ MinidumpWritableAtLocationDescriptorInternal(
+ file_contents,
+ location,
+ offsetof(CodeViewRecordBuildID, build_id),
+ true));
+
+ if (!cv) {
+ return nullptr;
+ }
+
+ if (cv->signature != CodeViewRecordBuildID::kSignature) {
+ return nullptr;
+ }
+
+ return cv;
}
-TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {
-}
+TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)
+ : MinidumpWritable(), value_(value) {}
+
+TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {}
size_t TestUInt32MinidumpWritable::SizeOfObject() {
return sizeof(value_);
diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h
index 5b176d2..6c706fb 100644
--- a/minidump/test/minidump_writable_test_util.h
+++ b/minidump/test/minidump_writable_test_util.h
@@ -104,6 +104,7 @@
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING);
MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20);
MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70);
+MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordBuildID);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String);
// minidump_file_writer_test accesses its variable-sized test streams via a
@@ -179,14 +180,16 @@
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const MINIDUMP_MEMORY_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MEMORY_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MEMORY_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MODULE_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MODULE_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MINIDUMP_UNLOADED_MODULE_LIST*
@@ -195,29 +198,40 @@
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_THREAD_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_THREAD_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_HANDLE_DATA_STREAM*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
- MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const MINIDUMP_MEMORY_INFO_LIST*
+MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
- CodeViewRecordPDB20>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const CodeViewRecordPDB20*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
-const CodeViewRecordPDB70* MinidumpWritableAtLocationDescriptor<
- CodeViewRecordPDB70>(const std::string& file_contents,
- const MINIDUMP_LOCATION_DESCRIPTOR& location);
+const CodeViewRecordPDB70*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
+
+template <>
+const CodeViewRecordBuildID*
+MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(
+ const std::string& file_contents,
+ const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MinidumpModuleCrashpadInfoList*
diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn
index 134df8b..fea22c2 100644
--- a/snapshot/BUILD.gn
+++ b/snapshot/BUILD.gn
@@ -17,6 +17,9 @@
if (crashpad_is_in_chromium) {
import("//build/config/compiler/compiler.gni")
+
+ # Prevent Chromium source assignment filters from being inherited.
+ set_sources_assignment_filter([])
}
static_library("snapshot") {
@@ -25,9 +28,6 @@
"annotation_snapshot.h",
"capture_memory.cc",
"capture_memory.h",
- "cpu_architecture.h",
- "cpu_context.cc",
- "cpu_context.h",
"crashpad_info_client_options.cc",
"crashpad_info_client_options.h",
"exception_snapshot.h",
@@ -36,10 +36,17 @@
"memory_snapshot.cc",
"memory_snapshot.h",
"memory_snapshot_generic.h",
+ "minidump/exception_snapshot_minidump.cc",
+ "minidump/exception_snapshot_minidump.h",
+ "minidump/memory_snapshot_minidump.cc",
+ "minidump/memory_snapshot_minidump.h",
"minidump/minidump_annotation_reader.cc",
"minidump/minidump_annotation_reader.h",
+ "minidump/minidump_context_converter.cc",
+ "minidump/minidump_context_converter.h",
"minidump/minidump_simple_string_dictionary_reader.cc",
"minidump/minidump_simple_string_dictionary_reader.h",
+ "minidump/minidump_stream.h",
"minidump/minidump_string_list_reader.cc",
"minidump/minidump_string_list_reader.h",
"minidump/minidump_string_reader.cc",
@@ -48,6 +55,10 @@
"minidump/module_snapshot_minidump.h",
"minidump/process_snapshot_minidump.cc",
"minidump/process_snapshot_minidump.h",
+ "minidump/system_snapshot_minidump.cc",
+ "minidump/system_snapshot_minidump.h",
+ "minidump/thread_snapshot_minidump.cc",
+ "minidump/thread_snapshot_minidump.h",
"module_snapshot.h",
"process_snapshot.h",
"snapshot_constants.h",
@@ -86,16 +97,9 @@
"mac/process_snapshot_mac.h",
"mac/process_types.cc",
"mac/process_types.h",
- "mac/process_types/all.proctype",
- "mac/process_types/annotation.proctype",
- "mac/process_types/crashpad_info.proctype",
- "mac/process_types/crashreporterclient.proctype",
"mac/process_types/custom.cc",
- "mac/process_types/dyld_images.proctype",
"mac/process_types/flavors.h",
"mac/process_types/internal.h",
- "mac/process_types/loader.proctype",
- "mac/process_types/nlist.proctype",
"mac/process_types/traits.h",
"mac/system_snapshot_mac.cc",
"mac/system_snapshot_mac.h",
@@ -104,8 +108,26 @@
]
}
+ if (crashpad_is_ios) {
+ sources += [
+ "ios/exception_snapshot_ios.cc",
+ "ios/exception_snapshot_ios.h",
+ "ios/memory_snapshot_ios.cc",
+ "ios/memory_snapshot_ios.h",
+ "ios/module_snapshot_ios.cc",
+ "ios/module_snapshot_ios.h",
+ "ios/process_snapshot_ios.cc",
+ "ios/process_snapshot_ios.h",
+ "ios/system_snapshot_ios.cc",
+ "ios/system_snapshot_ios.h",
+ "ios/thread_snapshot_ios.cc",
+ "ios/thread_snapshot_ios.h",
+ "mac/cpu_context_mac.cc",
+ "mac/cpu_context_mac.h",
+ ]
+ }
+
if (crashpad_is_linux || crashpad_is_android) {
- set_sources_assignment_filter([])
sources += [
"linux/cpu_context_linux.cc",
"linux/cpu_context_linux.h",
@@ -135,10 +157,16 @@
]
}
- if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
+ crashpad_is_win) {
sources += [
"crashpad_types/crashpad_info_reader.cc",
"crashpad_types/crashpad_info_reader.h",
+ ]
+ }
+
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ sources += [
"crashpad_types/image_annotation_reader.cc",
"crashpad_types/image_annotation_reader.h",
"elf/elf_dynamic_array_reader.cc",
@@ -162,8 +190,6 @@
"win/exception_snapshot_win.h",
"win/memory_map_region_snapshot_win.cc",
"win/memory_map_region_snapshot_win.h",
- "win/memory_snapshot_win.cc",
- "win/memory_snapshot_win.h",
"win/module_snapshot_win.cc",
"win/module_snapshot_win.h",
"win/pe_image_annotations_reader.cc",
@@ -193,6 +219,8 @@
"fuchsia/exception_snapshot_fuchsia.h",
"fuchsia/memory_map_fuchsia.cc",
"fuchsia/memory_map_fuchsia.h",
+ "fuchsia/memory_map_region_snapshot_fuchsia.cc",
+ "fuchsia/memory_map_region_snapshot_fuchsia.h",
"fuchsia/process_reader_fuchsia.cc",
"fuchsia/process_reader_fuchsia.h",
"fuchsia/process_snapshot_fuchsia.cc",
@@ -213,51 +241,58 @@
public_configs = [ "..:crashpad_config" ]
+ public_deps = [ ":context" ]
+
deps = [
"../client",
"../compat",
+ "../minidump:format",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+
+ if (crashpad_is_ios) {
+ deps -= [ "../client" ]
+ }
+
+ if (crashpad_is_win) {
+ cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
+ libs = [ "powrprof.lib" ]
+ }
+
+ configs += [ "..:disable_ubsan" ]
+}
+
+# :context is the only part of snapshot that minidump may depend on.
+static_library("context") {
+ sources = [
+ "cpu_architecture.h",
+ "cpu_context.cc",
+ "cpu_context.h",
+ ]
+
+ public_configs = [ "..:crashpad_config" ]
+
+ deps = [
"../third_party/mini_chromium:base",
"../util",
]
if (crashpad_is_win) {
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
- libs = [ "powrprof.lib" ]
}
}
-if (crashpad_is_win) {
- static_library("snapshot_api") {
- sources = [
- "api/module_annotations_win.cc",
- "api/module_annotations_win.h",
- ]
-
- public_configs = [ "..:crashpad_config" ]
-
- cflags = [ "/wd4201" ]
+if (crashpad_is_linux) {
+ crashpad_fuzzer_test("elf_image_reader_fuzzer") {
+ sources = [ "elf/elf_image_reader_fuzzer.cc" ]
deps = [
":snapshot",
- "../compat",
"../third_party/mini_chromium:base",
- "../util",
]
+ seed_corpus = "elf/elf_image_reader_fuzzer_corpus"
}
-} else {
- group("snapshot_api") {
- }
-}
-
-fuzzer_test("elf_image_reader_fuzzer") {
- sources = [
- "elf/elf_image_reader_fuzzer.cc",
- ]
-
- deps = [
- ":snapshot",
- "../third_party/mini_chromium:base",
- ]
}
static_library("test_support") {
@@ -284,9 +319,7 @@
public_configs = [ "..:crashpad_config" ]
- public_deps = [
- ":snapshot",
- ]
+ public_deps = [ ":snapshot" ]
deps = [
"../compat",
@@ -305,9 +338,7 @@
# There’s no way to make the link depend on this file. “inputs” doesn’t have
# the intended effect in a config. https://crbug.com/781858,
# https://crbug.com/796187.
- inputs = [
- "elf/test_exported_symbols.sym",
- ]
+ inputs = [ "elf/test_exported_symbols.sym" ]
ldflags = [ "-Wl,--dynamic-list," + rebase_path(inputs[0], root_build_dir) ]
}
}
@@ -342,27 +373,29 @@
"sanitized/process_snapshot_sanitized_test.cc",
"sanitized/sanitization_information_test.cc",
]
- } else {
+ } else if (!crashpad_is_ios) {
sources += [ "crashpad_info_client_options_test.cc" ]
}
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||
+ crashpad_is_win) {
+ sources += [ "crashpad_types/crashpad_info_reader_test.cc" ]
+ }
+
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
sources += [
- "crashpad_types/crashpad_info_reader_test.cc",
"crashpad_types/image_annotation_reader_test.cc",
"elf/elf_image_reader_test.cc",
"elf/elf_image_reader_test_note.S",
- "elf/test_exported_symbols.sym",
]
}
if (crashpad_is_win) {
sources += [
- "api/module_annotations_win_test.cc",
"win/cpu_context_win_test.cc",
"win/exception_snapshot_win_test.cc",
"win/extra_memory_ranges_test.cc",
- "win/pe_image_annotations_reader_test.cc",
+ "win/module_snapshot_win_test.cc",
"win/pe_image_reader_test.cc",
"win/process_reader_win_test.cc",
"win/process_snapshot_win_test.cc",
@@ -377,7 +410,10 @@
}
if (crashpad_is_fuchsia) {
- sources += [ "fuchsia/process_reader_fuchsia_test.cc" ]
+ sources += [
+ "fuchsia/process_reader_fuchsia_test.cc",
+ "fuchsia/process_snapshot_fuchsia_test.cc",
+ ]
}
# public_configs isn’t quite right. snapshot_test_link sets ldflags, and
@@ -387,10 +423,10 @@
public_configs = [ ":snapshot_test_link" ]
deps = [
- ":snapshot_api",
":test_support",
"../client",
"../compat",
+ "../minidump:format",
"../test",
"../third_party/gtest:gtest",
"../third_party/mini_chromium:base",
@@ -437,9 +473,7 @@
crashpad_loadable_module("crashpad_snapshot_test_module") {
testonly = true
- sources = [
- "crashpad_info_client_options_test_module.cc",
- ]
+ sources = [ "crashpad_info_client_options_test_module.cc" ]
deps = [
"../client",
"../third_party/mini_chromium:base",
@@ -448,9 +482,7 @@
crashpad_loadable_module("crashpad_snapshot_test_module_large") {
testonly = true
- sources = [
- "crashpad_info_size_test_module.cc",
- ]
+ sources = [ "crashpad_info_size_test_module.cc" ]
deps = []
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
@@ -464,9 +496,7 @@
crashpad_loadable_module("crashpad_snapshot_test_module_small") {
testonly = true
- sources = [
- "crashpad_info_size_test_module.cc",
- ]
+ sources = [ "crashpad_info_size_test_module.cc" ]
deps = []
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
@@ -482,9 +512,7 @@
target_cpu != "mipsel" && target_cpu != "mips64el") {
crashpad_loadable_module("crashpad_snapshot_test_both_dt_hash_styles") {
testonly = true
- sources = [
- "hash_types_test.cc",
- ]
+ sources = [ "hash_types_test.cc" ]
# This makes `ld` emit both .hash and .gnu.hash sections.
ldflags = [ "-Wl,--hash-style=both" ]
@@ -501,18 +529,14 @@
crashpad_executable("crashpad_snapshot_test_no_op") {
testonly = true
- sources = [
- "mac/mach_o_image_annotations_reader_test_no_op.cc",
- ]
+ sources = [ "mac/mach_o_image_annotations_reader_test_no_op.cc" ]
}
}
if (crashpad_is_win) {
crashpad_executable("crashpad_snapshot_test_annotations") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_annotations.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_annotations.cc" ]
deps = [
"../client",
"../compat",
@@ -522,9 +546,7 @@
crashpad_executable("crashpad_snapshot_test_crashing_child") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_crashing_child.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_crashing_child.cc" ]
deps = [
"../client",
"../compat",
@@ -535,9 +557,7 @@
crashpad_executable("crashpad_snapshot_test_dump_without_crashing") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_dump_without_crashing.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_dump_without_crashing.cc" ]
deps = [
"../client",
"../compat",
@@ -548,9 +568,7 @@
crashpad_executable("crashpad_snapshot_test_extra_memory_ranges") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_extra_memory_ranges.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_extra_memory_ranges.cc" ]
deps = [
"../client",
"../compat",
@@ -560,9 +578,7 @@
crashpad_executable("crashpad_snapshot_test_image_reader") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_image_reader.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_image_reader.cc" ]
deps = [
"../client",
"../compat",
@@ -581,9 +597,7 @@
crashpad_loadable_module("crashpad_snapshot_test_image_reader_module") {
testonly = true
- sources = [
- "win/crashpad_snapshot_test_image_reader_module.cc",
- ]
+ sources = [ "win/crashpad_snapshot_test_image_reader_module.cc" ]
deps = [
"../client",
"../third_party/mini_chromium:base",
diff --git a/snapshot/api/module_annotations_win.cc b/snapshot/api/module_annotations_win.cc
deleted file mode 100644
index fd46870..0000000
--- a/snapshot/api/module_annotations_win.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2016 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "snapshot/api/module_annotations_win.h"
-
-#include "snapshot/win/pe_image_annotations_reader.h"
-#include "snapshot/win/pe_image_reader.h"
-#include "snapshot/win/process_reader_win.h"
-#include "util/misc/from_pointer_cast.h"
-#include "util/win/get_module_information.h"
-
-namespace crashpad {
-
-bool ReadModuleAnnotations(HANDLE process,
- HMODULE module,
- std::map<std::string, std::string>* annotations) {
- ProcessReaderWin process_reader;
- if (!process_reader.Initialize(process, ProcessSuspensionState::kRunning))
- return false;
-
- MODULEINFO module_info;
- if (!CrashpadGetModuleInformation(
- process, module, &module_info, sizeof(module_info))) {
- PLOG(ERROR) << "CrashpadGetModuleInformation";
- return false;
- }
-
- PEImageReader image_reader;
- if (!image_reader.Initialize(
- &process_reader,
- FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll),
- module_info.SizeOfImage,
- ""))
- return false;
-
- PEImageAnnotationsReader annotations_reader(
- &process_reader, &image_reader, L"");
-
- *annotations = annotations_reader.SimpleMap();
- return true;
-}
-
-} // namespace crashpad
diff --git a/snapshot/api/module_annotations_win.h b/snapshot/api/module_annotations_win.h
deleted file mode 100644
index e024031..0000000
--- a/snapshot/api/module_annotations_win.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2016 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
-#define CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
-
-#include <windows.h>
-
-#include <map>
-#include <string>
-
-namespace crashpad {
-
-//! \brief Reads the module annotations from another process.
-//!
-//! \param[in] process The handle to the process that hosts the \a module.
-//! Requires PROCESS_QUERY_INFORMATION and PROCESS_VM_READ accesses.
-//! \param[in] module The handle to the module from which the \a annotations
-//! will be read. This module should be loaded in the target process.
-//! \param[out] annotations The map that will be filled with the annotations.
-//! Remains unchanged if the function returns 'false'.
-//!
-//! \return `true` if the annotations could be read succesfully, even if the
-//! module doesn't contain any annotations.
-bool ReadModuleAnnotations(HANDLE process,
- HMODULE module,
- std::map<std::string, std::string>* annotations);
-
-} // namespace crashpad
-
-#endif // CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
diff --git a/snapshot/api/module_annotations_win_test.cc b/snapshot/api/module_annotations_win_test.cc
deleted file mode 100644
index ecfa465..0000000
--- a/snapshot/api/module_annotations_win_test.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2016 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "snapshot/api/module_annotations_win.h"
-
-#include "client/crashpad_info.h"
-#include "gtest/gtest.h"
-#include "test/win/win_multiprocess.h"
-#include "util/file/file_io.h"
-
-namespace crashpad {
-namespace test {
-namespace {
-
-class ModuleAnnotationsMultiprocessTest final : public WinMultiprocess {
- private:
- void WinMultiprocessParent() override {
- // Read the child executable module.
- HMODULE module = nullptr;
- CheckedReadFileExactly(ReadPipeHandle(), &module, sizeof(module));
-
- // Reopen the child process with necessary access.
- HANDLE process_handle =
- OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
- FALSE,
- GetProcessId(ChildProcess()));
- EXPECT_TRUE(process_handle);
-
- // Read the module annotations in the child process and verify them.
- std::map<std::string, std::string> annotations;
- ASSERT_TRUE(ReadModuleAnnotations(process_handle, module, &annotations));
-
- EXPECT_GE(annotations.size(), 3u);
- EXPECT_EQ(annotations["#APITEST# key"], "value");
- EXPECT_EQ(annotations["#APITEST# x"], "y");
- EXPECT_EQ(annotations["#APITEST# empty_value"], "");
-
- // Signal the child process to terminate.
- char c = ' ';
- CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
- }
-
- void WinMultiprocessChild() override {
- // Set some test annotations.
- crashpad::CrashpadInfo* crashpad_info =
- crashpad::CrashpadInfo::GetCrashpadInfo();
-
- crashpad::SimpleStringDictionary* simple_annotations =
- new crashpad::SimpleStringDictionary();
- simple_annotations->SetKeyValue("#APITEST# key", "value");
- simple_annotations->SetKeyValue("#APITEST# x", "y");
- simple_annotations->SetKeyValue("#APITEST# empty_value", "");
-
- crashpad_info->set_simple_annotations(simple_annotations);
-
- // Send the executable module.
- HMODULE module = GetModuleHandle(nullptr);
- CheckedWriteFile(WritePipeHandle(), &module, sizeof(module));
-
- // Wait until a signal from the parent process to terminate.
- char c;
- CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));
- }
-};
-
-TEST(ModuleAnnotationsWin, ReadAnnotations) {
- WinMultiprocess::Run<ModuleAnnotationsMultiprocessTest>();
-}
-
-} // namespace
-} // namespace test
-} // namespace crashpad
diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc
index 98f400e..a51626c 100644
--- a/snapshot/capture_memory.cc
+++ b/snapshot/capture_memory.cc
@@ -19,6 +19,7 @@
#include <limits>
#include <memory>
+#include "base/stl_util.h"
#include "snapshot/memory_snapshot.h"
namespace crashpad {
@@ -97,17 +98,17 @@
#elif defined(ARCH_CPU_ARM_FAMILY)
if (context.architecture == kCPUArchitectureARM64) {
MaybeCaptureMemoryAround(delegate, context.arm64->pc);
- for (size_t i = 0; i < arraysize(context.arm64->regs); ++i) {
+ for (size_t i = 0; i < base::size(context.arm64->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.arm64->regs[i]);
}
} else {
MaybeCaptureMemoryAround(delegate, context.arm->pc);
- for (size_t i = 0; i < arraysize(context.arm->regs); ++i) {
+ for (size_t i = 0; i < base::size(context.arm->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.arm->regs[i]);
}
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
- for (size_t i = 0; i < arraysize(context.mipsel->regs); ++i) {
+ for (size_t i = 0; i < base::size(context.mipsel->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]);
}
#else
diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc
index 4d7c1e5..6fb8d7e 100644
--- a/snapshot/cpu_context.cc
+++ b/snapshot/cpu_context.cc
@@ -18,7 +18,8 @@
#include <string.h>
#include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
+#include "util/misc/arraysize.h"
#include "util/misc/implicit_cast.h"
namespace crashpad {
@@ -55,9 +56,9 @@
fsave->fpu_dp = fxsave.fpu_dp;
fsave->fpu_ds = fxsave.fpu_ds;
fsave->reserved_4 = 0;
- static_assert(arraysize(fsave->st) == arraysize(fxsave.st_mm),
+ static_assert(ArraySize(fsave->st) == ArraySize(fxsave.st_mm),
"FPU stack registers must be equivalent");
- for (size_t index = 0; index < arraysize(fsave->st); ++index) {
+ for (size_t index = 0; index < base::size(fsave->st); ++index) {
memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index]));
}
}
@@ -77,9 +78,9 @@
fxsave->reserved_3 = 0;
fxsave->mxcsr = 0;
fxsave->mxcsr_mask = 0;
- static_assert(arraysize(fxsave->st_mm) == arraysize(fsave.st),
+ static_assert(ArraySize(fxsave->st_mm) == ArraySize(fsave.st),
"FPU stack registers must be equivalent");
- for (size_t index = 0; index < arraysize(fsave.st); ++index) {
+ for (size_t index = 0; index < base::size(fsave.st); ++index) {
memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index]));
memset(fxsave->st_mm[index].st_reserved,
0,
diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h
index 4dde943..fb23c46 100644
--- a/snapshot/cpu_context.h
+++ b/snapshot/cpu_context.h
@@ -299,7 +299,7 @@
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
- uint64_t pstate;
+ uint32_t spsr;
uint128_struct fpsimd[32];
uint32_t fpsr;
diff --git a/snapshot/cpu_context_test.cc b/snapshot/cpu_context_test.cc
index 706f2fe..109510d 100644
--- a/snapshot/cpu_context_test.cc
+++ b/snapshot/cpu_context_test.cc
@@ -18,7 +18,7 @@
#include <string.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "test/hex_string.h"
@@ -124,7 +124,7 @@
&fxsave.st_mm[6].st, kExponentAllZero, false, kFractionAllZero);
SetX87Register(
&fxsave.st_mm[7].st, kExponentNormal, true, kFractionNormal); // valid
- for (size_t index = 0; index < arraysize(fxsave.st_mm); ++index) {
+ for (size_t index = 0; index < base::size(fxsave.st_mm); ++index) {
memset(&fxsave.st_mm[index].st_reserved,
0x5a,
sizeof(fxsave.st_mm[index].st_reserved));
@@ -148,10 +148,10 @@
EXPECT_EQ(fsave.fpu_dp, fxsave.fpu_dp);
EXPECT_EQ(fsave.fpu_ds, fxsave.fpu_ds);
EXPECT_EQ(fsave.reserved_4, 0);
- for (size_t index = 0; index < arraysize(fsave.st); ++index) {
- EXPECT_EQ(BytesToHexString(fsave.st[index], arraysize(fsave.st[index])),
+ for (size_t index = 0; index < base::size(fsave.st); ++index) {
+ EXPECT_EQ(BytesToHexString(fsave.st[index], base::size(fsave.st[index])),
BytesToHexString(fxsave.st_mm[index].st,
- arraysize(fxsave.st_mm[index].st)))
+ base::size(fxsave.st_mm[index].st)))
<< "index " << index;
}
}
@@ -204,14 +204,14 @@
EXPECT_EQ(fxsave.reserved_3, 0);
EXPECT_EQ(fxsave.mxcsr, 0u);
EXPECT_EQ(fxsave.mxcsr_mask, 0u);
- for (size_t index = 0; index < arraysize(fxsave.st_mm); ++index) {
+ for (size_t index = 0; index < base::size(fxsave.st_mm); ++index) {
EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st,
- arraysize(fxsave.st_mm[index].st)),
- BytesToHexString(fsave.st[index], arraysize(fsave.st[index])))
+ base::size(fxsave.st_mm[index].st)),
+ BytesToHexString(fsave.st[index], base::size(fsave.st[index])))
<< "index " << index;
EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st_reserved,
- arraysize(fxsave.st_mm[index].st_reserved)),
- std::string(arraysize(fxsave.st_mm[index].st_reserved) * 2, '0'))
+ base::size(fxsave.st_mm[index].st_reserved)),
+ std::string(base::size(fxsave.st_mm[index].st_reserved) * 2, '0'))
<< "index " << index;
}
size_t unused_len = sizeof(fxsave) - offsetof(decltype(fxsave), xmm);
@@ -318,7 +318,7 @@
// In this set, everything is valid.
fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7
fxsave_tag = 0xff; // nothing empty
- for (size_t index = 0; index < arraysize(st_mm); ++index) {
+ for (size_t index = 0; index < base::size(st_mm); ++index) {
SetX87OrMMXRegister(&st_mm[index], kExponentNormal, true, kFractionAllZero);
}
EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), 0);
diff --git a/snapshot/crashpad_info_client_options.cc b/snapshot/crashpad_info_client_options.cc
index 4a89d84..3a88a4c 100644
--- a/snapshot/crashpad_info_client_options.cc
+++ b/snapshot/crashpad_info_client_options.cc
@@ -15,7 +15,6 @@
#include "snapshot/crashpad_info_client_options.h"
#include "base/logging.h"
-#include "client/crashpad_info.h"
namespace crashpad {
@@ -39,7 +38,8 @@
CrashpadInfoClientOptions::CrashpadInfoClientOptions()
: crashpad_handler_behavior(TriState::kUnset),
system_crash_reporter_forwarding(TriState::kUnset),
- gather_indirectly_referenced_memory(TriState::kUnset) {
+ gather_indirectly_referenced_memory(TriState::kUnset),
+ indirectly_referenced_memory_cap(0) {
}
} // namespace crashpad
diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc
index 1fe38ac..1c5759e 100644
--- a/snapshot/crashpad_info_client_options_test.cc
+++ b/snapshot/crashpad_info_client_options_test.cc
@@ -32,7 +32,7 @@
#include <windows.h>
#include "snapshot/win/process_snapshot_win.h"
#elif defined(OS_FUCHSIA)
-#include <zircon/process.h>
+#include <lib/zx/process.h>
#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
#endif
@@ -86,7 +86,7 @@
GetCurrentProcess(), ProcessSuspensionState::kRunning, 0, 0));
#elif defined(OS_FUCHSIA)
ProcessSnapshotFuchsia process_snapshot;
- EXPECT_TRUE(process_snapshot.Initialize(zx_process_self()));
+ EXPECT_TRUE(process_snapshot.Initialize(*zx::process::self()));
#else
#error Port.
#endif // OS_MACOSX
@@ -326,10 +326,10 @@
}
}
-INSTANTIATE_TEST_CASE_P(CrashpadInfoSizes_ClientOptions,
- CrashpadInfoSizes_ClientOptions,
- testing::Values(FILE_PATH_LITERAL("small"),
- FILE_PATH_LITERAL("large")));
+INSTANTIATE_TEST_SUITE_P(CrashpadInfoSizes_ClientOptions,
+ CrashpadInfoSizes_ClientOptions,
+ testing::Values(FILE_PATH_LITERAL("small"),
+ FILE_PATH_LITERAL("large")));
} // namespace
} // namespace test
diff --git a/snapshot/crashpad_info_size_test_note.S b/snapshot/crashpad_info_size_test_note.S
index 96b996d..16b5d49 100644
--- a/snapshot/crashpad_info_size_test_note.S
+++ b/snapshot/crashpad_info_size_test_note.S
@@ -26,9 +26,11 @@
#define NOTE_ALIGN 4
// This section must be "a"llocated so that it appears in the final binary at
- // runtime, and "w"ritable so that the relocation to TEST_CRASHPAD_INFO_SYMBOL
- // can be performed.
- .section .note.crashpad.info,"aw",%note
+ // runtime. The reference to TEST_CRASHPAD_INFO_SYMBOL uses an offset relative
+ // to this note to avoid making this note writable, which triggers a bug in
+ // GNU ld, or adding text relocations which require the target system to allow
+ // making text segments writable. https://crbug.com/crashpad/260.
+ .section .note.crashpad.info,"a",%note
.balign NOTE_ALIGN
.type info_size_test_note, %object
info_size_test_note:
@@ -41,15 +43,9 @@
.balign NOTE_ALIGN
desc:
#if defined(__LP64__)
- .quad TEST_CRASHPAD_INFO_SYMBOL
+ .quad TEST_CRASHPAD_INFO_SYMBOL - desc
#else
-#if defined(__LITTLE_ENDIAN__)
- .long TEST_CRASHPAD_INFO_SYMBOL
- .long 0
-#else
- .long 0
- .long TEST_CRASHPAD_INFO_SYMBOL
-#endif // __LITTLE_ENDIAN__
+ .long TEST_CRASHPAD_INFO_SYMBOL - desc
#endif // __LP64__
desc_end:
.size info_size_test_note, .-info_size_test_note
diff --git a/snapshot/crashpad_types/crashpad_info_reader.cc b/snapshot/crashpad_types/crashpad_info_reader.cc
index dfc438f..07a9b48 100644
--- a/snapshot/crashpad_types/crashpad_info_reader.cc
+++ b/snapshot/crashpad_types/crashpad_info_reader.cc
@@ -66,9 +66,8 @@
return false;
}
- if (!memory->Read(address,
- std::min(VMSize{info.size}, VMSize{sizeof(info)}),
- &info)) {
+ if (!memory->Read(
+ address, std::min<VMSize>(info.size, sizeof(info)), &info)) {
return false;
}
@@ -116,7 +115,7 @@
#define NATIVE_TRAITS Traits32
#endif
static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value ||
- sizeof(info) == sizeof(CrashpadInfo),
+ sizeof(decltype(info)) == sizeof(CrashpadInfo),
"CrashpadInfo size mismtach");
#undef NATIVE_TRAITS
};
@@ -161,29 +160,29 @@
return GET_MEMBER(member); \
}
-DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior);
+DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior)
DEFINE_GETTER(TriState,
SystemCrashReporterForwarding,
- system_crash_reporter_forwarding);
+ system_crash_reporter_forwarding)
DEFINE_GETTER(TriState,
GatherIndirectlyReferencedMemory,
- gather_indirectly_referenced_memory);
+ gather_indirectly_referenced_memory)
DEFINE_GETTER(uint32_t,
IndirectlyReferencedMemoryCap,
- indirectly_referenced_memory_cap);
+ indirectly_referenced_memory_cap)
-DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges);
+DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges)
-DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations);
+DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations)
-DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list);
+DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list)
DEFINE_GETTER(VMAddress,
UserDataMinidumpStreamHead,
- user_data_minidump_stream_head);
+ user_data_minidump_stream_head)
#undef DEFINE_GETTER
#undef GET_MEMBER
diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc
index 87bafc6..ebc6a4d 100644
--- a/snapshot/crashpad_types/crashpad_info_reader_test.cc
+++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc
@@ -15,7 +15,6 @@
#include "snapshot/crashpad_types/crashpad_info_reader.h"
#include <sys/types.h>
-#include <unistd.h>
#include <memory>
@@ -31,10 +30,6 @@
#include "util/misc/from_pointer_cast.h"
#include "util/process/process_memory_native.h"
-#if defined(OS_FUCHSIA)
-#include <zircon/process.h>
-#endif
-
namespace crashpad {
namespace test {
namespace {
diff --git a/snapshot/elf/elf_dynamic_array_reader.cc b/snapshot/elf/elf_dynamic_array_reader.cc
index 9a44e12..a39712a 100644
--- a/snapshot/elf/elf_dynamic_array_reader.cc
+++ b/snapshot/elf/elf_dynamic_array_reader.cc
@@ -42,9 +42,6 @@
switch (entry.d_tag) {
case DT_NULL:
values->swap(local_values);
- if (size != 0) {
- LOG(WARNING) << size << " trailing bytes not read";
- }
return true;
case DT_NEEDED:
// Skip these entries for now.
diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc
index bbf9b54..7f6d7c7 100644
--- a/snapshot/elf/elf_image_reader.cc
+++ b/snapshot/elf/elf_image_reader.cc
@@ -22,6 +22,7 @@
#include <vector>
#include "base/logging.h"
+#include "base/numerics/safe_math.h"
#include "build/build_config.h"
#include "util/numeric/checked_vm_address_range.h"
@@ -191,7 +192,8 @@
ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::NextNote(
std::string* name,
NoteType* type,
- std::string* desc) {
+ std::string* desc,
+ VMAddress* desc_address) {
if (!is_valid_) {
LOG(ERROR) << "invalid note reader";
return Result::kError;
@@ -215,8 +217,9 @@
}
retry_ = false;
- result = range_->Is64Bit() ? ReadNote<Elf64_Nhdr>(name, type, desc)
- : ReadNote<Elf32_Nhdr>(name, type, desc);
+ result = range_->Is64Bit()
+ ? ReadNote<Elf64_Nhdr>(name, type, desc, desc_address)
+ : ReadNote<Elf32_Nhdr>(name, type, desc, desc_address);
} while (retry_);
if (result == Result::kSuccess) {
@@ -226,10 +229,19 @@
return Result::kError;
}
+namespace {
+
+// The maximum size the user can specify for maximum note size. Clamping this
+// ensures that buffer allocations cannot be wildly large. It is not expected
+// that a note would be larger than ~1k in normal usage.
+constexpr size_t kMaxMaxNoteSize = 16384;
+
+} // namespace
+
ElfImageReader::NoteReader::NoteReader(const ElfImageReader* elf_reader,
const ProcessMemoryRange* range,
const ProgramHeaderTable* phdr_table,
- ssize_t max_note_size,
+ size_t max_note_size,
const std::string& name_filter,
NoteType type_filter,
bool use_filter)
@@ -240,18 +252,21 @@
phdr_table_(phdr_table),
segment_range_(),
phdr_index_(0),
- max_note_size_(max_note_size),
+ max_note_size_(std::min(kMaxMaxNoteSize, max_note_size)),
name_filter_(name_filter),
type_filter_(type_filter),
use_filter_(use_filter),
is_valid_(true),
- retry_(false) {}
+ retry_(false) {
+ DCHECK_LT(max_note_size, kMaxMaxNoteSize);
+}
template <typename NhdrType>
ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote(
std::string* name,
NoteType* type,
- std::string* desc) {
+ std::string* desc,
+ VMAddress* desc_address) {
static_assert(sizeof(*type) >= sizeof(NhdrType::n_namesz),
"Note field size mismatch");
DCHECK_LT(current_address_, segment_end_address_);
@@ -263,10 +278,24 @@
current_address_ += sizeof(note_info);
constexpr size_t align = sizeof(note_info.n_namesz);
-#define PAD(x) (((x) + align - 1) & ~(align - 1))
- size_t padded_namesz = PAD(note_info.n_namesz);
- size_t padded_descsz = PAD(note_info.n_descsz);
- size_t note_size = padded_namesz + padded_descsz;
+
+#define CHECKED_PAD(x, into) \
+ base::CheckAnd(base::CheckAdd(x, align - 1), ~(align - 1)) \
+ .AssignIfValid(&into)
+
+ size_t padded_namesz;
+ if (!CHECKED_PAD(note_info.n_namesz, padded_namesz)) {
+ return Result::kError;
+ }
+ size_t padded_descsz;
+ if (!CHECKED_PAD(note_info.n_descsz, padded_descsz)) {
+ return Result::kError;
+ }
+
+ size_t note_size;
+ if (!base::CheckAdd(padded_namesz, padded_descsz).AssignIfValid(¬e_size)) {
+ return Result::kError;
+ }
// Notes typically have 4-byte alignment. However, .note.android.ident may
// inadvertently use 2-byte alignment.
@@ -275,11 +304,21 @@
// but there may be 4-byte aligned notes following it. If this note was
// aligned at less than 4-bytes, expect that the next note will be aligned at
// 4-bytes and add extra padding, if necessary.
- VMAddress end_of_note =
- std::min(PAD(current_address_ + note_size), segment_end_address_);
-#undef PAD
- if (max_note_size_ >= 0 && note_size > static_cast<size_t>(max_note_size_)) {
+ VMAddress end_of_note_candidate;
+ if (!base::CheckAdd(current_address_, note_size)
+ .AssignIfValid(&end_of_note_candidate)) {
+ return Result::kError;
+ }
+ VMAddress end_of_note;
+ if (!CHECKED_PAD(end_of_note_candidate, end_of_note)) {
+ return Result::kError;
+ }
+ end_of_note = std::min(end_of_note, segment_end_address_);
+
+#undef CHECKED_PAD
+
+ if (note_size > max_note_size_) {
current_address_ = end_of_note;
retry_ = true;
return Result::kError;
@@ -317,6 +356,7 @@
current_address_, note_info.n_descsz, &local_desc[0])) {
return Result::kError;
}
+ *desc_address = current_address_;
current_address_ = end_of_note;
@@ -467,6 +507,20 @@
return memory_.Is64Bit() ? header_64_.e_type : header_32_.e_type;
}
+bool ElfImageReader::SoName(std::string* name) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (!InitializeDynamicArray()) {
+ return false;
+ }
+
+ VMSize offset;
+ if (!dynamic_array_->GetValue(DT_SONAME, true, &offset)) {
+ return false;
+ }
+
+ return ReadDynamicStringTableAtOffset(offset, name);
+}
+
bool ElfImageReader::GetDynamicSymbol(const std::string& name,
VMAddress* address,
VMSize* size) {
@@ -772,7 +826,7 @@
}
std::unique_ptr<ElfImageReader::NoteReader> ElfImageReader::Notes(
- ssize_t max_note_size) {
+ size_t max_note_size) {
return std::make_unique<NoteReader>(
this, &memory_, program_headers_.get(), max_note_size);
}
@@ -780,7 +834,7 @@
std::unique_ptr<ElfImageReader::NoteReader>
ElfImageReader::NotesWithNameAndType(const std::string& name,
NoteReader::NoteType type,
- ssize_t max_note_size) {
+ size_t max_note_size) {
return std::make_unique<NoteReader>(
this, &memory_, program_headers_.get(), max_note_size, name, type, true);
}
diff --git a/snapshot/elf/elf_image_reader.h b/snapshot/elf/elf_image_reader.h
index 29f8b1e..ab1799f 100644
--- a/snapshot/elf/elf_image_reader.h
+++ b/snapshot/elf/elf_image_reader.h
@@ -70,15 +70,20 @@
//! \param[out] name The name of the note owner, if not `nullptr`.
//! \param[out] type A type for the note, if not `nullptr`.
//! \param[out] desc The note descriptor.
- //! \return a #Result value. \a name, \a type, and \a desc are only valid if
- //! this method returns Result::kSuccess.
- Result NextNote(std::string* name, NoteType* type, std::string* desc);
+ //! \param[out] desc_addr The address in the remote process' address space
+ //! \a desc was read from.
+ //! \return a #Result value. \a name, \a type, \a desc, and \a desc_addr are
+ //! only valid if this method returns Result::kSuccess.
+ Result NextNote(std::string* name,
+ NoteType* type,
+ std::string* desc,
+ VMAddress* desc_addr);
// private
NoteReader(const ElfImageReader* elf_reader_,
const ProcessMemoryRange* range,
const ProgramHeaderTable* phdr_table,
- ssize_t max_note_size,
+ size_t max_note_size,
const std::string& name_filter = std::string(),
NoteType type_filter = 0,
bool use_filter = false);
@@ -88,7 +93,10 @@
// and returns kError if use_filter_ is true and the note's name and type do
// not match name_filter_ and type_filter_.
template <typename T>
- Result ReadNote(std::string* name, NoteType* type, std::string* desc);
+ Result ReadNote(std::string* name,
+ NoteType* type,
+ std::string* desc,
+ VMAddress* desc_addr);
VMAddress current_address_;
VMAddress segment_end_address_;
@@ -97,7 +105,7 @@
const ProgramHeaderTable* phdr_table_; // weak
std::unique_ptr<ProcessMemoryRange> segment_range_;
size_t phdr_index_;
- ssize_t max_note_size_;
+ size_t max_note_size_;
std::string name_filter_;
NoteType type_filter_;
bool use_filter_;
@@ -149,6 +157,13 @@
//! The load bias is the actual load address minus the preferred load address.
VMOffset GetLoadBias() const { return load_bias_; }
+ //! \brief Determines the name of this object using `DT_SONAME`, if present.
+ //!
+ //! \param[out] name The name of this object, only valid if this method
+ //! returns `true`.
+ //! \return `true` if a name was found for this object.
+ bool SoName(std::string* name);
+
//! \brief Reads information from the dynamic symbol table about the symbol
//! identified by \a name.
//!
@@ -196,10 +211,9 @@
//!
//! \param[in] max_note_size The maximum note size to read. Notes whose
//! combined name, descriptor, and padding size are greater than
- //! \a max_note_size will be silently skipped. A \a max_note_size of -1
- //! indicates infinite maximum note size.
+ //! \a max_note_size will be silently skipped.
//! \return A NoteReader object capable of reading notes in this image.
- std::unique_ptr<NoteReader> Notes(ssize_t max_note_size);
+ std::unique_ptr<NoteReader> Notes(size_t max_note_size);
//! \brief Return a NoteReader for this image, which scans all PT_NOTE
//! segments in the image, filtering by name and type.
@@ -211,12 +225,11 @@
//! \param[in] type The note type to match.
//! \param[in] max_note_size The maximum note size to read. Notes whose
//! combined name, descriptor, and padding size are greater than
- //! \a max_note_size will be silently skipped. A \a max_note_size of -1
- //! indicates infinite maximum note size.
+ //! \a max_note_size will be silently skipped.
//! \return A NoteReader object capable of reading notes in this image.
std::unique_ptr<NoteReader> NotesWithNameAndType(const std::string& name,
NoteReader::NoteType type,
- ssize_t max_note_size);
+ size_t max_note_size);
//! \brief Return a ProcessMemoryRange restricted to the range of this image.
//!
diff --git a/snapshot/elf/elf_image_reader_fuzzer.cc b/snapshot/elf/elf_image_reader_fuzzer.cc
index 1686650..b780af4 100644
--- a/snapshot/elf/elf_image_reader_fuzzer.cc
+++ b/snapshot/elf/elf_image_reader_fuzzer.cc
@@ -31,7 +31,8 @@
VMAddress offset_in_data = address - fake_base_;
if (offset_in_data > size_)
return -1;
- ssize_t read_size = std::min(size_ - offset_in_data, size);
+ size_t read_size =
+ std::min(static_cast<size_t>(size_ - offset_in_data), size);
memcpy(buffer, &data_[offset_in_data], read_size);
return read_size;
}
@@ -65,8 +66,10 @@
std::string note_name;
std::string note_desc;
ElfImageReader::NoteReader::NoteType note_type;
- auto notes = reader.Notes(-1);
- while ((result = notes->NextNote(¬e_name, ¬e_type, ¬e_desc)) ==
+ VMAddress desc_addr;
+ auto notes = reader.Notes(9999);
+ while ((result = notes->NextNote(
+ ¬e_name, ¬e_type, ¬e_desc, &desc_addr)) ==
ElfImageReader::NoteReader::Result::kSuccess) {
LOG(ERROR) << note_name << note_type << note_desc;
}
diff --git a/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes b/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes
new file mode 100644
index 0000000..69463d1
--- /dev/null
+++ b/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes
@@ -0,0 +1,17 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ELF executables normally don’t have any extension, so there’s no pattern to
+# match in the root .gitattributes file.
+/ret42 binary
diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc
index 11fb1fe..cf8f33b 100644
--- a/snapshot/elf/elf_image_reader_test.cc
+++ b/snapshot/elf/elf_image_reader_test.cc
@@ -32,8 +32,7 @@
#include "util/process/process_memory_native.h"
#if defined(OS_FUCHSIA)
-
-#include <zircon/syscalls.h>
+#include <lib/zx/process.h>
#include "base/fuchsia/fuchsia_logging.h"
@@ -50,8 +49,8 @@
#endif // OS_FUCHSIA
extern "C" {
-__attribute__((visibility("default"))) void
-ElfImageReaderTestExportedSymbol(){};
+__attribute__((visibility("default"))) void ElfImageReaderTestExportedSymbol() {
+}
} // extern "C"
namespace crashpad {
@@ -61,14 +60,12 @@
#if defined(OS_FUCHSIA)
-void LocateExecutable(ProcessType process,
+void LocateExecutable(const ProcessType& process,
ProcessMemory* memory,
VMAddress* elf_address) {
uintptr_t debug_address;
- zx_status_t status = zx_object_get_property(process,
- ZX_PROP_PROCESS_DEBUG_ADDR,
- &debug_address,
- sizeof(debug_address));
+ zx_status_t status = process->get_property(
+ ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address));
ASSERT_EQ(status, ZX_OK)
<< "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR";
// Can be 0 if requested before the loader has loaded anything.
@@ -103,10 +100,9 @@
ASSERT_TRUE(memory_map.Initialize(connection));
const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping);
- std::vector<const MemoryMap::Mapping*> possible_mappings =
- memory_map.FindFilePossibleMmapStarts(*phdr_mapping);
- ASSERT_EQ(possible_mappings.size(), 1u);
- *elf_address = possible_mappings[0]->range.Base();
+ auto possible_mappings = memory_map.FindFilePossibleMmapStarts(*phdr_mapping);
+ ASSERT_EQ(possible_mappings->Count(), 1u);
+ *elf_address = possible_mappings->Next()->range.Base();
}
#endif // OS_FUCHSIA
@@ -157,22 +153,24 @@
std::string note_name;
std::string note_desc;
ElfImageReader::NoteReader::NoteType note_type;
+ VMAddress desc_addr;
- std::unique_ptr<ElfImageReader::NoteReader> notes = reader.Notes(-1);
- while ((result = notes->NextNote(¬e_name, ¬e_type, ¬e_desc)) ==
+ std::unique_ptr<ElfImageReader::NoteReader> notes = reader.Notes(10000);
+ while ((result = notes->NextNote(
+ ¬e_name, ¬e_type, ¬e_desc, &desc_addr)) ==
ElfImageReader::NoteReader::Result::kSuccess) {
}
EXPECT_EQ(result, ElfImageReader::NoteReader::Result::kNoMoreNotes);
notes = reader.Notes(0);
- EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc),
+ EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr),
ElfImageReader::NoteReader::Result::kNoMoreNotes);
// Find the note defined in elf_image_reader_test_note.S.
constexpr uint32_t kCrashpadNoteDesc = 42;
notes = reader.NotesWithNameAndType(
- CRASHPAD_ELF_NOTE_NAME, CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST, -1);
- ASSERT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc),
+ CRASHPAD_ELF_NOTE_NAME, CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST, 10000);
+ ASSERT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr),
ElfImageReader::NoteReader::Result::kSuccess);
EXPECT_EQ(note_name, CRASHPAD_ELF_NOTE_NAME);
EXPECT_EQ(note_type,
@@ -181,7 +179,7 @@
EXPECT_EQ(*reinterpret_cast<decltype(kCrashpadNoteDesc)*>(¬e_desc[0]),
kCrashpadNoteDesc);
- EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc),
+ EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr),
ElfImageReader::NoteReader::Result::kNoMoreNotes);
}
diff --git a/snapshot/elf/module_snapshot_elf.cc b/snapshot/elf/module_snapshot_elf.cc
index 62a961d..037e7cd 100644
--- a/snapshot/elf/module_snapshot_elf.cc
+++ b/snapshot/elf/module_snapshot_elf.cc
@@ -20,6 +20,7 @@
#include "base/files/file_path.h"
#include "snapshot/crashpad_types/image_annotation_reader.h"
+#include "snapshot/memory_snapshot_generic.h"
#include "util/misc/elf_note_types.h"
namespace crashpad {
@@ -28,14 +29,17 @@
ModuleSnapshotElf::ModuleSnapshotElf(const std::string& name,
ElfImageReader* elf_reader,
ModuleSnapshot::ModuleType type,
- ProcessMemoryRange* process_memory_range)
+ ProcessMemoryRange* process_memory_range,
+ const ProcessMemory* process_memory)
: ModuleSnapshot(),
name_(name),
elf_reader_(elf_reader),
process_memory_range_(process_memory_range),
+ process_memory_(process_memory),
crashpad_info_(),
type_(type),
- initialized_() {}
+ initialized_(),
+ streams_() {}
ModuleSnapshotElf::~ModuleSnapshotElf() = default;
@@ -56,9 +60,17 @@
kMaxNoteSize);
std::string desc;
VMAddress info_address;
- if (notes->NextNote(nullptr, nullptr, &desc) ==
+ VMAddress desc_address;
+ if (notes->NextNote(nullptr, nullptr, &desc, &desc_address) ==
ElfImageReader::NoteReader::Result::kSuccess) {
- info_address = *reinterpret_cast<VMAddress*>(&desc[0]);
+ VMOffset offset;
+ if (elf_reader_->Memory()->Is64Bit()) {
+ offset = *reinterpret_cast<VMOffset*>(&desc[0]);
+ } else {
+ int32_t offset32 = *reinterpret_cast<int32_t*>(&desc[0]);
+ offset = offset32;
+ }
+ info_address = desc_address + offset;
ProcessMemoryRange range;
if (range.Initialize(*elf_reader_->Memory())) {
@@ -142,12 +154,10 @@
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*age = 0;
- std::unique_ptr<ElfImageReader::NoteReader> notes =
- elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);
- std::string desc;
- notes->NextNote(nullptr, nullptr, &desc);
- desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0');
- uuid->InitializeFromBytes(reinterpret_cast<const uint8_t*>(&desc[0]));
+ auto build_id = BuildID();
+ build_id.insert(
+ build_id.end(), 16 - std::min(build_id.size(), size_t{16}), '\0');
+ uuid->InitializeFromBytes(build_id.data());
// TODO(scottmg): https://crashpad.chromium.org/bug/229. These are
// endian-swapped to match FileID::ConvertIdentifierToUUIDString() in
@@ -162,6 +172,21 @@
return base::FilePath(Name()).BaseName().value();
}
+std::vector<uint8_t> ModuleSnapshotElf::BuildID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ std::unique_ptr<ElfImageReader::NoteReader> notes =
+ elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);
+ std::string desc;
+ VMAddress desc_addr;
+ notes->NextNote(nullptr, nullptr, &desc, &desc_addr);
+
+ std::vector<uint8_t> build_id;
+ build_id.reserve(desc.size());
+ std::copy(desc.begin(), desc.end(), std::back_inserter(build_id));
+ return build_id;
+}
+
std::vector<std::string> ModuleSnapshotElf::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<std::string>();
@@ -195,7 +220,33 @@
std::vector<const UserMinidumpStream*>
ModuleSnapshotElf::CustomMinidumpStreams() const {
- return std::vector<const UserMinidumpStream*>();
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ streams_.clear();
+
+ std::vector<const UserMinidumpStream*> result;
+ if (!crashpad_info_)
+ return result;
+
+ for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) {
+ internal::UserDataMinidumpStreamListEntry list_entry;
+ if (!process_memory_->Read(cur, sizeof(list_entry), &list_entry)) {
+ LOG(WARNING) << "could not read user data stream entry from " << name_;
+ return result;
+ }
+
+ if (list_entry.size != 0) {
+ auto memory = std::make_unique<internal::MemorySnapshotGeneric>();
+ memory->Initialize(
+ process_memory_, list_entry.base_address, list_entry.size);
+ streams_.push_back(std::make_unique<UserMinidumpStream>(
+ list_entry.stream_type, memory.release()));
+ result.push_back(streams_.back().get());
+ }
+
+ cur = list_entry.next;
+ }
+
+ return result;
}
} // namespace internal
diff --git a/snapshot/elf/module_snapshot_elf.h b/snapshot/elf/module_snapshot_elf.h
index 15c3f3c..67ecdf7 100644
--- a/snapshot/elf/module_snapshot_elf.h
+++ b/snapshot/elf/module_snapshot_elf.h
@@ -41,11 +41,15 @@
//! \param[in] name The pathname used to load the module from disk.
//! \param[in] elf_reader An image reader for the module.
//! \param[in] type The module's type.
- //! \param[in] process_memory_range A memory reader for the target process.
+ //! \param[in] process_memory_range A memory reader giving protected access
+ //! to the target process.
+ //! \param[in] process_memory A memory reader for the target process which can
+ //! be used to initialize a MemorySnapshot.
ModuleSnapshotElf(const std::string& name,
ElfImageReader* elf_reader,
ModuleSnapshot::ModuleType type,
- ProcessMemoryRange* process_memory_range);
+ ProcessMemoryRange* process_memory_range,
+ const ProcessMemory* process_memory);
~ModuleSnapshotElf() override;
//! \brief Initializes the object.
@@ -77,6 +81,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -87,9 +92,12 @@
std::string name_;
ElfImageReader* elf_reader_;
ProcessMemoryRange* process_memory_range_;
+ const ProcessMemory* process_memory_;
std::unique_ptr<CrashpadInfoReader> crashpad_info_;
ModuleType type_;
InitializationStateDcheck initialized_;
+ // Too const-y: https://crashpad.chromium.org/bug/9.
+ mutable std::vector<std::unique_ptr<const UserMinidumpStream>> streams_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotElf);
};
diff --git a/snapshot/fuchsia/cpu_context_fuchsia.cc b/snapshot/fuchsia/cpu_context_fuchsia.cc
index 5a02f62..226bfc6 100644
--- a/snapshot/fuchsia/cpu_context_fuchsia.cc
+++ b/snapshot/fuchsia/cpu_context_fuchsia.cc
@@ -21,7 +21,7 @@
#if defined(ARCH_CPU_X86_64)
-void InitializeCPUContextX86_64(
+void InitializeCPUContextX86_64_NoFloatingPoint(
const zx_thread_state_general_regs_t& thread_context,
CPUContextX86_64* context) {
memset(context, 0, sizeof(*context));
@@ -45,6 +45,50 @@
context->rflags = thread_context.rflags;
}
+#elif defined(ARCH_CPU_ARM64)
+
+void InitializeCPUContextARM64(
+ const zx_thread_state_general_regs_t& thread_context,
+ const zx_thread_state_vector_regs_t& vector_context,
+ CPUContextARM64* context) {
+ memset(context, 0, sizeof(*context));
+
+ // Fuchsia stores the link register (x30) on its own while Crashpad stores it
+ // with the other general purpose x0-x28 and x29 frame pointer registers. So
+ // we expect the size and number of elements to be off by one unit.
+ static_assert(sizeof(context->regs) - sizeof(context->regs[30]) ==
+ sizeof(thread_context.r),
+ "registers size mismatch");
+ static_assert((sizeof(context->regs) - sizeof(context->regs[30])) /
+ sizeof(context->regs[0]) ==
+ sizeof(thread_context.r) / sizeof(thread_context.r[0]),
+ "registers number of elements mismatch");
+ memcpy(&context->regs, &thread_context.r, sizeof(thread_context.r));
+ context->regs[30] = thread_context.lr;
+ context->sp = thread_context.sp;
+ context->pc = thread_context.pc;
+
+ // Only the NZCV flags (bits 31 to 28 respectively) of the cpsr register are
+ // readable and writable by userland on ARM64.
+ constexpr uint32_t kNZCV = 0xf0000000;
+ // Fuchsia uses the old "cspr" terminology from armv7 while Crashpad uses the
+ // new "spsr" terminology for armv8.
+ context->spsr = thread_context.cpsr & kNZCV;
+ if (thread_context.cpsr >
+ std::numeric_limits<decltype(context->spsr)>::max()) {
+ LOG(WARNING) << "cpsr truncation: we only expect the first 32 bits to be "
+ "set in the cpsr";
+ }
+ context->spsr =
+ static_cast<decltype(context->spsr)>(thread_context.cpsr) & kNZCV;
+
+ context->fpcr = vector_context.fpcr;
+ context->fpsr = vector_context.fpsr;
+ static_assert(sizeof(context->fpsimd) == sizeof(vector_context.v),
+ "registers size mismatch");
+ memcpy(&context->fpsimd, &vector_context.v, sizeof(vector_context.v));
+}
+
#endif // ARCH_CPU_X86_64
} // namespace internal
diff --git a/snapshot/fuchsia/cpu_context_fuchsia.h b/snapshot/fuchsia/cpu_context_fuchsia.h
index f5336fd..9227bfc 100644
--- a/snapshot/fuchsia/cpu_context_fuchsia.h
+++ b/snapshot/fuchsia/cpu_context_fuchsia.h
@@ -34,12 +34,28 @@
//!
//! \param[in] thread_context The native thread context.
//! \param[out] context The CPUContextX86_64 structure to initialize.
-void InitializeCPUContextX86_64(
+void InitializeCPUContextX86_64_NoFloatingPoint(
const zx_thread_state_general_regs_t& thread_context,
CPUContextX86_64* context);
#endif // ARCH_CPU_X86_64 || DOXYGEN
+#if defined(ARCH_CPU_ARM64) || DOXYGEN
+
+//! \brief Initializes a CPUContextARM64 structure from native context
+//! structures on Fuchsia.
+//!
+//! \param[in] thread_context The native thread context.
+//! \param[in] vector_context The native vector context that also contains the
+//! floating point registers.
+//! \param[out] context The CPUContextARM64 structure to initialize.
+void InitializeCPUContextARM64(
+ const zx_thread_state_general_regs_t& thread_context,
+ const zx_thread_state_vector_regs_t& vector_context,
+ CPUContextARM64* context);
+
+#endif // ARCH_CPU_ARM64 || DOXYGEN
+
} // namespace internal
} // namespace crashpad
diff --git a/snapshot/fuchsia/exception_snapshot_fuchsia.cc b/snapshot/fuchsia/exception_snapshot_fuchsia.cc
index 44b4e5c..f70b966 100644
--- a/snapshot/fuchsia/exception_snapshot_fuchsia.cc
+++ b/snapshot/fuchsia/exception_snapshot_fuchsia.cc
@@ -59,13 +59,14 @@
#if defined(ARCH_CPU_X86_64)
context_.architecture = kCPUArchitectureX86_64;
context_.x86_64 = &context_arch_;
- // TODO(scottmg): Float context, once Fuchsia has a debug API to capture
- // floating point registers. ZX-1750 upstream.
- InitializeCPUContextX86_64(t.general_registers, context_.x86_64);
+ // TODO(fuchsia/DX-642): Add float context once saved in |t|.
+ InitializeCPUContextX86_64_NoFloatingPoint(t.general_registers,
+ context_.x86_64);
#elif defined(ARCH_CPU_ARM64)
context_.architecture = kCPUArchitectureARM64;
context_.arm64 = &context_arch_;
- // TODO(scottmg): Implement context capture for arm64.
+ InitializeCPUContextARM64(
+ t.general_registers, t.vector_registers, context_.arm64);
#else
#error Port.
#endif
@@ -85,7 +86,6 @@
#endif
}
-
INITIALIZATION_STATE_SET_VALID(initialized_);
}
diff --git a/snapshot/fuchsia/memory_map_fuchsia.cc b/snapshot/fuchsia/memory_map_fuchsia.cc
index b60531d..1586792 100644
--- a/snapshot/fuchsia/memory_map_fuchsia.cc
+++ b/snapshot/fuchsia/memory_map_fuchsia.cc
@@ -14,8 +14,6 @@
#include "snapshot/fuchsia/memory_map_fuchsia.h"
-#include <zircon/syscalls.h>
-
#include "base/fuchsia/fuchsia_logging.h"
#include "util/numeric/checked_range.h"
@@ -25,7 +23,7 @@
MemoryMapFuchsia::~MemoryMapFuchsia() = default;
-bool MemoryMapFuchsia::Initialize(zx_handle_t process) {
+bool MemoryMapFuchsia::Initialize(const zx::process& process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
// There's no way to know what an appropriate buffer size is before starting.
@@ -40,12 +38,11 @@
size_t actual;
size_t available;
zx_status_t status =
- zx_object_get_info(process,
- ZX_INFO_PROCESS_MAPS,
- &map_entries_[0],
- map_entries_.size() * sizeof(map_entries_[0]),
- &actual,
- &available);
+ process.get_info(ZX_INFO_PROCESS_MAPS,
+ &map_entries_[0],
+ map_entries_.size() * sizeof(map_entries_[0]),
+ &actual,
+ &available);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_info ZX_INFO_PROCESS_MAPS";
map_entries_.clear();
diff --git a/snapshot/fuchsia/memory_map_fuchsia.h b/snapshot/fuchsia/memory_map_fuchsia.h
index 88df569..cdbdb17 100644
--- a/snapshot/fuchsia/memory_map_fuchsia.h
+++ b/snapshot/fuchsia/memory_map_fuchsia.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_
#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_
+#include <lib/zx/process.h>
#include <zircon/syscalls/object.h>
#include <vector>
@@ -33,7 +34,7 @@
//! regions in the given process.
//!
//! \return `true` on success, or `false`, with an error logged.
- bool Initialize(zx_handle_t process);
+ bool Initialize(const zx::process& process);
//! \brief Searches through the previously retrieved memory map for the given
//! address. If found, returns the deepest `zx_info_maps_t` mapping that
@@ -45,6 +46,10 @@
//! will be filled out, otherwise `false` and \a map will be unchanged.
bool FindMappingForAddress(zx_vaddr_t address, zx_info_maps_t* map) const;
+ //! \brief Get a vector of `zx_info_maps_t` representing the memory map for
+ //! this process.
+ const std::vector<zx_info_maps_t>& Entries() const { return map_entries_; }
+
private:
std::vector<zx_info_maps_t> map_entries_;
InitializationStateDcheck initialized_;
diff --git a/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc b/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc
new file mode 100644
index 0000000..1133c4d
--- /dev/null
+++ b/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc
@@ -0,0 +1,84 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace crashpad {
+namespace internal {
+
+namespace {
+
+// Maps from bitwise OR of Zircon's flags to enumerated Windows version.
+uint32_t MmuFlagsToProtectFlags(zx_vm_option_t flags) {
+ // These bits are currently the lowest 3 of zx_vm_option_t. They're used to
+ // index into a mapping array, so make sure that we notice if they change
+ // values.
+ static_assert(
+ ZX_VM_PERM_READ == 1 && ZX_VM_PERM_WRITE == 2 && ZX_VM_PERM_EXECUTE == 4,
+ "table below will need fixing");
+
+ // The entries set to zero don't have good corresponding Windows minidump
+ // names. They also aren't currently supported by the mapping syscall on
+ // Zircon, so DCHECK that they don't happen in practice. EXECUTE-only also
+ // cannot currently happen, but as that has a good mapping to the Windows
+ // value, leave it in place in case it's supported by the syscall in the
+ // future.
+ static constexpr uint32_t mapping[] = {
+ /* --- */ PAGE_NOACCESS,
+ /* --r */ PAGE_READONLY,
+ /* -w- */ 0,
+ /* -wr */ PAGE_READWRITE,
+ /* x-- */ PAGE_EXECUTE,
+ /* x-r */ PAGE_EXECUTE_READ,
+ /* xw- */ 0,
+ /* xwr */ PAGE_EXECUTE_READWRITE,
+ };
+
+ const uint32_t index =
+ flags & (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE);
+ DCHECK_LT(index, base::size(mapping));
+
+ const uint32_t protect_flags = mapping[index];
+ DCHECK_NE(protect_flags, 0u);
+ return protect_flags;
+}
+
+} // namespace
+
+MemoryMapRegionSnapshotFuchsia::MemoryMapRegionSnapshotFuchsia(
+ const zx_info_maps_t& info_map)
+ : memory_info_() {
+ DCHECK_EQ(info_map.type, ZX_INFO_MAPS_TYPE_MAPPING);
+
+ memory_info_.BaseAddress = info_map.base;
+ memory_info_.AllocationBase = info_map.base;
+ memory_info_.RegionSize = info_map.size;
+ memory_info_.State = MEM_COMMIT;
+ memory_info_.Protect = memory_info_.AllocationProtect =
+ MmuFlagsToProtectFlags(info_map.u.mapping.mmu_flags);
+ memory_info_.Type = MEM_MAPPED;
+}
+
+MemoryMapRegionSnapshotFuchsia::~MemoryMapRegionSnapshotFuchsia() {}
+
+const MINIDUMP_MEMORY_INFO&
+MemoryMapRegionSnapshotFuchsia::AsMinidumpMemoryInfo() const {
+ return memory_info_;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h b/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h
new file mode 100644
index 0000000..37f7a1a
--- /dev/null
+++ b/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_
+#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_
+
+#include "snapshot/memory_map_region_snapshot.h"
+
+#include <zircon/syscalls/object.h>
+
+namespace crashpad {
+namespace internal {
+
+class MemoryMapRegionSnapshotFuchsia : public MemoryMapRegionSnapshot {
+ public:
+ explicit MemoryMapRegionSnapshotFuchsia(const zx_info_maps_t& info_map);
+ ~MemoryMapRegionSnapshotFuchsia() override;
+
+ virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;
+
+ private:
+ MINIDUMP_MEMORY_INFO memory_info_;
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_
diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc
index 0a6a84b..4522e51 100644
--- a/snapshot/fuchsia/process_reader_fuchsia.cc
+++ b/snapshot/fuchsia/process_reader_fuchsia.cc
@@ -14,11 +14,11 @@
#include "snapshot/fuchsia/process_reader_fuchsia.h"
+#include <lib/zx/thread.h>
#include <link.h>
#include <zircon/syscalls.h>
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/scoped_zx_handle.h"
#include "base/logging.h"
#include "util/fuchsia/koid_utilities.h"
@@ -53,16 +53,40 @@
}
if (range_with_sp.type != ZX_INFO_MAPS_TYPE_MAPPING) {
- LOG(ERROR) << "stack range has unexpected type, continuing anyway";
+ LOG(ERROR) << "stack range has unexpected type " << range_with_sp.type
+ << ", aborting";
+ return;
}
- if (range_with_sp.u.mapping.mmu_flags & ZX_VM_FLAG_PERM_EXECUTE) {
+ if (range_with_sp.u.mapping.mmu_flags & ZX_VM_PERM_EXECUTE) {
LOG(ERROR)
<< "stack range is unexpectedly marked executable, continuing anyway";
}
+ // The stack covers [range_with_sp.base, range_with_sp.base +
+ // range_with_sp.size). The stack pointer (sp) can be anywhere in that range.
+ // It starts at the end of the range (range_with_sp.base + range_with_sp.size)
+ // and goes downwards until range_with_sp.base. Capture the part of the stack
+ // that is currently used: [sp, range_with_sp.base + range_with_sp.size).
+
+ // Capture up to kExtraCaptureSize additional bytes of stack, but only if
+ // present in the region that was already found.
+ constexpr uint64_t kExtraCaptureSize = 128;
+ const uint64_t start_address =
+ std::max(sp >= kExtraCaptureSize ? sp - kExtraCaptureSize : sp,
+ range_with_sp.base);
+ const size_t region_size =
+ range_with_sp.size - (start_address - range_with_sp.base);
+
+ // Because most Fuchsia processes use safestack, it is very unlikely that a
+ // stack this large would be valid. Even if it were, avoid creating
+ // unreasonably large dumps by artificially limiting the captured amount.
+ constexpr uint64_t kMaxStackCapture = 1048576u;
+ LOG_IF(ERROR, region_size > kMaxStackCapture)
+ << "clamping unexpectedly large stack capture of " << region_size;
+ const size_t clamped_region_size = std::min(region_size, kMaxStackCapture);
stack_regions->push_back(
- CheckedRange<zx_vaddr_t, size_t>(range_with_sp.base, range_with_sp.size));
+ CheckedRange<zx_vaddr_t, size_t>(start_address, clamped_region_size));
// TODO(scottmg): https://crashpad.chromium.org/bug/196, once the retrievable
// registers include FS and similar for ARM, retrieve the region for the
@@ -83,15 +107,13 @@
ProcessReaderFuchsia::~ProcessReaderFuchsia() = default;
-bool ProcessReaderFuchsia::Initialize(zx_handle_t process) {
+bool ProcessReaderFuchsia::Initialize(const zx::process& process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- process_ = process;
+ process_ = zx::unowned_process(process);
process_memory_.reset(new ProcessMemoryFuchsia());
- process_memory_->Initialize(process_);
-
- memory_map_.Initialize(process_);
+ process_memory_->Initialize(*process_);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
@@ -119,6 +141,16 @@
return threads_;
}
+const MemoryMapFuchsia* ProcessReaderFuchsia::MemoryMap() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (!initialized_memory_map_) {
+ InitializeMemoryMap();
+ }
+
+ return memory_map_.get();
+}
+
void ProcessReaderFuchsia::InitializeModules() {
DCHECK(!initialized_modules_);
DCHECK(modules_.empty());
@@ -130,27 +162,12 @@
// retrieves (some of) the data into internal structures. It may be worth
// trying to refactor/upstream some of this into Fuchsia.
- std::string app_name;
- {
- char name[ZX_MAX_NAME_LEN];
- zx_status_t status =
- zx_object_get_property(process_, ZX_PROP_NAME, name, sizeof(name));
- if (status != ZX_OK) {
- LOG(ERROR) << "zx_object_get_property ZX_PROP_NAME";
- return;
- }
-
- app_name = name;
- }
-
// Starting from the ld.so's _dl_debug_addr, read the link_map structure and
// walk the list to fill out modules_.
uintptr_t debug_address;
- zx_status_t status = zx_object_get_property(process_,
- ZX_PROP_PROCESS_DEBUG_ADDR,
- &debug_address,
- sizeof(debug_address));
+ zx_status_t status = process_->get_property(
+ ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address));
if (status != ZX_OK || debug_address == 0) {
LOG(ERROR) << "zx_object_get_property ZX_PROP_PROCESS_DEBUG_ADDR";
return;
@@ -206,19 +223,44 @@
LOG(ERROR) << "ReadCString name";
}
- // The vDSO is libzircon.so, but it's not actually loaded normally, it's
- // injected by the kernel, so doesn't have a normal name. When dump_syms is
- // run on libzircon.so, it uses that file name, and in order for the crash
- // server to match symbols both the debug id and the name of the binary have
- // to match. So, map from "<vDSO>" to "libzircon.so" so that symbol
- // resolution works correctly.
+ // Debug symbols are indexed by module name x build-id on the crash server.
+ // The module name in the indexed Breakpad files is set at build time. So
+ // Crashpad needs to use the same module name at run time for symbol
+ // resolution to work properly.
+ //
+ // TODO(fuchsia/DX-1234): once Crashpad switches to elf-search, the
+ // following overwrites won't be necessary as only shared libraries will
+ // have a soname at runtime, just like at build time.
+ //
+ // * For shared libraries, the soname is used as module name at build time,
+ // which is the dsoname here except for libzircon.so (because it is
+ // injected by the kernel, its load name is "<vDSO>" and Crashpad needs to
+ // replace it for symbol resolution to work properly).
if (dsoname == "<vDSO>") {
dsoname = "libzircon.so";
}
+ // * For executables and loadable modules, the dummy value "<_>" is used as
+ // module name at build time. This is because executable and loadable
+ // modules don't have a name on Fuchsia. So we need to use the same dummy
+ // value at build and run times.
+ // Most executables have an empty dsoname. Loadable modules (and some rare
+ // executables) have a non-empty dsoname starting with a specific prefix,
+ // which Crashpas can use to identify loadable modules and clear the
+ // dsoname for them.
+ static constexpr const char kLoadableModuleLoadNamePrefix[] = "<VMO#";
+ // Pre-C++ 20 std::basic_string::starts_with
+ if (dsoname.compare(0,
+ strlen(kLoadableModuleLoadNamePrefix),
+ kLoadableModuleLoadNamePrefix) == 0) {
+ dsoname = "";
+ }
Module module;
if (dsoname.empty()) {
- module.name = app_name;
+ // This value must be kept in sync with what is used at build time to
+ // index symbols for executables and loadable modules.
+ // See fuchsia/DX-1193 for more details.
+ module.name = "<_>";
module.type = ModuleSnapshot::kModuleTypeExecutable;
} else {
module.name = dsoname;
@@ -231,13 +273,15 @@
std::unique_ptr<ProcessMemoryRange> process_memory_range(
new ProcessMemoryRange());
// TODO(scottmg): Could this be limited range?
- process_memory_range->Initialize(process_memory_.get(), true);
- process_memory_ranges_.push_back(std::move(process_memory_range));
+ if (process_memory_range->Initialize(process_memory_.get(), true)) {
+ process_memory_ranges_.push_back(std::move(process_memory_range));
- reader->Initialize(*process_memory_ranges_.back(), base);
- module.reader = reader.get();
- module_readers_.push_back(std::move(reader));
- modules_.push_back(module);
+ if (reader->Initialize(*process_memory_ranges_.back(), base)) {
+ module.reader = reader.get();
+ module_readers_.push_back(std::move(reader));
+ modules_.push_back(module);
+ }
+ }
map = next;
}
@@ -250,9 +294,9 @@
initialized_threads_ = true;
std::vector<zx_koid_t> thread_koids =
- GetChildKoids(process_, ZX_INFO_PROCESS_THREADS);
- std::vector<base::ScopedZxHandle> thread_handles =
- GetHandlesForChildKoids(process_, thread_koids);
+ GetChildKoids(*process_, ZX_INFO_PROCESS_THREADS);
+ std::vector<zx::thread> thread_handles =
+ GetHandlesForThreadKoids(*process_, thread_koids);
DCHECK_EQ(thread_koids.size(), thread_handles.size());
for (size_t i = 0; i < thread_handles.size(); ++i) {
@@ -261,8 +305,8 @@
if (thread_handles[i].is_valid()) {
char name[ZX_MAX_NAME_LEN] = {0};
- zx_status_t status = zx_object_get_property(
- thread_handles[i].get(), ZX_PROP_NAME, &name, sizeof(name));
+ zx_status_t status =
+ thread_handles[i].get_property(ZX_PROP_NAME, &name, sizeof(name));
if (status != ZX_OK) {
ZX_LOG(WARNING, status) << "zx_object_get_property ZX_PROP_NAME";
} else {
@@ -270,29 +314,40 @@
}
zx_info_thread_t thread_info;
- status = zx_object_get_info(thread_handles[i].get(),
- ZX_INFO_THREAD,
- &thread_info,
- sizeof(thread_info),
- nullptr,
- nullptr);
+ status = thread_handles[i].get_info(
+ ZX_INFO_THREAD, &thread_info, sizeof(thread_info), nullptr, nullptr);
if (status != ZX_OK) {
ZX_LOG(WARNING, status) << "zx_object_get_info ZX_INFO_THREAD";
} else {
thread.state = thread_info.state;
}
- zx_thread_state_general_regs_t regs;
- status = zx_thread_read_state(thread_handles[i].get(),
- ZX_THREAD_STATE_GENERAL_REGS,
- ®s,
- sizeof(regs));
+ zx_thread_state_general_regs_t general_regs;
+ status = thread_handles[i].read_state(
+ ZX_THREAD_STATE_GENERAL_REGS, &general_regs, sizeof(general_regs));
if (status != ZX_OK) {
- ZX_LOG(WARNING, status) << "zx_thread_read_state";
+ ZX_LOG(WARNING, status)
+ << "zx_thread_read_state(ZX_THREAD_STATE_GENERAL_REGS)";
} else {
- thread.general_registers = regs;
+ thread.general_registers = general_regs;
- GetStackRegions(regs, memory_map_, &thread.stack_regions);
+ const MemoryMapFuchsia* memory_map = MemoryMap();
+ if (memory_map) {
+ // Attempt to retrive stack regions if a memory map was retrieved. In
+ // particular, this may be null when operating on the current process
+ // where the memory map will not be able to be retrieved.
+ GetStackRegions(general_regs, *memory_map, &thread.stack_regions);
+ }
+ }
+
+ zx_thread_state_vector_regs_t vector_regs;
+ status = thread_handles[i].read_state(
+ ZX_THREAD_STATE_VECTOR_REGS, &vector_regs, sizeof(vector_regs));
+ if (status != ZX_OK) {
+ ZX_LOG(WARNING, status)
+ << "zx_thread_read_state(ZX_THREAD_STATE_VECTOR_REGS)";
+ } else {
+ thread.vector_registers = vector_regs;
}
}
@@ -300,4 +355,15 @@
}
}
+void ProcessReaderFuchsia::InitializeMemoryMap() {
+ DCHECK(!initialized_memory_map_);
+
+ initialized_memory_map_ = true;
+
+ memory_map_.reset(new MemoryMapFuchsia);
+ if (!memory_map_->Initialize(*process_)) {
+ memory_map_.reset();
+ }
+}
+
} // namespace crashpad
diff --git a/snapshot/fuchsia/process_reader_fuchsia.h b/snapshot/fuchsia/process_reader_fuchsia.h
index 2d0878c..91c6331 100644
--- a/snapshot/fuchsia/process_reader_fuchsia.h
+++ b/snapshot/fuchsia/process_reader_fuchsia.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_
#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_
+#include <lib/zx/process.h>
#include <zircon/syscalls/debug.h>
#include <memory>
@@ -76,6 +77,10 @@
//! returned by `zx_thread_read_state()`.
zx_thread_state_general_regs_t general_registers = {};
+ //! \brief The raw architecture-specific `zx_thread_state_vector_regs_t` as
+ //! returned by `zx_thread_read_state()`.
+ zx_thread_state_vector_regs_t vector_registers = {};
+
//! \brief The regions representing the stack. The first entry in the vector
//! represents the callstack, and further entries optionally identify
//! other stack data when the thread uses a split stack representation.
@@ -94,7 +99,7 @@
//! \return `true` on success, indicating that this object will respond
//! validly to further method calls. `false` on failure. On failure, no
//! further method calls should be made.
- bool Initialize(zx_handle_t process);
+ bool Initialize(const zx::process& process);
//! \return The modules loaded in the process. The first element (at index
//! `0`) corresponds to the main executable.
@@ -104,7 +109,10 @@
const std::vector<Thread>& Threads();
//! \brief Return a memory reader for the target process.
- ProcessMemory* Memory() { return process_memory_.get(); }
+ const ProcessMemory* Memory() const { return process_memory_.get(); }
+
+ //! \brief Return a memory map for the target process.
+ const MemoryMapFuchsia* MemoryMap();
private:
//! Performs lazy initialization of the \a modules_ vector on behalf of
@@ -115,15 +123,20 @@
//! Threads().
void InitializeThreads();
+ //! Performs lazy initialization of the \a memory_map_ on behalf of
+ //! MemoryMap().
+ void InitializeMemoryMap();
+
std::vector<Module> modules_;
std::vector<Thread> threads_;
std::vector<std::unique_ptr<ElfImageReader>> module_readers_;
std::vector<std::unique_ptr<ProcessMemoryRange>> process_memory_ranges_;
std::unique_ptr<ProcessMemoryFuchsia> process_memory_;
- MemoryMapFuchsia memory_map_;
- zx_handle_t process_;
+ std::unique_ptr<MemoryMapFuchsia> memory_map_;
+ zx::unowned_process process_;
bool initialized_modules_ = false;
bool initialized_threads_ = false;
+ bool initialized_memory_map_ = false;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessReaderFuchsia);
diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc
index 546d829..581fb4e 100644
--- a/snapshot/fuchsia/process_reader_fuchsia_test.cc
+++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc
@@ -20,6 +20,7 @@
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "test/multiprocess_exec.h"
#include "test/test_paths.h"
@@ -31,20 +32,34 @@
TEST(ProcessReaderFuchsia, SelfBasic) {
ProcessReaderFuchsia process_reader;
- ASSERT_TRUE(process_reader.Initialize(zx_process_self()));
+ ASSERT_TRUE(process_reader.Initialize(*zx::process::self()));
static constexpr char kTestMemory[] = "Some test memory";
- char buffer[arraysize(kTestMemory)];
+ char buffer[base::size(kTestMemory)];
ASSERT_TRUE(process_reader.Memory()->Read(
reinterpret_cast<zx_vaddr_t>(kTestMemory), sizeof(kTestMemory), &buffer));
EXPECT_STREQ(kTestMemory, buffer);
const auto& modules = process_reader.Modules();
+ // The process should have at least one module, the executable, and then some
+ // shared libraries, no loadable modules.
EXPECT_GT(modules.size(), 0u);
+ size_t num_executables = 0u;
+ size_t num_shared_libraries = 0u;
for (const auto& module : modules) {
EXPECT_FALSE(module.name.empty());
EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown);
+
+ if (module.type == ModuleSnapshot::kModuleTypeExecutable) {
+ EXPECT_EQ(module.name, "<_>");
+ num_executables++;
+ } else if (module.type == ModuleSnapshot::kModuleTypeSharedLibrary) {
+ EXPECT_NE(module.name, "<_>");
+ num_shared_libraries++;
+ }
}
+ EXPECT_EQ(num_executables, 1u);
+ EXPECT_EQ(num_shared_libraries, modules.size() - num_executables);
const auto& threads = process_reader.Threads();
EXPECT_GT(threads.size(), 0u);
@@ -82,7 +97,7 @@
private:
void MultiprocessParent() override {
ProcessReaderFuchsia process_reader;
- ASSERT_TRUE(process_reader.Initialize(ChildProcess()));
+ ASSERT_TRUE(process_reader.Initialize(*ChildProcess()));
zx_vaddr_t addr;
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &addr, sizeof(addr)));
@@ -149,24 +164,28 @@
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &c, 1));
ASSERT_EQ(c, ' ');
- ScopedTaskSuspend suspend(ChildProcess());
+ ScopedTaskSuspend suspend(*ChildProcess());
ProcessReaderFuchsia process_reader;
- ASSERT_TRUE(process_reader.Initialize(ChildProcess()));
+ ASSERT_TRUE(process_reader.Initialize(*ChildProcess()));
const auto& threads = process_reader.Threads();
EXPECT_EQ(threads.size(), 6u);
for (size_t i = 1; i < 6; ++i) {
ASSERT_GT(threads[i].stack_regions.size(), 0u);
- EXPECT_EQ(threads[i].stack_regions[0].size(), i * 4096u);
+ EXPECT_GT(threads[i].stack_regions[0].size(), 0u);
+ EXPECT_LE(threads[i].stack_regions[0].size(), i * 4096u);
}
}
DISALLOW_COPY_AND_ASSIGN(ThreadsChildTest);
};
-TEST(ProcessReaderFuchsia, ChildThreads) {
+// TODO(scottmg): US-553. ScopedTaskSuspend fails sometimes, with a 50ms
+// timeout. Currently unclear how to make that more reliable, so disable the
+// test for now as otherwise it flakes.
+TEST(ProcessReaderFuchsia, DISABLED_ChildThreads) {
ThreadsChildTest test;
test.Run();
}
diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc
index cc66db2..294c9f9 100644
--- a/snapshot/fuchsia/process_snapshot_fuchsia.cc
+++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc
@@ -14,8 +14,6 @@
#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
-#include <zircon/process.h>
-
#include "base/logging.h"
#include "util/fuchsia/koid_utilities.h"
@@ -25,7 +23,7 @@
ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() = default;
-bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) {
+bool ProcessSnapshotFuchsia::Initialize(const zx::process& process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
if (gettimeofday(&snapshot_time_, nullptr) != 0) {
@@ -43,6 +41,16 @@
InitializeThreads();
InitializeModules();
+ const MemoryMapFuchsia* memory_map = process_reader_.MemoryMap();
+ if (memory_map) {
+ for (const auto& entry : memory_map->Entries()) {
+ if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) {
+ memory_map_.push_back(
+ std::make_unique<internal::MemoryMapRegionSnapshotFuchsia>(entry));
+ }
+ }
+ }
+
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
@@ -93,12 +101,12 @@
*options = local_options;
}
-pid_t ProcessSnapshotFuchsia::ProcessID() const {
+crashpad::ProcessID ProcessSnapshotFuchsia::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return GetKoidForHandle(zx_process_self());
+ return GetKoidForHandle(*zx::process::self());
}
-pid_t ProcessSnapshotFuchsia::ParentProcessID() const {
+crashpad::ProcessID ProcessSnapshotFuchsia::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
return 0;
@@ -177,7 +185,11 @@
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotFuchsia::MemoryMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return std::vector<const MemoryMapRegionSnapshot*>();
+ std::vector<const MemoryMapRegionSnapshot*> memory_map;
+ for (const auto& item : memory_map_) {
+ memory_map.push_back(item.get());
+ }
+ return memory_map;
}
std::vector<HandleSnapshot> ProcessSnapshotFuchsia::Handles() const {
@@ -190,6 +202,11 @@
return std::vector<const MemorySnapshot*>();
}
+const ProcessMemory* ProcessSnapshotFuchsia::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.Memory();
+}
+
void ProcessSnapshotFuchsia::InitializeThreads() {
const std::vector<ProcessReaderFuchsia::Thread>& process_reader_threads =
process_reader_.Threads();
@@ -209,7 +226,8 @@
std::make_unique<internal::ModuleSnapshotElf>(reader_module.name,
reader_module.reader,
reader_module.type,
- &memory_range_);
+ &memory_range_,
+ process_reader_.Memory());
if (module->Initialize()) {
modules_.push_back(std::move(module));
}
diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h
index 2590ed5..6af2ebe 100644
--- a/snapshot/fuchsia/process_snapshot_fuchsia.h
+++ b/snapshot/fuchsia/process_snapshot_fuchsia.h
@@ -15,9 +15,10 @@
#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_
#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_
+#include <lib/zx/process.h>
#include <sys/time.h>
-#include <zircon/types.h>
#include <zircon/syscalls/exception.h>
+#include <zircon/types.h>
#include <memory>
#include <vector>
@@ -27,12 +28,14 @@
#include "snapshot/elf/elf_image_reader.h"
#include "snapshot/elf/module_snapshot_elf.h"
#include "snapshot/fuchsia/exception_snapshot_fuchsia.h"
+#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
#include "snapshot/fuchsia/process_reader_fuchsia.h"
#include "snapshot/fuchsia/system_snapshot_fuchsia.h"
#include "snapshot/fuchsia/thread_snapshot_fuchsia.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/unloaded_module_snapshot.h"
#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_id.h"
#include "util/process/process_memory_range.h"
namespace crashpad {
@@ -50,7 +53,7 @@
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
- bool Initialize(zx_handle_t process);
+ bool Initialize(const zx::process& process);
//! \brief Initializes the object's exception.
//!
@@ -103,8 +106,8 @@
}
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -120,6 +123,7 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
// Initializes threads_ on behalf of Initialize().
@@ -135,6 +139,8 @@
ProcessReaderFuchsia process_reader_;
ProcessMemoryRange memory_range_;
std::map<std::string, std::string> annotations_simple_map_;
+ std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotFuchsia>>
+ memory_map_;
UUID report_id_;
UUID client_id_;
timeval snapshot_time_;
diff --git a/snapshot/fuchsia/process_snapshot_fuchsia_test.cc b/snapshot/fuchsia/process_snapshot_fuchsia_test.cc
new file mode 100644
index 0000000..e1b83ac
--- /dev/null
+++ b/snapshot/fuchsia/process_snapshot_fuchsia_test.cc
@@ -0,0 +1,235 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h"
+
+#include <dbghelp.h>
+#include <zircon/syscalls.h>
+
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
+#include "test/multiprocess_exec.h"
+#include "util/fuchsia/koid_utilities.h"
+#include "util/fuchsia/scoped_task_suspend.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr struct {
+ uint32_t zircon_perm;
+ size_t pages;
+ uint32_t minidump_perm;
+} kTestMappingPermAndSizes[] = {
+ // Zircon doesn't currently allow write-only, execute-only, or
+ // write-execute-only, returning ZX_ERR_INVALID_ARGS on map.
+ {0, 5, PAGE_NOACCESS},
+ {ZX_VM_PERM_READ, 6, PAGE_READONLY},
+ // {ZX_VM_PERM_WRITE, 7, PAGE_WRITECOPY},
+ // {ZX_VM_PERM_EXECUTE, 8, PAGE_EXECUTE},
+ {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 9, PAGE_READWRITE},
+ {ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 10, PAGE_EXECUTE_READ},
+ // {ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, 11, PAGE_EXECUTE_WRITECOPY},
+ {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE,
+ 12,
+ PAGE_EXECUTE_READWRITE},
+};
+
+CRASHPAD_CHILD_TEST_MAIN(AddressSpaceChildTestMain) {
+ // Create specifically sized mappings/permissions and write the address in
+ // our address space to the parent so that the reader can check they're read
+ // correctly.
+ for (const auto& t : kTestMappingPermAndSizes) {
+ zx_handle_t vmo = ZX_HANDLE_INVALID;
+ const size_t size = t.pages * PAGE_SIZE;
+ zx_status_t status = zx_vmo_create(size, 0, &vmo);
+ ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create";
+ status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);
+ ZX_CHECK(status == ZX_OK, status) << "zx_vmo_replace_as_executable";
+ uintptr_t mapping_addr = 0;
+ status = zx_vmar_map(
+ zx_vmar_root_self(), t.zircon_perm, 0, vmo, 0, size, &mapping_addr);
+ ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map";
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &mapping_addr,
+ sizeof(mapping_addr));
+ }
+
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
+
+bool HasSingleMatchingMapping(
+ const std::vector<const MemoryMapRegionSnapshot*>& memory_map,
+ uintptr_t address,
+ size_t size,
+ uint32_t perm) {
+ const MemoryMapRegionSnapshot* matching = nullptr;
+ for (const auto* region : memory_map) {
+ const MINIDUMP_MEMORY_INFO& mmi = region->AsMinidumpMemoryInfo();
+ if (mmi.BaseAddress == address) {
+ if (matching) {
+ LOG(ERROR) << "multiple mappings matching address";
+ return false;
+ }
+ matching = region;
+ }
+ }
+
+ if (!matching)
+ return false;
+
+ const MINIDUMP_MEMORY_INFO& matching_mmi = matching->AsMinidumpMemoryInfo();
+ return matching_mmi.Protect == perm && matching_mmi.RegionSize == size;
+}
+
+class AddressSpaceTest : public MultiprocessExec {
+ public:
+ AddressSpaceTest() : MultiprocessExec() {
+ SetChildTestMainFunction("AddressSpaceChildTestMain");
+ }
+ ~AddressSpaceTest() {}
+
+ private:
+ void MultiprocessParent() override {
+ uintptr_t test_addresses[base::size(kTestMappingPermAndSizes)];
+ for (size_t i = 0; i < base::size(test_addresses); ++i) {
+ ASSERT_TRUE(ReadFileExactly(
+ ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i])));
+ }
+
+ ScopedTaskSuspend suspend(*ChildProcess());
+
+ ProcessSnapshotFuchsia process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
+
+ for (size_t i = 0; i < base::size(test_addresses); ++i) {
+ const auto& t = kTestMappingPermAndSizes[i];
+ EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(),
+ test_addresses[i],
+ t.pages * PAGE_SIZE,
+ t.minidump_perm))
+ << base::StringPrintf(
+ "index %zu, zircon_perm 0x%x, minidump_perm 0x%x",
+ i,
+ t.zircon_perm,
+ t.minidump_perm);
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AddressSpaceTest);
+};
+
+TEST(ProcessSnapshotFuchsiaTest, AddressSpaceMapping) {
+ AddressSpaceTest test;
+ test.Run();
+}
+
+CRASHPAD_CHILD_TEST_MAIN(StackPointerIntoInvalidLocation) {
+ // Map a large block, output the base address of it, and block. The parent
+ // will artificially set the SP into this large block to confirm that a huge
+ // stack is not accidentally captured.
+ zx_handle_t large_vmo;
+ constexpr uint64_t kSize = 1 << 30u;
+ zx_status_t status = zx_vmo_create(kSize, 0, &large_vmo);
+ ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create";
+ zx_vaddr_t mapped_addr;
+ status = zx_vmar_map(zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+ 0,
+ large_vmo,
+ 0,
+ kSize,
+ &mapped_addr);
+ ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map";
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &mapped_addr,
+ sizeof(mapped_addr));
+ zx_nanosleep(ZX_TIME_INFINITE);
+ return 0;
+}
+
+class InvalidStackPointerTest : public MultiprocessExec {
+ public:
+ InvalidStackPointerTest() : MultiprocessExec() {
+ SetChildTestMainFunction("StackPointerIntoInvalidLocation");
+ SetExpectedChildTermination(kTerminationNormal,
+ ZX_TASK_RETCODE_SYSCALL_KILL);
+ }
+ ~InvalidStackPointerTest() {}
+
+ private:
+ void MultiprocessParent() override {
+ uint64_t address_of_large_mapping;
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),
+ &address_of_large_mapping,
+ sizeof(address_of_large_mapping)));
+
+ ScopedTaskSuspend suspend(*ChildProcess());
+
+ std::vector<zx::thread> threads = GetThreadHandles(*ChildProcess());
+ ASSERT_EQ(threads.size(), 1u);
+
+ zx_thread_state_general_regs_t regs;
+ ASSERT_EQ(threads[0].read_state(
+ ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)),
+ ZX_OK);
+
+ constexpr uint64_t kOffsetIntoMapping = 1024;
+#if defined(ARCH_CPU_X86_64)
+ regs.rsp = address_of_large_mapping + kOffsetIntoMapping;
+#elif defined(ARCH_CPU_ARM64)
+ regs.sp = address_of_large_mapping + kOffsetIntoMapping;
+#else
+#error
+#endif
+
+ ASSERT_EQ(threads[0].write_state(
+ ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)),
+ ZX_OK);
+
+ ProcessSnapshotFuchsia process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));
+
+ ASSERT_EQ(process_snapshot.Threads().size(), 1u);
+ const MemorySnapshot* stack = process_snapshot.Threads()[0]->Stack();
+ ASSERT_TRUE(stack);
+ // Ensure the stack capture isn't unreasonably large.
+ EXPECT_LT(stack->Size(), 10 * 1048576u);
+
+ // As we've corrupted the child, don't let it run again.
+ ASSERT_EQ(ChildProcess()->kill(), ZX_OK);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(InvalidStackPointerTest);
+};
+
+// This is a test for a specific failure detailed in
+// https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=41212. A test of stack
+// behavior that was intentionally overflowing the stack, and so when Crashpad
+// received the exception the SP did not point into the actual stack. This
+// caused Crashpad to erronously capture the "stack" from the next mapping in
+// the address space (which could be very large, cause OOM, etc.).
+TEST(ProcessSnapshotFuchsiaTest, InvalidStackPointer) {
+ InvalidStackPointerTest test;
+ test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.cc b/snapshot/fuchsia/system_snapshot_fuchsia.cc
index 889817a..c108529 100644
--- a/snapshot/fuchsia/system_snapshot_fuchsia.cc
+++ b/snapshot/fuchsia/system_snapshot_fuchsia.cc
@@ -38,10 +38,7 @@
// garnet/bin/uname/uname.c, however, this information isn't provided by
// uname(). Additionally, uname() seems to hang if the network is in a bad
// state when attempting to retrieve the nodename, so avoid it for now.
- char kernel_version[256] = {};
- zx_status_t status =
- zx_system_get_version(kernel_version, sizeof(kernel_version));
- ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_system_get_version";
+ std::string kernel_version = zx_system_get_version_string();
#if defined(ARCH_CPU_X86_64)
static constexpr const char kArch[] = "x86_64";
@@ -50,8 +47,8 @@
#else
static constexpr const char kArch[] = "unknown";
#endif
- os_version_full_ =
- base::StringPrintf("Zircon prerelease %s %s", kernel_version, kArch);
+ os_version_full_ = base::StringPrintf(
+ "Zircon prerelease %s %s", kernel_version.c_str(), kArch);
INITIALIZATION_STATE_SET_VALID(initialized_);
}
@@ -73,7 +70,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.Revision();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ // TODO(fuchsia/DX-712): Read actual revision.
return 0;
#endif
}
@@ -88,7 +85,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.Vendor();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ // TODO(fuchsia/DX-712): Read actual vendor.
return std::string();
#endif
}
@@ -106,7 +103,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.Signature();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ NOTREACHED();
return 0;
#endif
}
@@ -116,7 +113,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.Features();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ NOTREACHED();
return 0;
#endif
}
@@ -126,7 +123,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.ExtendedFeatures();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ NOTREACHED();
return 0;
#endif
}
@@ -135,7 +132,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.Leaf7Features();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ NOTREACHED();
return 0;
#endif
}
@@ -145,7 +142,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.SupportsDAZ();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ NOTREACHED();
return false;
#endif
}
@@ -191,7 +188,7 @@
#if defined(ARCH_CPU_X86_64)
return cpuid_.NXEnabled();
#else
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196.
+ // TODO(fuchsia/DX-712): Read actual NX bit value.
return false;
#endif
}
diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/snapshot/fuchsia/thread_snapshot_fuchsia.cc
index 03055d8..462cdb1 100644
--- a/snapshot/fuchsia/thread_snapshot_fuchsia.cc
+++ b/snapshot/fuchsia/thread_snapshot_fuchsia.cc
@@ -39,21 +39,22 @@
#if defined(ARCH_CPU_X86_64)
context_.architecture = kCPUArchitectureX86_64;
context_.x86_64 = &context_arch_;
- // TODO(scottmg): Float context, once Fuchsia has a debug API to capture
- // floating point registers. ZX-1750 upstream.
- InitializeCPUContextX86_64(thread.general_registers, context_.x86_64);
+ // TODO(fuchsia/DX-642): Add float context once saved in |thread|.
+ InitializeCPUContextX86_64_NoFloatingPoint(thread.general_registers,
+ context_.x86_64);
#elif defined(ARCH_CPU_ARM64)
context_.architecture = kCPUArchitectureARM64;
context_.arm64 = &context_arch_;
- // TODO(scottmg): Implement context capture for arm64.
+ InitializeCPUContextARM64(
+ thread.general_registers, thread.vector_registers, context_.arm64);
#else
#error Port.
#endif
if (thread.stack_regions.empty()) {
- stack_.Initialize(process_reader, 0, 0);
+ stack_.Initialize(process_reader->Memory(), 0, 0);
} else {
- stack_.Initialize(process_reader,
+ stack_.Initialize(process_reader->Memory(),
thread.stack_regions[0].base(),
thread.stack_regions[0].size());
// TODO(scottmg): Handle split stack by adding other parts to ExtraMemory().
diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.h b/snapshot/fuchsia/thread_snapshot_fuchsia.h
index db97597..45d4f11 100644
--- a/snapshot/fuchsia/thread_snapshot_fuchsia.h
+++ b/snapshot/fuchsia/thread_snapshot_fuchsia.h
@@ -67,7 +67,7 @@
#error Port.
#endif
CPUContext context_;
- MemorySnapshotGeneric<ProcessReaderFuchsia> stack_;
+ MemorySnapshotGeneric stack_;
zx_koid_t thread_id_;
zx_vaddr_t thread_specific_data_address_;
InitializationStateDcheck initialized_;
diff --git a/snapshot/ios/exception_snapshot_ios.cc b/snapshot/ios/exception_snapshot_ios.cc
new file mode 100644
index 0000000..db9e489
--- /dev/null
+++ b/snapshot/ios/exception_snapshot_ios.cc
@@ -0,0 +1,264 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/exception_snapshot_ios.h"
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/mac/cpu_context_mac.h"
+#include "util/misc/from_pointer_cast.h"
+
+namespace crashpad {
+
+namespace internal {
+
+ExceptionSnapshotIOS::ExceptionSnapshotIOS()
+ : ExceptionSnapshot(),
+ context_(),
+ codes_(),
+ thread_id_(0),
+ exception_address_(0),
+ exception_(0),
+ exception_info_(0),
+ initialized_() {}
+
+ExceptionSnapshotIOS::~ExceptionSnapshotIOS() {}
+
+void ExceptionSnapshotIOS::InitializeFromSignal(const siginfo_t* siginfo,
+ const ucontext_t* context) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ mcontext_t mcontext = context->uc_mcontext;
+#if defined(ARCH_CPU_X86_64)
+ context_.architecture = kCPUArchitectureX86_64;
+ context_.x86_64 = &context_x86_64_;
+ x86_debug_state64_t empty_debug_state = {};
+ InitializeCPUContextX86_64(&context_x86_64_,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &mcontext->__ss,
+ &mcontext->__fs,
+ &empty_debug_state);
+#elif defined(ARCH_CPU_ARM64)
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_arm64_;
+ InitializeCPUContextARM64(&context_arm64_,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &mcontext->__ss,
+ &mcontext->__ns);
+#endif
+
+ // Thread ID.
+ thread_identifier_info identifier_info;
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t kr =
+ thread_info(mach_thread_self(),
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&identifier_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_identifier_info";
+ } else {
+ thread_id_ = identifier_info.thread_id;
+ }
+
+ exception_ = siginfo->si_signo;
+ exception_info_ = siginfo->si_code;
+
+ // TODO(justincohen): Investigate recording more codes_.
+
+ exception_address_ = FromPointerCast<uintptr_t>(siginfo->si_addr);
+
+ // TODO(justincohen): Record the source of the exception (signal, mach, etc).
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+void ExceptionSnapshotIOS::InitializeFromMachException(
+ exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ codes_.push_back(exception);
+ // TODO: rationalize with the macOS implementation.
+ for (mach_msg_type_number_t code_index = 0; code_index < code_count;
+ ++code_index) {
+ codes_.push_back(code[code_index]);
+ }
+ exception_ = exception;
+ exception_info_ = code[0];
+
+ // For serialization, float_state and, on x86, debug_state, will be identical
+ // between here and the thread_snapshot version for thread_id. That means
+ // this block getting float_state and debug_state can be skipped when doing
+ // proper serialization.
+#if defined(ARCH_CPU_X86_64)
+ x86_thread_state64_t thread_state;
+ x86_float_state64_t float_state;
+ x86_debug_state64_t debug_state;
+ mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
+ mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;
+ mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT;
+ const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;
+ const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64;
+ const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64;
+#elif defined(ARCH_CPU_ARM64)
+ arm_thread_state64_t thread_state;
+ arm_neon_state64_t float_state;
+ mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
+ mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
+ const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
+ const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+#endif
+
+ kern_return_t kr =
+ thread_get_state(exception_thread,
+ kThreadStateFlavor,
+ reinterpret_cast<thread_state_t>(&thread_state),
+ &thread_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
+ }
+
+ kr = thread_get_state(exception_thread,
+ kFloatStateFlavor,
+ reinterpret_cast<thread_state_t>(&float_state),
+ &float_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
+ }
+
+#if defined(ARCH_CPU_X86_64)
+ kr = thread_get_state(exception_thread,
+ kDebugStateFlavor,
+ reinterpret_cast<thread_state_t>(&debug_state),
+ &debug_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
+ }
+#endif
+
+#if defined(ARCH_CPU_X86_64)
+ context_.architecture = kCPUArchitectureX86_64;
+ context_.x86_64 = &context_x86_64_;
+ InitializeCPUContextX86_64(&context_x86_64_,
+ flavor,
+ state,
+ state_count,
+ &thread_state,
+ &float_state,
+ &debug_state);
+#elif defined(ARCH_CPU_ARM64)
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_arm64_;
+ InitializeCPUContextARM64(
+ &context_arm64_, flavor, state, state_count, &thread_state, &float_state);
+#endif
+
+ // Thread ID.
+ thread_identifier_info identifier_info;
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(mach_thread_self(),
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&identifier_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_identifier_info";
+ } else {
+ thread_id_ = identifier_info.thread_id;
+ }
+
+ // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present
+ // in code[1]. It may or may not be the instruction pointer address (usually
+ // it’s not). code[1] may carry the exception address for other exception
+ // types too, but it’s not guaranteed. But for all other exception types, the
+ // instruction pointer will be the exception address, and in fact will be
+ // equal to codes[1] when it’s carrying the exception address. In those cases,
+ // just use the instruction pointer directly.
+ bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
+
+#if defined(ARCH_CPU_X86_64)
+ // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values
+ // indicate that code[1] does not (or may not) carry the exception address:
+ // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for
+ // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)
+ // which collides with EXC_I386_BOUNDFLT (10.9.5
+ // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS
+ // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c
+ // user_page_fault_continue() and do contain the exception address in
+ // code[1].
+ if (exception_ == EXC_BAD_ACCESS &&
+ (exception_info_ == EXC_I386_GPFLT ||
+ exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
+ code_1_is_exception_address = false;
+ }
+#endif
+
+ if (code_1_is_exception_address) {
+ exception_address_ = code[1];
+ } else {
+ exception_address_ = context_.InstructionPointer();
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+const CPUContext* ExceptionSnapshotIOS::Context() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &context_;
+}
+
+uint64_t ExceptionSnapshotIOS::ThreadID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_id_;
+}
+
+uint32_t ExceptionSnapshotIOS::Exception() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_;
+}
+
+uint32_t ExceptionSnapshotIOS::ExceptionInfo() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_info_;
+}
+
+uint64_t ExceptionSnapshotIOS::ExceptionAddress() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_address_;
+}
+
+const std::vector<uint64_t>& ExceptionSnapshotIOS::Codes() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return codes_;
+}
+
+std::vector<const MemorySnapshot*> ExceptionSnapshotIOS::ExtraMemory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<const MemorySnapshot*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/ios/exception_snapshot_ios.h b/snapshot/ios/exception_snapshot_ios.h
new file mode 100644
index 0000000..5c1337d
--- /dev/null
+++ b/snapshot/ios/exception_snapshot_ios.h
@@ -0,0 +1,92 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
+
+#include <mach/mach.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/exception_snapshot.h"
+#include "util/mach/mach_extensions.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+namespace internal {
+
+//! \brief An ExceptionSnapshot of an exception sustained by a running (or
+//! crashed) process on an iOS system.
+class ExceptionSnapshotIOS final : public ExceptionSnapshot {
+ public:
+ ExceptionSnapshotIOS();
+ ~ExceptionSnapshotIOS() override;
+
+ //! \brief Initializes the object from a signal.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ void InitializeFromSignal(const siginfo_t* siginfo,
+ const ucontext_t* context);
+
+ //! \brief Initialize the object from a Mach exception for the current task.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ void InitializeFromMachException(exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count);
+ // ExceptionSnapshot:
+
+ const CPUContext* Context() const override;
+ uint64_t ThreadID() const override;
+ uint32_t Exception() const override;
+ uint32_t ExceptionInfo() const override;
+ uint64_t ExceptionAddress() const override;
+ const std::vector<uint64_t>& Codes() const override;
+ virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;
+
+ private:
+#if defined(ARCH_CPU_X86_64)
+ CPUContextX86_64 context_x86_64_;
+#elif defined(ARCH_CPU_ARM64)
+ CPUContextARM64 context_arm64_;
+#else
+#error Port.
+#endif // ARCH_CPU_X86_64
+ CPUContext context_;
+ std::vector<uint64_t> codes_;
+ uint64_t thread_id_;
+ uintptr_t exception_address_;
+ uint32_t exception_;
+ uint32_t exception_info_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOS);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
diff --git a/snapshot/ios/memory_snapshot_ios.cc b/snapshot/ios/memory_snapshot_ios.cc
new file mode 100644
index 0000000..e760465
--- /dev/null
+++ b/snapshot/ios/memory_snapshot_ios.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/memory_snapshot_ios.h"
+
+namespace crashpad {
+namespace internal {
+
+void MemorySnapshotIOS::Initialize(vm_address_t address, vm_size_t size) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ address_ = address;
+ size_ = base::checked_cast<size_t>(size);
+
+ // TODO(justincohen): This is temporary, as MemorySnapshotIOS will likely be
+ // able to point directly to the deserialized data dump rather than copying
+ // data around.
+ buffer_ = std::unique_ptr<uint8_t[]>(new uint8_t[size_]);
+ memcpy(buffer_.get(), reinterpret_cast<void*>(address_), size_);
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+uint64_t MemorySnapshotIOS::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return address_;
+}
+
+size_t MemorySnapshotIOS::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return size_;
+}
+
+bool MemorySnapshotIOS::Read(Delegate* delegate) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (size_ == 0) {
+ return delegate->MemorySnapshotDelegateRead(nullptr, size_);
+ }
+
+ return delegate->MemorySnapshotDelegateRead(buffer_.get(), size_);
+}
+
+const MemorySnapshot* MemorySnapshotIOS::MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const {
+ CheckedRange<uint64_t, size_t> merged(0, 0);
+ if (!LoggingDetermineMergedRange(this, other, &merged))
+ return nullptr;
+
+ auto result = std::make_unique<MemorySnapshotIOS>();
+ result->Initialize(merged.base(), merged.size());
+ return result.release();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/ios/memory_snapshot_ios.h b/snapshot/ios/memory_snapshot_ios.h
new file mode 100644
index 0000000..be99105
--- /dev/null
+++ b/snapshot/ios/memory_snapshot_ios.h
@@ -0,0 +1,63 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
+
+#include "base/macros.h"
+#include "snapshot/memory_snapshot.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A MemorySnapshot of a memory region.
+class MemorySnapshotIOS final : public MemorySnapshot {
+ public:
+ MemorySnapshotIOS() = default;
+ ~MemorySnapshotIOS() = default;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] address The base address of the memory region to snapshot.
+ //! \param[in] size The size of the memory region to snapshot.
+ void Initialize(vm_address_t address, vm_size_t size);
+
+ // MemorySnapshot:
+ uint64_t Address() const override;
+ size_t Size() const override;
+ bool Read(Delegate* delegate) const override;
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const override;
+
+ private:
+ template <class T>
+ friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
+ const T* self,
+ const MemorySnapshot* other);
+
+ // TODO(justincohen): This is temporary until deserialization is worked out.
+ std::unique_ptr<uint8_t[]> buffer_;
+ vm_address_t address_;
+ vm_size_t size_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOS);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
diff --git a/snapshot/ios/module_snapshot_ios.cc b/snapshot/ios/module_snapshot_ios.cc
new file mode 100644
index 0000000..8b251eb
--- /dev/null
+++ b/snapshot/ios/module_snapshot_ios.cc
@@ -0,0 +1,238 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/module_snapshot_ios.h"
+
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+#include "base/files/file_path.h"
+#include "base/mac/mach_logging.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/misc/uuid.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotIOS::ModuleSnapshotIOS()
+ : ModuleSnapshot(),
+ name_(),
+ address_(0),
+ size_(0),
+ timestamp_(0),
+ dylib_version_(0),
+ source_version_(0),
+ filetype_(0),
+ initialized_() {}
+
+ModuleSnapshotIOS::~ModuleSnapshotIOS() {}
+
+// static.
+const dyld_all_image_infos* ModuleSnapshotIOS::DyldAllImageInfo() {
+ task_dyld_info_data_t dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ kern_return_t kr = task_info(mach_task_self(),
+ TASK_DYLD_INFO,
+ reinterpret_cast<task_info_t>(&dyld_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info";
+ return 0;
+ }
+
+ return reinterpret_cast<dyld_all_image_infos*>(dyld_info.all_image_info_addr);
+}
+
+bool ModuleSnapshotIOS::InitializeDyld(const dyld_all_image_infos* images) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ name_ = images->dyldPath;
+ address_ = FromPointerCast<uint64_t>(images->dyldImageLoadAddress);
+ return FinishInitialization();
+}
+
+bool ModuleSnapshotIOS::Initialize(const dyld_image_info* image) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ name_ = image->imageFilePath;
+ address_ = FromPointerCast<uint64_t>(image->imageLoadAddress);
+ timestamp_ = image->imageFileModDate;
+ return FinishInitialization();
+}
+
+bool ModuleSnapshotIOS::FinishInitialization() {
+#ifndef ARCH_CPU_64_BITS
+#error Only 64-bit Mach-O is supported
+#endif
+ DCHECK(address_);
+ const mach_header_64* header =
+ reinterpret_cast<const mach_header_64*>(address_);
+ const load_command* command =
+ reinterpret_cast<const load_command*>(header + 1);
+ // Make sure that the basic load command structure doesn’t overflow the
+ // space allotted for load commands, as well as iterating through ncmds.
+ for (uint32_t cmd_index = 0, cumulative_cmd_size = 0;
+ cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds;
+ ++cmd_index, cumulative_cmd_size += command->cmdsize) {
+ if (command->cmd == LC_SEGMENT_64) {
+ const segment_command_64* segment =
+ reinterpret_cast<const segment_command_64*>(command);
+ if (strcmp(segment->segname, SEG_TEXT) == 0) {
+ size_ = segment->vmsize;
+ }
+ } else if (command->cmd == LC_ID_DYLIB) {
+ const dylib_command* dylib =
+ reinterpret_cast<const dylib_command*>(command);
+ dylib_version_ = dylib->dylib.current_version;
+ } else if (command->cmd == LC_SOURCE_VERSION) {
+ const source_version_command* source_version =
+ reinterpret_cast<const source_version_command*>(command);
+ source_version_ = source_version->version;
+ } else if (command->cmd == LC_UUID) {
+ const uuid_command* uuid = reinterpret_cast<const uuid_command*>(command);
+ uuid_.InitializeFromBytes(uuid->uuid);
+ }
+
+ command = reinterpret_cast<const load_command*>(
+ reinterpret_cast<const uint8_t*>(command) + command->cmdsize);
+
+ // TODO(justincohen): Warn-able things:
+ // - Bad Mach-O magic (and give up trying to process the module)
+ // - Unrecognized Mach-O type
+ // - No SEG_TEXT
+ // - More than one SEG_TEXT
+ // - More than one LC_ID_DYLIB, LC_SOURCE_VERSION, or LC_UUID
+ // - No LC_ID_DYLIB in a dylib file
+ // - LC_ID_DYLIB in a non-dylib file
+ // And more optional:
+ // - Missing LC_UUID (although it leaves us with a big "?")
+ // - Missing LC_SOURCE_VERSION.
+ }
+
+ filetype_ = header->filetype;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+std::string ModuleSnapshotIOS::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return name_;
+}
+
+uint64_t ModuleSnapshotIOS::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return address_;
+}
+
+uint64_t ModuleSnapshotIOS::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return size_;
+}
+
+time_t ModuleSnapshotIOS::Timestamp() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return timestamp_;
+}
+
+void ModuleSnapshotIOS::FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (filetype_ == MH_DYLIB) {
+ *version_0 = (dylib_version_ & 0xffff0000) >> 16;
+ *version_1 = (dylib_version_ & 0x0000ff00) >> 8;
+ *version_2 = (dylib_version_ & 0x000000ff);
+ *version_3 = 0;
+ } else {
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+ }
+}
+
+void ModuleSnapshotIOS::SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *version_0 = (source_version_ & 0xffff000000000000u) >> 48;
+ *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32;
+ *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16;
+ *version_3 = source_version_ & 0x000000000000ffffu;
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotIOS::GetModuleType() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ switch (filetype_) {
+ case MH_EXECUTE:
+ return kModuleTypeExecutable;
+ case MH_DYLIB:
+ return kModuleTypeSharedLibrary;
+ case MH_DYLINKER:
+ return kModuleTypeDynamicLoader;
+ case MH_BUNDLE:
+ return kModuleTypeLoadableModule;
+ default:
+ return kModuleTypeUnknown;
+ }
+}
+
+void ModuleSnapshotIOS::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *uuid = uuid_;
+ *age = 0;
+}
+
+std::string ModuleSnapshotIOS::DebugFileName() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return base::FilePath(Name()).BaseName().value();
+}
+
+std::vector<uint8_t> ModuleSnapshotIOS::BuildID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<uint8_t>();
+}
+
+std::vector<std::string> ModuleSnapshotIOS::AnnotationsVector() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<std::string>();
+}
+
+std::map<std::string, std::string> ModuleSnapshotIOS::AnnotationsSimpleMap()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::map<std::string, std::string>();
+}
+
+std::vector<AnnotationSnapshot> ModuleSnapshotIOS::AnnotationObjects() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<AnnotationSnapshot>();
+}
+
+std::set<CheckedRange<uint64_t>> ModuleSnapshotIOS::ExtraMemoryRanges() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::set<CheckedRange<uint64_t>>();
+}
+
+std::vector<const UserMinidumpStream*>
+ModuleSnapshotIOS::CustomMinidumpStreams() const {
+ return std::vector<const UserMinidumpStream*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/ios/module_snapshot_ios.h b/snapshot/ios/module_snapshot_ios.h
new file mode 100644
index 0000000..505c08f
--- /dev/null
+++ b/snapshot/ios/module_snapshot_ios.h
@@ -0,0 +1,111 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
+
+#include <mach-o/dyld_images.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/module_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
+//! running (or crashed) process on an iOS system.
+class ModuleSnapshotIOS final : public ModuleSnapshot {
+ public:
+ ModuleSnapshotIOS();
+ ~ModuleSnapshotIOS() override;
+
+ // TODO(justincohen): This function is temporary, and will be broken into two
+ // parts. One to do an in-process dump of all the relevant information, and
+ // two to initialize the snapshot after the in-process dump is loaded.
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] image The mach-o image to be loaded.
+ //!
+ //! \return `true` if the snapshot could be created.
+ bool Initialize(const dyld_image_info* image);
+
+ // TODO(justincohen): This function is temporary, and will be broken into two
+ // parts. One to do an in-process dump of all the relevant information, and
+ // two to initialize the snapshot after the in-process dump is loaded.
+ //! \brief Initializes the object specifically for the dyld module.
+ //!
+ //! \param[in] images The structure containing the necessary dyld information.
+ //!
+ //! \return `true` if the snapshot could be created.
+ bool InitializeDyld(const dyld_all_image_infos* images);
+
+ //! \brief Returns options from the module’s CrashpadInfo structure.
+ //!
+ //! \param[out] options Options set in the module’s CrashpadInfo structure.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ static const dyld_all_image_infos* DyldAllImageInfo();
+
+ // ModuleSnapshot:
+ std::string Name() const override;
+ uint64_t Address() const override;
+ uint64_t Size() const override;
+ time_t Timestamp() const override;
+ void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ ModuleType GetModuleType() const override;
+ void UUIDAndAge(UUID* uuid, uint32_t* age) const override;
+ std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
+ std::vector<std::string> AnnotationsVector() const override;
+ std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+ std::vector<AnnotationSnapshot> AnnotationObjects() const override;
+ std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
+ std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
+
+ private:
+ // Gather the the module information based off of a mach_header_64 |address_|.
+ bool FinishInitialization();
+
+ std::string name_;
+ uint64_t address_;
+ uint64_t size_;
+ time_t timestamp_;
+ uint32_t dylib_version_;
+ uint64_t source_version_;
+ uint32_t filetype_;
+ UUID uuid_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOS);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
diff --git a/snapshot/ios/process_snapshot_ios.cc b/snapshot/ios/process_snapshot_ios.cc
new file mode 100644
index 0000000..f45c6ee
--- /dev/null
+++ b/snapshot/ios/process_snapshot_ios.cc
@@ -0,0 +1,287 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/process_snapshot_ios.h"
+
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/stl_util.h"
+
+namespace {
+
+void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
+ tv->tv_sec = mach.seconds;
+ tv->tv_usec = mach.microseconds;
+}
+
+} // namespace
+
+namespace crashpad {
+
+ProcessSnapshotIOS::ProcessSnapshotIOS()
+ : ProcessSnapshot(),
+ kern_proc_info_(),
+ basic_info_user_time_(),
+ basic_info_system_time_(),
+ thread_times_user_time_(),
+ thread_times_system_time_(),
+ system_(),
+ threads_(),
+ modules_(),
+ exception_(),
+ report_id_(),
+ client_id_(),
+ annotations_simple_map_(),
+ snapshot_time_(),
+ initialized_() {}
+
+ProcessSnapshotIOS::~ProcessSnapshotIOS() {}
+
+bool ProcessSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ // Used by pid, parent pid and snapshot time.
+ int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
+ size_t len = sizeof(kern_proc_info_);
+ if (sysctl(mib, base::size(mib), &kern_proc_info_, &len, nullptr, 0)) {
+ PLOG(ERROR) << "sysctl";
+ return false;
+ }
+
+ // Used by user time and system time.
+ task_basic_info_64 task_basic_info;
+ mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;
+ kern_return_t kr = task_info(mach_task_self(),
+ TASK_BASIC_INFO_64,
+ reinterpret_cast<task_info_t>(&task_basic_info),
+ &task_basic_info_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64";
+ return false;
+ }
+
+ task_thread_times_info_data_t task_thread_times;
+ mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;
+ kr = task_info(mach_task_self(),
+ TASK_THREAD_TIMES_INFO,
+ reinterpret_cast<task_info_t>(&task_thread_times),
+ &task_thread_times_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES";
+ }
+
+ basic_info_user_time_ = task_basic_info.user_time;
+ basic_info_system_time_ = task_basic_info.system_time;
+ thread_times_user_time_ = task_thread_times.user_time;
+ thread_times_system_time_ = task_thread_times.system_time;
+
+ if (gettimeofday(&snapshot_time_, nullptr) != 0) {
+ PLOG(ERROR) << "gettimeofday";
+ return false;
+ }
+
+ system_.Initialize(system_data);
+ InitializeThreads();
+ InitializeModules();
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+void ProcessSnapshotIOS::SetExceptionFromSignal(const siginfo_t* siginfo,
+ const ucontext_t* context) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK(!exception_.get());
+
+ exception_.reset(new internal::ExceptionSnapshotIOS());
+ exception_->InitializeFromSignal(siginfo, context);
+}
+
+void ProcessSnapshotIOS::SetExceptionFromMachException(
+ exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK(!exception_.get());
+
+ exception_.reset(new internal::ExceptionSnapshotIOS());
+ exception_->InitializeFromMachException(behavior,
+ exception_thread,
+ exception,
+ code,
+ code_count,
+ flavor,
+ old_state,
+ old_state_count);
+}
+
+pid_t ProcessSnapshotIOS::ProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_proc.p_pid;
+}
+
+pid_t ProcessSnapshotIOS::ParentProcessID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kern_proc_info_.kp_eproc.e_ppid;
+}
+
+void ProcessSnapshotIOS::SnapshotTime(timeval* snapshot_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *snapshot_time = snapshot_time_;
+}
+
+void ProcessSnapshotIOS::ProcessStartTime(timeval* start_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *start_time = kern_proc_info_.kp_proc.p_starttime;
+}
+
+void ProcessSnapshotIOS::ProcessCPUTimes(timeval* user_time,
+ timeval* system_time) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // Calculate user and system time the same way the kernel does for
+ // getrusage(). See 10.15.0 xnu-6153.11.26/bsd/kern/kern_resource.c calcru().
+ timerclear(user_time);
+ timerclear(system_time);
+
+ MachTimeValueToTimeval(basic_info_user_time_, user_time);
+ MachTimeValueToTimeval(basic_info_system_time_, system_time);
+
+ timeval thread_user_time;
+ MachTimeValueToTimeval(thread_times_user_time_, &thread_user_time);
+ timeval thread_system_time;
+ MachTimeValueToTimeval(thread_times_system_time_, &thread_system_time);
+
+ timeradd(user_time, &thread_user_time, user_time);
+ timeradd(system_time, &thread_system_time, system_time);
+}
+
+void ProcessSnapshotIOS::ReportID(UUID* report_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *report_id = report_id_;
+}
+
+void ProcessSnapshotIOS::ClientID(UUID* client_id) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *client_id = client_id_;
+}
+
+const std::map<std::string, std::string>&
+ProcessSnapshotIOS::AnnotationsSimpleMap() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return annotations_simple_map_;
+}
+
+const SystemSnapshot* ProcessSnapshotIOS::System() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &system_;
+}
+
+std::vector<const ThreadSnapshot*> ProcessSnapshotIOS::Threads() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ThreadSnapshot*> threads;
+ for (const auto& thread : threads_) {
+ threads.push_back(thread.get());
+ }
+ return threads;
+}
+
+std::vector<const ModuleSnapshot*> ProcessSnapshotIOS::Modules() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<const ModuleSnapshot*> modules;
+ for (const auto& module : modules_) {
+ modules.push_back(module.get());
+ }
+ return modules;
+}
+
+std::vector<UnloadedModuleSnapshot> ProcessSnapshotIOS::UnloadedModules()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<UnloadedModuleSnapshot>();
+}
+
+const ExceptionSnapshot* ProcessSnapshotIOS::Exception() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return exception_.get();
+}
+
+std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotIOS::MemoryMap()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<const MemoryMapRegionSnapshot*>();
+}
+
+std::vector<HandleSnapshot> ProcessSnapshotIOS::Handles() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<HandleSnapshot>();
+}
+
+std::vector<const MemorySnapshot*> ProcessSnapshotIOS::ExtraMemory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<const MemorySnapshot*>();
+}
+
+const ProcessMemory* ProcessSnapshotIOS::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return nullptr;
+}
+
+void ProcessSnapshotIOS::InitializeThreads() {
+ mach_msg_type_number_t thread_count = 0;
+ const thread_act_array_t threads =
+ internal::ThreadSnapshotIOS::GetThreads(&thread_count);
+ for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {
+ thread_t thread = threads[thread_index];
+ auto thread_snapshot = std::make_unique<internal::ThreadSnapshotIOS>();
+ if (thread_snapshot->Initialize(thread)) {
+ threads_.push_back(std::move(thread_snapshot));
+ }
+ mach_port_deallocate(mach_task_self(), thread);
+ }
+ // TODO(justincohen): This dealloc above and below needs to move with the
+ // call to task_threads inside internal::ThreadSnapshotIOS::GetThreads.
+ vm_deallocate(mach_task_self(),
+ reinterpret_cast<vm_address_t>(threads),
+ sizeof(thread_t) * thread_count);
+}
+
+void ProcessSnapshotIOS::InitializeModules() {
+ const dyld_all_image_infos* image_infos =
+ internal::ModuleSnapshotIOS::DyldAllImageInfo();
+
+ uint32_t image_count = image_infos->infoArrayCount;
+ const dyld_image_info* image_array = image_infos->infoArray;
+ for (uint32_t image_index = 0; image_index < image_count; ++image_index) {
+ const dyld_image_info* image = &image_array[image_index];
+ auto module = std::make_unique<internal::ModuleSnapshotIOS>();
+ if (module->Initialize(image)) {
+ modules_.push_back(std::move(module));
+ }
+ }
+ auto module = std::make_unique<internal::ModuleSnapshotIOS>();
+ if (module->InitializeDyld(image_infos)) {
+ modules_.push_back(std::move(module));
+ }
+}
+
+} // namespace crashpad
diff --git a/snapshot/ios/process_snapshot_ios.h b/snapshot/ios/process_snapshot_ios.h
new file mode 100644
index 0000000..36b7ba1
--- /dev/null
+++ b/snapshot/ios/process_snapshot_ios.h
@@ -0,0 +1,122 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
+
+#include <sys/sysctl.h>
+
+#include <vector>
+
+#include "snapshot/ios/exception_snapshot_ios.h"
+#include "snapshot/ios/module_snapshot_ios.h"
+#include "snapshot/ios/system_snapshot_ios.h"
+#include "snapshot/ios/thread_snapshot_ios.h"
+#include "snapshot/process_snapshot.h"
+#include "snapshot/thread_snapshot.h"
+#include "snapshot/unloaded_module_snapshot.h"
+
+namespace crashpad {
+
+//! \brief A ProcessSnapshot of a running (or crashed) process running on a
+//! iphoneOS system.
+class ProcessSnapshotIOS final : public ProcessSnapshot {
+ public:
+ ProcessSnapshotIOS();
+ ~ProcessSnapshotIOS() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] system_data A class containing various system data points.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(const IOSSystemDataCollector& system_data);
+
+ //! \brief Initialize exception information from a signal.
+ void SetExceptionFromSignal(const siginfo_t* siginfo,
+ const ucontext_t* context);
+
+ //! \brief Initialize exception information from a Mach exception.
+ void SetExceptionFromMachException(exception_behavior_t behavior,
+ thread_t exception_thread,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t flavor,
+ ConstThreadState old_state,
+ mach_msg_type_number_t old_state_count);
+
+ //! \brief Sets the value to be returned by ClientID().
+ //!
+ //! On iOS, the client ID is under the control of the snapshot producer,
+ //! which may call this method to set the client ID. If this is not done,
+ //! ClientID() will return an identifier consisting entirely of zeroes.
+ void SetClientID(const UUID& client_id) { client_id_ = client_id; }
+
+ //! \brief Sets the value to be returned by ReportID().
+ //!
+ //! On iOS, the crash report ID is under the control of the snapshot
+ //! producer, which may call this method to set the report ID. If this is not
+ //! done, ReportID() will return an identifier consisting entirely of zeroes.
+ void SetReportID(const UUID& report_id) { report_id_ = report_id; }
+
+ // ProcessSnapshot:
+ pid_t ProcessID() const override;
+ pid_t ParentProcessID() const override;
+ void SnapshotTime(timeval* snapshot_time) const override;
+ void ProcessStartTime(timeval* start_time) const override;
+ void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
+ void ReportID(UUID* report_id) const override;
+ void ClientID(UUID* client_id) const override;
+ const std::map<std::string, std::string>& AnnotationsSimpleMap()
+ const override;
+ const SystemSnapshot* System() const override;
+ std::vector<const ThreadSnapshot*> Threads() const override;
+ std::vector<const ModuleSnapshot*> Modules() const override;
+ std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
+ const ExceptionSnapshot* Exception() const override;
+ std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
+ std::vector<HandleSnapshot> Handles() const override;
+ std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
+
+ private:
+ // Initializes modules_ on behalf of Initialize().
+ void InitializeModules();
+
+ // Initializes threads_ on behalf of Initialize().
+ void InitializeThreads();
+
+ kinfo_proc kern_proc_info_;
+ time_value_t basic_info_user_time_;
+ time_value_t basic_info_system_time_;
+ time_value_t thread_times_user_time_;
+ time_value_t thread_times_system_time_;
+ internal::SystemSnapshotIOS system_;
+ std::vector<std::unique_ptr<internal::ThreadSnapshotIOS>> threads_;
+ std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
+ std::unique_ptr<internal::ExceptionSnapshotIOS> exception_;
+ UUID report_id_;
+ UUID client_id_;
+ std::map<std::string, std::string> annotations_simple_map_;
+ timeval snapshot_time_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOS);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
diff --git a/snapshot/ios/system_snapshot_ios.cc b/snapshot/ios/system_snapshot_ios.cc
new file mode 100644
index 0000000..57c686e
--- /dev/null
+++ b/snapshot/ios/system_snapshot_ios.cc
@@ -0,0 +1,224 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/system_snapshot_ios.h"
+
+#include <mach/mach.h>
+#include <stddef.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/posix/timezone.h"
+#include "util/mac/mac_util.h"
+#include "util/numeric/in_range_cast.h"
+
+namespace crashpad {
+
+namespace internal {
+
+SystemSnapshotIOS::SystemSnapshotIOS()
+ : SystemSnapshot(),
+ os_version_build_(),
+ machine_description_(),
+ os_version_major_(0),
+ os_version_minor_(0),
+ os_version_bugfix_(0),
+ active_(0),
+ inactive_(0),
+ wired_(0),
+ free_(0),
+ cpu_count_(0),
+ cpu_vendor_(),
+ dst_status_(),
+ standard_offset_seconds_(0),
+ daylight_offset_seconds_(0),
+ standard_name_(),
+ daylight_name_(),
+ initialized_() {}
+
+SystemSnapshotIOS::~SystemSnapshotIOS() {}
+
+void SystemSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ system_data.OSVersion(&os_version_major_,
+ &os_version_minor_,
+ &os_version_bugfix_,
+ &os_version_build_);
+ machine_description_ = system_data.MachineDescription();
+ cpu_count_ = system_data.ProcessorCount();
+ cpu_vendor_ = system_data.CPUVendor();
+ if (system_data.HasDaylightSavingTime()) {
+ dst_status_ = system_data.IsDaylightSavingTime()
+ ? SystemSnapshot::kObservingDaylightSavingTime
+ : SystemSnapshot::kObservingStandardTime;
+ } else {
+ dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime;
+ }
+ standard_offset_seconds_ = system_data.StandardOffsetSeconds();
+ daylight_offset_seconds_ = system_data.DaylightOffsetSeconds();
+ standard_name_ = system_data.StandardName();
+ daylight_name_ = system_data.DaylightName();
+
+ // Currently unused by minidump.
+ vm_size_t page_size;
+ host_page_size(mach_host_self(), &page_size);
+ mach_msg_type_number_t host_size =
+ sizeof(vm_statistics_data_t) / sizeof(integer_t);
+ vm_statistics_data_t vm_stat;
+ kern_return_t kr = host_statistics(mach_host_self(),
+ HOST_VM_INFO,
+ reinterpret_cast<host_info_t>(&vm_stat),
+ &host_size);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "host_statistics";
+ }
+ active_ = vm_stat.active_count * page_size;
+ inactive_ = vm_stat.inactive_count * page_size;
+ wired_ = vm_stat.wire_count * page_size;
+ free_ = vm_stat.free_count * page_size;
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+}
+
+CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_64)
+ return kCPUArchitectureX86_64;
+#elif defined(ARCH_CPU_ARM64)
+ return kCPUArchitectureARM64;
+#endif
+}
+
+uint32_t SystemSnapshotIOS::CPURevision() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but
+ // consider recording this for X86_64 only.
+ return 0;
+}
+
+uint8_t SystemSnapshotIOS::CPUCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return cpu_count_;
+}
+
+std::string SystemSnapshotIOS::CPUVendor() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return cpu_vendor_;
+}
+
+void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz,
+ uint64_t* max_hz) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64,
+ // but consider recording this for X86_64 only.
+ *current_hz = 0;
+ *max_hz = 0;
+}
+
+uint32_t SystemSnapshotIOS::CPUX86Signature() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider recording this for X86_64 only.
+ return 0;
+}
+
+uint64_t SystemSnapshotIOS::CPUX86Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider recording this for X86_64 only.
+ return 0;
+}
+
+uint64_t SystemSnapshotIOS::CPUX86ExtendedFeatures() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider recording this for X86_64 only.
+ return 0;
+}
+
+uint32_t SystemSnapshotIOS::CPUX86Leaf7Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider recording this for X86_64 only.
+ return 0;
+}
+
+bool SystemSnapshotIOS::CPUX86SupportsDAZ() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider recording this for X86_64 only.
+ return false;
+}
+
+SystemSnapshot::OperatingSystem SystemSnapshotIOS::GetOperatingSystem() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return kOperatingSystemIOS;
+}
+
+bool SystemSnapshotIOS::OSServer() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return false;
+}
+
+void SystemSnapshotIOS::OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *major = os_version_major_;
+ *minor = os_version_minor_;
+ *bugfix = os_version_bugfix_;
+ build->assign(os_version_build_);
+}
+
+std::string SystemSnapshotIOS::OSVersionFull() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return base::StringPrintf("%d.%d.%d %s",
+ os_version_major_,
+ os_version_minor_,
+ os_version_bugfix_,
+ os_version_build_.c_str());
+}
+
+std::string SystemSnapshotIOS::MachineDescription() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return machine_description_;
+}
+
+bool SystemSnapshotIOS::NXEnabled() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // TODO(justincohen): Consider using kern.nx when available (pre-iOS 13,
+ // pre-OS X 10.15). Otherwise the bit is always enabled.
+ return true;
+}
+
+void SystemSnapshotIOS::TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *dst_status = dst_status_;
+ *standard_offset_seconds = standard_offset_seconds_;
+ *daylight_offset_seconds = daylight_offset_seconds_;
+ standard_name->assign(standard_name_);
+ daylight_name->assign(daylight_name_);
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/ios/system_snapshot_ios.h b/snapshot/ios/system_snapshot_ios.h
new file mode 100644
index 0000000..a38de4e
--- /dev/null
+++ b/snapshot/ios/system_snapshot_ios.h
@@ -0,0 +1,94 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "snapshot/system_snapshot.h"
+#include "util/ios/ios_system_data_collector.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+namespace internal {
+
+//! \brief A SystemSnapshot of the running system, when the system runs iOS.
+class SystemSnapshotIOS final : public SystemSnapshot {
+ public:
+ SystemSnapshotIOS();
+ ~SystemSnapshotIOS() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] system_data A class containing various system data points.
+ void Initialize(const IOSSystemDataCollector& system_data);
+
+ // SystemSnapshot:
+
+ CPUArchitecture GetCPUArchitecture() const override;
+ uint32_t CPURevision() const override;
+ uint8_t CPUCount() const override;
+ std::string CPUVendor() const override;
+ void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;
+ uint32_t CPUX86Signature() const override;
+ uint64_t CPUX86Features() const override;
+ uint64_t CPUX86ExtendedFeatures() const override;
+ uint32_t CPUX86Leaf7Features() const override;
+ bool CPUX86SupportsDAZ() const override;
+ OperatingSystem GetOperatingSystem() const override;
+ bool OSServer() const override;
+ void OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const override;
+ std::string OSVersionFull() const override;
+ bool NXEnabled() const override;
+ std::string MachineDescription() const override;
+ void TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const override;
+
+ private:
+ std::string os_version_build_;
+ std::string machine_description_;
+ int os_version_major_;
+ int os_version_minor_;
+ int os_version_bugfix_;
+ uint64_t active_;
+ uint64_t inactive_;
+ uint64_t wired_;
+ uint64_t free_;
+ int cpu_count_;
+ std::string cpu_vendor_;
+ DaylightSavingTimeStatus dst_status_;
+ int standard_offset_seconds_;
+ int daylight_offset_seconds_;
+ std::string standard_name_;
+ std::string daylight_name_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOS);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
diff --git a/snapshot/ios/thread_snapshot_ios.cc b/snapshot/ios/thread_snapshot_ios.cc
new file mode 100644
index 0000000..a52be51
--- /dev/null
+++ b/snapshot/ios/thread_snapshot_ios.cc
@@ -0,0 +1,477 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/ios/thread_snapshot_ios.h"
+
+#include "base/mac/mach_logging.h"
+#include "snapshot/mac/cpu_context_mac.h"
+
+namespace {
+
+#if defined(ARCH_CPU_X86_64)
+const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;
+const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64;
+const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64;
+#elif defined(ARCH_CPU_ARM64)
+const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
+const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+#endif
+
+kern_return_t MachVMRegionRecurseDeepest(task_t task,
+ vm_address_t* address,
+ vm_size_t* size,
+ natural_t* depth,
+ vm_prot_t* protection,
+ unsigned int* user_tag) {
+ vm_region_submap_short_info_64 submap_info;
+ mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+ while (true) {
+ kern_return_t kr = vm_region_recurse_64(
+ task,
+ address,
+ size,
+ depth,
+ reinterpret_cast<vm_region_recurse_info_t>(&submap_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ if (!submap_info.is_submap) {
+ *protection = submap_info.protection;
+ *user_tag = submap_info.user_tag;
+ return KERN_SUCCESS;
+ }
+
+ ++*depth;
+ }
+}
+
+//! \brief Adjusts the region for the red zone, if the ABI requires one.
+//!
+//! This method performs red zone calculation for CalculateStackRegion(). Its
+//! parameters are local variables used within that method, and may be
+//! modified as needed.
+//!
+//! Where a red zone is required, the region of memory captured for a thread’s
+//! stack will be extended to include the red zone below the stack pointer,
+//! provided that such memory is mapped, readable, and has the correct user
+//! tag value. If these conditions cannot be met fully, as much of the red
+//! zone will be captured as is possible while meeting these conditions.
+//!
+//! \param[in,out] start_address The base address of the region to begin
+//! capturing stack memory from. On entry, \a start_address is the stack
+//! pointer. On return, \a start_address may be decreased to encompass a
+//! red zone.
+//! \param[in,out] region_base The base address of the region that contains
+//! stack memory. This is distinct from \a start_address in that \a
+//! region_base will be page-aligned. On entry, \a region_base is the
+//! base address of a region that contains \a start_address. On return,
+//! if \a start_address is decremented and is outside of the region
+//! originally described by \a region_base, \a region_base will also be
+//! decremented appropriately.
+//! \param[in,out] region_size The size of the region that contains stack
+//! memory. This region begins at \a region_base. On return, if \a
+//! region_base is decremented, \a region_size will be incremented
+//! appropriately.
+//! \param[in] user_tag The Mach VM system’s user tag for the region described
+//! by the initial values of \a region_base and \a region_size. The red
+//! zone will only be allowed to extend out of the region described by
+//! these initial values if the user tag is appropriate for stack memory
+//! and the expanded region has the same user tag value.
+void LocateRedZone(vm_address_t* const start_address,
+ vm_address_t* const region_base,
+ vm_address_t* const region_size,
+ const unsigned int user_tag) {
+ // x86_64 has a red zone. See AMD64 ABI 0.99.8,
+ // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19,
+ // section 3.2.2, “The Stack Frame”.
+ // So does ARM64,
+ // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
+ // section "Red Zone".
+ constexpr vm_size_t kRedZoneSize = 128;
+ vm_address_t red_zone_base =
+ *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;
+ bool red_zone_ok = false;
+ if (red_zone_base >= *region_base) {
+ // The red zone is within the region already discovered.
+ red_zone_ok = true;
+ } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {
+ // Probe to see if there’s a region immediately below the one already
+ // discovered.
+ vm_address_t red_zone_region_base = red_zone_base;
+ vm_size_t red_zone_region_size;
+ natural_t red_zone_depth = 0;
+ vm_prot_t red_zone_protection;
+ unsigned int red_zone_user_tag;
+ kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
+ &red_zone_region_base,
+ &red_zone_region_size,
+ &red_zone_depth,
+ &red_zone_protection,
+ &red_zone_user_tag);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(INFO, kr) << "vm_region_recurse";
+ *start_address = *region_base;
+ } else if (red_zone_region_base + red_zone_region_size == *region_base &&
+ (red_zone_protection & VM_PROT_READ) != 0 &&
+ red_zone_user_tag == user_tag) {
+ // The region containing the red zone is immediately below the region
+ // already found, it’s readable (not the guard region), and it has the
+ // same user tag as the region already found, so merge them.
+ red_zone_ok = true;
+ *region_base -= red_zone_region_size;
+ *region_size += red_zone_region_size;
+ }
+ }
+
+ if (red_zone_ok) {
+ // Begin capturing from the base of the red zone (but not the entire
+ // region that encompasses the red zone).
+ *start_address = red_zone_base;
+ } else {
+ // The red zone would go lower into another region in memory, but no
+ // region was found. Memory can only be captured to an address as low as
+ // the base address of the region already found.
+ *start_address = *region_base;
+ }
+}
+
+//! \brief Calculates the base address and size of the region used as a
+//! thread’s stack.
+//!
+//! The region returned by this method may be formed by merging multiple
+//! adjacent regions in a process’ memory map if appropriate. The base address
+//! of the returned region may be lower than the \a stack_pointer passed in
+//! when the ABI mandates a red zone below the stack pointer.
+//!
+//! \param[in] stack_pointer The stack pointer, referring to the top (lowest
+//! address) of a thread’s stack.
+//! \param[out] stack_region_size The size of the memory region used as the
+//! thread’s stack.
+//!
+//! \return The base address (lowest address) of the memory region used as the
+//! thread’s stack.
+vm_address_t CalculateStackRegion(vm_address_t stack_pointer,
+ vm_size_t* stack_region_size) {
+ // For pthreads, it may be possible to compute the stack region based on the
+ // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct
+ // for a thread can be located at TSD slot 0, or the known offsets of
+ // stackaddr and stacksize from the TSD area could be used.
+ vm_address_t region_base = stack_pointer;
+ vm_size_t region_size;
+ natural_t depth = 0;
+ vm_prot_t protection;
+ unsigned int user_tag;
+ kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),
+ ®ion_base,
+ ®ion_size,
+ &depth,
+ &protection,
+ &user_tag);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(INFO, kr) << "mach_vm_region_recurse";
+ *stack_region_size = 0;
+ return 0;
+ }
+
+ if (region_base > stack_pointer) {
+ // There’s nothing mapped at the stack pointer’s address. Something may have
+ // trashed the stack pointer. Note that this shouldn’t happen for a normal
+ // stack guard region violation because the guard region is mapped but has
+ // VM_PROT_NONE protection.
+ *stack_region_size = 0;
+ return 0;
+ }
+
+ vm_address_t start_address = stack_pointer;
+
+ if ((protection & VM_PROT_READ) == 0) {
+ // If the region isn’t readable, the stack pointer probably points to the
+ // guard region. Don’t include it as part of the stack, and don’t include
+ // anything at any lower memory address. The code below may still possibly
+ // find the real stack region at a memory address higher than this region.
+ start_address = region_base + region_size;
+ } else {
+ // If the ABI requires a red zone, adjust the region to include it if
+ // possible.
+ LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag);
+
+ // Regardless of whether the ABI requires a red zone, capture up to
+ // kExtraCaptureSize additional bytes of stack, but only if present in the
+ // region that was already found.
+ constexpr vm_size_t kExtraCaptureSize = 128;
+ start_address = std::max(start_address >= kExtraCaptureSize
+ ? start_address - kExtraCaptureSize
+ : start_address,
+ region_base);
+
+ // Align start_address to a 16-byte boundary, which can help readers by
+ // ensuring that data is aligned properly. This could page-align instead,
+ // but that might be wasteful.
+ constexpr vm_size_t kDesiredAlignment = 16;
+ start_address &= ~(kDesiredAlignment - 1);
+ DCHECK_GE(start_address, region_base);
+ }
+
+ region_size -= (start_address - region_base);
+ region_base = start_address;
+
+ vm_size_t total_region_size = region_size;
+
+ // The stack region may have gotten split up into multiple abutting regions.
+ // Try to coalesce them. This frequently happens for the main thread’s stack
+ // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region
+ // is split up due to an mprotect() or vm_protect() call.
+ //
+ // Stack regions created by the kernel and the pthreads library will be marked
+ // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions
+ // with the same tag should find an entire stack region. Checking that the
+ // protection on individual regions is not VM_PROT_NONE should guarantee that
+ // this algorithm doesn’t collect map entries belonging to another thread’s
+ // stack: well-behaved stacks (such as those created by the kernel and the
+ // pthreads library) have VM_PROT_NONE guard regions at their low-address
+ // ends.
+ //
+ // Other stack regions may not be so well-behaved and thus if user_tag is not
+ // VM_MEMORY_STACK, the single region that was found is used as-is without
+ // trying to merge it with other adjacent regions.
+ if (user_tag == VM_MEMORY_STACK) {
+ vm_address_t try_address = region_base;
+ vm_address_t original_try_address;
+
+ while (try_address += region_size,
+ original_try_address = try_address,
+ (kr = MachVMRegionRecurseDeepest(mach_task_self(),
+ &try_address,
+ ®ion_size,
+ &depth,
+ &protection,
+ &user_tag) == KERN_SUCCESS) &&
+ try_address == original_try_address &&
+ (protection & VM_PROT_READ) != 0 &&
+ user_tag == VM_MEMORY_STACK) {
+ total_region_size += region_size;
+ }
+
+ if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {
+ // Tolerate KERN_INVALID_ADDRESS because it will be returned when there
+ // are no more regions in the map at or above the specified |try_address|.
+ MACH_LOG(INFO, kr) << "vm_region_recurse";
+ }
+ }
+
+ *stack_region_size = total_region_size;
+ return region_base;
+}
+
+} // namespace
+
+namespace crashpad {
+namespace internal {
+
+ThreadSnapshotIOS::ThreadSnapshotIOS()
+ : ThreadSnapshot(),
+ context_(),
+ stack_(),
+ thread_id_(0),
+ thread_specific_data_address_(0),
+ suspend_count_(0),
+ priority_(0),
+ initialized_() {}
+
+ThreadSnapshotIOS::~ThreadSnapshotIOS() {}
+
+// static
+thread_act_array_t ThreadSnapshotIOS::GetThreads(
+ mach_msg_type_number_t* count) {
+ thread_act_array_t threads;
+ kern_return_t kr = task_threads(mach_task_self(), &threads, count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "task_threads";
+ }
+ return threads;
+}
+
+bool ThreadSnapshotIOS::Initialize(thread_t thread) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ // TODO(justincohen): Move the following thread_get_state, thread_get_info,
+ // thread_policy_get and CalculateStackRegion to the serialize-on-read
+ // section.
+ thread_basic_info basic_info;
+ thread_precedence_policy precedence;
+ vm_size_t stack_region_size;
+ vm_address_t stack_region_address;
+#if defined(ARCH_CPU_X86_64)
+ x86_thread_state64_t thread_state;
+ x86_float_state64_t float_state;
+ x86_debug_state64_t debug_state;
+ mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
+ mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;
+ mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT;
+#elif defined(ARCH_CPU_ARM64)
+ arm_thread_state64_t thread_state;
+ arm_neon_state64_t float_state;
+ mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
+ mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
+#endif
+
+ kern_return_t kr =
+ thread_get_state(thread,
+ kThreadStateFlavor,
+ reinterpret_cast<thread_state_t>(&thread_state),
+ &thread_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")";
+ }
+
+ kr = thread_get_state(thread,
+ kFloatStateFlavor,
+ reinterpret_cast<thread_state_t>(&float_state),
+ &float_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
+ }
+
+#if defined(ARCH_CPU_X86_64)
+ kr = thread_get_state(thread,
+ kDebugStateFlavor,
+ reinterpret_cast<thread_state_t>(&debug_state),
+ &debug_state_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
+ }
+#endif
+
+ mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
+ kr = thread_info(thread,
+ THREAD_BASIC_INFO,
+ reinterpret_cast<thread_info_t>(&basic_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)";
+ }
+
+ thread_identifier_info identifier_info;
+ count = THREAD_IDENTIFIER_INFO_COUNT;
+ kr = thread_info(thread,
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&identifier_info),
+ &count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)";
+ }
+
+ count = THREAD_PRECEDENCE_POLICY_COUNT;
+ boolean_t get_default = FALSE;
+ kr = thread_policy_get(thread,
+ THREAD_PRECEDENCE_POLICY,
+ reinterpret_cast<thread_policy_t>(&precedence),
+ &count,
+ &get_default);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "thread_policy_get";
+ }
+
+#if defined(ARCH_CPU_X86_64)
+ vm_address_t stack_pointer = thread_state.__rsp;
+#elif defined(ARCH_CPU_ARM64)
+ vm_address_t stack_pointer = thread_state.__sp;
+#endif
+ stack_region_address =
+ CalculateStackRegion(stack_pointer, &stack_region_size);
+
+ // TODO(justincohen): Assume the following will fill in snapshot data from
+ // a deserialized object.
+ thread_id_ = identifier_info.thread_id;
+ suspend_count_ = basic_info.suspend_count;
+ priority_ = precedence.importance;
+
+ // thread_identifier_info::thread_handle contains the base of the
+ // thread-specific data area, which on x86 and x86_64 is the thread’s base
+ // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
+ // thread_info_internal() gets the value from
+ // machine_thread::cthread_self, which is the same value used to set the
+ // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
+ // act_machine_switch_pcb().
+ //
+ // On ARM64 10.15.0 xnu-6153.11.26/osfmk/kern/thread.c, it sets
+ // thread_identifier_info_t::thread_handle to
+ // thread->machine.cthread_self, which is set to tsd_base in
+ // osfmk/arm64/pcb.c.
+ thread_specific_data_address_ = identifier_info.thread_handle;
+ stack_.Initialize(stack_region_address, stack_region_size);
+
+#if defined(ARCH_CPU_X86_64)
+ context_.architecture = kCPUArchitectureX86_64;
+ context_.x86_64 = &context_x86_64_;
+ InitializeCPUContextX86_64(&context_x86_64_,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &thread_state,
+ &float_state,
+ &debug_state);
+#elif defined(ARCH_CPU_ARM64)
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_arm64_;
+ InitializeCPUContextARM64(&context_arm64_,
+ THREAD_STATE_NONE,
+ nullptr,
+ 0,
+ &thread_state,
+ &float_state);
+#endif
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+const CPUContext* ThreadSnapshotIOS::Context() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &context_;
+}
+
+const MemorySnapshot* ThreadSnapshotIOS::Stack() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &stack_;
+}
+
+uint64_t ThreadSnapshotIOS::ThreadID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_id_;
+}
+
+int ThreadSnapshotIOS::SuspendCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return suspend_count_;
+}
+
+int ThreadSnapshotIOS::Priority() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return priority_;
+}
+
+uint64_t ThreadSnapshotIOS::ThreadSpecificDataAddress() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return thread_specific_data_address_;
+}
+
+std::vector<const MemorySnapshot*> ThreadSnapshotIOS::ExtraMemory() const {
+ return std::vector<const MemorySnapshot*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/ios/thread_snapshot_ios.h b/snapshot/ios/thread_snapshot_ios.h
new file mode 100644
index 0000000..978a818
--- /dev/null
+++ b/snapshot/ios/thread_snapshot_ios.h
@@ -0,0 +1,77 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
+#define CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/ios/memory_snapshot_ios.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ThreadSnapshot of a thread on an iOS system.
+class ThreadSnapshotIOS final : public ThreadSnapshot {
+ public:
+ ThreadSnapshotIOS();
+ ~ThreadSnapshotIOS() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \brief thread The Mach thread used to initialize this object.
+ bool Initialize(thread_t thread);
+
+ //! \brief Returns an array of thread_t threads.
+ //!
+ //! \param[out] count The number of threads returned.
+ //!
+ //! \return An array of of size \a count threads.
+ static thread_act_array_t GetThreads(mach_msg_type_number_t* count);
+
+ // ThreadSnapshot:
+ const CPUContext* Context() const override;
+ const MemorySnapshot* Stack() const override;
+ uint64_t ThreadID() const override;
+ int SuspendCount() const override;
+ int Priority() const override;
+ uint64_t ThreadSpecificDataAddress() const override;
+ std::vector<const MemorySnapshot*> ExtraMemory() const override;
+
+ private:
+#if defined(ARCH_CPU_X86_64)
+ CPUContextX86_64 context_x86_64_;
+#elif defined(ARCH_CPU_ARM64)
+ CPUContextARM64 context_arm64_;
+#else
+#error Port.
+#endif // ARCH_CPU_X86_64
+ CPUContext context_;
+ MemorySnapshotIOS stack_;
+ uint64_t thread_id_;
+ uint64_t thread_specific_data_address_;
+ int suspend_count_;
+ int priority_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOS);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
diff --git a/snapshot/linux/cpu_context_linux.cc b/snapshot/linux/cpu_context_linux.cc
index ebf9d7e..8464a5a 100644
--- a/snapshot/linux/cpu_context_linux.cc
+++ b/snapshot/linux/cpu_context_linux.cc
@@ -17,6 +17,8 @@
#include <stddef.h>
#include <string.h>
+#include <limits>
+
#include "base/logging.h"
namespace crashpad {
@@ -62,7 +64,6 @@
context->dr5 = 0;
context->dr6 = 0;
context->dr7 = 0;
-
}
void InitializeCPUContextX86(const SignalThreadContext32& thread_context,
@@ -241,7 +242,14 @@
memcpy(context->regs, thread_context.regs, sizeof(context->regs));
context->sp = thread_context.sp;
context->pc = thread_context.pc;
- context->pstate = thread_context.pstate;
+ // Linux seems to only be putting the SPSR register in its "pstate" field.
+ // https://elixir.bootlin.com/linux/latest/source/arch/arm64/include/uapi/asm/ptrace.h
+ if (thread_context.pstate >
+ std::numeric_limits<decltype(context->spsr)>::max()) {
+ LOG(WARNING) << "pstate truncation: we only expect the SPSR bits to be set "
+ "in the pstate";
+ }
+ context->spsr = static_cast<decltype(context->spsr)>(thread_context.pstate);
memset(&context->fpsimd, 0, sizeof(context->fpsimd));
context->fpsr = 0;
diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h
index 37fbc43..9f46a48 100644
--- a/snapshot/linux/cpu_context_linux.h
+++ b/snapshot/linux/cpu_context_linux.h
@@ -170,7 +170,7 @@
memcpy(&context->fpregs, &float_context.fpregs, sizeof(context->fpregs));
context->fpcsr = float_context.fpcsr;
context->fir = float_context.fpu_id;
-};
+}
#endif // ARCH_CPU_MIPS_FAMILY || DOXYGEN
diff --git a/snapshot/linux/debug_rendezvous_test.cc b/snapshot/linux/debug_rendezvous_test.cc
index 431e2bb..be22c90 100644
--- a/snapshot/linux/debug_rendezvous_test.cc
+++ b/snapshot/linux/debug_rendezvous_test.cc
@@ -37,28 +37,13 @@
#include "util/process/process_memory_range.h"
#if defined(OS_ANDROID)
-#include <sys/system_properties.h>
+#include <android/api-level.h>
#endif
namespace crashpad {
namespace test {
namespace {
-#if defined(OS_ANDROID)
-int AndroidRuntimeAPI() {
- char api_string[PROP_VALUE_MAX];
- int length = __system_property_get("ro.build.version.sdk", api_string);
- if (length <= 0) {
- return -1;
- }
-
- int api_level;
- bool success =
- base::StringToInt(base::StringPiece(api_string, length), &api_level);
- return success ? api_level : -1;
-}
-#endif // OS_ANDROID
-
void TestAgainstTarget(PtraceConnection* connection) {
// Use ElfImageReader on the main executable which can tell us the debug
// address. glibc declares the symbol _r_debug in link.h which we can use to
@@ -74,10 +59,10 @@
const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping);
- std::vector<const MemoryMap::Mapping*> exe_mappings =
- mappings.FindFilePossibleMmapStarts(*phdr_mapping);
- ASSERT_EQ(exe_mappings.size(), 1u);
- LinuxVMAddress elf_address = exe_mappings[0]->range.Base();
+
+ auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping);
+ ASSERT_EQ(exe_mappings->Count(), 1u);
+ LinuxVMAddress elf_address = exe_mappings->Next()->range.Base();
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
@@ -94,7 +79,7 @@
ASSERT_TRUE(debug.Initialize(range, debug_address));
#if defined(OS_ANDROID)
- const int android_runtime_api = AndroidRuntimeAPI();
+ const int android_runtime_api = android_get_device_api_level();
ASSERT_GE(android_runtime_api, 1);
EXPECT_NE(debug.Executable()->name.find("crashpad_snapshot_test"),
@@ -143,13 +128,13 @@
mappings.FindMapping(module.dynamic_array);
ASSERT_TRUE(dyn_mapping);
- std::vector<const MemoryMap::Mapping*> possible_mappings =
- mappings.FindFilePossibleMmapStarts(*dyn_mapping);
- ASSERT_GE(possible_mappings.size(), 1u);
+ auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping);
+ ASSERT_GE(possible_mappings->Count(), 1u);
std::unique_ptr<ElfImageReader> module_reader;
const MemoryMap::Mapping* module_mapping = nullptr;
- for (const auto mapping : possible_mappings) {
+ const MemoryMap::Mapping* mapping = nullptr;
+ while ((mapping = possible_mappings->Next())) {
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(range, mapping->range.Base()) &&
diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc
index 4256f94..cd40b3b 100644
--- a/snapshot/linux/exception_snapshot_linux.cc
+++ b/snapshot/linux/exception_snapshot_linux.cc
@@ -126,7 +126,7 @@
context_.arm = &context_union_.arm;
CPUContextARM* dest_context = context_.arm;
- ProcessMemory* memory = reader->Memory();
+ const ProcessMemory* memory = reader->Memory();
LinuxVMAddress gprs_address =
context_address + offsetof(UContext<ContextTraits32>, mcontext32) +
@@ -203,7 +203,7 @@
context_.arm64 = &context_union_.arm64;
CPUContextARM64* dest_context = context_.arm64;
- ProcessMemory* memory = reader->Memory();
+ const ProcessMemory* memory = reader->Memory();
LinuxVMAddress gprs_address =
context_address + offsetof(UContext<ContextTraits64>, mcontext64) +
@@ -274,7 +274,7 @@
static bool ReadContext(ProcessReaderLinux* reader,
LinuxVMAddress context_address,
typename Traits::CPUContext* dest_context) {
- ProcessMemory* memory = reader->Memory();
+ const ProcessMemory* memory = reader->Memory();
LinuxVMAddress gregs_address = context_address +
offsetof(UContext<Traits>, mcontext) +
diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc
index df9ad9e..e4ff1ab 100644
--- a/snapshot/linux/exception_snapshot_linux_test.cc
+++ b/snapshot/linux/exception_snapshot_linux_test.cc
@@ -23,6 +23,7 @@
#include "base/bit_cast.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "snapshot/cpu_architecture.h"
@@ -170,7 +171,7 @@
test_context->vfp.head.magic = VFP_MAGIC;
test_context->vfp.head.size = sizeof(test_context->vfp);
memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context));
- for (size_t reg = 0; reg < arraysize(test_context->vfp.context.vfp.fpregs);
+ for (size_t reg = 0; reg < base::size(test_context->vfp.context.vfp.fpregs);
++reg) {
test_context->vfp.context.vfp.fpregs[reg] = reg;
}
@@ -218,7 +219,7 @@
void InitializeContext(NativeCPUContext* context) {
memset(context, 'x', sizeof(*context));
- for (size_t index = 0; index < arraysize(context->uc_mcontext.regs);
+ for (size_t index = 0; index < base::size(context->uc_mcontext.regs);
++index) {
context->uc_mcontext.regs[index] = index;
}
@@ -237,7 +238,7 @@
test_context->fpsimd.head.size = sizeof(test_context->fpsimd);
test_context->fpsimd.fpsr = 1;
test_context->fpsimd.fpcr = 2;
- for (size_t reg = 0; reg < arraysize(test_context->fpsimd.vregs); ++reg) {
+ for (size_t reg = 0; reg < base::size(test_context->fpsimd.vregs); ++reg) {
test_context->fpsimd.vregs[reg] = reg;
}
@@ -254,7 +255,7 @@
0);
EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp);
EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc);
- EXPECT_EQ(actual.arm64->pstate, expected.uc_mcontext.pstate);
+ EXPECT_EQ(actual.arm64->spsr, expected.uc_mcontext.pstate);
auto test_context = reinterpret_cast<const TestCoprocessorContext*>(
expected.uc_mcontext.__reserved);
@@ -270,7 +271,7 @@
using NativeCPUContext = ucontext_t;
void InitializeContext(NativeCPUContext* context) {
- for (size_t reg = 0; reg < arraysize(context->uc_mcontext.gregs); ++reg) {
+ for (size_t reg = 0; reg < base::size(context->uc_mcontext.gregs); ++reg) {
context->uc_mcontext.gregs[reg] = reg;
}
memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs));
@@ -285,7 +286,7 @@
#define CPU_ARCH_NAME mips64
#endif
- for (size_t reg = 0; reg < arraysize(expected.uc_mcontext.gregs); ++reg) {
+ for (size_t reg = 0; reg < base::size(expected.uc_mcontext.gregs); ++reg) {
EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]);
}
diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc
index 33d4f92..b96abfe 100644
--- a/snapshot/linux/process_reader_linux.cc
+++ b/snapshot/linux/process_reader_linux.cc
@@ -17,7 +17,6 @@
#include <elf.h>
#include <errno.h>
#include <sched.h>
-#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
@@ -25,13 +24,14 @@
#include <algorithm>
#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "snapshot/linux/debug_rendezvous.h"
-#include "util/file/directory_reader.h"
#include "util/linux/auxiliary_vector.h"
#include "util/linux/proc_stat_reader.h"
-#include "util/misc/as_underlying_type.h"
+
+#if defined(OS_ANDROID)
+#include <android/api-level.h>
+#endif
namespace crashpad {
@@ -64,31 +64,36 @@
return false;
}
+ // TODO(jperaza): Collect scheduling priorities via the broker when they can't
+ // be collected directly.
+ have_priorities = false;
+
// TODO(jperaza): Starting with Linux 3.14, scheduling policy, static
// priority, and nice value can be collected all in one call with
// sched_getattr().
int res = sched_getscheduler(tid);
if (res < 0) {
- PLOG(ERROR) << "sched_getscheduler";
- return false;
+ PLOG(WARNING) << "sched_getscheduler";
+ return true;
}
sched_policy = res;
sched_param param;
if (sched_getparam(tid, ¶m) != 0) {
- PLOG(ERROR) << "sched_getparam";
- return false;
+ PLOG(WARNING) << "sched_getparam";
+ return true;
}
static_priority = param.sched_priority;
errno = 0;
res = getpriority(PRIO_PROCESS, tid);
if (res == -1 && errno) {
- PLOG(ERROR) << "getpriority";
- return false;
+ PLOG(WARNING) << "getpriority";
+ return true;
}
nice_value = res;
+ have_priorities = true;
return true;
}
@@ -236,7 +241,7 @@
for (const Thread& thread : threads_) {
ProcStatReader stat;
- if (!stat.Initialize(thread.tid)) {
+ if (!stat.Initialize(connection_, thread.tid)) {
return false;
}
@@ -275,6 +280,81 @@
return modules_;
}
+void ProcessReaderLinux::InitializeAbortMessage() {
+#if defined(OS_ANDROID)
+ const MemoryMap::Mapping* mapping =
+ memory_map_.FindMappingWithName("[anon:abort message]");
+ if (!mapping) {
+ return;
+ }
+
+ if (is_64_bit_) {
+ ReadAbortMessage<true>(mapping);
+ } else {
+ ReadAbortMessage<false>(mapping);
+ }
+#endif
+}
+
+#if defined(OS_ANDROID)
+
+// These structure definitions and the magic numbers below were copied from
+// bionic/libc/bionic/android_set_abort_message.cpp
+
+template <bool is64Bit>
+struct abort_msg_t {
+ uint32_t size;
+ char msg[0];
+};
+
+template <>
+struct abort_msg_t<true> {
+ uint64_t size;
+ char msg[0];
+};
+
+template <bool is64Bit>
+struct magic_abort_msg_t {
+ uint64_t magic1;
+ uint64_t magic2;
+ abort_msg_t<is64Bit> msg;
+};
+
+template <bool is64Bit>
+void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) {
+ magic_abort_msg_t<is64Bit> header;
+ if (!Memory()->Read(
+ mapping->range.Base(), sizeof(magic_abort_msg_t<is64Bit>), &header)) {
+ return;
+ }
+
+ size_t size = header.msg.size - sizeof(magic_abort_msg_t<is64Bit>) - 1;
+ if (header.magic1 != 0xb18e40886ac388f0ULL ||
+ header.magic2 != 0xc6dfba755a1de0b5ULL ||
+ mapping->range.Size() <
+ offsetof(magic_abort_msg_t<is64Bit>, msg.msg) + size) {
+ return;
+ }
+
+ abort_message_.resize(size);
+ if (!Memory()->Read(
+ mapping->range.Base() + offsetof(magic_abort_msg_t<is64Bit>, msg.msg),
+ size,
+ &abort_message_[0])) {
+ abort_message_.clear();
+ }
+}
+
+#endif // OS_ANDROID
+
+const std::string& ProcessReaderLinux::AbortMessage() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (abort_message_.empty()) {
+ InitializeAbortMessage();
+ }
+ return abort_message_;
+}
+
void ProcessReaderLinux::InitializeThreads() {
DCHECK(threads_.empty());
initialized_threads_ = true;
@@ -298,23 +378,11 @@
LOG(WARNING) << "Couldn't initialize main thread.";
}
- char path[32];
- snprintf(path, arraysize(path), "/proc/%d/task", pid);
bool main_thread_found = false;
- DirectoryReader reader;
- if (!reader.Open(base::FilePath(path))) {
- return;
- }
- base::FilePath tid_str;
- DirectoryReader::Result result;
- while ((result = reader.NextFile(&tid_str)) ==
- DirectoryReader::Result::kSuccess) {
- pid_t tid;
- if (!base::StringToInt(tid_str.value(), &tid)) {
- LOG(ERROR) << "format error";
- continue;
- }
-
+ std::vector<pid_t> thread_ids;
+ bool result = connection_->Threads(&thread_ids);
+ DCHECK(result);
+ for (pid_t tid : thread_ids) {
if (tid == pid) {
DCHECK(!main_thread_found);
main_thread_found = true;
@@ -328,8 +396,6 @@
threads_.push_back(thread);
}
}
- DCHECK_EQ(AsUnderlyingType(result),
- AsUnderlyingType(DirectoryReader::Result::kNoMoreFiles));
DCHECK(main_thread_found);
}
@@ -365,17 +431,15 @@
return;
}
- std::vector<const MemoryMap::Mapping*> possible_mappings =
+ auto possible_mappings =
memory_map_.FindFilePossibleMmapStarts(*phdr_mapping);
- for (auto riter = possible_mappings.rbegin();
- riter != possible_mappings.rend();
- ++riter) {
- auto mapping = *riter;
+ const MemoryMap::Mapping* mapping = nullptr;
+ while ((mapping = possible_mappings->Next())) {
auto parsed_exe = std::make_unique<ElfImageReader>();
if (parsed_exe->Initialize(
range,
mapping->range.Base(),
- /* verbose= */ possible_mappings.size() == 1) &&
+ /* verbose= */ possible_mappings->Count() == 1) &&
parsed_exe->GetProgramHeaderTableAddress() == phdrs) {
exe_mapping = mapping;
exe_reader = std::move(parsed_exe);
@@ -383,7 +447,8 @@
}
}
if (!exe_mapping) {
- LOG(ERROR) << "no exe mappings " << possible_mappings.size();
+ LOG(ERROR) << "no exe mappings 0x" << std::hex
+ << phdr_mapping->range.Base();
return;
}
}
@@ -420,18 +485,30 @@
continue;
}
- std::vector<const MemoryMap::Mapping*> possible_mappings =
+#if defined(OS_ANDROID)
+ // Beginning at API 21, Bionic provides android_dlopen_ext() which allows
+ // passing a file descriptor with an existing relro segment to the loader.
+ // This means that the mapping attributes of dyn_mapping may be unrelated
+ // to the attributes of the other mappings for the module. In this case,
+ // search all mappings in reverse order from dyn_mapping until a module is
+ // parsed whose dynamic address matches the value in the debug link.
+ static int api_level = android_get_device_api_level();
+ auto possible_mappings =
+ (api_level >= 21 || api_level < 0)
+ ? memory_map_.ReverseIteratorFrom(*dyn_mapping)
+ : memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
+#else
+ auto possible_mappings =
memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
- for (auto riter = possible_mappings.rbegin();
- riter != possible_mappings.rend();
- ++riter) {
- auto mapping = *riter;
+#endif
+ const MemoryMap::Mapping* mapping = nullptr;
+ while ((mapping = possible_mappings->Next())) {
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(
range,
mapping->range.Base(),
- /* verbose= */ possible_mappings.size() == 1) &&
+ /* verbose= */ possible_mappings->Count() == 1) &&
parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
dynamic_address == entry.dynamic_array) {
module_mapping = mapping;
@@ -440,13 +517,19 @@
}
}
if (!module_mapping) {
- LOG(ERROR) << "no module mappings " << possible_mappings.size();
+ LOG(ERROR) << "no module mappings 0x" << std::hex
+ << dyn_mapping->range.Base();
continue;
}
}
Module module = {};
- module.name = !entry.name.empty() ? entry.name : module_mapping->name;
+ std::string soname;
+ if (elf_reader->SoName(&soname) && !soname.empty()) {
+ module.name = soname;
+ } else {
+ module.name = !entry.name.empty() ? entry.name : module_mapping->name;
+ }
module.elf_reader = elf_reader.get();
module.type = loader_base && elf_reader->Address() == loader_base
? ModuleSnapshot::kModuleTypeDynamicLoader
diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h
index 7c17d9d..258e102 100644
--- a/snapshot/linux/process_reader_linux.h
+++ b/snapshot/linux/process_reader_linux.h
@@ -66,6 +66,10 @@
int static_priority;
int nice_value;
+ //! \brief `true` if `sched_policy`, `static_priority`, and `nice_value` are
+ //! all valid.
+ bool have_priorities;
+
private:
friend class ProcessReaderLinux;
@@ -116,7 +120,7 @@
pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
//! \brief Return a memory reader for the target process.
- ProcessMemory* Memory() { return connection_->Memory(); }
+ const ProcessMemory* Memory() const { return connection_->Memory(); }
//! \brief Return a memory map of the target process.
MemoryMap* GetMemoryMap() { return &memory_map_; }
@@ -149,15 +153,23 @@
//! `0`) corresponds to the main executable.
const std::vector<Module>& Modules();
+ //! \return On Android, the abort message that was passed to
+ //! android_set_abort_message(). This is only available on Q or later.
+ const std::string& AbortMessage();
+
private:
void InitializeThreads();
void InitializeModules();
+ void InitializeAbortMessage();
+ template <bool Is64Bit>
+ void ReadAbortMessage(const MemoryMap::Mapping* mapping);
PtraceConnection* connection_; // weak
ProcessInfo process_info_;
MemoryMap memory_map_;
std::vector<Thread> threads_;
std::vector<Module> modules_;
+ std::string abort_message_;
std::vector<std::unique_ptr<ElfImageReader>> elf_readers_;
bool is_64_bit_;
bool initialized_threads_;
diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc
index d827f35..5b57236 100644
--- a/snapshot/linux/process_reader_linux_test.cc
+++ b/snapshot/linux/process_reader_linux_test.cc
@@ -33,6 +33,7 @@
#include "base/format_macros.h"
#include "base/memory/free_deleter.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -48,10 +49,17 @@
#include "util/linux/direct_ptrace_connection.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h"
+#include "util/misc/memory_sanitizer.h"
#include "util/synchronization/semaphore.h"
#if defined(OS_ANDROID)
#include <android/api-level.h>
+#include <android/set_abort_message.h>
+#include "dlfcn_internal.h"
+
+// Normally this comes from set_abort_message.h, but only at API level 21.
+extern "C" void android_set_abort_message(const char* msg)
+ __attribute__((weak));
#endif
namespace crashpad {
@@ -79,12 +87,14 @@
EXPECT_EQ(process_reader.ParentProcessID(), getppid());
static constexpr char kTestMemory[] = "Some test memory";
- char buffer[arraysize(kTestMemory)];
+ char buffer[base::size(kTestMemory)];
ASSERT_TRUE(process_reader.Memory()->Read(
reinterpret_cast<LinuxVMAddress>(kTestMemory),
sizeof(kTestMemory),
&buffer));
EXPECT_STREQ(kTestMemory, buffer);
+
+ EXPECT_EQ("", process_reader.AbortMessage());
}
constexpr char kTestMemory[] = "Read me from another process";
@@ -327,6 +337,11 @@
thread_pool.StartThreads(kThreadCount, stack_size_);
TestThreadPool::ThreadExpectation expectation;
+#if defined(MEMORY_SANITIZER)
+ // memset() + re-initialization is required to zero padding bytes for MSan.
+ memset(&expectation, 0, sizeof(expectation));
+#endif // defined(MEMORY_SANITIZER)
+ expectation = {};
expectation.tls = GetTLS();
expectation.stack_address = reinterpret_cast<LinuxVMAddress>(&thread_pool);
@@ -412,7 +427,7 @@
}
void MultiprocessChild() override {
- const LinuxVMSize stack_size = page_size_ * 3;
+ const LinuxVMSize stack_size = page_size_ * 4;
GrowStack(stack_size, reinterpret_cast<LinuxVMAddress>(&stack_size));
}
@@ -425,7 +440,7 @@
} else {
// Write-protect a page on our stack to split up the mapping
LinuxVMAddress page_addr =
- stack_address - (stack_address % page_size_) + page_size_;
+ stack_address - (stack_address % page_size_) + 2 * page_size_;
ASSERT_EQ(
mprotect(reinterpret_cast<void*>(page_addr), page_size_, PROT_READ),
0)
@@ -453,7 +468,14 @@
DISALLOW_COPY_AND_ASSIGN(ChildWithSplitStackTest);
};
-TEST(ProcessReaderLinux, ChildWithSplitStack) {
+// AddressSanitizer with use-after-return detection causes stack variables to
+// be allocated on the heap.
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ChildWithSplitStack DISABLED_ChildWithSplitStack
+#else
+#define MAYBE_ChildWithSplitStack ChildWithSplitStack
+#endif
+TEST(ProcessReaderLinux, MAYBE_ChildWithSplitStack) {
ChildWithSplitStackTest test;
test.Run();
}
@@ -715,7 +737,7 @@
auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr);
auto mappings =
reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping);
- EXPECT_EQ(mappings.size(), 2u);
+ EXPECT_EQ(mappings->Count(), 2u);
return;
}
}
@@ -761,7 +783,7 @@
ScopedModuleHandle empty_test_module(LoadTestModule(module_name_));
ASSERT_TRUE(empty_test_module.valid());
- char c;
+ char c = 0;
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, sizeof(c)));
CheckedReadFileAtEOF(ReadPipeHandle());
@@ -777,6 +799,30 @@
test.Run();
}
+#if defined(OS_ANDROID)
+const char kTestAbortMessage[] = "test abort message";
+
+TEST(ProcessReaderLinux, AbortMessage) {
+ // This test requires Q. The API level on Q devices will be 28 until the API
+ // is finalized, so we can't check API level yet. For now, test for the
+ // presence of a libc symbol which was introduced in Q.
+ if (!crashpad::internal::Dlsym(RTLD_DEFAULT,
+ "android_fdsan_close_with_tag")) {
+ GTEST_SKIP();
+ }
+
+ android_set_abort_message(kTestAbortMessage);
+
+ FakePtraceConnection connection;
+ connection.Initialize(getpid());
+
+ ProcessReaderLinux process_reader;
+ ASSERT_TRUE(process_reader.Initialize(&connection));
+
+ EXPECT_EQ(kTestAbortMessage, process_reader.AbortMessage());
+}
+#endif
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc
index 190e869..35f870e 100644
--- a/snapshot/linux/process_snapshot_linux.cc
+++ b/snapshot/linux/process_snapshot_linux.cc
@@ -43,13 +43,29 @@
InitializeThreads();
InitializeModules();
+ InitializeAnnotations();
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
+pid_t ProcessSnapshotLinux::FindThreadWithStackAddress(
+ VMAddress stack_address) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ for (const auto& thread : process_reader_.Threads()) {
+ if (stack_address >= thread.stack_region_address &&
+ stack_address <
+ thread.stack_region_address + thread.stack_region_size) {
+ return thread.tid;
+ }
+ }
+ return -1;
+}
+
bool ProcessSnapshotLinux::InitializeException(
- LinuxVMAddress exception_info_address) {
+ LinuxVMAddress exception_info_address,
+ pid_t exception_thread_id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
DCHECK(!exception_);
@@ -60,6 +76,10 @@
return false;
}
+ if (exception_thread_id >= 0) {
+ info.thread_id = exception_thread_id;
+ }
+
exception_.reset(new internal::ExceptionSnapshotLinux());
if (!exception_->Initialize(&process_reader_,
info.siginfo_address,
@@ -140,12 +160,12 @@
*options = local_options;
}
-pid_t ProcessSnapshotLinux::ProcessID() const {
+crashpad::ProcessID ProcessSnapshotLinux::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ProcessID();
}
-pid_t ProcessSnapshotLinux::ParentProcessID() const {
+crashpad::ProcessID ProcessSnapshotLinux::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ParentProcessID();
}
@@ -234,6 +254,11 @@
return std::vector<const MemorySnapshot*>();
}
+const ProcessMemory* ProcessSnapshotLinux::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.Memory();
+}
+
void ProcessSnapshotLinux::InitializeThreads() {
const std::vector<ProcessReaderLinux::Thread>& process_reader_threads =
process_reader_.Threads();
@@ -253,11 +278,21 @@
std::make_unique<internal::ModuleSnapshotElf>(reader_module.name,
reader_module.elf_reader,
reader_module.type,
- &memory_range_);
+ &memory_range_,
+ process_reader_.Memory());
if (module->Initialize()) {
modules_.push_back(std::move(module));
}
}
}
+void ProcessSnapshotLinux::InitializeAnnotations() {
+#if defined(OS_ANDROID)
+ const std::string& abort_message = process_reader_.AbortMessage();
+ if (!abort_message.empty()) {
+ annotations_simple_map_["abort_message"] = abort_message;
+ }
+#endif
+}
+
} // namespace crashpad
diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h
index 49d648f..06b72af 100644
--- a/snapshot/linux/process_snapshot_linux.h
+++ b/snapshot/linux/process_snapshot_linux.h
@@ -39,6 +39,7 @@
#include "util/linux/ptrace_connection.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/uuid.h"
+#include "util/process/process_id.h"
#include "util/process/process_memory_range.h"
namespace crashpad {
@@ -58,11 +59,23 @@
//! an appropriate message logged.
bool Initialize(PtraceConnection* connection);
+ //! \brief Finds the thread whose stack contains \a stack_address.
+ //!
+ //! \param[in] stack_address A stack address to search for.
+ //! \return The thread ID of the thread whose stack contains \a stack_address
+ //! or -1 if no matching thread is found.
+ pid_t FindThreadWithStackAddress(VMAddress stack_address);
+
//! \brief Initializes the object's exception.
//!
//! \param[in] exception_info The address of an ExceptionInformation in the
//! target process' address space.
- bool InitializeException(LinuxVMAddress exception_info);
+ //! \param[in] exception_thread_id The thread ID to assocaite the thread with.
+ //! Optional. If -1, the exception thread will be identified by the
+ //! ExceptionInformation struct which contains the thread ID in the target
+ //! process' namespace.
+ bool InitializeException(LinuxVMAddress exception_info,
+ pid_t exception_thread_id = -1);
//! \brief Sets the value to be returned by ReportID().
//!
@@ -78,15 +91,16 @@
//! ClientID() will return an identifier consisting entirely of zeroes.
void SetClientID(const UUID& client_id) { client_id_ = client_id; }
- //! \brief Sets the value to be returned by AnnotationsSimpleMap().
+ //! \brief Add an annotation to be returned by AnnotationsSimpleMap().
//!
- //! All process annotations are under the control of the snapshot
+ //! Most process annotations are under the control of the snapshot
//! producer, which may call this method to establish these annotations.
- //! Contrast this with module annotations, which are under the control of the
- //! process being snapshotted.
- void SetAnnotationsSimpleMap(
- const std::map<std::string, std::string>& annotations_simple_map) {
- annotations_simple_map_ = annotations_simple_map;
+ //! On Android Q or later, the process snapshot may add an "abort_message"
+ //! annotation, which will contain the abort message passed to the
+ //! android_set_abort_message() function. Contrast this with module
+ //! annotations, which are under the control of the process being snapshotted.
+ void AddAnnotation(const std::string& key, const std::string& value) {
+ annotations_simple_map_[key] = value;
}
//! \brief Returns options from CrashpadInfo structures found in modules in
@@ -98,8 +112,8 @@
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -115,10 +129,12 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
void InitializeThreads();
void InitializeModules();
+ void InitializeAnnotations();
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
diff --git a/snapshot/linux/system_snapshot_linux_test.cc b/snapshot/linux/system_snapshot_linux_test.cc
index 46d3845..f3013b5 100644
--- a/snapshot/linux/system_snapshot_linux_test.cc
+++ b/snapshot/linux/system_snapshot_linux_test.cc
@@ -77,7 +77,8 @@
EXPECT_PRED1(
[](std::string vendor) {
- return vendor == "GenuineIntel" || vendor == "AuthenticAMD";
+ return vendor == "GenuineIntel" || vendor == "AuthenticAMD" ||
+ vendor == "HygonGenuine";
},
system.CPUVendor());
diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc
index 8ffbfe8..e3e2beb 100644
--- a/snapshot/linux/thread_snapshot_linux.cc
+++ b/snapshot/linux/thread_snapshot_linux.cc
@@ -23,6 +23,109 @@
namespace crashpad {
namespace internal {
+namespace {
+
+int ComputeThreadPriority(int static_priority,
+ int sched_policy,
+ int nice_value) {
+ // Map Linux scheduling policy, static priority, and nice value into a
+ // single int value.
+ //
+ // The possible policies in order of approximate priority (low to high) are
+ // SCHED_IDLE
+ // SCHED_BATCH
+ // SCHED_OTHER
+ // SCHED_RR
+ // SCHED_FIFO
+ //
+ // static_priority is not used for OTHER, BATCH, or IDLE and should be 0.
+ // For FIFO and RR, static_priority should range from 1 to 99 with 99 being
+ // the highest priority.
+ //
+ // nice value ranges from -20 to 19, with -20 being highest priority
+
+ enum class Policy : uint8_t {
+ kUnknown = 0,
+ kIdle,
+ kBatch,
+ kOther,
+ kRR,
+ kFIFO
+ };
+
+ struct LinuxPriority {
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ // nice values affect how dynamic priorities are updated, which only
+ // matters for threads with the same static priority.
+ uint8_t nice_value = 0;
+
+ // The scheduling policy also affects how threads with the same static
+ // priority are ordered, but has greater impact than nice value.
+ Policy policy = Policy::kUnknown;
+
+ // The static priority is the most significant in determining overall
+ // priority.
+ uint8_t static_priority = 0;
+
+ // Put this in the most significant byte position to prevent negative
+ // priorities.
+ uint8_t unused = 0;
+#elif defined(ARCH_CPU_BIG_ENDIAN)
+ uint8_t unused = 0;
+ uint8_t static_priority = 0;
+ Policy policy = Policy::kUnknown;
+ uint8_t nice_value = 0;
+#endif // ARCH_CPU_LITTLE_ENDIAN
+ };
+ static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large");
+
+ LinuxPriority prio;
+
+ // Lower nice values have higher priority, so negate them and add 20 to put
+ // them in the range 1-40 with 40 being highest priority.
+ if (nice_value < -20 || nice_value > 19) {
+ LOG(WARNING) << "invalid nice value " << nice_value;
+ prio.nice_value = 0;
+ } else {
+ prio.nice_value = -1 * nice_value + 20;
+ }
+
+ switch (sched_policy) {
+ case SCHED_IDLE:
+ prio.policy = Policy::kIdle;
+ break;
+ case SCHED_BATCH:
+ prio.policy = Policy::kBatch;
+ break;
+ case SCHED_OTHER:
+ prio.policy = Policy::kOther;
+ break;
+ case SCHED_RR:
+ prio.policy = Policy::kRR;
+ break;
+ case SCHED_FIFO:
+ prio.policy = Policy::kFIFO;
+ break;
+ default:
+ prio.policy = Policy::kUnknown;
+ LOG(WARNING) << "Unknown scheduling policy " << sched_policy;
+ }
+
+ if (static_priority < 0 || static_priority > 99) {
+ LOG(WARNING) << "invalid static priority " << static_priority;
+ }
+ prio.static_priority = static_priority;
+
+ int priority;
+ if (!ReinterpretBytes(prio, &priority)) {
+ LOG(ERROR) << "Couldn't set priority";
+ return -1;
+ }
+ return priority;
+}
+
+} // namespace
+
ThreadSnapshotLinux::ThreadSnapshotLinux()
: ThreadSnapshot(),
context_union_(),
@@ -31,11 +134,9 @@
thread_specific_data_address_(0),
thread_id_(-1),
priority_(-1),
- initialized_() {
-}
+ initialized_() {}
-ThreadSnapshotLinux::~ThreadSnapshotLinux() {
-}
+ThreadSnapshotLinux::~ThreadSnapshotLinux() {}
bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,
const ProcessReaderLinux::Thread& thread) {
@@ -89,7 +190,7 @@
#error Port.
#endif
- stack_.Initialize(process_reader,
+ stack_.Initialize(process_reader->Memory(),
thread.stack_region_address,
thread.stack_region_size);
@@ -98,98 +199,11 @@
thread_id_ = thread.tid;
- // Map Linux scheduling policy, static priority, and nice value into a single
- // int value.
- //
- // The possible policies in order of approximate priority (low to high) are
- // SCHED_IDLE
- // SCHED_BATCH
- // SCHED_OTHER
- // SCHED_RR
- // SCHED_FIFO
- //
- // static_priority is not used for OTHER, BATCH, or IDLE and should be 0.
- // For FIFO and RR, static_priority should range from 1 to 99 with 99 being
- // the highest priority.
- //
- // nice value ranges from -20 to 19, with -20 being highest priority
-
- enum class Policy : uint8_t {
- kUnknown = 0,
- kIdle,
- kBatch,
- kOther,
- kRR,
- kFIFO
- };
-
- struct LinuxPriority {
-#if defined(ARCH_CPU_LITTLE_ENDIAN)
- // nice values affect how dynamic priorities are updated, which only matters
- // for threads with the same static priority.
- uint8_t nice_value = 0;
-
- // The scheduling policy also affects how threads with the same static
- // priority are ordered, but has greater impact than nice value.
- Policy policy = Policy::kUnknown;
-
- // The static priority is the most significant in determining overall
- // priority.
- uint8_t static_priority = 0;
-
- // Put this in the most significant byte position to prevent negative
- // priorities.
- uint8_t unused = 0;
-#elif defined(ARCH_CPU_BIG_ENDIAN)
- uint8_t unused = 0;
- uint8_t static_priority = 0;
- Policy policy = Policy::kUnknown;
- uint8_t nice_value = 0;
-#endif // ARCH_CPU_LITTLE_ENDIAN
- };
- static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large");
-
- LinuxPriority prio;
-
- // Lower nice values have higher priority, so negate them and add 20 to put
- // them in the range 1-40 with 40 being highest priority.
- if (thread.nice_value < -20 || thread.nice_value > 19) {
- LOG(WARNING) << "invalid nice value " << thread.nice_value;
- prio.nice_value = 0;
- } else {
- prio.nice_value = -1 * thread.nice_value + 20;
- }
-
- switch (thread.sched_policy) {
- case SCHED_IDLE:
- prio.policy = Policy::kIdle;
- break;
- case SCHED_BATCH:
- prio.policy = Policy::kBatch;
- break;
- case SCHED_OTHER:
- prio.policy = Policy::kOther;
- break;
- case SCHED_RR:
- prio.policy = Policy::kRR;
- break;
- case SCHED_FIFO:
- prio.policy = Policy::kFIFO;
- break;
- default:
- prio.policy = Policy::kUnknown;
- LOG(WARNING) << "Unknown scheduling policy " << thread.sched_policy;
- }
-
- if (thread.static_priority < 0 || thread.static_priority > 99) {
- LOG(WARNING) << "invalid static priority " << thread.static_priority;
- }
- prio.static_priority = thread.static_priority;
-
- if (!ReinterpretBytes(prio, &priority_)) {
- LOG(ERROR) << "Couldn't set priority";
- return false;
- }
+ priority_ =
+ thread.have_priorities
+ ? ComputeThreadPriority(
+ thread.static_priority, thread.sched_policy, thread.nice_value)
+ : -1;
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h
index 17e471f..44cc6f6 100644
--- a/snapshot/linux/thread_snapshot_linux.h
+++ b/snapshot/linux/thread_snapshot_linux.h
@@ -73,7 +73,7 @@
#endif // ARCH_CPU_X86_FAMILY
} context_union_;
CPUContext context_;
- MemorySnapshotGeneric<ProcessReaderLinux> stack_;
+ MemorySnapshotGeneric stack_;
LinuxVMAddress thread_specific_data_address_;
pid_t thread_id_;
int priority_;
diff --git a/snapshot/mac/cpu_context_mac.cc b/snapshot/mac/cpu_context_mac.cc
index c91e331..03893c4 100644
--- a/snapshot/mac/cpu_context_mac.cc
+++ b/snapshot/mac/cpu_context_mac.cc
@@ -436,6 +436,136 @@
} // namespace internal
+#elif defined(ARCH_CPU_ARM_FAMILY)
+
+namespace {
+
+void InitializeCPUContextARM64Thread(
+ CPUContextARM64* context,
+ const arm_thread_state64_t* arm_thread_state64) {
+ // The structures of context->regs and arm_thread_state64->__x are laid out
+ // identically for this copy, even though the members are organized
+ // differently. Because of this difference, there can't be a static assert
+ // similar to the one below for fpsimd.
+ memcpy(context->regs, arm_thread_state64->__x, sizeof(context->regs));
+ context->sp = arm_thread_state64->__sp;
+ context->pc = arm_thread_state64->__pc;
+ context->spsr =
+ static_cast<decltype(context->spsr)>(arm_thread_state64->__cpsr);
+}
+
+void InitializeCPUContextARM64Neon(CPUContextARM64* context,
+ const arm_neon_state64_t* arm_neon_state64) {
+ static_assert(sizeof(context->fpsimd) == sizeof(arm_neon_state64->__v),
+ "fpsimd context size mismatch");
+ memcpy(context->fpsimd, arm_neon_state64->__v, sizeof(arm_neon_state64->__v));
+ context->fpsr = arm_neon_state64->__fpsr;
+ context->fpcr = arm_neon_state64->__fpcr;
+}
+
+thread_state_flavor_t InitializeCPUContextARM64Flavor(
+ CPUContextARM64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count) {
+ mach_msg_type_number_t expected_state_count;
+ switch (flavor) {
+ case ARM_THREAD_STATE:
+ expected_state_count = ARM_THREAD_STATE_COUNT;
+ break;
+ case ARM_THREAD_STATE64:
+ expected_state_count = ARM_THREAD_STATE64_COUNT;
+ break;
+ case ARM_NEON_STATE64:
+ expected_state_count = ARM_NEON_STATE64_COUNT;
+ break;
+ case THREAD_STATE_NONE: {
+ // This may happen without error when called without exception-style
+ // flavor data, or even from an exception handler when the exception
+ // behavior is EXCEPTION_DEFAULT.
+ return flavor;
+ }
+ default:
+ LOG(WARNING) << "unhandled flavor " << flavor;
+ return THREAD_STATE_NONE;
+ }
+
+ if (state_count < expected_state_count) {
+ LOG(WARNING) << "expected state_count " << expected_state_count
+ << " for flavor " << flavor << ", observed " << state_count;
+ return THREAD_STATE_NONE;
+ }
+
+ switch (flavor) {
+ case ARM_THREAD_STATE: {
+ const arm_unified_thread_state_t* arm_thread_state =
+ reinterpret_cast<const arm_unified_thread_state_t*>(state);
+ if (arm_thread_state->ash.flavor != ARM_THREAD_STATE64) {
+ LOG(WARNING) << "expected flavor ARM_THREAD_STATE64, observed "
+ << arm_thread_state->ash.flavor;
+ return THREAD_STATE_NONE;
+ }
+ return InitializeCPUContextARM64Flavor(
+ context,
+ arm_thread_state->ash.flavor,
+ reinterpret_cast<ConstThreadState>(&arm_thread_state->ts_64),
+ arm_thread_state->ash.count);
+ }
+
+ case ARM_THREAD_STATE64: {
+ const arm_thread_state64_t* arm_thread_state =
+ reinterpret_cast<const arm_thread_state64_t*>(state);
+ InitializeCPUContextARM64Thread(context, arm_thread_state);
+ return ARM_THREAD_STATE64;
+ }
+
+ case ARM_NEON_STATE64: {
+ const arm_neon_state64_t* arm_neon_state =
+ reinterpret_cast<const arm_neon_state64_t*>(state);
+ InitializeCPUContextARM64Neon(context, arm_neon_state);
+ return ARM_NEON_STATE64;
+ }
+
+ case THREAD_STATE_NONE: {
+ // This may happen without error when called without exception-style
+ // flavor data, or even from an exception handler when the exception
+ // behavior is EXCEPTION_DEFAULT.
+ return flavor;
+ }
+
+ default: {
+ NOTREACHED();
+ return THREAD_STATE_NONE;
+ }
+ }
+}
+
+} // namespace
+
+namespace internal {
+
+void InitializeCPUContextARM64(CPUContextARM64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const arm_thread_state64_t* arm_thread_state64,
+ const arm_neon_state64_t* arm_neon_state64) {
+ thread_state_flavor_t set_flavor = THREAD_STATE_NONE;
+ if (flavor != THREAD_STATE_NONE) {
+ set_flavor =
+ InitializeCPUContextARM64Flavor(context, flavor, state, state_count);
+ }
+
+ if (set_flavor != ARM_THREAD_STATE64) {
+ InitializeCPUContextARM64Thread(context, arm_thread_state64);
+ }
+ if (set_flavor != ARM_NEON_STATE64) {
+ InitializeCPUContextARM64Neon(context, arm_neon_state64);
+ }
+}
+
+} // namespace internal
+
#endif
} // namespace crashpad
diff --git a/snapshot/mac/cpu_context_mac.h b/snapshot/mac/cpu_context_mac.h
index 30281c1..05c035a 100644
--- a/snapshot/mac/cpu_context_mac.h
+++ b/snapshot/mac/cpu_context_mac.h
@@ -108,6 +108,44 @@
const x86_float_state64_t* x86_float_state64,
const x86_debug_state64_t* x86_debug_state64);
+#elif defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
+//! \brief Initializes a CPUContextARM64 structure from native context
+//! structures on iOS.
+//!
+//! \a flavor, \a state, and \a state_count may be supplied by exception
+//! handlers in order for the \a context parameter to be initialized by the
+//! thread state received by the exception handler to the extent possible. In
+//! that case, whatever thread state specified by these three parameters will
+//! supersede \a arm_thread_state64 or \a arm_neon_state64. If thread state in
+//! this format is not available, \a flavor may be set to `THREAD_STATE_NONE`,
+//! and all of \a arm_thread_state64 abd \a arm_neon_state64 will be honored.
+//!
+//! If \a flavor, \a state, and \a state_count are provided but do not contain
+//! valid values, a message will be logged and their values will be ignored as
+//! though \a flavor were specified as `THREAD_STATE_NONE`.
+//!
+//! \param[out] context The CPUContextARM64 structure to initialize.
+//! \param[in] flavor The native thread state flavor of \a state. This may be
+//! `ARM_THREAD_STATE64`, `ARM_THREAD_STATE` or `ARM_NEON_STATE64`. It may
+//! also be `THREAD_STATE_NONE` if \a state is not supplied (and is
+//! `nullptr`).
+//! \param[in] state The native thread state, which may be a casted pointer to
+//! `arm_thread_state64_t`, `arm_unified_thread_state` or
+//! `arm_neon_state64_t`. This parameter may be `nullptr` to not supply this
+//! data, in which case \a flavor must be `THREAD_STATE_NONE`. If a
+//! “universal” structure is used, it must carry 64-bit state data of the
+//! correct type.
+//! \param[in] state_count The number of `int`-sized units in \a state. This
+//! may be 0 if \a state is `nullptr`.
+//! \param[in] arm_thread_state64 The state of the thread’s integer registers.
+//! \param[in] arm_neon_state64 The state of the thread’s floating-point
+//! registers.
+void InitializeCPUContextARM64(CPUContextARM64* context,
+ thread_state_flavor_t flavor,
+ ConstThreadState state,
+ mach_msg_type_number_t state_count,
+ const arm_thread_state64_t* arm_thread_state64,
+ const arm_neon_state64_t* arm_neon_state64);
#endif
} // namespace internal
diff --git a/snapshot/mac/exception_snapshot_mac.cc b/snapshot/mac/exception_snapshot_mac.cc
index 50d1a12..8eefac9 100644
--- a/snapshot/mac/exception_snapshot_mac.cc
+++ b/snapshot/mac/exception_snapshot_mac.cc
@@ -142,8 +142,13 @@
thread_id_ = thread->id;
- // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS
- // exceptions, but not for other types of exceptions.
+ // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present
+ // in code[1]. It may or may not be the instruction pointer address (usually
+ // it’s not). code[1] may carry the exception address for other exception
+ // types too, but it’s not guaranteed. But for all other exception types, the
+ // instruction pointer will be the exception address, and in fact will be
+ // equal to codes[1] when it’s carrying the exception address. In those cases,
+ // just use the instruction pointer directly.
bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
#if defined(ARCH_CPU_X86_FAMILY)
diff --git a/snapshot/mac/mach_o_image_annotations_reader.cc b/snapshot/mac/mach_o_image_annotations_reader.cc
index b02acae..eefd7f4 100644
--- a/snapshot/mac/mach_o_image_annotations_reader.cc
+++ b/snapshot/mac/mach_o_image_annotations_reader.cc
@@ -26,7 +26,6 @@
#include "snapshot/mac/mach_o_image_reader.h"
#include "snapshot/mac/process_reader_mac.h"
#include "snapshot/snapshot_constants.h"
-#include "util/mach/task_memory.h"
#include "util/stdlib/strnlen.h"
namespace crashpad {
@@ -179,9 +178,9 @@
}
}
-// TODO(rsesek): When there is a platform-agnostic remote memory reader
-// interface available, use it so that the implementation is not duplicated
-// in the PEImageAnnotationsReader.
+// TODO(https://crbug.com/crashpad/270): Replace implementations of
+// ReadCrashpadAnnotationsList and ReadCrashpadSimpleAnnotations with the
+// platform-agnostic implementations in ImageAnnotationReader.
void MachOImageAnnotationsReader::ReadCrashpadAnnotationsList(
std::vector<AnnotationSnapshot>* annotations) const {
process_types::CrashpadInfo crashpad_info;
diff --git a/snapshot/mac/mach_o_image_annotations_reader_test.cc b/snapshot/mac/mach_o_image_annotations_reader_test.cc
index 6930250..8659a14 100644
--- a/snapshot/mac/mach_o_image_annotations_reader_test.cc
+++ b/snapshot/mac/mach_o_image_annotations_reader_test.cc
@@ -461,7 +461,13 @@
test_mach_o_image_annotations_reader.Run();
}
-TEST(MachOImageAnnotationsReader, CrashModuleInitialization) {
+#if defined(ADDRESS_SANITIZER)
+// https://crbug.com/844396
+#define MAYBE_CrashModuleInitialization DISABLED_CrashModuleInitialization
+#else
+#define MAYBE_CrashModuleInitialization CrashModuleInitialization
+#endif
+TEST(MachOImageAnnotationsReader, MAYBE_CrashModuleInitialization) {
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
TestMachOImageAnnotationsReader::kCrashModuleInitialization);
test_mach_o_image_annotations_reader.Run();
diff --git a/snapshot/mac/mach_o_image_reader.cc b/snapshot/mac/mach_o_image_reader.cc
index 6baee77..bd258ca 100644
--- a/snapshot/mac/mach_o_image_reader.cc
+++ b/snapshot/mac/mach_o_image_reader.cc
@@ -22,6 +22,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "snapshot/mac/mach_o_image_segment_reader.h"
@@ -182,7 +183,7 @@
// This vector is parallel to the kLoadCommandReaders array, and tracks
// whether a singleton load command matching the |command| field has been
// found yet.
- std::vector<uint32_t> singleton_indices(arraysize(kLoadCommandReaders),
+ std::vector<uint32_t> singleton_indices(base::size(kLoadCommandReaders),
kInvalidSegmentIndex);
size_t offset = mach_header.Size();
@@ -236,7 +237,7 @@
}
for (size_t reader_index = 0;
- reader_index < arraysize(kLoadCommandReaders);
+ reader_index < base::size(kLoadCommandReaders);
++reader_index) {
if (load_command.cmd != kLoadCommandReaders[reader_index].command) {
continue;
diff --git a/snapshot/mac/mach_o_image_segment_reader_test.cc b/snapshot/mac/mach_o_image_segment_reader_test.cc
index 2da97e8..4731a5f 100644
--- a/snapshot/mac/mach_o_image_segment_reader_test.cc
+++ b/snapshot/mac/mach_o_image_segment_reader_test.cc
@@ -16,7 +16,7 @@
#include <mach-o/loader.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
@@ -63,7 +63,7 @@
SEG_IMPORT,
};
- for (size_t index = 0; index < arraysize(kSegmentTestData); ++index) {
+ for (size_t index = 0; index < base::size(kSegmentTestData); ++index) {
EXPECT_EQ(
MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index]),
kSegmentTestData[index])
@@ -106,7 +106,7 @@
SECT_ICON_TIFF,
};
- for (size_t index = 0; index < arraysize(kSectionTestData); ++index) {
+ for (size_t index = 0; index < base::size(kSectionTestData); ++index) {
EXPECT_EQ(
MachOImageSegmentReader::SectionNameString(kSectionTestData[index]),
kSectionTestData[index])
@@ -169,7 +169,7 @@
{SEG_IMPORT, "", "__IMPORT,"},
};
- for (size_t index = 0; index < arraysize(kSegmentAndSectionTestData);
+ for (size_t index = 0; index < base::size(kSegmentAndSectionTestData);
++index) {
const auto& test = kSegmentAndSectionTestData[index];
EXPECT_EQ(MachOImageSegmentReader::SegmentAndSectionNameString(
diff --git a/snapshot/mac/mach_o_image_symbol_table_reader.cc b/snapshot/mac/mach_o_image_symbol_table_reader.cc
index 361253c..cbdac40 100644
--- a/snapshot/mac/mach_o_image_symbol_table_reader.cc
+++ b/snapshot/mac/mach_o_image_symbol_table_reader.cc
@@ -23,7 +23,7 @@
#include "base/strings/stringprintf.h"
#include "util/mac/checked_mach_address_range.h"
-#include "util/mach/task_memory.h"
+#include "util/process/process_memory_mac.h"
namespace crashpad {
@@ -108,7 +108,7 @@
return false;
}
- std::unique_ptr<TaskMemory::MappedMemory> string_table;
+ std::unique_ptr<ProcessMemoryMac::MappedMemory> string_table;
for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) {
const process_types::nlist& symbol = symbols[symbol_index];
std::string symbol_info = base::StringPrintf(", symbol index %zu%s",
diff --git a/snapshot/mac/mach_o_image_symbol_table_reader.h b/snapshot/mac/mach_o_image_symbol_table_reader.h
index 841b479..b1bcc3a 100644
--- a/snapshot/mac/mach_o_image_symbol_table_reader.h
+++ b/snapshot/mac/mach_o_image_symbol_table_reader.h
@@ -58,7 +58,7 @@
// there aren’t expected to be very many of those that performance would
// become a problem. In reality, std::unordered_map does not appear to provide
// a performance advantage. It appears that the memory copies currently done
- // by TaskMemory::Read() have substantially more impact on symbol table
+ // by ProcessMemoryMac::Read() have substantially more impact on symbol table
// operations.
//
// This is public so that the type is available to
diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc
index 4160897..947744d 100644
--- a/snapshot/mac/module_snapshot_mac.cc
+++ b/snapshot/mac/module_snapshot_mac.cc
@@ -14,8 +14,8 @@
#include "snapshot/mac/module_snapshot_mac.h"
-#include <mach/mach.h>
#include <mach-o/loader.h>
+#include <mach/mach.h>
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
@@ -34,11 +34,9 @@
timestamp_(0),
mach_o_image_reader_(nullptr),
process_reader_(nullptr),
- initialized_() {
-}
+ initialized_() {}
-ModuleSnapshotMac::~ModuleSnapshotMac() {
-}
+ModuleSnapshotMac::~ModuleSnapshotMac() {}
bool ModuleSnapshotMac::Initialize(
ProcessReaderMac* process_reader,
@@ -170,6 +168,11 @@
return base::FilePath(Name()).BaseName().value();
}
+std::vector<uint8_t> ModuleSnapshotMac::BuildID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<uint8_t>();
+}
+
std::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
MachOImageAnnotationsReader annotations_reader(
diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h
index fe2d40a..d78c326 100644
--- a/snapshot/mac/module_snapshot_mac.h
+++ b/snapshot/mac/module_snapshot_mac.h
@@ -77,6 +77,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/snapshot/mac/process_reader_mac.cc b/snapshot/mac/process_reader_mac.cc
index e142fd2..61dc3a1 100644
--- a/snapshot/mac/process_reader_mac.cc
+++ b/snapshot/mac/process_reader_mac.cc
@@ -92,7 +92,7 @@
threads_(),
modules_(),
module_readers_(),
- task_memory_(),
+ process_memory_(),
task_(TASK_NULL),
initialized_(),
is_64_bit_(false),
@@ -113,9 +113,11 @@
return false;
}
- is_64_bit_ = process_info_.Is64Bit();
+ if (!process_memory_.Initialize(task)) {
+ return false;
+ }
- task_memory_.reset(new TaskMemory(task));
+ is_64_bit_ = process_info_.Is64Bit();
task_ = task;
INITIALIZATION_STATE_SET_VALID(initialized_);
@@ -441,7 +443,7 @@
Module module;
module.timestamp = image_info.imageFileModDate;
- if (!task_memory_->ReadCString(image_info.imageFilePath, &module.name)) {
+ if (!process_memory_.ReadCString(image_info.imageFilePath, &module.name)) {
LOG(WARNING) << "could not read dyld_image_info::imageFilePath";
// Proceed anyway with an empty module name.
}
diff --git a/snapshot/mac/process_reader_mac.h b/snapshot/mac/process_reader_mac.h
index 91836db..9feb8b0 100644
--- a/snapshot/mac/process_reader_mac.h
+++ b/snapshot/mac/process_reader_mac.h
@@ -27,9 +27,9 @@
#include "base/macros.h"
#include "build/build_config.h"
-#include "util/mach/task_memory.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/posix/process_info.h"
+#include "util/process/process_memory_mac.h"
namespace crashpad {
@@ -138,7 +138,7 @@
bool CPUTimes(timeval* user_time, timeval* system_time) const;
//! \return Accesses the memory of the target task.
- TaskMemory* Memory() { return task_memory_.get(); }
+ const ProcessMemoryMac* Memory() const { return &process_memory_; }
//! \return The threads that are in the task (process). The first element (at
//! index `0`) corresponds to the main thread.
@@ -232,7 +232,7 @@
std::vector<Thread> threads_; // owns send rights
std::vector<Module> modules_;
std::vector<std::unique_ptr<MachOImageReader>> module_readers_;
- std::unique_ptr<TaskMemory> task_memory_;
+ ProcessMemoryMac process_memory_;
task_t task_; // weak
InitializationStateDcheck initialized_;
diff --git a/snapshot/mac/process_reader_mac_test.cc b/snapshot/mac/process_reader_mac_test.cc
index 59884a6..9325b2c 100644
--- a/snapshot/mac/process_reader_mac_test.cc
+++ b/snapshot/mac/process_reader_mac_test.cc
@@ -15,6 +15,7 @@
#include "snapshot/mac/process_reader_mac.h"
#include <AvailabilityMacros.h>
+#include <errno.h>
#include <OpenCL/opencl.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
@@ -26,8 +27,9 @@
#include <utility>
#include "base/logging.h"
-#include "base/mac/scoped_mach_port.h"
+#include "base/mac/mach_logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -63,7 +65,7 @@
EXPECT_EQ(process_reader.ParentProcessID(), getppid());
static constexpr char kTestMemory[] = "Some test memory";
- char buffer[arraysize(kTestMemory)];
+ char buffer[base::size(kTestMemory)];
ASSERT_TRUE(process_reader.Memory()->Read(
FromPointerCast<mach_vm_address_t>(kTestMemory),
sizeof(kTestMemory),
@@ -127,8 +129,8 @@
// This function CHECKs success and returns the thread ID directly.
uint64_t PthreadToThreadID(pthread_t pthread) {
uint64_t thread_id;
- int rv = pthread_threadid_np(pthread, &thread_id);
- CHECK_EQ(rv, 0);
+ errno = pthread_threadid_np(pthread, &thread_id);
+ PCHECK(errno == 0) << "pthread_threadid_np";
return thread_id;
}
@@ -410,6 +412,18 @@
EXPECT_TRUE(found_thread_self);
}
+uint64_t GetThreadID() {
+ thread_identifier_info info;
+ mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t kr = thread_info(MachThreadSelf(),
+ THREAD_IDENTIFIER_INFO,
+ reinterpret_cast<thread_info_t>(&info),
+ &info_count);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_info";
+
+ return info.thread_id;
+}
+
class ProcessReaderThreadedChild final : public MachMultiprocess {
public:
explicit ProcessReaderThreadedChild(size_t thread_count)
@@ -463,7 +477,7 @@
// This thread isn’t part of the thread pool, but the parent will be able
// to inspect it. Write an entry for it.
- uint64_t thread_id = PthreadToThreadID(pthread_self());
+ uint64_t thread_id = GetThreadID();
CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));
@@ -599,11 +613,11 @@
const size_t source_lengths[] = {
strlen(sources[0]),
};
- static_assert(arraysize(sources) == arraysize(source_lengths),
+ static_assert(base::size(sources) == base::size(source_lengths),
"arrays must be parallel");
program_ = clCreateProgramWithSource(
- context_, arraysize(sources), sources, source_lengths, &rv);
+ context_, base::size(sources), sources, source_lengths, &rv);
ASSERT_EQ(rv, CL_SUCCESS) << "clCreateProgramWithSource";
rv = clBuildProgram(
diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc
index cf5233a..528e921 100644
--- a/snapshot/mac/process_snapshot_mac.cc
+++ b/snapshot/mac/process_snapshot_mac.cc
@@ -217,6 +217,11 @@
return std::vector<const MemorySnapshot*>();
}
+const ProcessMemory* ProcessSnapshotMac::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.Memory();
+}
+
void ProcessSnapshotMac::InitializeThreads() {
const std::vector<ProcessReaderMac::Thread>& process_reader_threads =
process_reader_.Threads();
diff --git a/snapshot/mac/process_snapshot_mac.h b/snapshot/mac/process_snapshot_mac.h
index 06bac74..2edc222 100644
--- a/snapshot/mac/process_snapshot_mac.h
+++ b/snapshot/mac/process_snapshot_mac.h
@@ -131,6 +131,7 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
// Initializes threads_ on behalf of Initialize().
diff --git a/snapshot/mac/process_types.cc b/snapshot/mac/process_types.cc
index 65c39ea..0a1b7f9 100644
--- a/snapshot/mac/process_types.cc
+++ b/snapshot/mac/process_types.cc
@@ -20,9 +20,9 @@
#include <memory>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "snapshot/mac/process_types/internal.h"
-#include "util/mach/task_memory.h"
+#include "util/process/process_memory_mac.h"
namespace crashpad {
namespace {
@@ -74,7 +74,7 @@
template <>
inline void Assign<UInt64Array4, UInt32Array4>(UInt64Array4* destination,
const UInt32Array4& source) {
- for (size_t index = 0; index < arraysize(source); ++index) {
+ for (size_t index = 0; index < base::size(source); ++index) {
(*destination)[index] = source[index];
}
}
diff --git a/snapshot/mac/process_types/custom.cc b/snapshot/mac/process_types/custom.cc
index 7c7b172..a76c3eb 100644
--- a/snapshot/mac/process_types/custom.cc
+++ b/snapshot/mac/process_types/custom.cc
@@ -19,13 +19,16 @@
#include <sys/types.h>
#include <algorithm>
+#include <limits>
#include <type_traits>
#include "base/logging.h"
#include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "snapshot/mac/process_types/internal.h"
-#include "util/mach/task_memory.h"
+#include "util/mac/mac_util.h"
+#include "util/process/process_memory_mac.h"
#if !DOXYGEN
@@ -36,13 +39,13 @@
namespace {
template <typename T>
-bool ReadIntoAndZero(TaskMemory* task_memory,
+bool ReadIntoAndZero(const ProcessMemoryMac* process_memory,
mach_vm_address_t address,
mach_vm_size_t size,
T* specific) {
DCHECK_LE(size, sizeof(*specific));
- if (!task_memory->Read(address, size, specific)) {
+ if (!process_memory->Read(address, size, specific)) {
return false;
}
@@ -84,14 +87,14 @@
return false;
}
- TaskMemory* task_memory = process_reader->Memory();
+ const ProcessMemoryMac* process_memory = process_reader->Memory();
decltype(specific->version) version;
- if (!task_memory->Read(field_address, sizeof(version), &version)) {
+ if (!process_memory->Read(field_address, sizeof(version), &version)) {
return false;
}
const size_t size = T::ExpectedSizeForVersion(version);
- return ReadIntoAndZero(task_memory, address, size, specific);
+ return ReadIntoAndZero(process_memory, address, size, specific);
}
template <typename T>
@@ -103,9 +106,9 @@
return false;
}
- TaskMemory* task_memory = process_reader->Memory();
+ const ProcessMemoryMac* process_memory = process_reader->Memory();
decltype(specific->size) size;
- if (!task_memory->Read(address + offsetof(T, size), sizeof(size), &size)) {
+ if (!process_memory->Read(address + offsetof(T, size), sizeof(size), &size)) {
return false;
}
@@ -115,7 +118,7 @@
}
size = std::min(static_cast<size_t>(size), sizeof(*specific));
- return ReadIntoAndZero(task_memory, address, size, specific);
+ return ReadIntoAndZero(process_memory, address, size, specific);
}
} // namespace
@@ -139,18 +142,38 @@
offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide), // 11
offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID), // 12
offsetof(dyld_all_image_infos<Traits>, infoArrayChangeTimestamp), // 13
- offsetof(dyld_all_image_infos<Traits>, end_14_15), // 14
- offsetof(dyld_all_image_infos<Traits>, end_14_15), // 15
+ offsetof(dyld_all_image_infos<Traits>, end_14), // 14
+ std::numeric_limits<size_t>::max(), // 15, see below
sizeof(dyld_all_image_infos<Traits>), // 16
};
- if (version >= arraysize(kSizeForVersion)) {
- return kSizeForVersion[arraysize(kSizeForVersion) - 1];
+ if (version >= base::size(kSizeForVersion)) {
+ return kSizeForVersion[base::size(kSizeForVersion) - 1];
}
static_assert(std::is_unsigned<decltype(version)>::value,
"version must be unsigned");
- return kSizeForVersion[version];
+
+ if (version == 15) {
+ // Disambiguate between the two different layouts for version 15. The
+ // original one introduced in macOS 10.12 had the same size as version 14.
+ // The revised one in macOS 10.13 grew. It’s safe to assume that the
+ // dyld_all_image_infos structure came from the same system that’s now
+ // interpreting it, so use an OS version check.
+ int mac_os_x_minor_version = MacOSXMinorVersion();
+ if (mac_os_x_minor_version == 12) {
+ return offsetof(dyld_all_image_infos<Traits>, end_14);
+ }
+
+ DCHECK_GE(mac_os_x_minor_version, 13);
+ DCHECK_LE(mac_os_x_minor_version, 14);
+ return offsetof(dyld_all_image_infos<Traits>, platform);
+ }
+
+ size_t size = kSizeForVersion[version];
+ DCHECK_NE(size, std::numeric_limits<size_t>::max());
+
+ return size;
}
// static
diff --git a/snapshot/mac/process_types/dyld_images.proctype b/snapshot/mac/process_types/dyld_images.proctype
index f92a800..3b04085 100644
--- a/snapshot/mac/process_types/dyld_images.proctype
+++ b/snapshot/mac/process_types/dyld_images.proctype
@@ -121,23 +121,29 @@
// the runtimes that use versions 14 and 15 were built with SDKs that did not
// have this extra padding, it’s necessary to treat the element at index 4 on
// 32-bit systems as outside of the version 14 and 15 structure. This is why
- // |reserved| is only declared a 4-element array, with a special end_14_15
- // member (not present in the native definition) available to indicate the
- // end of the native version 14 and 15 structure, preceding the padding in the
- // 32-bit structure that would natively be addressed at index 4 of |reserved|.
- // Treat reserved_4_32 as only available in version 16 of the structure.
+ // |reserved| is only declared a 4-element array, with a special end_14 member
+ // (not present in the native definition) available to indicate the end of the
+ // native version 14 structure and the 10.12 version 15 structure, preceding
+ // the padding in the 32-bit structure that would natively be addressed at
+ // index 4 of |reserved|. Treat reserved_4_32 as only available in version 16
+ // of the structure.
PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [4])
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4_64)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_5)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_7)
PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_8)
- PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_14_15)
+ PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_14)
PROCESS_TYPE_STRUCT_MEMBER(Reserved32_32Only, reserved_4_32)
- // Version 16 (macOS 10.13)
+ // Version 15 (macOS 10.13). <mach-o/dyld_images.h> incorrectly claims that
+ // these were introduced at version 16. These fields are not present in macOS
+ // 10.12, which also identifies its structure as version 15.
PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, compact_dyld_image_info_addr)
PROCESS_TYPE_STRUCT_MEMBER(ULong, compact_dyld_image_info_size) // size_t
+
+ // Version 16 (macOS 10.15)
+ PROCESS_TYPE_STRUCT_MEMBER(uint32_t, platform) // dyld_platform_t
PROCESS_TYPE_STRUCT_END(dyld_all_image_infos)
#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&
diff --git a/snapshot/mac/process_types_test.cc b/snapshot/mac/process_types_test.cc
index f116c4d..0ad2869 100644
--- a/snapshot/mac/process_types_test.cc
+++ b/snapshot/mac/process_types_test.cc
@@ -18,9 +18,10 @@
#include <mach/mach.h>
#include <string.h>
+#include <limits>
#include <vector>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -47,13 +48,11 @@
TEST(ProcessTypes, DyldImagesSelf) {
// Get the in-process view of dyld_all_image_infos, and check it for sanity.
const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos();
- int mac_os_x_minor_version = MacOSXMinorVersion();
+ const int mac_os_x_minor_version = MacOSXMinorVersion();
- // The 10.13 SDK defines dyld_all_image_infos version 16 and says that it’s
- // used on 10.13, but 10.13db1 17A264c uses version 15.
- //
- // TODO(mark): Recheck later in the beta period, up to the 10.13 release.
- if (mac_os_x_minor_version >= 12) {
+ if (mac_os_x_minor_version >= 15) {
+ EXPECT_GE(self_image_infos->version, 16u);
+ } else if (mac_os_x_minor_version >= 12) {
EXPECT_GE(self_image_infos->version, 15u);
} else if (mac_os_x_minor_version >= 9) {
EXPECT_GE(self_image_infos->version, 13u);
@@ -104,7 +103,7 @@
ProcessReaderMac process_reader;
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 16;
#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
constexpr uint32_t kDyldAllImageInfosVersionInSDK = 15;
@@ -120,12 +119,33 @@
// Make sure that the size of the structure as declared in the SDK matches the
// size expected for the version of the structure that the SDK describes.
- EXPECT_EQ(process_types::dyld_all_image_infos::ExpectedSizeForVersion(
- &process_reader, kDyldAllImageInfosVersionInSDK),
- sizeof(dyld_all_image_infos));
+ //
+ // There are two possible layouts for version 15, and the
+ // ExpectedSizeForVersion() implementation infers the correct one based on the
+ // run-time OS version, so if the SDK defines the version 15 structure, this
+ // test can only be performed if the run-time OS natively uses the same format
+ // structure as the SDK.
+ bool test_expected_size_for_version_matches_sdk_sizeof;
+#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_12
+ test_expected_size_for_version_matches_sdk_sizeof =
+ mac_os_x_minor_version == 12;
+#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 && \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_15
+ test_expected_size_for_version_matches_sdk_sizeof =
+ mac_os_x_minor_version >= 13 && mac_os_x_minor_version <= 14;
+#else
+ test_expected_size_for_version_matches_sdk_sizeof = true;
+#endif
+
+ if (test_expected_size_for_version_matches_sdk_sizeof) {
+ EXPECT_EQ(process_types::dyld_all_image_infos::ExpectedSizeForVersion(
+ &process_reader, kDyldAllImageInfosVersionInSDK),
+ sizeof(dyld_all_image_infos));
+ }
// Make sure that the computed sizes of various versions of this structure are
// correct at different bitnessses.
+ constexpr size_t kSpecialCase = std::numeric_limits<size_t>::max();
constexpr struct {
uint32_t version;
size_t size_32;
@@ -144,13 +164,40 @@
{12, 84, 160},
{13, 104, 184},
{14, 164, 304},
- {15, 164, 304},
- {16, 176, 320},
+ {15, kSpecialCase, kSpecialCase},
+ {16, 184, 328},
};
- for (size_t index = 0; index < arraysize(kVersionsAndSizes); ++index) {
+ for (size_t index = 0; index < base::size(kVersionsAndSizes); ++index) {
uint32_t version = kVersionsAndSizes[index].version;
SCOPED_TRACE(base::StringPrintf("index %zu, version %u", index, version));
+ if (version == 15) {
+ if (mac_os_x_minor_version == 12) {
+ EXPECT_EQ(process_types::internal::dyld_all_image_infos<
+ process_types::internal::Traits32>::
+ ExpectedSizeForVersion(version),
+ 164u);
+ EXPECT_EQ(process_types::internal::dyld_all_image_infos<
+ process_types::internal::Traits64>::
+ ExpectedSizeForVersion(version),
+ 304u);
+ } else if (mac_os_x_minor_version >= 13 && mac_os_x_minor_version <= 14) {
+ EXPECT_EQ(process_types::internal::dyld_all_image_infos<
+ process_types::internal::Traits32>::
+ ExpectedSizeForVersion(version),
+ 176u);
+ EXPECT_EQ(process_types::internal::dyld_all_image_infos<
+ process_types::internal::Traits64>::
+ ExpectedSizeForVersion(version),
+ 320u);
+ }
+
+ continue;
+ }
+
+ ASSERT_NE(kVersionsAndSizes[index].size_32, kSpecialCase);
+ ASSERT_NE(kVersionsAndSizes[index].size_64, kSpecialCase);
+
EXPECT_EQ(
process_types::internal::dyld_all_image_infos<
process_types::internal::Traits32>::ExpectedSizeForVersion(version),
@@ -268,8 +315,7 @@
self_image_infos->sharedCacheBaseAddress);
EXPECT_EQ(proctype_image_infos.dyldPath,
reinterpret_cast<uint64_t>(self_image_infos->dyldPath));
- for (size_t index = 0;
- index < arraysize(self_image_infos->notifyPorts);
+ for (size_t index = 0; index < base::size(self_image_infos->notifyPorts);
++index) {
EXPECT_EQ(proctype_image_infos.notifyPorts[index],
self_image_infos->notifyPorts[index])
@@ -289,8 +335,7 @@
// process_types version. It’s difficult to compare the reserved fields in
// these older SDKs, so only do it where the declarations match.
if (proctype_image_infos.version >= 14) {
- for (size_t index = 0;
- index < arraysize(proctype_image_infos.reserved);
+ for (size_t index = 0; index < base::size(proctype_image_infos.reserved);
++index) {
EXPECT_EQ(proctype_image_infos.reserved[index],
implicit_cast<uint64_t>(self_image_infos->reserved[index]))
@@ -308,7 +353,7 @@
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
- if (proctype_image_infos.version >= 16) {
+ if (proctype_image_infos.version >= 15 && mac_os_x_minor_version >= 13) {
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr,
self_image_infos->compact_dyld_image_info_addr);
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size,
@@ -316,6 +361,12 @@
}
#endif
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
+ if (proctype_image_infos.version >= 16) {
+ EXPECT_EQ(proctype_image_infos.platform, self_image_infos->platform);
+ }
+#endif
+
if (proctype_image_infos.version >= 1) {
std::vector<process_types::dyld_image_info> proctype_image_info_vector(
proctype_image_infos.infoArrayCount);
diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc
index 21f254e..5410be0 100644
--- a/snapshot/mac/system_snapshot_mac.cc
+++ b/snapshot/mac/system_snapshot_mac.cc
@@ -14,6 +14,7 @@
#include "snapshot/mac/system_snapshot_mac.h"
+#include <AvailabilityMacros.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <sys/types.h>
@@ -22,6 +23,7 @@
#include <algorithm>
#include "base/logging.h"
+#include "base/scoped_clear_last_error.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
@@ -35,10 +37,15 @@
namespace {
template <typename T>
+int ReadIntSysctlByName_NoLog(const char* name, T* value) {
+ size_t value_len = sizeof(*value);
+ return sysctlbyname(name, value, &value_len, nullptr, 0);
+}
+
+template <typename T>
T ReadIntSysctlByName(const char* name, T default_value) {
T value;
- size_t value_len = sizeof(value);
- if (sysctlbyname(name, &value, &value_len, nullptr, 0) != 0) {
+ if (ReadIntSysctlByName_NoLog(name, &value) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return default_value;
}
@@ -338,7 +345,35 @@
bool SystemSnapshotMac::NXEnabled() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return ReadIntSysctlByName<int>("kern.nx", 0);
+
+ int value;
+ if (ReadIntSysctlByName_NoLog("kern.nx", &value) != 0) {
+ {
+ // Support for the kern.nx sysctlbyname is compiled out of production
+ // kernels on macOS 10.14.5 and later, although it’s available in
+ // development and debug kernels. Compare 10.14.3
+ // xnu-4903.241.1/bsd/kern/kern_sysctl.c to 10.15.0
+ // xnu-6153.11.26/bsd/kern/kern_sysctl.c (10.14.4 and 10.14.5 xnu source
+ // are not yet available). In newer production kernels, NX is always
+ // enabled. See 10.15.0 xnu-6153.11.26/osfmk/x86_64/pmap.c nx_enabled.
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
+ const bool nx_always_enabled = true;
+#else // DT >= 10.14
+ base::ScopedClearLastError reset_errno;
+ const bool nx_always_enabled = MacOSXMinorVersion() >= 14;
+#endif // DT >= 10.14
+ if (nx_always_enabled) {
+ return true;
+ }
+ }
+
+ // Even if sysctlbyname should have worked, NX is enabled by default in all
+ // supported configurations, so return true even while warning.
+ PLOG(WARNING) << "sysctlbyname kern.nx";
+ return true;
+ }
+
+ return value;
}
void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
diff --git a/snapshot/mac/system_snapshot_mac_test.cc b/snapshot/mac/system_snapshot_mac_test.cc
index 69048eb..2b66592 100644
--- a/snapshot/mac/system_snapshot_mac_test.cc
+++ b/snapshot/mac/system_snapshot_mac_test.cc
@@ -126,6 +126,12 @@
EXPECT_FALSE(system_snapshot().MachineDescription().empty());
}
+TEST_F(SystemSnapshotMacTest, NXEnabled) {
+ // Assume NX will always be enabled, as it was always enabled by default on
+ // all supported versions of macOS.
+ EXPECT_TRUE(system_snapshot().NXEnabled());
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/snapshot/mac/thread_snapshot_mac.cc b/snapshot/mac/thread_snapshot_mac.cc
index f45fc41..8fbb3e7 100644
--- a/snapshot/mac/thread_snapshot_mac.cc
+++ b/snapshot/mac/thread_snapshot_mac.cc
@@ -49,7 +49,7 @@
thread_specific_data_address_ =
process_reader_thread.thread_specific_data_address;
- stack_.Initialize(process_reader,
+ stack_.Initialize(process_reader->Memory(),
process_reader_thread.stack_region_address,
process_reader_thread.stack_region_size);
diff --git a/snapshot/mac/thread_snapshot_mac.h b/snapshot/mac/thread_snapshot_mac.h
index 8f5d722..946b008 100644
--- a/snapshot/mac/thread_snapshot_mac.h
+++ b/snapshot/mac/thread_snapshot_mac.h
@@ -70,7 +70,7 @@
} context_union_;
#endif
CPUContext context_;
- MemorySnapshotGeneric<ProcessReaderMac> stack_;
+ MemorySnapshotGeneric stack_;
uint64_t thread_id_;
uint64_t thread_specific_data_address_;
thread_t thread_;
diff --git a/snapshot/memory_snapshot.h b/snapshot/memory_snapshot.h
index 9e274a0..5dbac5a 100644
--- a/snapshot/memory_snapshot.h
+++ b/snapshot/memory_snapshot.h
@@ -111,31 +111,6 @@
const MemorySnapshot* b,
CheckedRange<uint64_t, size_t>* merged);
-namespace internal {
-
-//! \brief A standard implementation of MemorySnapshot::MergeWithOtherSnapshot()
-//! for concrete MemorySnapshot implementations that use a
-//! `process_reader_`.
-template <class T>
-const MemorySnapshot* MergeWithOtherSnapshotImpl(const T* self,
- const MemorySnapshot* other) {
- const T* other_as_memory_snapshot_concrete =
- reinterpret_cast<const T*>(other);
- if (self->process_reader_ !=
- other_as_memory_snapshot_concrete->process_reader_) {
- LOG(ERROR) << "different process_reader_ for snapshots";
- return nullptr;
- }
- CheckedRange<uint64_t, size_t> merged(0, 0);
- if (!LoggingDetermineMergedRange(self, other, &merged))
- return nullptr;
-
- std::unique_ptr<T> result(new T());
- result->Initialize(self->process_reader_, merged.base(), merged.size());
- return result.release();
-}
-
-} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
diff --git a/snapshot/memory_snapshot_generic.h b/snapshot/memory_snapshot_generic.h
index 402e913..d8bc9ec 100644
--- a/snapshot/memory_snapshot_generic.h
+++ b/snapshot/memory_snapshot_generic.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include "base/macros.h"
+#include "base/numerics/safe_math.h"
#include "snapshot/memory_snapshot.h"
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
@@ -28,9 +29,8 @@
namespace internal {
//! \brief A MemorySnapshot of a memory region in a process on the running
-//! system. Used on Mac, Linux, Android, and Fuchsia, templated on the
-//! platform-specific ProcessReader type.
-template <class ProcessReaderType>
+//! system. Works on multiple platforms by using a platform-specific
+//! ProcessMemory object.
class MemorySnapshotGeneric final : public MemorySnapshot {
public:
MemorySnapshotGeneric() = default;
@@ -42,25 +42,25 @@
//! until Read() is called, and the memory snapshot data is discared when
//! Read() returns.
//!
- //! \param[in] process_reader A reader for the process being snapshotted.
+ //! \param[in] process_memory A reader for the process being snapshotted.
//! \param[in] address The base address of the memory region to snapshot, in
//! the snapshot process’ address space.
//! \param[in] size The size of the memory region to snapshot.
- void Initialize(ProcessReaderType* process_reader,
+ void Initialize(const ProcessMemory* process_memory,
VMAddress address,
VMSize size) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- process_reader_ = process_reader;
+ process_memory_ = process_memory;
address_ = address;
- size_ = size;
+ size_ = base::checked_cast<size_t>(size);
INITIALIZATION_STATE_SET_VALID(initialized_);
}
// MemorySnapshot:
uint64_t Address() const override {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return address_;
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return address_;
}
size_t Size() const override {
@@ -76,7 +76,7 @@
}
std::unique_ptr<uint8_t[]> buffer(new uint8_t[size_]);
- if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) {
+ if (!process_memory_->Read(address_, size_, buffer.get())) {
return false;
}
return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
@@ -84,7 +84,19 @@
const MemorySnapshot* MergeWithOtherSnapshot(
const MemorySnapshot* other) const override {
- return MergeWithOtherSnapshotImpl(this, other);
+ const MemorySnapshotGeneric* other_as_memory_snapshot_concrete =
+ reinterpret_cast<const MemorySnapshotGeneric*>(other);
+ if (process_memory_ != other_as_memory_snapshot_concrete->process_memory_) {
+ LOG(ERROR) << "different process_memory_ for snapshots";
+ return nullptr;
+ }
+ CheckedRange<uint64_t, size_t> merged(0, 0);
+ if (!LoggingDetermineMergedRange(this, other, &merged))
+ return nullptr;
+
+ auto result = std::make_unique<MemorySnapshotGeneric>();
+ result->Initialize(process_memory_, merged.base(), merged.size());
+ return result.release();
}
private:
@@ -93,9 +105,9 @@
const T* self,
const MemorySnapshot* other);
- ProcessReaderType* process_reader_; // weak
- uint64_t address_;
- uint64_t size_;
+ const ProcessMemory* process_memory_; // weak
+ VMAddress address_;
+ size_t size_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotGeneric);
diff --git a/snapshot/minidump/exception_snapshot_minidump.cc b/snapshot/minidump/exception_snapshot_minidump.cc
new file mode 100644
index 0000000..afd3282
--- /dev/null
+++ b/snapshot/minidump/exception_snapshot_minidump.cc
@@ -0,0 +1,111 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/exception_snapshot_minidump.h"
+
+#include "snapshot/minidump/minidump_string_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+ExceptionSnapshotMinidump::ExceptionSnapshotMinidump()
+ : ExceptionSnapshot(),
+ minidump_exception_stream_(),
+ context_(),
+ exception_information_(),
+ initialized_() {}
+
+ExceptionSnapshotMinidump::~ExceptionSnapshotMinidump() {}
+
+bool ExceptionSnapshotMinidump::Initialize(FileReaderInterface* file_reader,
+ CPUArchitecture arch,
+ RVA minidump_exception_stream_rva) {
+ DCHECK(initialized_.is_uninitialized());
+ initialized_.set_invalid();
+
+ std::vector<unsigned char> minidump_context;
+
+ if (!file_reader->SeekSet(minidump_exception_stream_rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&minidump_exception_stream_,
+ sizeof(minidump_exception_stream_))) {
+ return false;
+ }
+
+ const size_t num_parameters =
+ minidump_exception_stream_.ExceptionRecord.NumberParameters;
+ for (size_t i = 0; i < num_parameters; ++i) {
+ exception_information_.push_back(
+ minidump_exception_stream_.ExceptionRecord.ExceptionInformation[i]);
+ }
+
+ if (!file_reader->SeekSet(minidump_exception_stream_.ThreadContext.Rva)) {
+ return false;
+ }
+
+ minidump_context.resize(minidump_exception_stream_.ThreadContext.DataSize);
+
+ if (!file_reader->ReadExactly(minidump_context.data(),
+ minidump_context.size())) {
+ return false;
+ }
+
+ if (!context_.Initialize(arch, minidump_context)) {
+ return false;
+ }
+
+ initialized_.set_valid();
+ return true;
+}
+
+const CPUContext* ExceptionSnapshotMinidump::Context() const {
+ DCHECK(initialized_.is_valid());
+ return context_.Get();
+}
+
+uint64_t ExceptionSnapshotMinidump::ThreadID() const {
+ DCHECK(initialized_.is_valid());
+ return minidump_exception_stream_.ThreadId;
+}
+
+uint32_t ExceptionSnapshotMinidump::Exception() const {
+ DCHECK(initialized_.is_valid());
+ return minidump_exception_stream_.ExceptionRecord.ExceptionCode;
+}
+
+uint32_t ExceptionSnapshotMinidump::ExceptionInfo() const {
+ DCHECK(initialized_.is_valid());
+ return minidump_exception_stream_.ExceptionRecord.ExceptionFlags;
+}
+
+uint64_t ExceptionSnapshotMinidump::ExceptionAddress() const {
+ DCHECK(initialized_.is_valid());
+ return minidump_exception_stream_.ExceptionRecord.ExceptionAddress;
+}
+
+const std::vector<uint64_t>& ExceptionSnapshotMinidump::Codes() const {
+ DCHECK(initialized_.is_valid());
+ return exception_information_;
+}
+
+std::vector<const MemorySnapshot*> ExceptionSnapshotMinidump::ExtraMemory()
+ const {
+ DCHECK(initialized_.is_valid());
+ return std::vector<const MemorySnapshot*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/minidump/exception_snapshot_minidump.h b/snapshot/minidump/exception_snapshot_minidump.h
new file mode 100644
index 0000000..62f834f
--- /dev/null
+++ b/snapshot/minidump/exception_snapshot_minidump.h
@@ -0,0 +1,76 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include "build/build_config.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/exception_snapshot.h"
+#include "snapshot/minidump/minidump_context_converter.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief An ExceptionSnapshot based on a minidump file.
+class ExceptionSnapshotMinidump final : public ExceptionSnapshot {
+ public:
+ ExceptionSnapshotMinidump();
+ ~ExceptionSnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //! \param[in] arch The CPU architecture of this snapshot.
+ //! \param[in] minidump_exception_stream_rva The file offset in \a file_reader
+ //! at which the MINIDUMP_EXCEPTION_STREAM structure is located.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader,
+ CPUArchitecture arch,
+ RVA minidump_exception_stream_rva);
+
+ // ExceptionSnapshot:
+ const CPUContext* Context() const override;
+ uint64_t ThreadID() const override;
+ uint32_t Exception() const override;
+ uint32_t ExceptionInfo() const override;
+ uint64_t ExceptionAddress() const override;
+ const std::vector<uint64_t>& Codes() const override;
+ std::vector<const MemorySnapshot*> ExtraMemory() const override;
+
+ // Allow callers to explicitly check whether this exception snapshot has been
+ // initialized.
+ bool IsValid() const { return initialized_.is_valid(); }
+
+ private:
+ MINIDUMP_EXCEPTION_STREAM minidump_exception_stream_;
+ MinidumpContextConverter context_;
+ std::vector<uint64_t> exception_information_;
+ InitializationState initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotMinidump);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_
diff --git a/snapshot/minidump/memory_snapshot_minidump.cc b/snapshot/minidump/memory_snapshot_minidump.cc
new file mode 100644
index 0000000..2c6c899
--- /dev/null
+++ b/snapshot/minidump/memory_snapshot_minidump.cc
@@ -0,0 +1,111 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/memory_snapshot_minidump.h"
+
+#include <memory>
+
+#include "base/numerics/safe_math.h"
+
+namespace crashpad {
+namespace internal {
+
+MemorySnapshotMinidump::MemorySnapshotMinidump()
+ : MemorySnapshot(),
+ address_(0),
+ data_(),
+ initialized_() {}
+
+MemorySnapshotMinidump::~MemorySnapshotMinidump() {}
+
+bool MemorySnapshotMinidump::Initialize(FileReaderInterface* file_reader,
+ RVA location) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ MINIDUMP_MEMORY_DESCRIPTOR descriptor;
+
+ if (!file_reader->SeekSet(location)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&descriptor, sizeof(descriptor))) {
+ return false;
+ }
+
+ address_ = descriptor.StartOfMemoryRange;
+ data_.resize(descriptor.Memory.DataSize);
+
+ if (!file_reader->SeekSet(descriptor.Memory.Rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(data_.data(), data_.size())) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+uint64_t MemorySnapshotMinidump::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return address_;
+}
+
+size_t MemorySnapshotMinidump::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return data_.size();
+}
+
+bool MemorySnapshotMinidump::Read(Delegate* delegate) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return delegate->MemorySnapshotDelegateRead(
+ const_cast<uint8_t*>(data_.data()), data_.size());
+}
+
+const MemorySnapshot* MemorySnapshotMinidump::MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ // TODO: Verify type of other
+ auto other_cast = reinterpret_cast<const MemorySnapshotMinidump*>(other);
+
+ INITIALIZATION_STATE_DCHECK_VALID(other_cast->initialized_);
+
+ if (other_cast->address_ < address_) {
+ return other_cast->MergeWithOtherSnapshot(this);
+ }
+
+ CheckedRange<uint64_t, size_t> merged(0, 0);
+ if (!LoggingDetermineMergedRange(this, other, &merged)) {
+ return nullptr;
+ }
+
+ auto result = std::make_unique<MemorySnapshotMinidump>();
+ result->address_ = merged.base();
+ result->data_ = data_;
+
+ if (result->data_.size() == merged.size()) {
+ return result.release();
+ }
+
+ result->data_.resize(
+ base::checked_cast<size_t>(other_cast->address_ - address_));
+ result->data_.insert(result->data_.end(), other_cast->data_.begin(),
+ other_cast->data_.end());
+ return result.release();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/minidump/memory_snapshot_minidump.h b/snapshot/minidump/memory_snapshot_minidump.h
new file mode 100644
index 0000000..69521be
--- /dev/null
+++ b/snapshot/minidump/memory_snapshot_minidump.h
@@ -0,0 +1,63 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_
+
+#include <windows.h>
+#include <dbghelp.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "snapshot/memory_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+class MemorySnapshotMinidump : public MemorySnapshot {
+ public:
+ MemorySnapshotMinidump();
+ ~MemorySnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //! \param[in] location The location within the file where we will find a
+ //! MINIDUMP_MEMORY_DESCRIPTOR from which to initialize this object.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader, RVA location);
+
+ uint64_t Address() const override;
+ size_t Size() const override;
+ bool Read(Delegate* delegate) const override;
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const override;
+
+ private:
+ uint64_t address_;
+ std::vector<uint8_t> data_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemorySnapshotMinidump);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_
diff --git a/snapshot/minidump/minidump_context_converter.cc b/snapshot/minidump/minidump_context_converter.cc
new file mode 100644
index 0000000..0c840de
--- /dev/null
+++ b/snapshot/minidump/minidump_context_converter.cc
@@ -0,0 +1,275 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/minidump_context_converter.h"
+
+#include "base/stl_util.h"
+#include "minidump/minidump_context.h"
+
+namespace crashpad {
+namespace internal {
+
+MinidumpContextConverter::MinidumpContextConverter() : initialized_() {
+ context_.architecture = CPUArchitecture::kCPUArchitectureUnknown;
+}
+
+bool MinidumpContextConverter::Initialize(
+ CPUArchitecture arch,
+ const std::vector<unsigned char>& minidump_context) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (minidump_context.size() == 0) {
+ // Thread has no context.
+ context_.architecture = CPUArchitecture::kCPUArchitectureUnknown;
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+ }
+
+ context_.architecture = arch;
+
+ if (context_.architecture == CPUArchitecture::kCPUArchitectureX86) {
+ context_memory_.resize(sizeof(CPUContextX86));
+ context_.x86 = reinterpret_cast<CPUContextX86*>(context_memory_.data());
+ const MinidumpContextX86* src =
+ reinterpret_cast<const MinidumpContextX86*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextX86)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextX86)) {
+ return false;
+ }
+
+ if (src->context_flags & kMinidumpContextX86Extended) {
+ context_.x86->fxsave = src->fxsave;
+ } else if (src->context_flags & kMinidumpContextX86FloatingPoint) {
+ CPUContextX86::FsaveToFxsave(src->fsave, &context_.x86->fxsave);
+ }
+
+ context_.x86->eax = src->eax;
+ context_.x86->ebx = src->ebx;
+ context_.x86->ecx = src->ecx;
+ context_.x86->edx = src->edx;
+ context_.x86->edi = src->edi;
+ context_.x86->esi = src->esi;
+ context_.x86->ebp = src->ebp;
+ context_.x86->esp = src->esp;
+ context_.x86->eip = src->eip;
+ context_.x86->eflags = src->eflags;
+ context_.x86->cs = static_cast<uint16_t>(src->cs);
+ context_.x86->ds = static_cast<uint16_t>(src->ds);
+ context_.x86->es = static_cast<uint16_t>(src->es);
+ context_.x86->fs = static_cast<uint16_t>(src->fs);
+ context_.x86->gs = static_cast<uint16_t>(src->gs);
+ context_.x86->ss = static_cast<uint16_t>(src->ss);
+ context_.x86->dr0 = src->dr0;
+ context_.x86->dr1 = src->dr1;
+ context_.x86->dr2 = src->dr2;
+ context_.x86->dr3 = src->dr3;
+ context_.x86->dr6 = src->dr6;
+ context_.x86->dr7 = src->dr7;
+
+ // Minidump passes no value for dr4/5. Our output context has space for
+ // them. According to spec they're obsolete, but when present read as
+ // aliases for dr6/7, so we'll do this.
+ context_.x86->dr4 = src->dr6;
+ context_.x86->dr5 = src->dr7;
+ } else if (context_.architecture == CPUArchitecture::kCPUArchitectureX86_64) {
+ context_memory_.resize(sizeof(CPUContextX86_64));
+ context_.x86_64 =
+ reinterpret_cast<CPUContextX86_64*>(context_memory_.data());
+ const MinidumpContextAMD64* src =
+ reinterpret_cast<const MinidumpContextAMD64*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextAMD64)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextAMD64)) {
+ return false;
+ }
+
+ context_.x86_64->fxsave = src->fxsave;
+ context_.x86_64->cs = src->cs;
+ context_.x86_64->fs = src->fs;
+ context_.x86_64->gs = src->gs;
+ context_.x86_64->rflags = src->eflags;
+ context_.x86_64->dr0 = src->dr0;
+ context_.x86_64->dr1 = src->dr1;
+ context_.x86_64->dr2 = src->dr2;
+ context_.x86_64->dr3 = src->dr3;
+ context_.x86_64->dr6 = src->dr6;
+ context_.x86_64->dr7 = src->dr7;
+ context_.x86_64->rax = src->rax;
+ context_.x86_64->rcx = src->rcx;
+ context_.x86_64->rdx = src->rdx;
+ context_.x86_64->rbx = src->rbx;
+ context_.x86_64->rsp = src->rsp;
+ context_.x86_64->rbp = src->rbp;
+ context_.x86_64->rsi = src->rsi;
+ context_.x86_64->rdi = src->rdi;
+ context_.x86_64->r8 = src->r8;
+ context_.x86_64->r9 = src->r9;
+ context_.x86_64->r10 = src->r10;
+ context_.x86_64->r11 = src->r11;
+ context_.x86_64->r12 = src->r12;
+ context_.x86_64->r13 = src->r13;
+ context_.x86_64->r14 = src->r14;
+ context_.x86_64->r15 = src->r15;
+ context_.x86_64->rip = src->rip;
+
+ // See comments on x86 above.
+ context_.x86_64->dr4 = src->dr6;
+ context_.x86_64->dr5 = src->dr7;
+ } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM) {
+ context_memory_.resize(sizeof(CPUContextARM));
+ context_.arm = reinterpret_cast<CPUContextARM*>(context_memory_.data());
+ const MinidumpContextARM* src =
+ reinterpret_cast<const MinidumpContextARM*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextARM)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextARM)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < base::size(src->regs); i++) {
+ context_.arm->regs[i] = src->regs[i];
+ }
+
+ context_.arm->fp = src->fp;
+ context_.arm->ip = src->ip;
+ context_.arm->sp = src->sp;
+ context_.arm->lr = src->lr;
+ context_.arm->pc = src->pc;
+ context_.arm->cpsr = src->cpsr;
+ context_.arm->vfp_regs.fpscr = src->fpscr;
+
+ for (size_t i = 0; i < base::size(src->vfp); i++) {
+ context_.arm->vfp_regs.vfp[i] = src->vfp[i];
+ }
+
+ context_.arm->have_fpa_regs = false;
+ context_.arm->have_vfp_regs =
+ !!(src->context_flags & kMinidumpContextARMVFP);
+ } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM64) {
+ context_memory_.resize(sizeof(CPUContextARM64));
+ context_.arm64 = reinterpret_cast<CPUContextARM64*>(context_memory_.data());
+ const MinidumpContextARM64* src =
+ reinterpret_cast<const MinidumpContextARM64*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextARM64)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextARM64)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < base::size(src->regs); i++) {
+ context_.arm64->regs[i] = src->regs[i];
+ }
+
+ context_.arm64->regs[29] = src->fp;
+ context_.arm64->regs[30] = src->lr;
+
+ for (size_t i = 0; i < base::size(src->fpsimd); i++) {
+ context_.arm64->fpsimd[i] = src->fpsimd[i];
+ }
+
+ context_.arm64->sp = src->sp;
+ context_.arm64->pc = src->pc;
+ context_.arm64->fpcr = src->fpcr;
+ context_.arm64->fpsr = src->fpsr;
+ context_.arm64->spsr = src->cpsr;
+ } else if (context_.architecture == CPUArchitecture::kCPUArchitectureMIPSEL) {
+ context_memory_.resize(sizeof(CPUContextMIPS));
+ context_.mipsel = reinterpret_cast<CPUContextMIPS*>(context_memory_.data());
+ const MinidumpContextMIPS* src =
+ reinterpret_cast<const MinidumpContextMIPS*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextMIPS)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextMIPS)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < base::size(src->regs); i++) {
+ context_.mipsel->regs[i] = src->regs[i];
+ }
+
+ context_.mipsel->mdhi = static_cast<uint32_t>(src->mdhi);
+ context_.mipsel->mdlo = static_cast<uint32_t>(src->mdlo);
+ context_.mipsel->dsp_control = src->dsp_control;
+
+ for (size_t i = 0; i < base::size(src->hi); i++) {
+ context_.mipsel->hi[i] = src->hi[i];
+ context_.mipsel->lo[i] = src->lo[i];
+ }
+
+ context_.mipsel->cp0_epc = static_cast<uint32_t>(src->epc);
+ context_.mipsel->cp0_badvaddr = static_cast<uint32_t>(src->badvaddr);
+ context_.mipsel->cp0_status = src->status;
+ context_.mipsel->cp0_cause = src->cause;
+ context_.mipsel->fpcsr = src->fpcsr;
+ context_.mipsel->fir = src->fir;
+
+ memcpy(&context_.mipsel->fpregs, &src->fpregs, sizeof(src->fpregs));
+ } else if (context_.architecture ==
+ CPUArchitecture::kCPUArchitectureMIPS64EL) {
+ context_memory_.resize(sizeof(CPUContextMIPS64));
+ context_.mips64 =
+ reinterpret_cast<CPUContextMIPS64*>(context_memory_.data());
+ const MinidumpContextMIPS64* src =
+ reinterpret_cast<const MinidumpContextMIPS64*>(minidump_context.data());
+ if (minidump_context.size() < sizeof(MinidumpContextMIPS64)) {
+ return false;
+ }
+
+ if (!(src->context_flags & kMinidumpContextMIPS64)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < base::size(src->regs); i++) {
+ context_.mips64->regs[i] = src->regs[i];
+ }
+
+ context_.mips64->mdhi = src->mdhi;
+ context_.mips64->mdlo = src->mdlo;
+ context_.mips64->dsp_control = src->dsp_control;
+
+ for (size_t i = 0; i < base::size(src->hi); i++) {
+ context_.mips64->hi[i] = src->hi[i];
+ context_.mips64->lo[i] = src->lo[i];
+ }
+
+ context_.mips64->cp0_epc = src->epc;
+ context_.mips64->cp0_badvaddr = src->badvaddr;
+ context_.mips64->cp0_status = src->status;
+ context_.mips64->cp0_cause = src->cause;
+ context_.mips64->fpcsr = src->fpcsr;
+ context_.mips64->fir = src->fir;
+
+ memcpy(&context_.mips64->fpregs, &src->fpregs, sizeof(src->fpregs));
+ } else {
+ // Architecture is listed as "unknown".
+ DLOG(ERROR) << "Unknown architecture";
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/minidump/minidump_context_converter.h b/snapshot/minidump/minidump_context_converter.h
new file mode 100644
index 0000000..48e36c1
--- /dev/null
+++ b/snapshot/minidump/minidump_context_converter.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_
+
+#include <vector>
+
+#include "snapshot/cpu_context.h"
+#include "util/misc/initialization_state.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+class MinidumpContextConverter {
+ public:
+ MinidumpContextConverter();
+
+ bool Initialize(CPUArchitecture arch,
+ const std::vector<unsigned char>& minidump_context);
+ const CPUContext* Get() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &context_;
+ }
+
+ private:
+ CPUContext context_;
+ std::vector<unsigned char> context_memory_;
+ InitializationStateDcheck initialized_;
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_
diff --git a/snapshot/minidump/minidump_stream.h b/snapshot/minidump/minidump_stream.h
new file mode 100644
index 0000000..2b0ac2e
--- /dev/null
+++ b/snapshot/minidump/minidump_stream.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief Stores a minidump stream along with its stream ID.
+class MinidumpStream {
+ public:
+ MinidumpStream(uint32_t stream_type, std::vector<uint8_t> data)
+ : stream_type_(stream_type), data_(data) {}
+
+ uint32_t stream_type() const { return stream_type_; }
+ const std::vector<uint8_t>& data() const { return data_; }
+
+ private:
+ uint32_t stream_type_;
+ std::vector<uint8_t> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
diff --git a/snapshot/minidump/minidump_string_reader.cc b/snapshot/minidump/minidump_string_reader.cc
index 4a5cb13..d642e9d 100644
--- a/snapshot/minidump/minidump_string_reader.cc
+++ b/snapshot/minidump/minidump_string_reader.cc
@@ -17,14 +17,18 @@
#include <stdint.h>
#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
#include "minidump/minidump_extensions.h"
namespace crashpad {
namespace internal {
-bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
+namespace {
+
+template<typename StringType>
+bool ReadMinidumpString(FileReaderInterface* file_reader,
RVA rva,
- std::string* string) {
+ StringType* string) {
if (rva == 0) {
string->clear();
return true;
@@ -39,7 +43,7 @@
return false;
}
- std::string local_string(string_size, '\0');
+ StringType local_string(string_size / sizeof((*string)[0]), '\0');
if (!file_reader->ReadExactly(&local_string[0], string_size)) {
return false;
}
@@ -48,5 +52,33 @@
return true;
}
+} // namespace
+
+bool ReadMinidumpUTF8String(FileReaderInterface* file_reader,
+ RVA rva,
+ std::string* string) {
+ return ReadMinidumpString(file_reader, rva, string);
+}
+
+bool ReadMinidumpUTF16String(FileReaderInterface* file_reader,
+ RVA rva,
+ base::string16* string) {
+ return ReadMinidumpString(file_reader, rva, string);
+}
+
+bool ReadMinidumpUTF16String(FileReaderInterface* file_reader,
+ RVA rva,
+ std::string* string) {
+ base::string16 string_raw;
+
+ if (!ReadMinidumpString(file_reader, rva, &string_raw)) {
+ return false;
+ }
+
+ base::UTF16ToUTF8(string_raw.data(), string_raw.size(), string);
+
+ return true;
+}
+
} // namespace internal
} // namespace crashpad
diff --git a/snapshot/minidump/minidump_string_reader.h b/snapshot/minidump/minidump_string_reader.h
index e5667ec..b7ecdac 100644
--- a/snapshot/minidump/minidump_string_reader.h
+++ b/snapshot/minidump/minidump_string_reader.h
@@ -20,6 +20,7 @@
#include <string>
+#include "base/strings/string16.h"
#include "util/file/file_reader.h"
namespace crashpad {
@@ -34,6 +35,24 @@
RVA rva,
std::string* string);
+//! \brief Reads a MinidumpUTF16String from a minidump file at offset \a rva in
+//! \a file_reader, and returns it in \a string.
+//!
+//! \return `true` on success, with \a string set. `false` on failure, with a
+//! message logged.
+bool ReadMinidumpUTF16String(FileReaderInterface* file_reader,
+ RVA rva,
+ base::string16* string);
+
+//! \brief Reads a MinidumpUTF16String from a minidump file at offset \a rva in
+//! \a file_reader, and returns it in \a string.
+//!
+//! \return `true` on success, with \a string set. `false` on failure, with a
+//! message logged.
+bool ReadMinidumpUTF16String(FileReaderInterface* file_reader,
+ RVA rva,
+ std::string* string);
+
} // namespace internal
} // namespace crashpad
diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc
index 06cd1bb..a316e83 100644
--- a/snapshot/minidump/module_snapshot_minidump.cc
+++ b/snapshot/minidump/module_snapshot_minidump.cc
@@ -14,10 +14,16 @@
#include "snapshot/minidump/module_snapshot_minidump.h"
+#include <stddef.h>
+#include <string.h>
+
+#include "base/logging.h"
#include "minidump/minidump_extensions.h"
#include "snapshot/minidump/minidump_annotation_reader.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
#include "snapshot/minidump/minidump_string_list_reader.h"
+#include "snapshot/minidump/minidump_string_reader.h"
+#include "util/misc/pdb_structures.h"
namespace crashpad {
namespace internal {
@@ -27,11 +33,15 @@
minidump_module_(),
annotations_vector_(),
annotations_simple_map_(),
- initialized_() {
-}
+ annotation_objects_(),
+ uuid_(),
+ build_id_(),
+ name_(),
+ debug_file_name_(),
+ age_(0),
+ initialized_() {}
-ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {
-}
+ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {}
bool ModuleSnapshotMinidump::Initialize(
FileReaderInterface* file_reader,
@@ -53,32 +63,87 @@
return false;
}
+ ReadMinidumpUTF16String(file_reader, minidump_module_.ModuleNameRva, &name_);
+
+ if (minidump_module_.CvRecord.Rva != 0 &&
+ !InitializeModuleCodeView(file_reader)) {
+ return false;
+ }
+
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
+bool ModuleSnapshotMinidump::InitializeModuleCodeView(
+ FileReaderInterface* file_reader) {
+ uint32_t signature;
+
+ DCHECK_NE(minidump_module_.CvRecord.Rva, 0u);
+
+ if (minidump_module_.CvRecord.DataSize < sizeof(signature)) {
+ LOG(ERROR) << "CodeView record in module too small to contain signature";
+ return false;
+ }
+
+ if (!file_reader->SeekSet(minidump_module_.CvRecord.Rva)) {
+ return false;
+ }
+
+ std::vector<uint8_t> cv_record;
+ cv_record.resize(minidump_module_.CvRecord.DataSize);
+
+ if (!file_reader->ReadExactly(cv_record.data(), cv_record.size())) {
+ return false;
+ }
+
+ signature = *reinterpret_cast<uint32_t*>(cv_record.data());
+
+ if (signature == CodeViewRecordPDB70::kSignature) {
+ if (cv_record.size() < offsetof(CodeViewRecordPDB70, pdb_name)) {
+ LOG(ERROR) << "CodeView record in module marked as PDB70 but too small";
+ return false;
+ }
+
+ auto cv_record_pdb70 =
+ reinterpret_cast<CodeViewRecordPDB70*>(cv_record.data());
+
+ age_ = cv_record_pdb70->age;
+ uuid_ = cv_record_pdb70->uuid;
+
+ std::copy(cv_record.begin() + offsetof(CodeViewRecordPDB70, pdb_name),
+ cv_record.end(),
+ std::back_inserter(debug_file_name_));
+ return true;
+ }
+
+ if (signature == CodeViewRecordBuildID::kSignature) {
+ std::copy(cv_record.begin() + offsetof(CodeViewRecordBuildID, build_id),
+ cv_record.end(),
+ std::back_inserter(build_id_));
+ return true;
+ }
+
+ LOG(ERROR) << "Bad CodeView signature in module";
+ return false;
+}
+
std::string ModuleSnapshotMinidump::Name() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return std::string();
+ return name_;
}
uint64_t ModuleSnapshotMinidump::Address() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return 0;
+ return minidump_module_.BaseOfImage;
}
uint64_t ModuleSnapshotMinidump::Size() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return 0;
+ return minidump_module_.SizeOfImage;
}
time_t ModuleSnapshotMinidump::Timestamp() const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return 0;
+ return minidump_module_.TimeDateStamp;
}
void ModuleSnapshotMinidump::FileVersion(uint16_t* version_0,
@@ -86,11 +151,12 @@
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- *version_0 = 0;
- *version_1 = 0;
- *version_2 = 0;
- *version_3 = 0;
+ uint32_t version_01 = minidump_module_.VersionInfo.dwFileVersionMS;
+ uint32_t version_23 = minidump_module_.VersionInfo.dwFileVersionLS;
+ *version_0 = static_cast<uint16_t>(version_01 >> 16);
+ *version_1 = static_cast<uint16_t>(version_01 & 0xFFFF);
+ *version_2 = static_cast<uint16_t>(version_23 >> 16);
+ *version_3 = static_cast<uint16_t>(version_23 & 0xFFFF);
}
void ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0,
@@ -98,31 +164,40 @@
uint16_t* version_2,
uint16_t* version_3) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- *version_0 = 0;
- *version_1 = 0;
- *version_2 = 0;
- *version_3 = 0;
+ uint32_t version_01 = minidump_module_.VersionInfo.dwProductVersionMS;
+ uint32_t version_23 = minidump_module_.VersionInfo.dwProductVersionLS;
+ *version_0 = static_cast<uint16_t>(version_01 >> 16);
+ *version_1 = static_cast<uint16_t>(version_01 & 0xFFFF);
+ *version_2 = static_cast<uint16_t>(version_23 >> 16);
+ *version_3 = static_cast<uint16_t>(version_23 & 0xFFFF);
}
ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ switch (minidump_module_.VersionInfo.dwFileType) {
+ case VFT_APP:
+ return kModuleTypeExecutable;
+ case VFT_DLL:
+ return kModuleTypeSharedLibrary;
+ }
return kModuleTypeUnknown;
}
void ModuleSnapshotMinidump::UUIDAndAge(crashpad::UUID* uuid,
uint32_t* age) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- *uuid = crashpad::UUID();
- *age = 0;
+ *uuid = uuid_;
+ *age = age_;
}
std::string ModuleSnapshotMinidump::DebugFileName() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return std::string();
+ return debug_file_name_;
+}
+
+std::vector<uint8_t> ModuleSnapshotMinidump::BuildID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return build_id_;
}
std::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const {
@@ -167,7 +242,7 @@
MinidumpModuleCrashpadInfo minidump_module_crashpad_info;
if (minidump_module_crashpad_info_location->DataSize <
- sizeof(minidump_module_crashpad_info)) {
+ sizeof(minidump_module_crashpad_info)) {
LOG(ERROR) << "minidump_module_crashpad_info size mismatch";
return false;
}
@@ -182,7 +257,7 @@
}
if (minidump_module_crashpad_info.version !=
- MinidumpModuleCrashpadInfo::kVersion) {
+ MinidumpModuleCrashpadInfo::kVersion) {
LOG(ERROR) << "minidump_module_crashpad_info version mismatch";
return false;
}
diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h
index e9dfa77..023d0d5 100644
--- a/snapshot/minidump/module_snapshot_minidump.h
+++ b/snapshot/minidump/module_snapshot_minidump.h
@@ -75,6 +75,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -88,10 +89,19 @@
const MINIDUMP_LOCATION_DESCRIPTOR*
minidump_module_crashpad_info_location);
+ // Initializes data from the CodeView record, which usually points toward
+ // debug symbols.
+ bool InitializeModuleCodeView(FileReaderInterface* file_reader);
+
MINIDUMP_MODULE minidump_module_;
std::vector<std::string> annotations_vector_;
std::map<std::string, std::string> annotations_simple_map_;
std::vector<AnnotationSnapshot> annotation_objects_;
+ UUID uuid_;
+ std::vector<uint8_t> build_id_;
+ std::string name_;
+ std::string debug_file_name_;
+ uint32_t age_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMinidump);
diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc
index 652e0e4..6a032f9 100644
--- a/snapshot/minidump/process_snapshot_minidump.cc
+++ b/snapshot/minidump/process_snapshot_minidump.cc
@@ -16,27 +16,55 @@
#include <utility>
+#include "base/strings/utf_string_conversions.h"
+#include "minidump/minidump_extensions.h"
+#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
#include "util/file/file_io.h"
namespace crashpad {
+namespace internal {
+
+class MemoryMapRegionSnapshotMinidump : public MemoryMapRegionSnapshot {
+ public:
+ MemoryMapRegionSnapshotMinidump(MINIDUMP_MEMORY_INFO info) : info_(info) {}
+ ~MemoryMapRegionSnapshotMinidump() override = default;
+
+ const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override {
+ return info_;
+ }
+
+ private:
+ MINIDUMP_MEMORY_INFO info_;
+};
+
+} // namespace internal
+
ProcessSnapshotMinidump::ProcessSnapshotMinidump()
: ProcessSnapshot(),
header_(),
stream_directory_(),
stream_map_(),
modules_(),
+ threads_(),
unloaded_modules_(),
+ mem_regions_(),
+ mem_regions_exposed_(),
+ custom_streams_(),
crashpad_info_(),
+ system_snapshot_(),
+ exception_snapshot_(),
+ arch_(CPUArchitecture::kCPUArchitectureUnknown),
annotations_simple_map_(),
file_reader_(nullptr),
- process_id_(static_cast<pid_t>(-1)),
- initialized_() {
-}
+ process_id_(kInvalidProcessID),
+ create_time_(0),
+ user_time_(0),
+ kernel_time_(0),
+ initialized_() {}
-ProcessSnapshotMinidump::~ProcessSnapshotMinidump() {
-}
+ProcessSnapshotMinidump::~ProcessSnapshotMinidump() {}
bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
@@ -84,9 +112,10 @@
stream_map_[stream_type] = &directory.Location;
}
- if (!InitializeCrashpadInfo() ||
- !InitializeMiscInfo() ||
- !InitializeModules()) {
+ if (!InitializeCrashpadInfo() || !InitializeMiscInfo() ||
+ !InitializeModules() || !InitializeSystemSnapshot() ||
+ !InitializeMemoryInfo() || !InitializeThreads() ||
+ !InitializeCustomMinidumpStreams() || !InitializeExceptionSnapshot()) {
return false;
}
@@ -94,12 +123,12 @@
return true;
}
-pid_t ProcessSnapshotMinidump::ProcessID() const {
+crashpad::ProcessID ProcessSnapshotMinidump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_id_;
}
-pid_t ProcessSnapshotMinidump::ParentProcessID() const {
+crashpad::ProcessID ProcessSnapshotMinidump::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://crashpad.chromium.org/bug/10
return 0;
@@ -107,25 +136,22 @@
void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- snapshot_time->tv_sec = 0;
+ snapshot_time->tv_sec = header_.TimeDateStamp;
snapshot_time->tv_usec = 0;
}
void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- start_time->tv_sec = 0;
+ start_time->tv_sec = create_time_;
start_time->tv_usec = 0;
}
void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time,
timeval* system_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- user_time->tv_sec = 0;
+ user_time->tv_sec = user_time_;
user_time->tv_usec = 0;
- system_time->tv_sec = 0;
+ system_time->tv_sec = kernel_time_;
system_time->tv_usec = 0;
}
@@ -153,14 +179,16 @@
const SystemSnapshot* ProcessSnapshotMinidump::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return nullptr;
+ return &system_snapshot_;
}
std::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return std::vector<const ThreadSnapshot*>();
+ std::vector<const ThreadSnapshot*> threads;
+ for (const auto& thread : threads_) {
+ threads.push_back(thread.get());
+ }
+ return threads;
}
std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
@@ -181,15 +209,17 @@
const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ if (exception_snapshot_.IsValid()) {
+ return &exception_snapshot_;
+ }
+ // Allow caller to know whether the minidump contained an exception stream.
return nullptr;
}
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMinidump::MemoryMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- NOTREACHED(); // https://crashpad.chromium.org/bug/10
- return std::vector<const MemoryMapRegionSnapshot*>();
+ return mem_regions_exposed_;
}
std::vector<HandleSnapshot> ProcessSnapshotMinidump::Handles() const {
@@ -205,6 +235,23 @@
return std::vector<const MemorySnapshot*>();
}
+const ProcessMemory* ProcessSnapshotMinidump::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return nullptr;
+}
+
+std::vector<const MinidumpStream*>
+ProcessSnapshotMinidump::CustomMinidumpStreams() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ std::vector<const MinidumpStream*> result;
+ result.reserve(custom_streams_.size());
+ for (const auto& custom_stream : custom_streams_) {
+ result.push_back(custom_stream.get());
+ }
+ return result;
+}
+
bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
if (stream_it == stream_map_.end()) {
@@ -263,12 +310,18 @@
switch (stream_it->second->DataSize) {
case sizeof(MINIDUMP_MISC_INFO_5):
case sizeof(MINIDUMP_MISC_INFO_4):
+ full_version_ = base::UTF16ToUTF8(info.BuildString);
+ full_version_ = full_version_.substr(0, full_version_.find(";"));
+ FALLTHROUGH;
case sizeof(MINIDUMP_MISC_INFO_3):
case sizeof(MINIDUMP_MISC_INFO_2):
case sizeof(MINIDUMP_MISC_INFO):
// TODO(jperaza): Save the remaining misc info.
// https://crashpad.chromium.org/bug/10
process_id_ = info.ProcessId;
+ create_time_ = info.ProcessCreateTime;
+ user_time_ = info.ProcessUserTime;
+ kernel_time_ = info.ProcessKernelTime;
}
return true;
@@ -300,7 +353,7 @@
}
if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) !=
- stream_it->second->DataSize) {
+ stream_it->second->DataSize) {
LOG(ERROR) << "module_list size mismatch";
return false;
}
@@ -342,7 +395,7 @@
}
if (crashpad_info_.module_list.DataSize <
- sizeof(MinidumpModuleCrashpadInfoList)) {
+ sizeof(MinidumpModuleCrashpadInfoList)) {
LOG(ERROR) << "module_crashpad_info_list size mismatch";
return false;
}
@@ -358,8 +411,8 @@
}
if (crashpad_info_.module_list.DataSize !=
- sizeof(MinidumpModuleCrashpadInfoList) +
- crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) {
+ sizeof(MinidumpModuleCrashpadInfoList) +
+ crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) {
LOG(ERROR) << "module_crashpad_info_list size mismatch";
return false;
}
@@ -379,7 +432,8 @@
minidump_links[crashpad_module_index];
if (!module_crashpad_info_links
->insert(std::make_pair(minidump_link.minidump_module_list_index,
- minidump_link.location)).second) {
+ minidump_link.location))
+ .second) {
LOG(WARNING)
<< "duplicate module_crashpad_info_list minidump_module_list_index "
<< minidump_link.minidump_module_list_index;
@@ -390,4 +444,164 @@
return true;
}
+bool ProcessSnapshotMinidump::InitializeMemoryInfo() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMemoryInfoList);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ if (stream_it->second->DataSize < sizeof(MINIDUMP_MEMORY_INFO_LIST)) {
+ LOG(ERROR) << "memory_info_list size mismatch";
+ return false;
+ }
+
+ if (!file_reader_->SeekSet(stream_it->second->Rva)) {
+ return false;
+ }
+
+ MINIDUMP_MEMORY_INFO_LIST list;
+
+ if (!file_reader_->ReadExactly(&list, sizeof(list))) {
+ return false;
+ }
+
+ if (list.SizeOfHeader != sizeof(list)) {
+ return false;
+ }
+
+ if (list.SizeOfEntry != sizeof(MINIDUMP_MEMORY_INFO)) {
+ return false;
+ }
+
+ if (sizeof(MINIDUMP_MEMORY_INFO_LIST) +
+ list.NumberOfEntries * list.SizeOfEntry !=
+ stream_it->second->DataSize) {
+ LOG(ERROR) << "memory_info_list size mismatch";
+ return false;
+ }
+
+ for (uint32_t i = 0; i < list.NumberOfEntries; i++) {
+ MINIDUMP_MEMORY_INFO info;
+
+ if (!file_reader_->ReadExactly(&info, sizeof(info))) {
+ return false;
+ }
+
+ mem_regions_.emplace_back(
+ std::make_unique<internal::MemoryMapRegionSnapshotMinidump>(info));
+ mem_regions_exposed_.emplace_back(mem_regions_.back().get());
+ }
+
+ return true;
+}
+
+bool ProcessSnapshotMinidump::InitializeThreads() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadList);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_LIST)) {
+ LOG(ERROR) << "thread_list size mismatch";
+ return false;
+ }
+
+ if (!file_reader_->SeekSet(stream_it->second->Rva)) {
+ return false;
+ }
+
+ uint32_t thread_count;
+ if (!file_reader_->ReadExactly(&thread_count, sizeof(thread_count))) {
+ return false;
+ }
+
+ if (sizeof(MINIDUMP_THREAD_LIST) + thread_count * sizeof(MINIDUMP_THREAD) !=
+ stream_it->second->DataSize) {
+ LOG(ERROR) << "thread_list size mismatch";
+ return false;
+ }
+
+ for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {
+ const RVA thread_rva = stream_it->second->Rva + sizeof(thread_count) +
+ thread_index * sizeof(MINIDUMP_THREAD);
+
+ auto thread = std::make_unique<internal::ThreadSnapshotMinidump>();
+ if (!thread->Initialize(file_reader_, thread_rva, arch_)) {
+ return false;
+ }
+
+ threads_.push_back(std::move(thread));
+ }
+
+ return true;
+}
+
+bool ProcessSnapshotMinidump::InitializeSystemSnapshot() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeSystemInfo);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ if (stream_it->second->DataSize < sizeof(MINIDUMP_SYSTEM_INFO)) {
+ LOG(ERROR) << "system info size mismatch";
+ return false;
+ }
+
+ if (!system_snapshot_.Initialize(
+ file_reader_, stream_it->second->Rva, full_version_)) {
+ return false;
+ }
+
+ arch_ = system_snapshot_.GetCPUArchitecture();
+ return true;
+}
+
+bool ProcessSnapshotMinidump::InitializeCustomMinidumpStreams() {
+ for (size_t i = 0; i < stream_directory_.size(); i++) {
+ const auto& stream = stream_directory_[i];
+
+ // Filter out reserved minidump and crashpad streams.
+ const uint32_t stream_type = stream.StreamType;
+ if (stream_type <=
+ MinidumpStreamType::kMinidumpStreamTypeLastReservedStream ||
+ (stream_type >= MinidumpStreamType::kMinidumpStreamTypeCrashpadInfo &&
+ stream_type <= MinidumpStreamType::
+ kMinidumpStreamTypeCrashpadLastReservedStream)) {
+ continue;
+ }
+
+ std::vector<uint8_t> data(stream.Location.DataSize);
+ if (!file_reader_->SeekSet(stream.Location.Rva) ||
+ !file_reader_->ReadExactly(data.data(), data.size())) {
+ LOG(ERROR) << "Failed to read stream with ID 0x" << std::hex
+ << stream_type << std::dec << " at index " << i;
+ return false;
+ }
+
+ custom_streams_.push_back(
+ std::make_unique<MinidumpStream>(stream_type, std::move(data)));
+ }
+
+ return true;
+}
+
+bool ProcessSnapshotMinidump::InitializeExceptionSnapshot() {
+ const auto& stream_it = stream_map_.find(kMinidumpStreamTypeException);
+ if (stream_it == stream_map_.end()) {
+ return true;
+ }
+
+ if (stream_it->second->DataSize < sizeof(MINIDUMP_EXCEPTION_STREAM)) {
+ LOG(ERROR) << "system info size mismatch";
+ return false;
+ }
+
+ if (!exception_snapshot_.Initialize(
+ file_reader_, arch_, stream_it->second->Rva)) {
+ return false;
+ }
+
+ return true;
+}
+
} // namespace crashpad
diff --git a/snapshot/minidump/process_snapshot_minidump.h b/snapshot/minidump/process_snapshot_minidump.h
index 9ba5d9c..cf2d82b 100644
--- a/snapshot/minidump/process_snapshot_minidump.h
+++ b/snapshot/minidump/process_snapshot_minidump.h
@@ -29,7 +29,11 @@
#include "minidump/minidump_extensions.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/memory_snapshot.h"
+#include "snapshot/minidump/exception_snapshot_minidump.h"
+#include "snapshot/minidump/minidump_stream.h"
#include "snapshot/minidump/module_snapshot_minidump.h"
+#include "snapshot/minidump/system_snapshot_minidump.h"
+#include "snapshot/minidump/thread_snapshot_minidump.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
@@ -38,9 +42,14 @@
#include "util/file/file_reader.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/uuid.h"
+#include "util/process/process_id.h"
namespace crashpad {
+namespace internal {
+class MemoryMapRegionSnapshotMinidump;
+} // namespace internal
+
//! \brief A ProcessSnapshot based on a minidump file.
class ProcessSnapshotMinidump final : public ProcessSnapshot {
public:
@@ -58,8 +67,8 @@
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -75,6 +84,17 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
+
+ //! \brief Returns a list of custom minidump streams. This routine is the
+ //! equivalent of ModuleSnapshot::CustomMinidumpStreams(), except that in
+ //! a minidump it is impossible to associate a custom stream to a specific
+ //! module.
+ //!
+ //! \return The caller does not take ownership of the returned objects, they
+ //! are scoped to the lifetime of the ProcessSnapshotMinidump object that
+ //! they were obtained from.
+ std::vector<const MinidumpStream*> CustomMinidumpStreams() const;
private:
// Initializes data carried in a MinidumpCrashpadInfo stream on behalf of
@@ -85,6 +105,18 @@
// Initialize().
bool InitializeModules();
+ // Initializes data carried in a MINIDUMP_THREAD_LIST stream on behalf of
+ // Initialize().
+ bool InitializeThreads();
+
+ // Initializes data carried in a MINIDUMP_MEMORY_INFO_LIST stream on behalf of
+ // Initialize().
+ bool InitializeMemoryInfo();
+
+ // Initializes data carried in a MINIDUMP_SYSTEM_INFO stream on behalf of
+ // Initialize().
+ bool InitializeSystemSnapshot();
+
// Initializes data carried in a MinidumpModuleCrashpadInfoList structure on
// behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as
// well, so it must be called after InitializeCrashpadInfo().
@@ -96,15 +128,34 @@
// Initialize().
bool InitializeMiscInfo();
+ // Initializes custom minidump streams.
+ bool InitializeCustomMinidumpStreams();
+
+ // Initializes data carried in a MINIDUMP_EXCEPTION_STREAM stream on behalf of
+ // Initialize().
+ bool InitializeExceptionSnapshot();
+
MINIDUMP_HEADER header_;
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
std::vector<std::unique_ptr<internal::ModuleSnapshotMinidump>> modules_;
+ std::vector<std::unique_ptr<internal::ThreadSnapshotMinidump>> threads_;
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
+ std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotMinidump>>
+ mem_regions_;
+ std::vector<const MemoryMapRegionSnapshot*> mem_regions_exposed_;
+ std::vector<std::unique_ptr<MinidumpStream>> custom_streams_;
MinidumpCrashpadInfo crashpad_info_;
+ internal::SystemSnapshotMinidump system_snapshot_;
+ internal::ExceptionSnapshotMinidump exception_snapshot_;
+ CPUArchitecture arch_;
std::map<std::string, std::string> annotations_simple_map_;
+ std::string full_version_;
FileReaderInterface* file_reader_; // weak
- pid_t process_id_;
+ crashpad::ProcessID process_id_;
+ uint32_t create_time_;
+ uint32_t user_time_;
+ uint32_t kernel_time_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump);
diff --git a/snapshot/minidump/process_snapshot_minidump_test.cc b/snapshot/minidump/process_snapshot_minidump_test.cc
index ed17939..9794979 100644
--- a/snapshot/minidump/process_snapshot_minidump_test.cc
+++ b/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -18,17 +18,72 @@
#include <dbghelp.h>
#include <string.h>
+#include <algorithm>
#include <memory>
+#include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
+#include "minidump/minidump_context.h"
+#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/minidump/minidump_annotation_reader.h"
#include "snapshot/module_snapshot.h"
#include "util/file/string_file.h"
+#include "util/misc/pdb_structures.h"
namespace crashpad {
namespace test {
namespace {
+class ReadToVector : public crashpad::MemorySnapshot::Delegate {
+ public:
+ std::vector<uint8_t> result;
+
+ bool MemorySnapshotDelegateRead(void* data, size_t size) override {
+ result.resize(size);
+ memcpy(result.data(), data, size);
+ return true;
+ }
+};
+
+MinidumpContextARM64 GetArm64MinidumpContext() {
+ MinidumpContextARM64 minidump_context;
+
+ minidump_context.context_flags = kMinidumpContextARM64Full;
+
+ minidump_context.cpsr = 0;
+
+ for (int i = 0; i < 29; i++) {
+ minidump_context.regs[i] = i + 1;
+ }
+
+ minidump_context.fp = 30;
+ minidump_context.lr = 31;
+ minidump_context.sp = 32;
+ minidump_context.pc = 33;
+
+ for (int i = 0; i < 32; i++) {
+ minidump_context.fpsimd[i].lo = i * 2 + 34;
+ minidump_context.fpsimd[i].hi = i * 2 + 35;
+ }
+
+ minidump_context.fpcr = 98;
+ minidump_context.fpsr = 99;
+
+ for (int i = 0; i < 8; i++) {
+ minidump_context.bcr[i] = i * 2 + 100;
+ minidump_context.bvr[i] = i * 2 + 101;
+ }
+
+ for (int i = 0; i < 2; i++) {
+ minidump_context.wcr[i] = i * 2 + 115;
+ minidump_context.wvr[i] = i * 2 + 116;
+ }
+
+ return minidump_context;
+}
+
TEST(ProcessSnapshotMinidump, EmptyFile) {
StringFile string_file;
ProcessSnapshotMinidump process_snapshot;
@@ -287,7 +342,55 @@
EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
MINIDUMP_MODULE minidump_module = {};
- uint32_t minidump_module_count = 4;
+ constexpr uint32_t minidump_module_count = 4;
+ RVA name_rvas[minidump_module_count];
+ std::string names[minidump_module_count] = {
+ "libtacotruck",
+ "libevidencebased",
+ "libgeorgism",
+ "librealistutopia",
+ };
+
+ minidump_module.BaseOfImage = 0xbadf00d;
+ minidump_module.SizeOfImage = 9001;
+ minidump_module.TimeDateStamp = 1970;
+ minidump_module.VersionInfo.dwFileVersionMS = 0xAABBCCDD;
+ minidump_module.VersionInfo.dwFileVersionLS = 0xEEFF4242;
+ minidump_module.VersionInfo.dwProductVersionMS = 0xAAAABBBB;
+ minidump_module.VersionInfo.dwProductVersionLS = 0xCCCCDDDD;
+ minidump_module.VersionInfo.dwFileType = VFT_APP;
+
+ for (uint32_t i = 0; i < minidump_module_count; i++) {
+ name_rvas[i] = static_cast<RVA>(string_file.SeekGet());
+ auto name16 = base::UTF8ToUTF16(names[i]);
+ uint32_t size =
+ base::checked_cast<uint32_t>(sizeof(name16[0]) * name16.size());
+ EXPECT_TRUE(string_file.Write(&size, sizeof(size)));
+ EXPECT_TRUE(string_file.Write(&name16[0], size));
+ }
+
+ CodeViewRecordPDB70 pdb70_cv;
+ pdb70_cv.signature = CodeViewRecordPDB70::kSignature;
+ pdb70_cv.age = 7;
+ pdb70_cv.uuid.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff");
+ pdb70_cv.pdb_name[0] = '\0';
+
+ auto pdb70_loc = static_cast<RVA>(string_file.SeekGet());
+ auto pdb70_size = sizeof(pdb70_cv);
+
+ EXPECT_TRUE(string_file.Write(&pdb70_cv, sizeof(pdb70_cv)));
+
+ CodeViewRecordBuildID build_id_cv;
+ build_id_cv.signature = CodeViewRecordBuildID::kSignature;
+
+ auto build_id_cv_loc = static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(string_file.Write(&build_id_cv,
+ offsetof(CodeViewRecordBuildID, build_id)));
+ EXPECT_TRUE(string_file.Write("atestbuildidbecausewhynot", 25));
+
+ auto build_id_cv_size =
+ static_cast<size_t>(string_file.SeekGet() - build_id_cv_loc);
MINIDUMP_DIRECTORY minidump_module_list_directory = {};
minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;
@@ -302,7 +405,18 @@
for (uint32_t minidump_module_index = 0;
minidump_module_index < minidump_module_count;
++minidump_module_index) {
+ if (minidump_module_index % 2) {
+ minidump_module.CvRecord.Rva = pdb70_loc;
+ minidump_module.CvRecord.DataSize = static_cast<uint32_t>(pdb70_size);
+ } else {
+ minidump_module.CvRecord.Rva = build_id_cv_loc;
+ minidump_module.CvRecord.DataSize =
+ static_cast<uint32_t>(build_id_cv_size);
+ }
+
+ minidump_module.ModuleNameRva = name_rvas[minidump_module_index];
EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));
+ minidump_module.TimeDateStamp++;
}
MinidumpModuleCrashpadInfo crashpad_module_0 = {};
@@ -398,6 +512,47 @@
std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules();
ASSERT_EQ(modules.size(), minidump_module_count);
+ for (uint32_t i = 0; i < minidump_module_count; i++) {
+ EXPECT_EQ(modules[i]->Name(), names[i]);
+ EXPECT_EQ(modules[i]->Address(), 0xbadf00dU);
+ EXPECT_EQ(modules[i]->Size(), 9001U);
+ EXPECT_EQ(modules[i]->Timestamp(), static_cast<time_t>(1970U + i));
+
+ uint16_t v0;
+ uint16_t v1;
+ uint16_t v2;
+ uint16_t v3;
+
+ modules[i]->FileVersion(&v0, &v1, &v2, &v3);
+ EXPECT_EQ(v0, 0xAABBU);
+ EXPECT_EQ(v1, 0xCCDDU);
+ EXPECT_EQ(v2, 0xEEFFU);
+ EXPECT_EQ(v3, 0x4242U);
+
+ modules[i]->SourceVersion(&v0, &v1, &v2, &v3);
+ EXPECT_EQ(v0, 0xAAAAU);
+ EXPECT_EQ(v1, 0xBBBBU);
+ EXPECT_EQ(v2, 0xCCCCU);
+ EXPECT_EQ(v3, 0xDDDDU);
+
+ EXPECT_EQ(modules[i]->GetModuleType(),
+ ModuleSnapshot::kModuleTypeExecutable);
+
+ if (i % 2) {
+ uint32_t age;
+ UUID uuid;
+ modules[i]->UUIDAndAge(&uuid, &age);
+
+ EXPECT_EQ(uuid.ToString(), "00112233-4455-6677-8899-aabbccddeeff");
+ EXPECT_EQ(age, 7U);
+ } else {
+ auto build_id = modules[i]->BuildID();
+ std::string build_id_text(build_id.data(),
+ build_id.data() + build_id.size());
+ EXPECT_EQ(build_id_text, "atestbuildidbecausewhynot");
+ }
+ }
+
auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();
EXPECT_EQ(annotations_simple_map, dictionary_0);
@@ -426,7 +581,7 @@
MINIDUMP_HEADER header = {};
ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
- static const pid_t kTestProcessId = 42;
+ static const crashpad::ProcessID kTestProcessId = 42;
MINIDUMP_MISC_INFO misc_info = {};
misc_info.SizeOfInfo = sizeof(misc_info);
misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
@@ -452,6 +607,827 @@
EXPECT_EQ(process_snapshot.ProcessID(), kTestProcessId);
}
+TEST(ProcessSnapshotMinidump, SnapshotTime) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.TimeDateStamp = 42;
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(&string_file));
+
+ timeval snapshot_time;
+ process_snapshot.SnapshotTime(&snapshot_time);
+ EXPECT_EQ(snapshot_time.tv_sec, 42);
+ EXPECT_EQ(snapshot_time.tv_usec, 0);
+}
+
+TEST(ProcessSnapshotMinidump, MiscTimes) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_MISC_INFO misc_info = {};
+ misc_info.SizeOfInfo = sizeof(misc_info);
+ misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES;
+ misc_info.ProcessCreateTime = 42;
+ misc_info.ProcessUserTime = 43;
+ misc_info.ProcessKernelTime = 44;
+
+ MINIDUMP_DIRECTORY misc_directory = {};
+ misc_directory.StreamType = kMinidumpStreamTypeMiscInfo;
+ misc_directory.Location.DataSize = sizeof(misc_info);
+ misc_directory.Location.Rva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ ASSERT_TRUE(string_file.SeekSet(0));
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(&string_file));
+
+ timeval start_time, user_time, kernel_time;
+ process_snapshot.ProcessStartTime(&start_time);
+ process_snapshot.ProcessCPUTimes(&user_time, &kernel_time);
+ EXPECT_EQ(static_cast<uint32_t>(start_time.tv_sec),
+ misc_info.ProcessCreateTime);
+ EXPECT_EQ(start_time.tv_usec, 0);
+ EXPECT_EQ(static_cast<uint32_t>(user_time.tv_sec), misc_info.ProcessUserTime);
+ EXPECT_EQ(user_time.tv_usec, 0);
+ EXPECT_EQ(static_cast<uint32_t>(kernel_time.tv_sec),
+ misc_info.ProcessKernelTime);
+ EXPECT_EQ(kernel_time.tv_usec, 0);
+}
+
+TEST(ProcessSnapshotMinidump, Threads) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_THREAD minidump_thread = {};
+ uint32_t minidump_thread_count = 4;
+
+ minidump_thread.ThreadId = 42;
+ minidump_thread.Teb = 24;
+
+ MINIDUMP_DIRECTORY minidump_thread_list_directory = {};
+ minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;
+ minidump_thread_list_directory.Location.DataSize =
+ sizeof(MINIDUMP_THREAD_LIST) +
+ minidump_thread_count * sizeof(MINIDUMP_THREAD);
+ minidump_thread_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ // Fields in MINIDUMP_THREAD_LIST.
+ EXPECT_TRUE(
+ string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));
+ for (uint32_t minidump_thread_index = 0;
+ minidump_thread_index < minidump_thread_count;
+ ++minidump_thread_index) {
+ EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));
+ minidump_thread.ThreadId++;
+ }
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory,
+ sizeof(minidump_thread_list_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();
+ ASSERT_EQ(threads.size(), minidump_thread_count);
+
+ uint32_t thread_id = 42;
+ for (const auto& thread : threads) {
+ EXPECT_EQ(thread->ThreadID(), thread_id);
+ EXPECT_EQ(thread->ThreadSpecificDataAddress(), 24UL);
+ thread_id++;
+ }
+}
+
+TEST(ProcessSnapshotMinidump, System) {
+ const char* cpu_info = "GenuineIntel";
+ const uint32_t* cpu_info_bytes = reinterpret_cast<const uint32_t*>(cpu_info);
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_SYSTEM_INFO minidump_system_info = {};
+
+ minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86;
+ minidump_system_info.ProcessorLevel = 3;
+ minidump_system_info.ProcessorRevision = 3;
+ minidump_system_info.NumberOfProcessors = 8;
+ minidump_system_info.ProductType = kMinidumpOSTypeServer;
+ minidump_system_info.PlatformId = kMinidumpOSFuchsia;
+ minidump_system_info.MajorVersion = 3;
+ minidump_system_info.MinorVersion = 4;
+ minidump_system_info.BuildNumber = 56;
+ minidump_system_info.CSDVersionRva = WriteString(&string_file, "Snazzle");
+ minidump_system_info.Cpu.X86CpuInfo.VendorId[0] = cpu_info_bytes[0];
+ minidump_system_info.Cpu.X86CpuInfo.VendorId[1] = cpu_info_bytes[1];
+ minidump_system_info.Cpu.X86CpuInfo.VendorId[2] = cpu_info_bytes[2];
+
+ MINIDUMP_MISC_INFO_5 minidump_misc_info = {};
+ base::string16 build_string;
+ ASSERT_TRUE(base::UTF8ToUTF16(
+ "MyOSVersion; MyMachineDescription", 33, &build_string));
+ std::copy(build_string.begin(), build_string.end(),
+ minidump_misc_info.BuildString);
+
+ MINIDUMP_DIRECTORY minidump_system_info_directory = {};
+ minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;
+ minidump_system_info_directory.Location.DataSize =
+ sizeof(MINIDUMP_SYSTEM_INFO);
+ minidump_system_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(
+ string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
+
+ MINIDUMP_DIRECTORY minidump_misc_info_directory = {};
+ minidump_misc_info_directory.StreamType = kMinidumpStreamTypeMiscInfo;
+ minidump_misc_info_directory.Location.DataSize = sizeof(MINIDUMP_MISC_INFO_5);
+ minidump_misc_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(
+ string_file.Write(&minidump_misc_info, sizeof(minidump_misc_info)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,
+ sizeof(minidump_system_info_directory)));
+ ASSERT_TRUE(string_file.Write(&minidump_misc_info_directory,
+ sizeof(minidump_misc_info_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 2;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ const SystemSnapshot* s = process_snapshot.System();
+
+ EXPECT_EQ(s->GetCPUArchitecture(), kCPUArchitectureX86);
+ EXPECT_EQ(s->CPURevision(), 3UL);
+ EXPECT_EQ(s->CPUVendor(), "GenuineIntel");
+ EXPECT_EQ(s->GetOperatingSystem(),
+ SystemSnapshot::OperatingSystem::kOperatingSystemFuchsia);
+ EXPECT_EQ(s->OSVersionFull(), "MyOSVersion");
+
+ int major, minor, bugfix;
+ std::string build;
+ s->OSVersion(&major, &minor, &bugfix, &build);
+
+ EXPECT_EQ(major, 3);
+ EXPECT_EQ(minor, 4);
+ EXPECT_EQ(bugfix, 56);
+ EXPECT_EQ(build, "Snazzle");
+}
+
+TEST(ProcessSnapshotMinidump, ThreadContextARM64) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_SYSTEM_INFO minidump_system_info = {};
+
+ minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64;
+ minidump_system_info.ProductType = kMinidumpOSTypeServer;
+ minidump_system_info.PlatformId = kMinidumpOSFuchsia;
+ minidump_system_info.CSDVersionRva = WriteString(&string_file, "");
+
+ MINIDUMP_DIRECTORY minidump_system_info_directory = {};
+ minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;
+ minidump_system_info_directory.Location.DataSize =
+ sizeof(MINIDUMP_SYSTEM_INFO);
+ minidump_system_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(
+ string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
+
+ MINIDUMP_THREAD minidump_thread = {};
+ uint32_t minidump_thread_count = 1;
+
+ minidump_thread.ThreadId = 42;
+ minidump_thread.Teb = 24;
+
+ MinidumpContextARM64 minidump_context = GetArm64MinidumpContext();
+
+ minidump_thread.ThreadContext.DataSize = sizeof(minidump_context);
+ minidump_thread.ThreadContext.Rva = static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));
+
+ MINIDUMP_DIRECTORY minidump_thread_list_directory = {};
+ minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;
+ minidump_thread_list_directory.Location.DataSize =
+ sizeof(MINIDUMP_THREAD_LIST) +
+ minidump_thread_count * sizeof(MINIDUMP_THREAD);
+ minidump_thread_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ // Fields in MINIDUMP_THREAD_LIST.
+ EXPECT_TRUE(
+ string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));
+ EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,
+ sizeof(minidump_system_info_directory)));
+ ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,
+ sizeof(minidump_thread_list_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 2;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();
+ ASSERT_EQ(threads.size(), minidump_thread_count);
+
+ const CPUContext* ctx_generic = threads[0]->Context();
+
+ ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64);
+
+ const CPUContextARM64* ctx = ctx_generic->arm64;
+
+ EXPECT_EQ(ctx->spsr, 0UL);
+
+ for (unsigned int i = 0; i < 31; i++) {
+ EXPECT_EQ(ctx->regs[i], i + 1);
+ }
+
+ EXPECT_EQ(ctx->sp, 32UL);
+ EXPECT_EQ(ctx->pc, 33UL);
+ EXPECT_EQ(ctx->fpcr, 98UL);
+ EXPECT_EQ(ctx->fpsr, 99UL);
+
+ for (unsigned int i = 0; i < 32; i++) {
+ EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34);
+ EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35);
+ }
+}
+
+TEST(ProcessSnapshotMinidump, ThreadContextX86_64) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_SYSTEM_INFO minidump_system_info = {};
+
+ minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64;
+ minidump_system_info.ProductType = kMinidumpOSTypeServer;
+ minidump_system_info.PlatformId = kMinidumpOSFuchsia;
+ minidump_system_info.CSDVersionRva = WriteString(&string_file, "");
+
+ MINIDUMP_DIRECTORY minidump_system_info_directory = {};
+ minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;
+ minidump_system_info_directory.Location.DataSize =
+ sizeof(MINIDUMP_SYSTEM_INFO);
+ minidump_system_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(
+ string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
+
+ MINIDUMP_THREAD minidump_thread = {};
+ uint32_t minidump_thread_count = 1;
+
+ minidump_thread.ThreadId = 42;
+ minidump_thread.Teb = 24;
+
+ MinidumpContextAMD64 minidump_context;
+
+ minidump_context.context_flags = kMinidumpContextAMD64Full;
+
+ minidump_context.mx_csr = 0;
+ minidump_context.cs = 1;
+ minidump_context.ds = 2;
+ minidump_context.es = 3;
+ minidump_context.fs = 4;
+ minidump_context.gs = 5;
+ minidump_context.ss = 6;
+ minidump_context.eflags = 7;
+ minidump_context.dr0 = 8;
+ minidump_context.dr1 = 9;
+ minidump_context.dr2 = 10;
+ minidump_context.dr3 = 11;
+ minidump_context.dr6 = 12;
+ minidump_context.dr7 = 13;
+ minidump_context.rax = 14;
+ minidump_context.rcx = 15;
+ minidump_context.rdx = 16;
+ minidump_context.rbx = 17;
+ minidump_context.rsp = 18;
+ minidump_context.rbp = 19;
+ minidump_context.rsi = 20;
+ minidump_context.rdi = 21;
+ minidump_context.r8 = 22;
+ minidump_context.r9 = 23;
+ minidump_context.r10 = 24;
+ minidump_context.r11 = 25;
+ minidump_context.r12 = 26;
+ minidump_context.r13 = 27;
+ minidump_context.r14 = 28;
+ minidump_context.r15 = 29;
+ minidump_context.rip = 30;
+ minidump_context.vector_control = 31;
+ minidump_context.debug_control = 32;
+ minidump_context.last_branch_to_rip = 33;
+ minidump_context.last_branch_from_rip = 34;
+ minidump_context.last_exception_to_rip = 35;
+ minidump_context.last_exception_from_rip = 36;
+ minidump_context.fxsave.fcw = 37;
+ minidump_context.fxsave.fsw = 38;
+ minidump_context.fxsave.ftw = 39;
+ minidump_context.fxsave.reserved_1 = 40;
+ minidump_context.fxsave.fop = 41;
+ minidump_context.fxsave.fpu_ip_64 = 42;
+ minidump_context.fxsave.fpu_dp_64 = 43;
+
+ for (size_t i = 0; i < base::size(minidump_context.vector_register); i++) {
+ minidump_context.vector_register[i].lo = i * 2 + 44;
+ minidump_context.vector_register[i].hi = i * 2 + 45;
+ }
+
+ for (uint8_t i = 0; i < base::size(minidump_context.fxsave.reserved_4); i++) {
+ minidump_context.fxsave.reserved_4[i] = i * 2 + 115;
+ minidump_context.fxsave.available[i] = i * 2 + 116;
+ }
+
+ for (size_t i = 0; i < base::size(minidump_context.fxsave.st_mm); i++) {
+ for (uint8_t j = 0;
+ j < base::size(minidump_context.fxsave.st_mm[0].mm_value);
+ j++) {
+ minidump_context.fxsave.st_mm[i].mm_value[j] = j + 1;
+ minidump_context.fxsave.st_mm[i].mm_reserved[j] = j + 1;
+ }
+ }
+
+ for (size_t i = 0; i < base::size(minidump_context.fxsave.xmm); i++) {
+ for (uint8_t j = 0; j < base::size(minidump_context.fxsave.xmm[0]); j++) {
+ minidump_context.fxsave.xmm[i][j] = j + 1;
+ }
+ }
+
+ minidump_thread.ThreadContext.DataSize = sizeof(minidump_context);
+ minidump_thread.ThreadContext.Rva = static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));
+
+ MINIDUMP_DIRECTORY minidump_thread_list_directory = {};
+ minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;
+ minidump_thread_list_directory.Location.DataSize =
+ sizeof(MINIDUMP_THREAD_LIST) +
+ minidump_thread_count * sizeof(MINIDUMP_THREAD);
+ minidump_thread_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ // Fields in MINIDUMP_THREAD_LIST.
+ EXPECT_TRUE(
+ string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));
+ EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,
+ sizeof(minidump_system_info_directory)));
+ ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,
+ sizeof(minidump_thread_list_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 2;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();
+ ASSERT_EQ(threads.size(), minidump_thread_count);
+
+ const CPUContext* ctx_generic = threads[0]->Context();
+
+ ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureX86_64);
+
+ const CPUContextX86_64* ctx = ctx_generic->x86_64;
+ EXPECT_EQ(ctx->cs, 1);
+ EXPECT_EQ(ctx->fs, 4);
+ EXPECT_EQ(ctx->gs, 5);
+ EXPECT_EQ(ctx->rflags, 7UL);
+ EXPECT_EQ(ctx->dr0, 8UL);
+ EXPECT_EQ(ctx->dr1, 9U);
+ EXPECT_EQ(ctx->dr2, 10U);
+ EXPECT_EQ(ctx->dr3, 11U);
+ EXPECT_EQ(ctx->dr4, 12U);
+ EXPECT_EQ(ctx->dr5, 13U);
+ EXPECT_EQ(ctx->dr6, 12U);
+ EXPECT_EQ(ctx->dr7, 13U);
+ EXPECT_EQ(ctx->rax, 14U);
+ EXPECT_EQ(ctx->rcx, 15U);
+ EXPECT_EQ(ctx->rdx, 16U);
+ EXPECT_EQ(ctx->rbx, 17U);
+ EXPECT_EQ(ctx->rsp, 18U);
+ EXPECT_EQ(ctx->rbp, 19U);
+ EXPECT_EQ(ctx->rsi, 20U);
+ EXPECT_EQ(ctx->rdi, 21U);
+ EXPECT_EQ(ctx->r8, 22U);
+ EXPECT_EQ(ctx->r9, 23U);
+ EXPECT_EQ(ctx->r10, 24U);
+ EXPECT_EQ(ctx->r11, 25U);
+ EXPECT_EQ(ctx->r12, 26U);
+ EXPECT_EQ(ctx->r13, 27U);
+ EXPECT_EQ(ctx->r14, 28U);
+ EXPECT_EQ(ctx->r15, 29U);
+ EXPECT_EQ(ctx->rip, 30U);
+ EXPECT_EQ(ctx->fxsave.fcw, 37U);
+ EXPECT_EQ(ctx->fxsave.fsw, 38U);
+ EXPECT_EQ(ctx->fxsave.ftw, 39U);
+ EXPECT_EQ(ctx->fxsave.reserved_1, 40U);
+ EXPECT_EQ(ctx->fxsave.fop, 41U);
+ EXPECT_EQ(ctx->fxsave.fpu_ip_64, 42U);
+ EXPECT_EQ(ctx->fxsave.fpu_dp_64, 43U);
+
+ for (uint8_t i = 0; i < base::size(ctx->fxsave.reserved_4); i++) {
+ EXPECT_EQ(ctx->fxsave.reserved_4[i], i * 2 + 115);
+ EXPECT_EQ(ctx->fxsave.available[i], i * 2 + 116);
+ }
+
+ for (size_t i = 0; i < base::size(ctx->fxsave.st_mm); i++) {
+ for (uint8_t j = 0; j < base::size(ctx->fxsave.st_mm[0].mm_value); j++) {
+ EXPECT_EQ(ctx->fxsave.st_mm[i].mm_value[j], j + 1);
+ EXPECT_EQ(ctx->fxsave.st_mm[i].mm_reserved[j], j + 1);
+ }
+ }
+
+ for (size_t i = 0; i < base::size(ctx->fxsave.xmm); i++) {
+ for (uint8_t j = 0; j < base::size(ctx->fxsave.xmm[0]); j++) {
+ EXPECT_EQ(ctx->fxsave.xmm[i][j], j + 1);
+ }
+ }
+}
+
+TEST(ProcessSnapshotMinidump, MemoryMap) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_MEMORY_INFO minidump_memory_info_1 = {};
+ MINIDUMP_MEMORY_INFO minidump_memory_info_2 = {};
+ uint32_t minidump_memory_info_count = 2;
+
+ minidump_memory_info_1.BaseAddress = 1;
+ minidump_memory_info_1.AllocationBase = 2;
+ minidump_memory_info_1.AllocationProtect = 3;
+ minidump_memory_info_1.RegionSize = 4;
+ minidump_memory_info_1.State = 5;
+ minidump_memory_info_1.Protect = 6;
+ minidump_memory_info_1.Type = 6;
+
+ minidump_memory_info_2.BaseAddress = 7;
+ minidump_memory_info_2.AllocationBase = 8;
+ minidump_memory_info_2.AllocationProtect = 9;
+ minidump_memory_info_2.RegionSize = 10;
+ minidump_memory_info_2.State = 11;
+ minidump_memory_info_2.Protect = 12;
+ minidump_memory_info_2.Type = 13;
+
+ MINIDUMP_MEMORY_INFO_LIST minidump_memory_info_list = {};
+
+ minidump_memory_info_list.SizeOfHeader = sizeof(minidump_memory_info_list);
+ minidump_memory_info_list.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO);
+ minidump_memory_info_list.NumberOfEntries = minidump_memory_info_count;
+
+ MINIDUMP_DIRECTORY minidump_memory_info_list_directory = {};
+ minidump_memory_info_list_directory.StreamType =
+ kMinidumpStreamTypeMemoryInfoList;
+ minidump_memory_info_list_directory.Location.DataSize =
+ sizeof(minidump_memory_info_list) +
+ minidump_memory_info_count * sizeof(MINIDUMP_MEMORY_INFO);
+ minidump_memory_info_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(string_file.Write(&minidump_memory_info_list,
+ sizeof(minidump_memory_info_list)));
+ EXPECT_TRUE(string_file.Write(&minidump_memory_info_1,
+ sizeof(minidump_memory_info_1)));
+ EXPECT_TRUE(string_file.Write(&minidump_memory_info_2,
+ sizeof(minidump_memory_info_2)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ EXPECT_TRUE(string_file.Write(&minidump_memory_info_list_directory,
+ sizeof(minidump_memory_info_list_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const MemoryMapRegionSnapshot*> map =
+ process_snapshot.MemoryMap();
+ ASSERT_EQ(map.size(), minidump_memory_info_count);
+ EXPECT_EQ(memcmp(&map[0]->AsMinidumpMemoryInfo(),
+ &minidump_memory_info_1,
+ sizeof(minidump_memory_info_1)),
+ 0);
+ EXPECT_EQ(memcmp(&map[1]->AsMinidumpMemoryInfo(),
+ &minidump_memory_info_2,
+ sizeof(minidump_memory_info_2)),
+ 0);
+}
+
+TEST(ProcessSnapshotMinidump, Stacks) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ MINIDUMP_THREAD minidump_thread = {};
+ uint32_t minidump_thread_count = 1;
+
+ minidump_thread.ThreadId = 42;
+ minidump_thread.Stack.StartOfMemoryRange = 0xbeefd00d;
+
+ std::vector<uint8_t> minidump_stack = {'1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ 'a',
+ 'b',
+ 'c',
+ 'd',
+ 'e',
+ 'f'};
+
+ minidump_thread.Stack.Memory.DataSize =
+ base::checked_cast<uint32_t>(minidump_stack.size());
+ minidump_thread.Stack.Memory.Rva = static_cast<RVA>(string_file.SeekGet());
+
+ EXPECT_TRUE(string_file.Write(minidump_stack.data(), minidump_stack.size()));
+
+ MINIDUMP_DIRECTORY minidump_thread_list_directory = {};
+ minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;
+ minidump_thread_list_directory.Location.DataSize =
+ sizeof(MINIDUMP_THREAD_LIST) +
+ minidump_thread_count * sizeof(MINIDUMP_THREAD);
+ minidump_thread_list_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ // Fields in MINIDUMP_THREAD_LIST.
+ EXPECT_TRUE(
+ string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));
+ EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,
+ sizeof(minidump_thread_list_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 1;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();
+ ASSERT_EQ(threads.size(), minidump_thread_count);
+
+ ReadToVector delegate;
+ threads[0]->Stack()->Read(&delegate);
+
+ EXPECT_EQ(delegate.result, minidump_stack);
+}
+
+TEST(ProcessSnapshotMinidump, CustomMinidumpStreams) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ static const char kStreamReservedData[] = "A string";
+ static const char kStreamUnreservedData[] = "Another string";
+ // In the minidump reserved range
+ constexpr MinidumpStreamType kStreamTypeReserved1 =
+ (MinidumpStreamType)0x1111;
+ // In the crashpad reserved range
+ constexpr MinidumpStreamType kStreamTypeReserved2 =
+ (MinidumpStreamType)0x43501111;
+ constexpr MinidumpStreamType kStreamTypeUnreserved =
+ (MinidumpStreamType)0xffffffff;
+
+ MINIDUMP_DIRECTORY misc_directory = {};
+ RVA reserved1_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));
+ RVA reserved2_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));
+ RVA unreserved_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamUnreservedData, sizeof(kStreamUnreservedData)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ misc_directory.StreamType = kStreamTypeReserved1;
+ misc_directory.Location.DataSize = sizeof(kStreamReservedData);
+ misc_directory.Location.Rva = reserved1_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ misc_directory.StreamType = kStreamTypeReserved2;
+ misc_directory.Location.DataSize = sizeof(kStreamReservedData);
+ misc_directory.Location.Rva = reserved2_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ misc_directory.StreamType = kStreamTypeUnreserved;
+ misc_directory.Location.DataSize = sizeof(kStreamUnreservedData);
+ misc_directory.Location.Rva = unreserved_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 3;
+ ASSERT_TRUE(string_file.SeekSet(0));
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(&string_file));
+
+ auto custom_streams = process_snapshot.CustomMinidumpStreams();
+ ASSERT_EQ(custom_streams.size(), 1U);
+ EXPECT_EQ(custom_streams[0]->stream_type(), (uint32_t)kStreamTypeUnreserved);
+
+ auto stream_data = custom_streams[0]->data();
+ EXPECT_EQ(stream_data.size(), sizeof(kStreamUnreservedData));
+ EXPECT_STREQ((char*)&stream_data.front(), kStreamUnreservedData);
+}
+
+TEST(ProcessSnapshotMinidump, Exception) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ uint32_t exception_signo =
+ static_cast<uint32_t>(-1); // crashpad::Signals::kSimulatedSigno
+
+ MINIDUMP_EXCEPTION minidump_exception = {};
+ minidump_exception.ExceptionCode = exception_signo;
+ minidump_exception.ExceptionFlags = 2;
+ minidump_exception.ExceptionRecord = 4;
+ minidump_exception.ExceptionAddress = 0xdeedb00f;
+ minidump_exception.NumberParameters = 2;
+ minidump_exception.ExceptionInformation[0] = 51;
+ minidump_exception.ExceptionInformation[1] = 62;
+
+ MINIDUMP_SYSTEM_INFO minidump_system_info = {};
+
+ minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64;
+ minidump_system_info.ProductType = kMinidumpOSTypeServer;
+ minidump_system_info.PlatformId = kMinidumpOSFuchsia;
+ minidump_system_info.CSDVersionRva = WriteString(&string_file, "");
+
+ MINIDUMP_DIRECTORY minidump_system_info_directory = {};
+ minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;
+ minidump_system_info_directory.Location.DataSize =
+ sizeof(MINIDUMP_SYSTEM_INFO);
+ minidump_system_info_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(
+ string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));
+
+ MINIDUMP_EXCEPTION_STREAM minidump_exception_stream = {};
+ minidump_exception_stream.ThreadId = 5;
+ minidump_exception_stream.ExceptionRecord = minidump_exception;
+
+ MinidumpContextARM64 minidump_context = GetArm64MinidumpContext();
+
+ minidump_exception_stream.ThreadContext.DataSize = sizeof(minidump_context);
+ minidump_exception_stream.ThreadContext.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));
+
+ MINIDUMP_DIRECTORY minidump_exception_directory = {};
+ minidump_exception_directory.StreamType = kMinidumpStreamTypeException;
+ minidump_exception_directory.Location.DataSize =
+ sizeof(MINIDUMP_EXCEPTION_STREAM);
+ minidump_exception_directory.Location.Rva =
+ static_cast<RVA>(string_file.SeekGet());
+
+ ASSERT_TRUE(string_file.Write(&minidump_exception_stream,
+ sizeof(minidump_exception_stream)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(string_file.Write(&minidump_exception_directory,
+ sizeof(minidump_exception_directory)));
+ ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,
+ sizeof(minidump_system_info_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 2;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ const ExceptionSnapshot* s = process_snapshot.Exception();
+
+ EXPECT_EQ(s->ThreadID(), 5UL);
+ EXPECT_EQ(s->Exception(), exception_signo);
+ EXPECT_EQ(s->ExceptionInfo(), 2U);
+ EXPECT_EQ(s->ExceptionAddress(), 0xdeedb00f);
+
+ const std::vector<uint64_t> codes = s->Codes();
+ EXPECT_EQ(codes.size(), 2UL);
+ EXPECT_EQ(codes[0], 51UL);
+ EXPECT_EQ(codes[1], 62UL);
+
+ const CPUContext* ctx_generic = s->Context();
+
+ ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64);
+
+ const CPUContextARM64* ctx = ctx_generic->arm64;
+
+ EXPECT_EQ(ctx->spsr, 0UL);
+
+ for (unsigned int i = 0; i < 31; i++) {
+ EXPECT_EQ(ctx->regs[i], i + 1);
+ }
+
+ EXPECT_EQ(ctx->sp, 32UL);
+ EXPECT_EQ(ctx->pc, 33UL);
+ EXPECT_EQ(ctx->fpcr, 98UL);
+ EXPECT_EQ(ctx->fpsr, 99UL);
+
+ for (unsigned int i = 0; i < 32; i++) {
+ EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34);
+ EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35);
+ }
+}
+
+TEST(ProcessSnapshotMinidump, NoExceptionInMinidump) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 0;
+ EXPECT_TRUE(string_file.SeekSet(0));
+ EXPECT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ EXPECT_TRUE(process_snapshot.Initialize(&string_file));
+
+ const ExceptionSnapshot* s = process_snapshot.Exception();
+ EXPECT_EQ(s, nullptr);
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/snapshot/minidump/system_snapshot_minidump.cc b/snapshot/minidump/system_snapshot_minidump.cc
new file mode 100644
index 0000000..06bae48
--- /dev/null
+++ b/snapshot/minidump/system_snapshot_minidump.cc
@@ -0,0 +1,198 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/system_snapshot_minidump.h"
+
+#include "snapshot/minidump/minidump_string_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+SystemSnapshotMinidump::SystemSnapshotMinidump()
+ : SystemSnapshot(), minidump_system_info_(), initialized_() {}
+
+SystemSnapshotMinidump::~SystemSnapshotMinidump() {}
+
+bool SystemSnapshotMinidump::Initialize(FileReaderInterface* file_reader,
+ RVA minidump_system_info_rva,
+ const std::string& version) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ full_version_ = version;
+
+ if (!file_reader->SeekSet(minidump_system_info_rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&minidump_system_info_,
+ sizeof(minidump_system_info_))) {
+ return false;
+ }
+
+ if (!ReadMinidumpUTF8String(file_reader,
+ minidump_system_info_.CSDVersionRva,
+ &minidump_build_name_)) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+CPUArchitecture SystemSnapshotMinidump::GetCPUArchitecture() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ switch (minidump_system_info_.ProcessorArchitecture) {
+ case kMinidumpCPUArchitectureAMD64:
+ return kCPUArchitectureX86_64;
+ case kMinidumpCPUArchitectureX86:
+ case kMinidumpCPUArchitectureX86Win64:
+ return kCPUArchitectureX86;
+ case kMinidumpCPUArchitectureARM:
+ case kMinidumpCPUArchitectureARM32Win64:
+ return kCPUArchitectureARM;
+ case kMinidumpCPUArchitectureARM64:
+ case kMinidumpCPUArchitectureARM64Breakpad:
+ return kCPUArchitectureARM64;
+ case kMinidumpCPUArchitectureMIPS:
+ return kCPUArchitectureMIPSEL;
+ // No word on how MIPS64 is signalled
+
+ default:
+ return CPUArchitecture::kCPUArchitectureUnknown;
+ }
+}
+
+uint32_t SystemSnapshotMinidump::CPURevision() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_system_info_.ProcessorRevision;
+}
+
+uint8_t SystemSnapshotMinidump::CPUCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_system_info_.NumberOfProcessors;
+}
+
+std::string SystemSnapshotMinidump::CPUVendor() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (GetCPUArchitecture() == kCPUArchitectureX86) {
+ const char* ptr = reinterpret_cast<const char*>(
+ minidump_system_info_.Cpu.X86CpuInfo.VendorId);
+ return std::string(ptr, ptr + (3 * sizeof(uint32_t)));
+ } else {
+ return std::string();
+ }
+}
+
+void SystemSnapshotMinidump::CPUFrequency(uint64_t* current_hz,
+ uint64_t* max_hz) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+}
+
+uint32_t SystemSnapshotMinidump::CPUX86Signature() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return 0;
+}
+
+uint64_t SystemSnapshotMinidump::CPUX86Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return 0;
+}
+
+uint64_t SystemSnapshotMinidump::CPUX86ExtendedFeatures() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return 0;
+}
+
+uint32_t SystemSnapshotMinidump::CPUX86Leaf7Features() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return 0;
+}
+
+bool SystemSnapshotMinidump::CPUX86SupportsDAZ() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return false;
+}
+
+SystemSnapshot::OperatingSystem SystemSnapshotMinidump::GetOperatingSystem()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ switch (minidump_system_info_.PlatformId) {
+ case kMinidumpOSMacOSX:
+ return OperatingSystem::kOperatingSystemMacOSX;
+ case kMinidumpOSWin32s:
+ case kMinidumpOSWin32Windows:
+ case kMinidumpOSWin32NT:
+ return OperatingSystem::kOperatingSystemWindows;
+ case kMinidumpOSLinux:
+ return OperatingSystem::kOperatingSystemLinux;
+ case kMinidumpOSAndroid:
+ return OperatingSystem::kOperatingSystemAndroid;
+ case kMinidumpOSFuchsia:
+ return OperatingSystem::kOperatingSystemFuchsia;
+ default:
+ return OperatingSystem::kOperatingSystemUnknown;
+ }
+}
+
+bool SystemSnapshotMinidump::OSServer() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_system_info_.ProductType == kMinidumpOSTypeServer;
+}
+
+void SystemSnapshotMinidump::OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *major = minidump_system_info_.MajorVersion;
+ *minor = minidump_system_info_.MinorVersion;
+ *bugfix = minidump_system_info_.BuildNumber;
+ *build = minidump_build_name_;
+}
+
+std::string SystemSnapshotMinidump::OSVersionFull() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return full_version_;
+}
+
+std::string SystemSnapshotMinidump::MachineDescription() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return std::string();
+}
+
+bool SystemSnapshotMinidump::NXEnabled() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+ return false;
+}
+
+void SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ NOTREACHED(); // https://crashpad.chromium.org/bug/10
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/minidump/system_snapshot_minidump.h b/snapshot/minidump/system_snapshot_minidump.h
new file mode 100644
index 0000000..0f2880e
--- /dev/null
+++ b/snapshot/minidump/system_snapshot_minidump.h
@@ -0,0 +1,87 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "minidump/minidump_extensions.h"
+#include "snapshot/system_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A SystemSnapshot based on a minidump file.
+class SystemSnapshotMinidump : public SystemSnapshot {
+ public:
+ SystemSnapshotMinidump();
+ ~SystemSnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //! \param[in] minidump_system_info_rva The file offset in \a file_reader at
+ //! which the thread’s MINIDUMP_SYSTEM_INFO structure is located.
+ //! \param[in] version The OS version taken from the build string in
+ //! MINIDUMP_MISC_INFO_4.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader,
+ RVA minidump_system_info_rva,
+ const std::string& version);
+
+ CPUArchitecture GetCPUArchitecture() const override;
+ uint32_t CPURevision() const override;
+ uint8_t CPUCount() const override;
+ std::string CPUVendor() const override;
+ void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;
+ uint32_t CPUX86Signature() const override;
+ uint64_t CPUX86Features() const override;
+ uint64_t CPUX86ExtendedFeatures() const override;
+ uint32_t CPUX86Leaf7Features() const override;
+ bool CPUX86SupportsDAZ() const override;
+ OperatingSystem GetOperatingSystem() const override;
+ bool OSServer() const override;
+ void OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const override;
+ std::string OSVersionFull() const override;
+ std::string MachineDescription() const override;
+ bool NXEnabled() const override;
+ void TimeZone(DaylightSavingTimeStatus* dst_status,
+ int* standard_offset_seconds,
+ int* daylight_offset_seconds,
+ std::string* standard_name,
+ std::string* daylight_name) const override;
+
+ private:
+ MINIDUMP_SYSTEM_INFO minidump_system_info_;
+ std::string minidump_build_name_;
+ std::string full_version_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(SystemSnapshotMinidump);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_
diff --git a/snapshot/minidump/thread_snapshot_minidump.cc b/snapshot/minidump/thread_snapshot_minidump.cc
new file mode 100644
index 0000000..8a5a2fd
--- /dev/null
+++ b/snapshot/minidump/thread_snapshot_minidump.cc
@@ -0,0 +1,114 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/minidump/thread_snapshot_minidump.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include "base/stl_util.h"
+#include "minidump/minidump_context.h"
+
+namespace crashpad {
+namespace internal {
+
+ThreadSnapshotMinidump::ThreadSnapshotMinidump()
+ : ThreadSnapshot(),
+ minidump_thread_(),
+ context_(),
+ stack_(),
+ initialized_() {}
+
+ThreadSnapshotMinidump::~ThreadSnapshotMinidump() {}
+
+bool ThreadSnapshotMinidump::Initialize(FileReaderInterface* file_reader,
+ RVA minidump_thread_rva,
+ CPUArchitecture arch) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ std::vector<unsigned char> minidump_context;
+
+ if (!file_reader->SeekSet(minidump_thread_rva)) {
+ return false;
+ }
+
+ if (!file_reader->ReadExactly(&minidump_thread_, sizeof(minidump_thread_))) {
+ return false;
+ }
+
+ if (!file_reader->SeekSet(minidump_thread_.ThreadContext.Rva)) {
+ return false;
+ }
+
+ minidump_context.resize(minidump_thread_.ThreadContext.DataSize);
+
+ if (!file_reader->ReadExactly(minidump_context.data(),
+ minidump_context.size())) {
+ return false;
+ }
+
+ if (!context_.Initialize(arch, minidump_context)) {
+ return false;
+ }
+
+ RVA stack_info_location =
+ minidump_thread_rva + offsetof(MINIDUMP_THREAD, Stack);
+
+ if (!stack_.Initialize(file_reader, stack_info_location)) {
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+uint64_t ThreadSnapshotMinidump::ThreadID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_thread_.ThreadId;
+}
+
+int ThreadSnapshotMinidump::SuspendCount() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_thread_.SuspendCount;
+}
+
+uint64_t ThreadSnapshotMinidump::ThreadSpecificDataAddress() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_thread_.Teb;
+}
+
+int ThreadSnapshotMinidump::Priority() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return minidump_thread_.Priority;
+}
+
+const CPUContext* ThreadSnapshotMinidump::Context() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return context_.Get();
+}
+
+const MemorySnapshot* ThreadSnapshotMinidump::Stack() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &stack_;
+}
+
+std::vector<const MemorySnapshot*> ThreadSnapshotMinidump::ExtraMemory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ // This doesn't correspond to anything minidump can give us, with the
+ // exception of the BackingStore field in the MINIDUMP_THREAD_EX structure,
+ // which is only valid for IA-64.
+ return std::vector<const MemorySnapshot*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/snapshot/minidump/thread_snapshot_minidump.h b/snapshot/minidump/thread_snapshot_minidump.h
new file mode 100644
index 0000000..262b8da
--- /dev/null
+++ b/snapshot/minidump/thread_snapshot_minidump.h
@@ -0,0 +1,80 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_
+
+#include <windows.h>
+
+#include "minidump/minidump_extensions.h"
+#include "snapshot/cpu_context.h"
+#include "snapshot/minidump/memory_snapshot_minidump.h"
+#include "snapshot/minidump/minidump_context_converter.h"
+#include "snapshot/thread_snapshot.h"
+#include "util/file/file_reader.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A ThreadSnapshot based on a thread in a minidump file.
+class ThreadSnapshotMinidump : public ThreadSnapshot {
+ public:
+ ThreadSnapshotMinidump();
+ ~ThreadSnapshotMinidump() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] file_reader A file reader corresponding to a minidump file.
+ //! The file reader must support seeking.
+ //! \param[in] minidump_thread_rva The file offset in \a file_reader at which
+ //! the thread’s MINIDUMP_THREAD structure is located.
+ //! \param[in] arch The architecture of the system this thread is running on.
+ //! Used to decode CPU Context.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(FileReaderInterface* file_reader,
+ RVA minidump_thread_rva,
+ CPUArchitecture arch);
+
+ const CPUContext* Context() const override;
+ const MemorySnapshot* Stack() const override;
+ uint64_t ThreadID() const override;
+ int SuspendCount() const override;
+ int Priority() const override;
+ uint64_t ThreadSpecificDataAddress() const override;
+ std::vector<const MemorySnapshot*> ExtraMemory() const override;
+
+ private:
+ //! \brief Initializes the CPU Context
+ //!
+ //! \param[in] minidump_context the raw bytes of the context data from the
+ //! minidump file.
+ //!
+ //! \return `true` if the context could be decoded without error.
+ bool InitializeContext(const std::vector<unsigned char>& minidump_context);
+
+ MINIDUMP_THREAD minidump_thread_;
+ MinidumpContextConverter context_;
+ MemorySnapshotMinidump stack_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotMinidump);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_
diff --git a/snapshot/module_snapshot.h b/snapshot/module_snapshot.h
index eea7466..f7eb83c 100644
--- a/snapshot/module_snapshot.h
+++ b/snapshot/module_snapshot.h
@@ -146,6 +146,7 @@
//! Windows with incremental linking. On other platforms \a age will always be
//! `0`.
//!
+ //! \sa BuildID()
//! \sa DebugFileName()
virtual void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const = 0;
@@ -159,6 +160,20 @@
//! \sa UUIDAndAge()
virtual std::string DebugFileName() const = 0;
+ //! \brief Returns the module’s build ID.
+ //!
+ //! On ELF platforms, the build ID is a variable-length byte stream that
+ //! identifies a library uniquely, and is usually used to look up its debug
+ //! symbols when stored separately. This will return an empty vector if it is
+ //! unsupported.
+ //!
+ //! BuildID() and UUIDAndAge() are never available in the same place. When
+ //! UUIDAndAge() is unavailable, it will be filled out with the contents of
+ //! BuildID() (either 0-padded or truncated) and age will be zero.
+ //!
+ //! \sa UUIDAndAge()
+ virtual std::vector<uint8_t> BuildID() const = 0;
+
//! \brief Returns string annotations recorded in the module.
//!
//! This method retrieves annotations recorded in a module. These annotations
diff --git a/snapshot/posix/timezone.cc b/snapshot/posix/timezone.cc
index 47c6ecf..9451e11 100644
--- a/snapshot/posix/timezone.cc
+++ b/snapshot/posix/timezone.cc
@@ -18,6 +18,7 @@
#include <time.h>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
namespace crashpad {
@@ -60,7 +61,7 @@
{0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6,
7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12};
for (size_t index = 0;
- index < arraysize(kMonthDeltas) && !found_transition;
+ index < base::size(kMonthDeltas) && !found_transition;
++index) {
// Look at a day of each month at local noon. Set tm_isdst to -1 to avoid
// giving mktime() any hints about whether to consider daylight saving
diff --git a/snapshot/posix/timezone_test.cc b/snapshot/posix/timezone_test.cc
index 814506f..b4405ba 100644
--- a/snapshot/posix/timezone_test.cc
+++ b/snapshot/posix/timezone_test.cc
@@ -22,6 +22,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/errors.h"
@@ -154,7 +155,7 @@
{"UTC", false, 0, 0, "UTC", "UTC"},
};
- for (size_t index = 0; index < arraysize(kTestTimeZones); ++index) {
+ for (size_t index = 0; index < base::size(kTestTimeZones); ++index) {
const auto& test_time_zone = kTestTimeZones[index];
const char* tz = test_time_zone.tz;
SCOPED_TRACE(base::StringPrintf("index %zu, tz %s", index, tz));
diff --git a/snapshot/process_snapshot.h b/snapshot/process_snapshot.h
index 8d5520c..08d9f2b 100644
--- a/snapshot/process_snapshot.h
+++ b/snapshot/process_snapshot.h
@@ -24,6 +24,7 @@
#include "snapshot/handle_snapshot.h"
#include "util/misc/uuid.h"
+#include "util/process/process_id.h"
namespace crashpad {
@@ -31,6 +32,7 @@
class MemoryMapRegionSnapshot;
class MemorySnapshot;
class ModuleSnapshot;
+class ProcessMemory;
class SystemSnapshot;
class ThreadSnapshot;
class UnloadedModuleSnapshot;
@@ -49,10 +51,10 @@
virtual ~ProcessSnapshot() {}
//! \brief Returns the snapshot process’ process ID.
- virtual pid_t ProcessID() const = 0;
+ virtual crashpad::ProcessID ProcessID() const = 0;
//! \brief Returns the snapshot process’ parent process’ process ID.
- virtual pid_t ParentProcessID() const = 0;
+ virtual crashpad::ProcessID ParentProcessID() const = 0;
//! \brief Returns the time that the snapshot was taken in \a snapshot_time.
//!
@@ -193,6 +195,14 @@
//! are scoped to the lifetime of the ProcessSnapshot object that they
//! were obtained from.
virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0;
+
+ //! \brief Returns a ProcessMemory object that allows accessing the process'
+ //! memory directly.
+ //!
+ //! \return A ProcessMemory object. The caller does not take ownership of this
+ //! object, it is scoped to the lifetime of the ProcessSnapshot object
+ //! that it was obtained from.
+ virtual const ProcessMemory* Memory() const = 0;
};
} // namespace crashpad
diff --git a/snapshot/sanitized/module_snapshot_sanitized.cc b/snapshot/sanitized/module_snapshot_sanitized.cc
index 94cb3d0..32b1357 100644
--- a/snapshot/sanitized/module_snapshot_sanitized.cc
+++ b/snapshot/sanitized/module_snapshot_sanitized.cc
@@ -81,6 +81,10 @@
return snapshot_->DebugFileName();
}
+std::vector<uint8_t> ModuleSnapshotSanitized::BuildID() const {
+ return snapshot_->BuildID();
+}
+
std::vector<std::string> ModuleSnapshotSanitized::AnnotationsVector() const {
// TODO(jperaza): If/when AnnotationsVector() begins to be used, determine
// whether and how the content should be sanitized.
diff --git a/snapshot/sanitized/module_snapshot_sanitized.h b/snapshot/sanitized/module_snapshot_sanitized.h
index 4f375ce..bbe1f5f 100644
--- a/snapshot/sanitized/module_snapshot_sanitized.h
+++ b/snapshot/sanitized/module_snapshot_sanitized.h
@@ -56,6 +56,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
diff --git a/snapshot/sanitized/process_snapshot_sanitized.cc b/snapshot/sanitized/process_snapshot_sanitized.cc
index 76caeb5..722abac 100644
--- a/snapshot/sanitized/process_snapshot_sanitized.cc
+++ b/snapshot/sanitized/process_snapshot_sanitized.cc
@@ -84,12 +84,14 @@
bool ProcessSnapshotSanitized::Initialize(
const ProcessSnapshot* snapshot,
- const std::vector<std::string>* annotations_whitelist,
+ std::unique_ptr<const std::vector<std::string>> annotations_whitelist,
+ std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>
+ memory_range_whitelist,
VMAddress target_module_address,
bool sanitize_stacks) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
snapshot_ = snapshot;
- annotations_whitelist_ = annotations_whitelist;
+ annotations_whitelist_ = std::move(annotations_whitelist);
sanitize_stacks_ = sanitize_stacks;
if (target_module_address) {
@@ -140,7 +142,7 @@
if (annotations_whitelist_) {
for (const auto module : snapshot_->Modules()) {
modules_.emplace_back(std::make_unique<internal::ModuleSnapshotSanitized>(
- module, annotations_whitelist_));
+ module, annotations_whitelist_.get()));
}
}
@@ -157,16 +159,18 @@
}
}
+ process_memory_.Initialize(snapshot_->Memory(), memory_range_whitelist.get());
+
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
-pid_t ProcessSnapshotSanitized::ProcessID() const {
+crashpad::ProcessID ProcessSnapshotSanitized::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->ProcessID();
}
-pid_t ProcessSnapshotSanitized::ParentProcessID() const {
+crashpad::ProcessID ProcessSnapshotSanitized::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return snapshot_->ParentProcessID();
}
@@ -262,4 +266,9 @@
return snapshot_->ExtraMemory();
}
+const ProcessMemory* ProcessSnapshotSanitized::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return &process_memory_;
+}
+
} // namespace crashpad
diff --git a/snapshot/sanitized/process_snapshot_sanitized.h b/snapshot/sanitized/process_snapshot_sanitized.h
index f5cf5fa..2d38798 100644
--- a/snapshot/sanitized/process_snapshot_sanitized.h
+++ b/snapshot/sanitized/process_snapshot_sanitized.h
@@ -29,6 +29,8 @@
#include "util/misc/address_types.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/range_set.h"
+#include "util/process/process_id.h"
+#include "util/process/process_memory_sanitized.h"
namespace crashpad {
@@ -48,6 +50,8 @@
//! \param[in] annotations_whitelist A list of annotations names to allow to
//! be returned by AnnotationsSimpleMap() or from this object's module
//! snapshots. If `nullptr`, all annotations will be returned.
+ //! \param[in] memory_range_whitelist A list of memory ranges to allow to be
+ //! accessible via Memory(), or `nullptr` to allow all ranges.
//! \param[in] target_module_address An address in the target process'
//! address space within the bounds of a module to target. If the
//! crashing thread's context and stack do not contain any pointers into
@@ -59,15 +63,18 @@
//! internal::StackSnapshotSanitized.
//! \return `false` if \a snapshot does not meet sanitization requirements and
//! should be filtered entirely. Otherwise `true`.
- bool Initialize(const ProcessSnapshot* snapshot,
- const std::vector<std::string>* annotations_whitelist,
- VMAddress target_module_address,
- bool sanitize_stacks);
+ bool Initialize(
+ const ProcessSnapshot* snapshot,
+ std::unique_ptr<const std::vector<std::string>> annotations_whitelist,
+ std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>
+ memory_range_whitelist,
+ VMAddress target_module_address,
+ bool sanitize_stacks);
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -83,6 +90,7 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
// Only used when annotations_whitelist_ != nullptr.
@@ -93,7 +101,8 @@
RangeSet address_ranges_;
const ProcessSnapshot* snapshot_;
- const std::vector<std::string>* annotations_whitelist_;
+ ProcessMemorySanitized process_memory_;
+ std::unique_ptr<const std::vector<std::string>> annotations_whitelist_;
bool sanitize_stacks_;
InitializationStateDcheck initialized_;
diff --git a/snapshot/sanitized/process_snapshot_sanitized_test.cc b/snapshot/sanitized/process_snapshot_sanitized_test.cc
index cb413e3..5c5ff1a 100644
--- a/snapshot/sanitized/process_snapshot_sanitized_test.cc
+++ b/snapshot/sanitized/process_snapshot_sanitized_test.cc
@@ -145,6 +145,22 @@
}
}
+void ExpectProcessMemory(ProcessSnapshot* snapshot,
+ VMAddress whitelisted_byte,
+ bool sanitized) {
+ auto memory = snapshot->Memory();
+
+ char out;
+ EXPECT_TRUE(memory->Read(whitelisted_byte, 1, &out));
+
+ bool unwhitelisted_read = memory->Read(whitelisted_byte + 1, 1, &out);
+ if (sanitized) {
+ EXPECT_FALSE(unwhitelisted_read);
+ } else {
+ EXPECT_TRUE(unwhitelisted_read);
+ }
+}
+
class StackSanitizationChecker : public MemorySnapshot::Delegate {
public:
StackSanitizationChecker() = default;
@@ -163,23 +179,9 @@
// MemorySnapshot::Delegate
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
-#if defined(ADDRESS_SANITIZER) && (defined(OS_LINUX) || defined(OS_ANDROID))
- // AddressSanitizer causes stack variables to be stored separately from the
- // call stack.
- auto addr_not_in_stack_range =
- [](VMAddress addr, VMAddress stack_addr, VMSize stack_size) {
- return addr < stack_addr || addr >= stack_addr + stack_size;
- };
- EXPECT_PRED3(addr_not_in_stack_range,
- addrs_.code_pointer_address,
- stack_->Address(),
- size);
- EXPECT_PRED3(addr_not_in_stack_range,
- addrs_.string_address,
- stack_->Address(),
- size);
- return true;
-#else
+ // AddressSanitizer with use-after-return detection causes stack variables
+ // to be allocated on the heap.
+#if !defined(ADDRESS_SANITIZER)
size_t pointer_offset;
if (!AssignIfInRange(&pointer_offset,
addrs_.code_pointer_address - stack_->Address())) {
@@ -209,8 +211,8 @@
} else {
EXPECT_STREQ(string, kSensitiveStackData);
}
+#endif // !ADDRESS_SANITIZER
return true;
-#endif // ADDRESS_SANITIZER && (OS_LINUX || OS_ANDROID)
}
private:
@@ -265,20 +267,34 @@
ExpectAnnotations(&snapshot, /* sanitized= */ false);
ExpectStackData(&snapshot, addrs, /* sanitized= */ false);
+ ExpectProcessMemory(&snapshot,
+ addrs.string_address,
+ /* sanitized= */ false);
- std::vector<std::string> whitelist;
- whitelist.push_back(kWhitelistedAnnotationName);
+ auto annotations_whitelist = std::make_unique<std::vector<std::string>>();
+ annotations_whitelist->push_back(kWhitelistedAnnotationName);
+
+ auto memory_ranges_whitelist =
+ std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
+ memory_ranges_whitelist->push_back(
+ std::make_pair(addrs.string_address, addrs.string_address + 1));
ProcessSnapshotSanitized sanitized;
- ASSERT_TRUE(sanitized.Initialize(
- &snapshot, &whitelist, addrs.module_address, true));
+ ASSERT_TRUE(sanitized.Initialize(&snapshot,
+ std::move(annotations_whitelist),
+ std::move(memory_ranges_whitelist),
+ addrs.module_address,
+ true));
ExpectAnnotations(&sanitized, /* sanitized= */ true);
ExpectStackData(&sanitized, addrs, /* sanitized= */ true);
+ ExpectProcessMemory(&sanitized,
+ addrs.string_address,
+ /* sanitized= */ true);
ProcessSnapshotSanitized screened_snapshot;
EXPECT_FALSE(screened_snapshot.Initialize(
- &snapshot, nullptr, addrs.non_module_address, false));
+ &snapshot, nullptr, nullptr, addrs.non_module_address, false));
}
DISALLOW_COPY_AND_ASSIGN(SanitizeTest);
diff --git a/snapshot/sanitized/sanitization_information.cc b/snapshot/sanitized/sanitization_information.cc
index f7be9b0..ff376a3 100644
--- a/snapshot/sanitized/sanitization_information.cc
+++ b/snapshot/sanitized/sanitization_information.cc
@@ -14,6 +14,8 @@
#include "snapshot/sanitized/sanitization_information.h"
+#include <limits>
+
#include "client/annotation.h"
namespace crashpad {
@@ -21,9 +23,9 @@
namespace {
template <typename Pointer>
-bool ReadWhitelist(const ProcessMemoryRange& memory,
- VMAddress whitelist_address,
- std::vector<std::string>* whitelist) {
+bool ReadAnnotationsWhitelist(const ProcessMemoryRange& memory,
+ VMAddress whitelist_address,
+ std::vector<std::string>* whitelist) {
if (!whitelist_address) {
return true;
}
@@ -53,9 +55,60 @@
bool ReadAnnotationsWhitelist(const ProcessMemoryRange& memory,
VMAddress whitelist_address,
std::vector<std::string>* whitelist) {
- return memory.Is64Bit()
- ? ReadWhitelist<uint64_t>(memory, whitelist_address, whitelist)
- : ReadWhitelist<uint32_t>(memory, whitelist_address, whitelist);
+ return memory.Is64Bit() ? ReadAnnotationsWhitelist<uint64_t>(
+ memory, whitelist_address, whitelist)
+ : ReadAnnotationsWhitelist<uint32_t>(
+ memory, whitelist_address, whitelist);
+}
+
+bool ReadMemoryRangeWhitelist(
+ const ProcessMemoryRange& memory,
+ VMAddress whitelist_address,
+ std::vector<std::pair<VMAddress, VMAddress>>* whitelist) {
+ whitelist->clear();
+ if (!whitelist_address) {
+ return true;
+ }
+
+ SanitizationMemoryRangeWhitelist list;
+ if (!memory.Read(whitelist_address, sizeof(list), &list)) {
+ LOG(ERROR) << "Failed to read memory range whitelist.";
+ return false;
+ }
+
+ if (!list.size) {
+ return true;
+ }
+
+ // An upper bound of entries that we never expect to see more than.
+ constexpr size_t kMaxListSize = 256;
+ if (list.size > kMaxListSize) {
+ LOG(ERROR) << "Whitelist exceeded maximum, size=" << list.size;
+ return false;
+ }
+
+ SanitizationMemoryRangeWhitelist::Range ranges[list.size];
+ if (!memory.Read(list.entries, sizeof(ranges), &ranges)) {
+ LOG(ERROR) << "Failed to read memory range whitelist entries.";
+ return false;
+ }
+
+ const VMAddress vm_max = memory.Is64Bit()
+ ? std::numeric_limits<uint64_t>::max()
+ : std::numeric_limits<uint32_t>::max();
+ std::vector<std::pair<VMAddress, VMAddress>> local_whitelist;
+ for (size_t i = 0; i < list.size; i++) {
+ if (ranges[i].base > vm_max || ranges[i].length > vm_max - ranges[i].base) {
+ LOG(ERROR) << "Invalid memory range whitelist entry base="
+ << ranges[i].base << " length=" << ranges[i].length;
+ return false;
+ }
+ local_whitelist.emplace_back(ranges[i].base,
+ ranges[i].base + ranges[i].length);
+ }
+
+ whitelist->swap(local_whitelist);
+ return true;
}
} // namespace crashpad
diff --git a/snapshot/sanitized/sanitization_information.h b/snapshot/sanitized/sanitization_information.h
index 6a6d908..0717ba7 100644
--- a/snapshot/sanitized/sanitization_information.h
+++ b/snapshot/sanitized/sanitization_information.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <string>
+#include <utility>
#include <vector>
#include "util/misc/address_types.h"
@@ -45,10 +46,29 @@
//! if there is no target module.
VMAddress target_module_address;
+ //! \brief The address in the client process' address space of a
+ //! a \a SanitizationMemoryRangeWhitelist, a list of whitelisted address
+ //! ranges allowed to be accessed by ProcessMemorySanitized. This value
+ //! is 0 if no memory is allowed to be read using ProcessMemorySanitized.
+ VMAddress memory_range_whitelist_address;
+
//! \brief Non-zero if stacks should be sanitized for possible PII.
uint8_t sanitize_stacks;
};
+//! \brief Describes a list of white listed memory ranges.
+struct SanitizationMemoryRangeWhitelist {
+ //! \brief Describes a range of memory.
+ struct Range {
+ VMAddress base;
+ VMSize length;
+ };
+
+ //! \brief Address of an array of |size| elements of type Range.
+ VMAddress entries;
+ VMSize size;
+};
+
#pragma pack(pop)
//! \brief Reads an annotations whitelist from another process.
@@ -63,6 +83,19 @@
VMAddress whitelist_address,
std::vector<std::string>* whitelist);
+//! \brief Reads a memory range whitelist from another process.
+//!
+//! \param[in] memory A memory reader for the target process.
+//! \param[in] whitelist_address The address in the target process' address
+//! space of a nullptr terminated array of NUL-terminated strings.
+//! \param[out] whitelist A list of whitelisted memory regions, valid only if
+//! this function returns `true`.
+//! \return `true` on success, `false` on failure with a message logged.
+bool ReadMemoryRangeWhitelist(
+ const ProcessMemoryRange& memory,
+ VMAddress whitelist_address,
+ std::vector<std::pair<VMAddress, VMAddress>>* whitelist);
+
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_
diff --git a/snapshot/sanitized/sanitization_information_test.cc b/snapshot/sanitized/sanitization_information_test.cc
index a7bf826..e426f28 100644
--- a/snapshot/sanitized/sanitization_information_test.cc
+++ b/snapshot/sanitized/sanitization_information_test.cc
@@ -14,6 +14,7 @@
#include "snapshot/sanitized/sanitization_information.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "util/misc/from_pointer_cast.h"
@@ -59,8 +60,8 @@
TEST_F(WhitelistTest, NonEmptyWhitelist) {
ASSERT_TRUE(ReadWhitelist(kNonEmptyWhitelist));
- ASSERT_EQ(whitelist_.size(), arraysize(kNonEmptyWhitelist) - 1);
- for (size_t index = 0; index < arraysize(kNonEmptyWhitelist) - 1; ++index) {
+ ASSERT_EQ(whitelist_.size(), base::size(kNonEmptyWhitelist) - 1);
+ for (size_t index = 0; index < base::size(kNonEmptyWhitelist) - 1; ++index) {
EXPECT_EQ(whitelist_[index], kNonEmptyWhitelist[index]);
}
}
diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp
index a78d21f..788ea3d 100644
--- a/snapshot/snapshot.gyp
+++ b/snapshot/snapshot.gyp
@@ -109,16 +109,27 @@
'memory_snapshot_generic.h',
'minidump/minidump_annotation_reader.cc',
'minidump/minidump_annotation_reader.h',
+ 'minidump/minidump_context_converter.cc',
+ 'minidump/minidump_context_converter.h',
'minidump/minidump_simple_string_dictionary_reader.cc',
'minidump/minidump_simple_string_dictionary_reader.h',
+ 'minidump/minidump_stream.h',
'minidump/minidump_string_list_reader.cc',
'minidump/minidump_string_list_reader.h',
'minidump/minidump_string_reader.cc',
'minidump/minidump_string_reader.h',
+ 'minidump/exception_snapshot_minidump.cc',
+ 'minidump/exception_snapshot_minidump.h',
+ 'minidump/memory_snapshot_minidump.cc',
+ 'minidump/memory_snapshot_minidump.h',
'minidump/module_snapshot_minidump.cc',
'minidump/module_snapshot_minidump.h',
'minidump/process_snapshot_minidump.cc',
'minidump/process_snapshot_minidump.h',
+ 'minidump/system_snapshot_minidump.cc',
+ 'minidump/system_snapshot_minidump.h',
+ 'minidump/thread_snapshot_minidump.cc',
+ 'minidump/thread_snapshot_minidump.h',
'module_snapshot.h',
'posix/timezone.cc',
'posix/timezone.h',
@@ -146,8 +157,6 @@
'win/capture_memory_delegate_win.h',
'win/memory_map_region_snapshot_win.cc',
'win/memory_map_region_snapshot_win.h',
- 'win/memory_snapshot_win.cc',
- 'win/memory_snapshot_win.h',
'win/module_snapshot_win.cc',
'win/module_snapshot_win.h',
'win/pe_image_annotations_reader.cc',
@@ -203,32 +212,5 @@
}],
],
},
- {
- 'variables': {
- 'conditions': [
- ['OS == "win"', {
- 'snapshot_api_target_type%': 'static_library',
- }, {
- # There are no source files except on Windows.
- 'snapshot_api_target_type%': 'none',
- }],
- ],
- },
- 'target_name': 'crashpad_snapshot_api',
- 'type': '<(snapshot_api_target_type)',
- 'dependencies': [
- 'crashpad_snapshot',
- '../compat/compat.gyp:crashpad_compat',
- '../third_party/mini_chromium/mini_chromium.gyp:base',
- '../util/util.gyp:crashpad_util',
- ],
- 'include_dirs': [
- '..',
- ],
- 'sources': [
- 'api/module_annotations_win.cc',
- 'api/module_annotations_win.h',
- ],
- },
],
}
diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp
index b795d92..cf7b8ae 100644
--- a/snapshot/snapshot_test.gyp
+++ b/snapshot/snapshot_test.gyp
@@ -57,7 +57,6 @@
'crashpad_snapshot_test_module_large',
'crashpad_snapshot_test_module_small',
'snapshot.gyp:crashpad_snapshot',
- 'snapshot.gyp:crashpad_snapshot_api',
'../client/client.gyp:crashpad_client',
'../compat/compat.gyp:crashpad_compat',
'../test/test.gyp:crashpad_gtest_main',
@@ -70,7 +69,6 @@
'..',
],
'sources': [
- 'api/module_annotations_win_test.cc',
'cpu_context_test.cc',
'memory_snapshot_test.cc',
'crashpad_info_client_options_test.cc',
@@ -96,7 +94,7 @@
'win/cpu_context_win_test.cc',
'win/exception_snapshot_win_test.cc',
'win/extra_memory_ranges_test.cc',
- 'win/pe_image_annotations_reader_test.cc',
+ 'win/module_snapshot_win_test.cc',
'win/pe_image_reader_test.cc',
'win/process_reader_win_test.cc',
'win/process_snapshot_win_test.cc',
diff --git a/snapshot/system_snapshot.h b/snapshot/system_snapshot.h
index a363c0c..d78cc23 100644
--- a/snapshot/system_snapshot.h
+++ b/snapshot/system_snapshot.h
@@ -50,6 +50,9 @@
//! \brief Fuchsia.
kOperatingSystemFuchsia,
+
+ //! \brief iOS.
+ kOperatingSystemIOS,
};
//! \brief A system’s daylight saving time status.
diff --git a/snapshot/test/test_cpu_context.cc b/snapshot/test/test_cpu_context.cc
index 1e785af..746e340 100644
--- a/snapshot/test/test_cpu_context.cc
+++ b/snapshot/test/test_cpu_context.cc
@@ -17,7 +17,7 @@
#include <string.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
namespace crashpad {
namespace test {
@@ -44,28 +44,28 @@
fxsave->reserved_3 = static_cast<uint16_t>(value++);
fxsave->mxcsr = value++;
fxsave->mxcsr_mask = value++;
- for (size_t st_mm_index = 0; st_mm_index < arraysize(fxsave->st_mm);
+ for (size_t st_mm_index = 0; st_mm_index < base::size(fxsave->st_mm);
++st_mm_index) {
- for (size_t byte = 0; byte < arraysize(fxsave->st_mm[st_mm_index].st);
+ for (size_t byte = 0; byte < base::size(fxsave->st_mm[st_mm_index].st);
++byte) {
fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++);
}
for (size_t byte = 0;
- byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved);
+ byte < base::size(fxsave->st_mm[st_mm_index].st_reserved);
++byte) {
fxsave->st_mm[st_mm_index].st_reserved[byte] =
static_cast<uint8_t>(value);
}
}
- for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) {
- for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) {
+ for (size_t xmm_index = 0; xmm_index < base::size(fxsave->xmm); ++xmm_index) {
+ for (size_t byte = 0; byte < base::size(fxsave->xmm[xmm_index]); ++byte) {
fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++);
}
}
- for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) {
+ for (size_t byte = 0; byte < base::size(fxsave->reserved_4); ++byte) {
fxsave->reserved_4[byte] = static_cast<uint8_t>(value++);
}
- for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) {
+ for (size_t byte = 0; byte < base::size(fxsave->available); ++byte) {
fxsave->available[byte] = static_cast<uint8_t>(value++);
}
@@ -174,7 +174,7 @@
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(arm->regs); ++index) {
+ for (size_t index = 0; index < base::size(arm->regs); ++index) {
arm->regs[index] = value++;
}
arm->fp = value++;
@@ -185,7 +185,7 @@
arm->pc = value++;
arm->cpsr = value++;
- for (size_t index = 0; index < arraysize(arm->vfp_regs.vfp); ++index) {
+ for (size_t index = 0; index < base::size(arm->vfp_regs.vfp); ++index) {
arm->vfp_regs.vfp[index] = value++;
}
arm->vfp_regs.fpscr = value++;
@@ -205,14 +205,14 @@
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(arm64->regs); ++index) {
+ for (size_t index = 0; index < base::size(arm64->regs); ++index) {
arm64->regs[index] = value++;
}
arm64->sp = value++;
arm64->pc = value++;
- arm64->pstate = value++;
+ arm64->spsr = value++;
- for (size_t index = 0; index < arraysize(arm64->fpsimd); ++index) {
+ for (size_t index = 0; index < base::size(arm64->fpsimd); ++index) {
arm64->fpsimd[index].lo = value++;
arm64->fpsimd[index].hi = value++;
}
@@ -231,7 +231,7 @@
uint32_t value = seed;
- for (size_t index = 0; index < arraysize(mipsel->regs); ++index) {
+ for (size_t index = 0; index < base::size(mipsel->regs); ++index) {
mipsel->regs[index] = value++;
}
@@ -242,7 +242,7 @@
mipsel->cp0_status = value++;
mipsel->cp0_cause = value++;
- for (size_t index = 0; index < arraysize(mipsel->fpregs.fregs); ++index) {
+ for (size_t index = 0; index < base::size(mipsel->fpregs.fregs); ++index) {
mipsel->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
}
@@ -267,7 +267,7 @@
uint64_t value = seed;
- for (size_t index = 0; index < arraysize(mips64->regs); ++index) {
+ for (size_t index = 0; index < base::size(mips64->regs); ++index) {
mips64->regs[index] = value++;
}
@@ -278,7 +278,7 @@
mips64->cp0_status = value++;
mips64->cp0_cause = value++;
- for (size_t index = 0; index < arraysize(mips64->fpregs.dregs); ++index) {
+ for (size_t index = 0; index < base::size(mips64->fpregs.dregs); ++index) {
mips64->fpregs.dregs[index] = static_cast<double>(value++);
}
diff --git a/snapshot/test/test_module_snapshot.cc b/snapshot/test/test_module_snapshot.cc
index 9141eda..ad0c2de 100644
--- a/snapshot/test/test_module_snapshot.cc
+++ b/snapshot/test/test_module_snapshot.cc
@@ -30,11 +30,9 @@
debug_file_name_(),
annotations_vector_(),
annotations_simple_map_(),
- extra_memory_ranges_() {
-}
+ extra_memory_ranges_() {}
-TestModuleSnapshot::~TestModuleSnapshot() {
-}
+TestModuleSnapshot::~TestModuleSnapshot() {}
std::string TestModuleSnapshot::Name() const {
return name_;
@@ -85,6 +83,10 @@
return debug_file_name_;
}
+std::vector<uint8_t> TestModuleSnapshot::BuildID() const {
+ return build_id_;
+}
+
std::vector<std::string> TestModuleSnapshot::AnnotationsVector() const {
return annotations_vector_;
}
diff --git a/snapshot/test/test_module_snapshot.h b/snapshot/test/test_module_snapshot.h
index d1262fa..fb84aaf 100644
--- a/snapshot/test/test_module_snapshot.h
+++ b/snapshot/test/test_module_snapshot.h
@@ -64,6 +64,9 @@
uuid_ = uuid;
age_ = age;
}
+ void SetBuildID(const std::vector<uint8_t>& build_id) {
+ build_id_ = build_id;
+ }
void SetDebugFileName(const std::string& debug_file_name) {
debug_file_name_ = debug_file_name;
}
@@ -101,6 +104,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -117,6 +121,7 @@
ModuleType module_type_;
uint32_t age_;
crashpad::UUID uuid_;
+ std::vector<uint8_t> build_id_;
std::string debug_file_name_;
std::vector<std::string> annotations_vector_;
std::map<std::string, std::string> annotations_simple_map_;
diff --git a/snapshot/test/test_process_snapshot.cc b/snapshot/test/test_process_snapshot.cc
index c430fd9..42a049a 100644
--- a/snapshot/test/test_process_snapshot.cc
+++ b/snapshot/test/test_process_snapshot.cc
@@ -35,17 +35,18 @@
exception_(),
memory_map_(),
handles_(),
- extra_memory_() {
+ extra_memory_(),
+ process_memory_() {
}
TestProcessSnapshot::~TestProcessSnapshot() {
}
-pid_t TestProcessSnapshot::ProcessID() const {
+crashpad::ProcessID TestProcessSnapshot::ProcessID() const {
return process_id_;
}
-pid_t TestProcessSnapshot::ParentProcessID() const {
+crashpad::ProcessID TestProcessSnapshot::ParentProcessID() const {
return parent_process_id_;
}
@@ -126,5 +127,9 @@
return extra_memory;
}
+const ProcessMemory* TestProcessSnapshot::Memory() const {
+ return process_memory_.get();
+}
+
} // namespace test
} // namespace crashpad
diff --git a/snapshot/test/test_process_snapshot.h b/snapshot/test/test_process_snapshot.h
index 5c63e12..5495cba 100644
--- a/snapshot/test/test_process_snapshot.h
+++ b/snapshot/test/test_process_snapshot.h
@@ -35,6 +35,8 @@
#include "snapshot/thread_snapshot.h"
#include "snapshot/unloaded_module_snapshot.h"
#include "util/misc/uuid.h"
+#include "util/process/process_id.h"
+#include "util/process/process_memory.h"
namespace crashpad {
namespace test {
@@ -46,8 +48,10 @@
TestProcessSnapshot();
~TestProcessSnapshot() override;
- void SetProcessID(pid_t process_id) { process_id_ = process_id; }
- void SetParentProcessID(pid_t parent_process_id) {
+ void SetProcessID(crashpad::ProcessID process_id) {
+ process_id_ = process_id;
+ }
+ void SetParentProcessID(crashpad::ProcessID parent_process_id) {
parent_process_id_ = parent_process_id;
}
void SetSnapshotTime(const timeval& snapshot_time) {
@@ -134,10 +138,19 @@
extra_memory_.push_back(std::move(extra_memory));
}
+ //! \brief Add a process memory object to be returned by Memory().
+ //!
+ //! \param[in] process_memory The memory object that will be returned by
+ //! Memory(). The TestProcessSnapshot object takes ownership of \a
+ //! extra_memory.
+ void SetProcessMemory(std::unique_ptr<ProcessMemory> process_memory) {
+ process_memory_ = std::move(process_memory);
+ }
+
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -153,10 +166,11 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
- pid_t process_id_;
- pid_t parent_process_id_;
+ crashpad::ProcessID process_id_;
+ crashpad::ProcessID parent_process_id_;
timeval snapshot_time_;
timeval process_start_time_;
timeval process_cpu_user_time_;
@@ -172,6 +186,7 @@
std::vector<std::unique_ptr<MemoryMapRegionSnapshot>> memory_map_;
std::vector<HandleSnapshot> handles_;
std::vector<std::unique_ptr<MemorySnapshot>> extra_memory_;
+ std::unique_ptr<ProcessMemory> process_memory_;
DISALLOW_COPY_AND_ASSIGN(TestProcessSnapshot);
};
diff --git a/snapshot/win/capture_memory_delegate_win.cc b/snapshot/win/capture_memory_delegate_win.cc
index fc1c608..ee5e5d6 100644
--- a/snapshot/win/capture_memory_delegate_win.cc
+++ b/snapshot/win/capture_memory_delegate_win.cc
@@ -17,7 +17,7 @@
#include <utility>
#include "base/numerics/safe_conversions.h"
-#include "snapshot/win/memory_snapshot_win.h"
+#include "snapshot/memory_snapshot_generic.h"
namespace crashpad {
namespace internal {
@@ -25,7 +25,7 @@
CaptureMemoryDelegateWin::CaptureMemoryDelegateWin(
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
- std::vector<std::unique_ptr<MemorySnapshotWin>>* snapshots,
+ std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,
uint32_t* budget_remaining)
: stack_(thread.stack_region_address, thread.stack_region_size),
process_reader_(process_reader),
@@ -39,7 +39,8 @@
bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at,
uint64_t num_bytes,
void* into) const {
- return process_reader_->ReadMemory(at, num_bytes, into);
+ return process_reader_->Memory()->Read(
+ at, base::checked_cast<size_t>(num_bytes), into);
}
std::vector<CheckedRange<uint64_t>> CaptureMemoryDelegateWin::GetReadableRanges(
@@ -56,9 +57,9 @@
return;
if (budget_remaining_ && *budget_remaining_ == 0)
return;
- snapshots_->push_back(std::make_unique<internal::MemorySnapshotWin>());
- internal::MemorySnapshotWin* snapshot = snapshots_->back().get();
- snapshot->Initialize(process_reader_, range.base(), range.size());
+ snapshots_->push_back(std::make_unique<internal::MemorySnapshotGeneric>());
+ internal::MemorySnapshotGeneric* snapshot = snapshots_->back().get();
+ snapshot->Initialize(process_reader_->Memory(), range.base(), range.size());
if (budget_remaining_) {
if (!base::IsValueInRangeForNumericType<int64_t>(range.size())) {
*budget_remaining_ = 0;
diff --git a/snapshot/win/capture_memory_delegate_win.h b/snapshot/win/capture_memory_delegate_win.h
index 175b4c9..85c3db9 100644
--- a/snapshot/win/capture_memory_delegate_win.h
+++ b/snapshot/win/capture_memory_delegate_win.h
@@ -28,7 +28,7 @@
namespace crashpad {
namespace internal {
-class MemorySnapshotWin;
+class MemorySnapshotGeneric;
class CaptureMemoryDelegateWin : public CaptureMemory::Delegate {
public:
@@ -38,15 +38,15 @@
//! \param[in] thread The thread being inspected. Memory ranges overlapping
//! this thread's stack will be ignored on the assumption that they're
//! already captured elsewhere.
- //! \param[in] snapshots A vector of MemorySnapshotWin to which the captured
- //! memory will be added.
+ //! \param[in] snapshots A vector of MemorySnapshotGeneric to which the
+ //! captured memory will be added.
//! \param[in] budget_remaining If non-null, a pointer to the remaining number
//! of bytes to capture. If this is `0`, no further memory will be
//! captured.
CaptureMemoryDelegateWin(
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
- std::vector<std::unique_ptr<MemorySnapshotWin>>* snapshots,
+ std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,
uint32_t* budget_remaining);
// MemoryCaptureDelegate:
@@ -60,7 +60,7 @@
private:
CheckedRange<uint64_t, uint64_t> stack_;
ProcessReaderWin* process_reader_; // weak
- std::vector<std::unique_ptr<MemorySnapshotWin>>* snapshots_; // weak
+ std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots_; // weak
uint32_t* budget_remaining_;
};
diff --git a/snapshot/win/cpu_context_win.cc b/snapshot/win/cpu_context_win.cc
index 1a76c6d..db2e803 100644
--- a/snapshot/win/cpu_context_win.cc
+++ b/snapshot/win/cpu_context_win.cc
@@ -126,7 +126,13 @@
} // namespace
-#if defined(ARCH_CPU_64_BITS)
+#if defined(ARCH_CPU_X86)
+
+void InitializeX86Context(const CONTEXT& context, CPUContextX86* out) {
+ CommonInitializeX86Context(context, out);
+}
+
+#elif defined(ARCH_CPU_X86_64)
void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out) {
CommonInitializeX86Context(context, out);
@@ -192,12 +198,36 @@
}
}
-#else // ARCH_CPU_64_BITS
+#elif defined(ARCH_CPU_ARM64)
-void InitializeX86Context(const CONTEXT& context, CPUContextX86* out) {
- CommonInitializeX86Context(context, out);
+void InitializeARM64Context(const CONTEXT& context, CPUContextARM64* out) {
+ memset(out, 0, sizeof(*out));
+
+ LOG_IF(ERROR, !HasContextPart(context, CONTEXT_ARM64)) << "non-arm64 context";
+
+ if (HasContextPart(context, CONTEXT_CONTROL)) {
+ out->spsr = context.Cpsr;
+ out->pc = context.Pc;
+ out->regs[30] = context.Lr;
+ out->sp = context.Sp;
+ out->regs[29] = context.Fp;
+ }
+
+ if (HasContextPart(context, CONTEXT_INTEGER)) {
+ memcpy(&out->regs[0], &context.X0, 18 * sizeof(context.X0));
+ // Don't copy x18 which is reserved as platform register.
+ memcpy(&out->regs[19], &context.X19, 10 * sizeof(context.X0));
+ }
+
+ if (HasContextPart(context, CONTEXT_FLOATING_POINT)) {
+ static_assert(sizeof(out->fpsimd) == sizeof(context.V),
+ "types must be equivalent");
+ memcpy(&out->fpsimd, &context.V, sizeof(out->fpsimd));
+ }
}
-#endif // ARCH_CPU_64_BITS
+#else
+#error Unsupported Windows Arch
+#endif // ARCH_CPU_X86
} // namespace crashpad
diff --git a/snapshot/win/cpu_context_win.h b/snapshot/win/cpu_context_win.h
index a384ccb..9718f49 100644
--- a/snapshot/win/cpu_context_win.h
+++ b/snapshot/win/cpu_context_win.h
@@ -23,8 +23,17 @@
struct CPUContextX86;
struct CPUContextX86_64;
+struct CPUContextARM64;
-#if defined(ARCH_CPU_64_BITS) || DOXYGEN
+#if defined(ARCH_CPU_X86) || DOXYGEN
+
+//! \brief Initializes a CPUContextX86 structure from a native context structure
+//! on Windows.
+void InitializeX86Context(const CONTEXT& context, CPUContextX86* out);
+
+#endif // ARCH_CPU_X86
+
+#if defined(ARCH_CPU_X86_64) || DOXYGEN
//! \brief Initializes a CPUContextX86 structure from a native context structure
//! on Windows.
@@ -34,13 +43,15 @@
//! structure on Windows.
void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out);
-#else // ARCH_CPU_64_BITS
+#endif // ARCH_CPU_X86_64
-//! \brief Initializes a CPUContextX86 structure from a native context structure
-//! on Windows.
-void InitializeX86Context(const CONTEXT& context, CPUContextX86* out);
+#if defined(ARCH_CPU_ARM64) || DOXYGEN
-#endif // ARCH_CPU_64_BITS
+//! \brief Initializes a CPUContextARM64 structure from a native context
+//! structure on Windows.
+void InitializeARM64Context(const CONTEXT& context, CPUContextARM64* out);
+
+#endif // ARCH_CPU_ARM64
} // namespace crashpad
diff --git a/snapshot/win/cpu_context_win_test.cc b/snapshot/win/cpu_context_win_test.cc
index aa2afe9..e63f04e 100644
--- a/snapshot/win/cpu_context_win_test.cc
+++ b/snapshot/win/cpu_context_win_test.cc
@@ -16,11 +16,11 @@
#include <windows.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
-#include "test/hex_string.h"
#include "snapshot/cpu_context.h"
+#include "test/hex_string.h"
namespace crashpad {
namespace test {
@@ -87,13 +87,13 @@
for (size_t st_mm = 0; st_mm < 7; ++st_mm) {
EXPECT_EQ(
BytesToHexString(cpu_context_x86.fxsave.st_mm[st_mm].st,
- arraysize(cpu_context_x86.fxsave.st_mm[st_mm].st)),
- std::string(arraysize(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,
+ base::size(cpu_context_x86.fxsave.st_mm[st_mm].st)),
+ std::string(base::size(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,
'0'))
<< "st_mm " << st_mm;
}
EXPECT_EQ(BytesToHexString(cpu_context_x86.fxsave.st_mm[7].st,
- arraysize(cpu_context_x86.fxsave.st_mm[7].st)),
+ base::size(cpu_context_x86.fxsave.st_mm[7].st)),
"0000000000000080ff7f");
EXPECT_EQ(cpu_context_x86.dr0, 3u);
diff --git a/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/snapshot/win/crashpad_snapshot_test_crashing_child.cc
index 759cc13..cc1369e 100644
--- a/snapshot/win/crashpad_snapshot_test_crashing_child.cc
+++ b/snapshot/win/crashpad_snapshot_test_crashing_child.cc
@@ -16,10 +16,10 @@
#include <windows.h>
#include "base/logging.h"
-#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "util/misc/capture_context.h"
#include "util/win/address_types.h"
+#include "util/win/context_wrappers.h"
int wmain(int argc, wchar_t* argv[]) {
CHECK_EQ(argc, 2);
@@ -32,11 +32,9 @@
CONTEXT context;
crashpad::CaptureContext(&context);
-#if defined(ARCH_CPU_64_BITS)
- crashpad::WinVMAddress break_address = context.Rip;
-#else
- crashpad::WinVMAddress break_address = context.Eip;
-#endif
+ crashpad::WinVMAddress break_address =
+ reinterpret_cast<crashpad::WinVMAddress>(
+ crashpad::ProgramCounterFromCONTEXT(&context));
// This does not used CheckedWriteFile() because at high optimization
// settings, a lot of logging code can be inlined, causing there to be a large
diff --git a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
index e2c524a..cc441a4 100644
--- a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
+++ b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc
@@ -20,6 +20,7 @@
#include "client/simulate_crash.h"
#include "util/misc/capture_context.h"
#include "util/win/address_types.h"
+#include "util/win/context_wrappers.h"
int wmain(int argc, wchar_t* argv[]) {
CHECK_EQ(argc, 2);
@@ -32,11 +33,9 @@
CONTEXT context;
crashpad::CaptureContext(&context);
-#if defined(ARCH_CPU_64_BITS)
- crashpad::WinVMAddress break_address = context.Rip;
-#else
- crashpad::WinVMAddress break_address = context.Eip;
-#endif
+ crashpad::WinVMAddress break_address =
+ reinterpret_cast<crashpad::WinVMAddress>(
+ crashpad::ProgramCounterFromCONTEXT(&context));
// This does not used CheckedWriteFile() because at high optimization
// settings, a lot of logging code can be inlined, causing there to be a large
diff --git a/snapshot/win/crashpad_snapshot_test_image_reader.cc b/snapshot/win/crashpad_snapshot_test_image_reader.cc
index 4ebd98e..8bccbd6 100644
--- a/snapshot/win/crashpad_snapshot_test_image_reader.cc
+++ b/snapshot/win/crashpad_snapshot_test_image_reader.cc
@@ -15,6 +15,7 @@
#include <windows.h>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "client/crashpad_info.h"
#include "util/file/file_io.h"
#include "util/synchronization/semaphore.h"
@@ -28,7 +29,7 @@
// Allocate a bunch of pointers to things on the stack.
int* pointers[1000];
- for (size_t i = 0; i < arraysize(pointers); ++i) {
+ for (size_t i = 0; i < base::size(pointers); ++i) {
pointers[i] = new int[2048];
}
@@ -52,7 +53,7 @@
// verify the cap on pointed-to memory.
crashpad::Semaphore semaphore(0);
crashpad::ScopedKernelHANDLE threads[100];
- for (size_t i = 0; i < arraysize(threads); ++i) {
+ for (size_t i = 0; i < base::size(threads); ++i) {
threads[i].reset(CreateThread(nullptr,
0,
&LotsOfReferencesThreadProc,
@@ -65,7 +66,7 @@
}
}
- for (size_t i = 0; i < arraysize(threads); ++i) {
+ for (size_t i = 0; i < base::size(threads); ++i) {
semaphore.Wait();
}
diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py
index fd602fb..b1919ad 100755
--- a/snapshot/win/end_to_end_test.py
+++ b/snapshot/win/end_to_end_test.py
@@ -29,479 +29,452 @@
import win32pipe
import winerror
-
g_temp_dirs = []
g_had_failures = False
def MakeTempDir():
- global g_temp_dirs
- new_dir = tempfile.mkdtemp()
- g_temp_dirs.append(new_dir)
- return new_dir
+ global g_temp_dirs
+ new_dir = tempfile.mkdtemp()
+ g_temp_dirs.append(new_dir)
+ return new_dir
def CleanUpTempDirs():
- global g_temp_dirs
- for d in g_temp_dirs:
- subprocess.call(['rmdir', '/s', '/q', d], shell=True)
+ global g_temp_dirs
+ for d in g_temp_dirs:
+ subprocess.call(['rmdir', '/s', '/q', d], shell=True)
def FindInstalledWindowsApplication(app_path):
- search_paths = [os.getenv('PROGRAMFILES(X86)'),
- os.getenv('PROGRAMFILES'),
- os.getenv('PROGRAMW6432'),
- os.getenv('LOCALAPPDATA')]
- search_paths += os.getenv('PATH', '').split(os.pathsep)
+ search_paths = [
+ os.getenv('PROGRAMFILES(X86)'),
+ os.getenv('PROGRAMFILES'),
+ os.getenv('PROGRAMW6432'),
+ os.getenv('LOCALAPPDATA')
+ ]
+ search_paths += os.getenv('PATH', '').split(os.pathsep)
- for search_path in search_paths:
- if not search_path:
- continue
- path = os.path.join(search_path, app_path)
- if os.path.isfile(path):
- return path
+ for search_path in search_paths:
+ if not search_path:
+ continue
+ path = os.path.join(search_path, app_path)
+ if os.path.isfile(path):
+ return path
- return None
-
-
-def GetCdbPath():
- """Search in some reasonable places to find cdb.exe. Searches x64 before x86
- and newer versions before older versions.
- """
- possible_paths = (
- os.path.join('Windows Kits', '10', 'Debuggers', 'x64'),
- os.path.join('Windows Kits', '10', 'Debuggers', 'x86'),
- os.path.join('Windows Kits', '8.1', 'Debuggers', 'x64'),
- os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'),
- os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'),
- os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'),
- 'Debugging Tools For Windows (x64)',
- 'Debugging Tools For Windows (x86)',
- 'Debugging Tools For Windows',)
- for possible_path in possible_paths:
- app_path = os.path.join(possible_path, 'cdb.exe')
- app_path = FindInstalledWindowsApplication(app_path)
- if app_path:
- return app_path
- return None
-
-
-def NamedPipeExistsAndReady(pipe_name):
- """Returns False if pipe_name does not exist. If pipe_name does exist, blocks
- until the pipe is ready to service clients, and then returns True.
-
- This is used as a drop-in replacement for os.path.exists() and os.access() to
- test for the pipe's existence. Both of those calls tickle the pipe in a way
- that appears to the server to be a client connecting, triggering error
- messages when no data is received.
-
- Although this function only needs to test pipe existence (waiting for
- CreateNamedPipe()), it actually winds up testing pipe readiness
- (waiting for ConnectNamedPipe()). This is unnecessary but harmless.
- """
- try:
- win32pipe.WaitNamedPipe(pipe_name, win32pipe.NMPWAIT_WAIT_FOREVER)
- except pywintypes.error as e:
- if e[0] == winerror.ERROR_FILE_NOT_FOUND:
- return False
- raise
- return True
-
-
-def GetDumpFromProgram(
- out_dir, pipe_name, executable_name, expect_exit_code, *args):
- """Initialize a crash database, and run |executable_name| connecting to a
- crash handler. If pipe_name is set, crashpad_handler will be started first. If
- pipe_name is empty, the executable is responsible for starting
- crashpad_handler. *args will be passed after other arguments to
- executable_name. If the child process does not exit with |expect_exit_code|,
- an exception will be raised. Returns the path to the minidump generated by
- crashpad_handler for further testing.
- """
- test_database = MakeTempDir()
- handler = None
-
- try:
- subprocess.check_call(
- [os.path.join(out_dir, 'crashpad_database_util.exe'), '--create',
- '--database=' + test_database])
-
- if pipe_name is not None:
- handler = subprocess.Popen([
- os.path.join(out_dir, 'crashpad_handler.com'),
- '--pipe-name=' + pipe_name,
- '--database=' + test_database
- ])
-
- # Wait until the server is ready.
- printed = False
- while not NamedPipeExistsAndReady(pipe_name):
- if not printed:
- print('Waiting for crashpad_handler to be ready...')
- printed = True
- time.sleep(0.001)
-
- command = [os.path.join(out_dir, executable_name), pipe_name] + list(args)
- else:
- command = ([os.path.join(out_dir, executable_name),
- os.path.join(out_dir, 'crashpad_handler.com'),
- test_database] +
- list(args))
- print('Running %s' % os.path.basename(command[0]))
- exit_code = subprocess.call(command)
- if exit_code != expect_exit_code:
- raise subprocess.CalledProcessError(exit_code, executable_name)
-
- out = subprocess.check_output([
- os.path.join(out_dir, 'crashpad_database_util.exe'),
- '--database=' + test_database,
- '--show-pending-reports',
- '--show-all-report-info',
- ])
- for line in out.splitlines():
- if line.strip().startswith('Path:'):
- return line.partition(':')[2].strip()
- finally:
- if handler:
- handler.kill()
-
-
-def GetDumpFromCrashyProgram(out_dir, pipe_name):
- return GetDumpFromProgram(out_dir,
- pipe_name,
- 'crashy_program.exe',
- win32con.EXCEPTION_ACCESS_VIOLATION)
-
-
-def GetDumpFromOtherProgram(out_dir, pipe_name, *args):
- return GetDumpFromProgram(
- out_dir, pipe_name, 'crash_other_program.exe', 0, *args)
-
-
-def GetDumpFromSignal(out_dir, pipe_name, *args):
- STATUS_FATAL_APP_EXIT = 0x40000015 # Not known by win32con.
- return GetDumpFromProgram(out_dir,
- pipe_name,
- 'crashy_signal.exe',
- STATUS_FATAL_APP_EXIT,
- *args)
-
-
-def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name):
- return GetDumpFromProgram(out_dir,
- pipe_name,
- 'self_destroying_program.exe',
- win32con.EXCEPTION_BREAKPOINT)
-
-
-def GetDumpFromZ7Program(out_dir, pipe_name):
- return GetDumpFromProgram(out_dir,
- pipe_name,
- 'crashy_z7_loader.exe',
- win32con.EXCEPTION_ACCESS_VIOLATION)
-
-
-class CdbRun(object):
- """Run cdb.exe passing it a cdb command and capturing the output.
- `Check()` searches for regex patterns in sequence allowing verification of
- expected output.
- """
-
- def __init__(self, cdb_path, dump_path, command):
- # Run a command line that loads the dump, runs the specified cdb command,
- # and then quits, and capturing stdout.
- self.out = subprocess.check_output([
- cdb_path,
- '-z', dump_path,
- '-c', command + ';q'
- ])
-
- def Check(self, pattern, message, re_flags=0):
- match_obj = re.search(pattern, self.out, re_flags)
- if match_obj:
- # Matched. Consume up to end of match.
- self.out = self.out[match_obj.end(0):]
- print('ok - %s' % message)
- sys.stdout.flush()
- else:
- print('-' * 80, file=sys.stderr)
- print('FAILED - %s' % message, file=sys.stderr)
- print('-' * 80, file=sys.stderr)
- print('did not match:\n %s' % pattern, file=sys.stderr)
- print('-' * 80, file=sys.stderr)
- print('remaining output was:\n %s' % self.out, file=sys.stderr)
- print('-' * 80, file=sys.stderr)
- sys.stderr.flush()
- global g_had_failures
- g_had_failures = True
-
- def Find(self, pattern, re_flags=0):
- match_obj = re.search(pattern, self.out, re_flags)
- if match_obj:
- # Matched. Consume up to end of match.
- self.out = self.out[match_obj.end(0):]
- return match_obj
return None
-def RunTests(cdb_path,
- dump_path,
- start_handler_dump_path,
- destroyed_dump_path,
- z7_dump_path,
- other_program_path,
- other_program_no_exception_path,
- sigabrt_main_path,
- sigabrt_background_path,
- pipe_name):
- """Runs various tests in sequence. Runs a new cdb instance on the dump for
- each block of tests to reduce the chances that output from one command is
- confused for output from another.
- """
- out = CdbRun(cdb_path, dump_path, '.ecxr')
- out.Check('This dump file has an exception of interest stored in it',
- 'captured exception')
+def GetCdbPath():
+ """Search in some reasonable places to find cdb.exe. Searches x64 before x86
+ and newer versions before older versions.
+ """
+ possible_paths = (
+ os.path.join('Windows Kits', '10', 'Debuggers', 'x64'),
+ os.path.join('Windows Kits', '10', 'Debuggers', 'x86'),
+ os.path.join('Windows Kits', '8.1', 'Debuggers', 'x64'),
+ os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'),
+ os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'),
+ os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'),
+ 'Debugging Tools For Windows (x64)',
+ 'Debugging Tools For Windows (x86)',
+ 'Debugging Tools For Windows',
+ )
+ for possible_path in possible_paths:
+ app_path = os.path.join(possible_path, 'cdb.exe')
+ app_path = FindInstalledWindowsApplication(app_path)
+ if app_path:
+ return app_path
+ return None
- # When SomeCrashyFunction is inlined, cdb doesn't demangle its namespace as
- # "`anonymous namespace'" and instead gives the decorated form.
- out.Check('crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::'
- 'SomeCrashyFunction',
- 'exception at correct location')
- out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr')
- out.Check('This dump file has an exception of interest stored in it',
- 'captured exception (using StartHandler())')
- out.Check('crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::'
- 'SomeCrashyFunction',
- 'exception at correct location (using StartHandler())')
+def NamedPipeExistsAndReady(pipe_name):
+ """Returns False if pipe_name does not exist. If pipe_name does exist,
+ blocks until the pipe is ready to service clients, and then returns True.
- out = CdbRun(cdb_path, dump_path, '!peb')
- out.Check(r'PEB at', 'found the PEB')
- out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
- out.Check(r'Base TimeStamp Module', 'module list present')
- pipe_name_escaped = pipe_name.replace('\\', '\\\\')
- out.Check(r'CommandLine: *\'.*crashy_program\.exe *' + pipe_name_escaped,
- 'some PEB data is correct')
- out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured',
- re.IGNORECASE)
+ This is used as a drop-in replacement for os.path.exists() and os.access()
+ to test for the pipe's existence. Both of those calls tickle the pipe in a
+ way that appears to the server to be a client connecting, triggering error
+ messages when no data is received.
- out = CdbRun(cdb_path, dump_path, '?? @$peb->ProcessParameters')
- out.Check(r' ImagePathName *: _UNICODE_STRING ".*\\crashy_program\.exe"',
- 'PEB->ProcessParameters.ImagePathName string captured')
- out.Check(' DesktopInfo *: '
- '_UNICODE_STRING "(?!--- memory read error at address ).*"',
- 'PEB->ProcessParameters.DesktopInfo string captured')
+ Although this function only needs to test pipe existence (waiting for
+ CreateNamedPipe()), it actually winds up testing pipe readiness (waiting for
+ ConnectNamedPipe()). This is unnecessary but harmless.
+ """
+ try:
+ win32pipe.WaitNamedPipe(pipe_name, win32pipe.NMPWAIT_WAIT_FOREVER)
+ except pywintypes.error as e:
+ if e[0] == winerror.ERROR_FILE_NOT_FOUND:
+ return False
+ raise
+ return True
- out = CdbRun(cdb_path, dump_path, '!teb')
- out.Check(r'TEB at', 'found the TEB')
- out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data')
- out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue')
- out = CdbRun(cdb_path, dump_path, '!gle')
- out.Check('LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the '
- 'file specified.', '!gle gets last error')
- out.Check('LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The '
- 'file %hs does not exist.', '!gle gets last ntstatus')
+def GetDumpFromProgram(out_dir, pipe_name, executable_name, expect_exit_code,
+ *args):
+ """Initialize a crash database, and run |executable_name| connecting to a
+ crash handler. If pipe_name is set, crashpad_handler will be started first.
+ If pipe_name is empty, the executable is responsible for starting
+ crashpad_handler. *args will be passed after other arguments to
+ executable_name. If the child process does not exit with |expect_exit_code|,
+ an exception will be raised. Returns the path to the minidump generated by
+ crashpad_handler for further testing.
+ """
+ test_database = MakeTempDir()
+ handler = None
- if False:
- # TODO(scottmg): Re-enable when we grab ntdll!RtlCriticalSectionList.
- out = CdbRun(cdb_path, dump_path, '!locks')
- out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
- r'g_test_critical_section', 'lock was captured')
- if platform.win32_ver()[0] != '7':
- # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
- out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
+ try:
+ subprocess.check_call([
+ os.path.join(out_dir, 'crashpad_database_util.exe'), '--create',
+ '--database=' + test_database
+ ])
- out = CdbRun(cdb_path, dump_path, '!handle')
- out.Check(r'\d+ Handles', 'captured handles')
- out.Check(r'Event\s+\d+', 'capture some event handles')
- out.Check(r'File\s+\d+', 'capture some file handles')
+ if pipe_name is not None:
+ handler = subprocess.Popen([
+ os.path.join(out_dir, 'crashpad_handler.com'),
+ '--pipe-name=' + pipe_name, '--database=' + test_database
+ ])
- out = CdbRun(cdb_path, dump_path, 'lm')
- out.Check(r'Unloaded modules:', 'captured some unloaded modules')
- out.Check(r'lz32\.dll', 'found expected unloaded module lz32')
- out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror')
+ # Wait until the server is ready.
+ printed = False
+ while not NamedPipeExistsAndReady(pipe_name):
+ if not printed:
+ print('Waiting for crashpad_handler to be ready...')
+ printed = True
+ time.sleep(0.001)
- out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2')
- out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
- out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE)
+ command = [os.path.join(out_dir, executable_name), pipe_name
+ ] + list(args)
+ else:
+ command = ([
+ os.path.join(out_dir, executable_name),
+ os.path.join(out_dir, 'crashpad_handler.com'), test_database
+ ] + list(args))
+ print('Running %s' % os.path.basename(command[0]))
+ exit_code = subprocess.call(command)
+ if exit_code != expect_exit_code:
+ raise subprocess.CalledProcessError(exit_code, executable_name)
- # Check that there is no stack trace in the self-destroyed process. Confirm
- # that the top is where we expect it (that's based only on IP), but subsequent
- # stack entries will not be available. This confirms that we have a mostly
- # valid dump, but that the stack was omitted.
- out.Check(r'self_destroying_program!crashpad::`anonymous namespace\'::'
- r'FreeOwnStackAndBreak.*\nquit:',
- 'at correct location, no additional stack entries')
+ out = subprocess.check_output([
+ os.path.join(out_dir, 'crashpad_database_util.exe'),
+ '--database=' + test_database,
+ '--show-pending-reports',
+ '--show-all-report-info',
+ ])
+ for line in out.splitlines():
+ if line.strip().startswith('Path:'):
+ return line.partition(':')[2].strip()
+ finally:
+ if handler:
+ handler.kill()
- # Dump memory pointed to be EDI on the background suspended thread. We don't
- # know the index of the thread because the system may have started other
- # threads, so first do a run to extract the thread index that's suspended, and
- # then another run to dump the data pointed to by EDI for that thread.
- out = CdbRun(cdb_path, dump_path, '.ecxr;~')
- match_obj = out.Find(r'(\d+)\s+Id: [0-9a-f.]+ Suspend: 1 Teb:')
- if match_obj:
- thread = match_obj.group(1)
- out = CdbRun(cdb_path, dump_path, '.ecxr;~' + thread + 's;db /c14 edi')
- out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',
- 'data pointed to by registers captured')
- # Move up one stack frame after jumping to the exception, and examine memory.
- out = CdbRun(cdb_path, dump_path,
- '.ecxr; .f+; dd /c100 poi(offset_pointer)-20')
- out.Check(r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e '
- r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 '
- r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c '
- r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 '
- r'80000094 00000095 80000096 00000097',
- 'data pointed to by stack captured')
+def GetDumpFromCrashyProgram(out_dir, pipe_name):
+ return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe',
+ win32con.EXCEPTION_ACCESS_VIOLATION)
- # Attempt to retrieve the value of g_extra_memory_pointer (by name), and then
- # examine the memory at which it points. Both should have been saved.
- out = CdbRun(cdb_path, dump_path,
- 'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 '
- 'L8')
- out.Check(r'0000655e 0000656b 00006578 00006585',
- 'extra memory range captured')
- out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? '
- r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?',
- ' and not memory after range')
- if False:
- # TODO(scottmg): This is flakily capturing too much memory in Debug builds,
- # possibly because a stale pointer is being captured via the stack.
- # See: https://bugs.chromium.org/p/crashpad/issues/detail?id=101.
- out = CdbRun(cdb_path, dump_path,
- 'dd poi(crashy_program!crashpad::g_extra_memory_not_saved)'
- '+0x1f30 L4')
- # We save only the pointer, not the pointed-to data. If the pointer itself
- # wasn't saved, then we won't get any memory printed, so here we're
- # confirming the pointer was saved but the memory wasn't.
- out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? '
- r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?',
- 'extra memory removal')
+def GetDumpFromOtherProgram(out_dir, pipe_name, *args):
+ return GetDumpFromProgram(out_dir, pipe_name, 'crash_other_program.exe', 0,
+ *args)
- out = CdbRun(cdb_path, dump_path, '.dumpdebug')
- out.Check(r'type \?\?\? \(333333\), size 00001000',
- 'first user stream')
- out.Check(r'type \?\?\? \(222222\), size 00000080',
- 'second user stream')
- if z7_dump_path:
- out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm')
+def GetDumpFromSignal(out_dir, pipe_name, *args):
+ STATUS_FATAL_APP_EXIT = 0x40000015 # Not known by win32con.
+ return GetDumpFromProgram(out_dir, pipe_name, 'crashy_signal.exe',
+ STATUS_FATAL_APP_EXIT, *args)
+
+
+def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name):
+ return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe',
+ win32con.EXCEPTION_BREAKPOINT)
+
+
+def GetDumpFromZ7Program(out_dir, pipe_name):
+ return GetDumpFromProgram(out_dir, pipe_name, 'crashy_z7_loader.exe',
+ win32con.EXCEPTION_ACCESS_VIOLATION)
+
+
+class CdbRun(object):
+ """Run cdb.exe passing it a cdb command and capturing the output.
+ `Check()` searches for regex patterns in sequence allowing verification of
+ expected output.
+ """
+
+ def __init__(self, cdb_path, dump_path, command):
+ # Run a command line that loads the dump, runs the specified cdb
+ # command, and then quits, and capturing stdout.
+ self.out = subprocess.check_output(
+ [cdb_path, '-z', dump_path, '-c', command + ';q'])
+
+ def Check(self, pattern, message, re_flags=0):
+ match_obj = re.search(pattern, self.out, re_flags)
+ if match_obj:
+ # Matched. Consume up to end of match.
+ self.out = self.out[match_obj.end(0):]
+ print('ok - %s' % message)
+ sys.stdout.flush()
+ else:
+ print('-' * 80, file=sys.stderr)
+ print('FAILED - %s' % message, file=sys.stderr)
+ print('-' * 80, file=sys.stderr)
+ print('did not match:\n %s' % pattern, file=sys.stderr)
+ print('-' * 80, file=sys.stderr)
+ print('remaining output was:\n %s' % self.out, file=sys.stderr)
+ print('-' * 80, file=sys.stderr)
+ sys.stderr.flush()
+ global g_had_failures
+ g_had_failures = True
+
+ def Find(self, pattern, re_flags=0):
+ match_obj = re.search(pattern, self.out, re_flags)
+ if match_obj:
+ # Matched. Consume up to end of match.
+ self.out = self.out[match_obj.end(0):]
+ return match_obj
+ return None
+
+
+def RunTests(cdb_path, dump_path, start_handler_dump_path, destroyed_dump_path,
+ z7_dump_path, other_program_path, other_program_no_exception_path,
+ sigabrt_main_path, sigabrt_background_path, pipe_name):
+ """Runs various tests in sequence. Runs a new cdb instance on the dump for
+ each block of tests to reduce the chances that output from one command is
+ confused for output from another.
+ """
+ out = CdbRun(cdb_path, dump_path, '.ecxr')
out.Check('This dump file has an exception of interest stored in it',
- 'captured exception in z7 module')
- # Older versions of cdb display relative to exports for /Z7 modules, newer
- # ones just display the offset.
- out.Check(r'z7_test(!CrashMe\+0xe|\+0x100e):',
- 'exception in z7 at correct location')
- out.Check(r'z7_test C \(codeview symbols\) z7_test\.dll',
- 'expected non-pdb symbol format')
+ 'captured exception')
- out = CdbRun(cdb_path, other_program_path, '.ecxr;k;~')
- out.Check('Unknown exception - code deadbea7',
- 'other program dump exception code')
- out.Check('!Sleep', 'other program reasonable location')
- out.Check("hanging_program!`anonymous namespace'::Thread1",
- 'other program dump right thread')
- count = 0
- while True:
- match_obj = out.Find(r'Id.*Suspend: (\d+) ')
+ # When SomeCrashyFunction is inlined, cdb doesn't demangle its namespace as
+ # "`anonymous namespace'" and instead gives the decorated form.
+ out.Check(
+ 'crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::'
+ 'SomeCrashyFunction', 'exception at correct location')
+
+ out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr')
+ out.Check('This dump file has an exception of interest stored in it',
+ 'captured exception (using StartHandler())')
+ out.Check(
+ 'crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::'
+ 'SomeCrashyFunction',
+ 'exception at correct location (using StartHandler())')
+
+ out = CdbRun(cdb_path, dump_path, '!peb')
+ out.Check(r'PEB at', 'found the PEB')
+ out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+',
+ 'PEB_LDR_DATA saved')
+ out.Check(r'Base TimeStamp Module',
+ 'module list present')
+ pipe_name_escaped = pipe_name.replace('\\', '\\\\')
+ out.Check(r'CommandLine: *\'.*crashy_program\.exe *' + pipe_name_escaped,
+ 'some PEB data is correct')
+ out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured',
+ re.IGNORECASE)
+
+ out = CdbRun(cdb_path, dump_path, '?? @$peb->ProcessParameters')
+ out.Check(r' ImagePathName *: _UNICODE_STRING ".*\\crashy_program\.exe"',
+ 'PEB->ProcessParameters.ImagePathName string captured')
+ out.Check(
+ ' DesktopInfo *: '
+ '_UNICODE_STRING "(?!--- memory read error at address ).*"',
+ 'PEB->ProcessParameters.DesktopInfo string captured')
+
+ out = CdbRun(cdb_path, dump_path, '!teb')
+ out.Check(r'TEB at', 'found the TEB')
+ out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data')
+ out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue')
+
+ out = CdbRun(cdb_path, dump_path, '!gle')
+ out.Check(
+ 'LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the '
+ 'file specified.', '!gle gets last error')
+ out.Check(
+ 'LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The '
+ 'file %hs does not exist.', '!gle gets last ntstatus')
+
+ if False:
+ # TODO(scottmg): Re-enable when we grab ntdll!RtlCriticalSectionList.
+ out = CdbRun(cdb_path, dump_path, '!locks')
+ out.Check(
+ r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
+ r'g_test_critical_section', 'lock was captured')
+ if platform.win32_ver()[0] != '7':
+ # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
+ out.Check(r'\*\*\* Locked',
+ 'lock debug info was captured, and is locked')
+
+ out = CdbRun(cdb_path, dump_path, '!handle')
+ out.Check(r'\d+ Handles', 'captured handles')
+ out.Check(r'Event\s+\d+', 'capture some event handles')
+ out.Check(r'File\s+\d+', 'capture some file handles')
+
+ out = CdbRun(cdb_path, dump_path, 'lm')
+ out.Check(r'Unloaded modules:', 'captured some unloaded modules')
+ out.Check(r'lz32\.dll', 'found expected unloaded module lz32')
+ out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror')
+
+ out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2')
+ out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+',
+ 'PEB_LDR_DATA saved')
+ out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE)
+
+ # Check that there is no stack trace in the self-destroyed process. Confirm
+ # that the top is where we expect it (that's based only on IP), but
+ # subsequent stack entries will not be available. This confirms that we have
+ # a mostly valid dump, but that the stack was omitted.
+ out.Check(
+ r'self_destroying_program!crashpad::`anonymous namespace\'::'
+ r'FreeOwnStackAndBreak.*\nquit:',
+ 'at correct location, no additional stack entries')
+
+ # Dump memory pointed to be EDI on the background suspended thread. We don't
+ # know the index of the thread because the system may have started other
+ # threads, so first do a run to extract the thread index that's suspended,
+ # and then another run to dump the data pointed to by EDI for that thread.
+ out = CdbRun(cdb_path, dump_path, '.ecxr;~')
+ match_obj = out.Find(r'(\d+)\s+Id: [0-9a-f.]+ Suspend: 1 Teb:')
if match_obj:
- if match_obj.group(1) != '0':
- out.Check(r'FAILED', 'all suspend counts should be 0')
- else:
- count += 1
- else:
- break
- assert count > 2
+ thread = match_obj.group(1)
+ out = CdbRun(cdb_path, dump_path, '.ecxr;~' + thread + 's;db /c14 edi')
+ out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',
+ 'data pointed to by registers captured')
- out = CdbRun(cdb_path, other_program_no_exception_path, '.ecxr;k')
- out.Check('Unknown exception - code 0cca11ed',
- 'other program with no exception given')
- out.Check('!RaiseException', 'other program in RaiseException()')
+ # Move up one stack frame after jumping to the exception, and examine
+ # memory.
+ out = CdbRun(cdb_path, dump_path,
+ '.ecxr; .f+; dd /c100 poi(offset_pointer)-20')
+ out.Check(
+ r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e '
+ r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 '
+ r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c '
+ r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 '
+ r'80000094 00000095 80000096 00000097',
+ 'data pointed to by stack captured')
- out = CdbRun(cdb_path, sigabrt_main_path, '.ecxr')
- out.Check('code 40000015', 'got sigabrt signal')
- out.Check('::HandleAbortSignal', ' stack in expected location')
+ # Attempt to retrieve the value of g_extra_memory_pointer (by name), and
+ # then examine the memory at which it points. Both should have been saved.
+ out = CdbRun(
+ cdb_path, dump_path,
+ 'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 '
+ 'L8')
+ out.Check(r'0000655e 0000656b 00006578 00006585',
+ 'extra memory range captured')
- out = CdbRun(cdb_path, sigabrt_background_path, '.ecxr')
- out.Check('code 40000015', 'got sigabrt signal from background thread')
+ out = CdbRun(cdb_path, dump_path, '.dumpdebug')
+ out.Check(r'type \?\?\? \(333333\), size 00001000', 'first user stream')
+ out.Check(r'type \?\?\? \(222222\), size 00000080', 'second user stream')
+
+ if z7_dump_path:
+ out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm')
+ out.Check('This dump file has an exception of interest stored in it',
+ 'captured exception in z7 module')
+ # Older versions of cdb display relative to exports for /Z7 modules,
+ # newer ones just display the offset.
+ out.Check(r'z7_test(!CrashMe\+0xe|\+0x100e):',
+ 'exception in z7 at correct location')
+ out.Check(r'z7_test C \(codeview symbols\) z7_test\.dll',
+ 'expected non-pdb symbol format')
+
+ out = CdbRun(cdb_path, other_program_path, '.ecxr;k;~')
+ out.Check('Unknown exception - code deadbea7',
+ 'other program dump exception code')
+ out.Check('!Sleep', 'other program reasonable location')
+ out.Check("hanging_program!`anonymous namespace'::Thread1",
+ 'other program dump right thread')
+ count = 0
+ while True:
+ match_obj = out.Find(r'Id.*Suspend: (\d+) ')
+ if match_obj:
+ if match_obj.group(1) != '0':
+ out.Check(r'FAILED', 'all suspend counts should be 0')
+ else:
+ count += 1
+ else:
+ break
+ assert count > 2
+
+ out = CdbRun(cdb_path, other_program_no_exception_path, '.ecxr;k')
+ out.Check('Unknown exception - code 0cca11ed',
+ 'other program with no exception given')
+ out.Check('!RaiseException', 'other program in RaiseException()')
+
+ out = CdbRun(cdb_path, sigabrt_main_path, '.ecxr')
+ out.Check('code 40000015', 'got sigabrt signal')
+ out.Check('::HandleAbortSignal', ' stack in expected location')
+
+ out = CdbRun(cdb_path, sigabrt_background_path, '.ecxr')
+ out.Check('code 40000015', 'got sigabrt signal from background thread')
def main(args):
- try:
- if len(args) != 1:
- print('must supply binary dir', file=sys.stderr)
- return 1
+ try:
+ if len(args) != 1:
+ print('must supply binary dir', file=sys.stderr)
+ return 1
- cdb_path = GetCdbPath()
- if not cdb_path:
- print('could not find cdb', file=sys.stderr)
- return 1
+ cdb_path = GetCdbPath()
+ if not cdb_path:
+ print('could not find cdb', file=sys.stderr)
+ return 1
- # Make sure we can download Windows symbols.
- if not os.environ.get('_NT_SYMBOL_PATH'):
- symbol_dir = MakeTempDir()
- protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http'
- os.environ['_NT_SYMBOL_PATH'] = (
- 'SRV*' + symbol_dir + '*' +
- protocol + '://msdl.microsoft.com/download/symbols')
+ # Make sure we can download Windows symbols.
+ if not os.environ.get('_NT_SYMBOL_PATH'):
+ symbol_dir = MakeTempDir()
+ protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http'
+ os.environ['_NT_SYMBOL_PATH'] = (
+ 'SRV*' + symbol_dir + '*' + protocol +
+ '://msdl.microsoft.com/download/symbols')
- pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
- os.getpid(), str(random.getrandbits(64)))
+ pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (os.getpid(),
+ str(random.getrandbits(64)))
- crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
- if not crashy_dump_path:
- return 1
+ crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
+ if not crashy_dump_path:
+ return 1
- start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None)
- if not start_handler_dump_path:
- return 1
+ start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None)
+ if not start_handler_dump_path:
+ return 1
- destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name)
- if not destroyed_dump_path:
- return 1
+ destroyed_dump_path = GetDumpFromSelfDestroyingProgram(
+ args[0], pipe_name)
+ if not destroyed_dump_path:
+ return 1
- z7_dump_path = None
- if not args[0].endswith('_x64'):
- z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name)
- if not z7_dump_path:
- return 1
+ z7_dump_path = None
+ if not args[0].endswith('_x64'):
+ z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name)
+ if not z7_dump_path:
+ return 1
- other_program_path = GetDumpFromOtherProgram(args[0], pipe_name)
- if not other_program_path:
- return 1
+ other_program_path = GetDumpFromOtherProgram(args[0], pipe_name)
+ if not other_program_path:
+ return 1
- other_program_no_exception_path = GetDumpFromOtherProgram(
- args[0], pipe_name, 'noexception')
- if not other_program_no_exception_path:
- return 1
+ other_program_no_exception_path = GetDumpFromOtherProgram(
+ args[0], pipe_name, 'noexception')
+ if not other_program_no_exception_path:
+ return 1
- sigabrt_main_path = GetDumpFromSignal(args[0], pipe_name, 'main')
- if not sigabrt_main_path:
- return 1
+ sigabrt_main_path = GetDumpFromSignal(args[0], pipe_name, 'main')
+ if not sigabrt_main_path:
+ return 1
- sigabrt_background_path = GetDumpFromSignal(
- args[0], pipe_name, 'background')
- if not sigabrt_background_path:
- return 1
+ sigabrt_background_path = GetDumpFromSignal(args[0], pipe_name,
+ 'background')
+ if not sigabrt_background_path:
+ return 1
- RunTests(cdb_path,
- crashy_dump_path,
- start_handler_dump_path,
- destroyed_dump_path,
- z7_dump_path,
- other_program_path,
- other_program_no_exception_path,
- sigabrt_main_path,
- sigabrt_background_path,
- pipe_name)
+ RunTests(cdb_path, crashy_dump_path, start_handler_dump_path,
+ destroyed_dump_path, z7_dump_path, other_program_path,
+ other_program_no_exception_path, sigabrt_main_path,
+ sigabrt_background_path, pipe_name)
- return 1 if g_had_failures else 0
- finally:
- CleanUpTempDirs()
+ return 1 if g_had_failures else 0
+ finally:
+ CleanUpTempDirs()
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/snapshot/win/exception_snapshot_win.cc b/snapshot/win/exception_snapshot_win.cc
index 64b5342..3413a40 100644
--- a/snapshot/win/exception_snapshot_win.cc
+++ b/snapshot/win/exception_snapshot_win.cc
@@ -17,9 +17,9 @@
#include "client/crashpad_client.h"
#include "snapshot/capture_memory.h"
#include "snapshot/memory_snapshot.h"
-#include "snapshot/win/cpu_context_win.h"
+#include "snapshot/memory_snapshot_generic.h"
#include "snapshot/win/capture_memory_delegate_win.h"
-#include "snapshot/win/memory_snapshot_win.h"
+#include "snapshot/win/cpu_context_win.h"
#include "snapshot/win/process_reader_win.h"
#include "util/win/nt_internals.h"
@@ -28,22 +28,13 @@
namespace {
+#if defined(ARCH_CPU_X86_FAMILY)
#if defined(ARCH_CPU_32_BITS)
using Context32 = CONTEXT;
#elif defined(ARCH_CPU_64_BITS)
using Context32 = WOW64_CONTEXT;
#endif
-#if defined(ARCH_CPU_64_BITS)
-void NativeContextToCPUContext64(const CONTEXT& context_record,
- CPUContext* context,
- CPUContextUnion* context_union) {
- context->architecture = kCPUArchitectureX86_64;
- context->x86_64 = &context_union->x86_64;
- InitializeX64Context(context_record, context->x86_64);
-}
-#endif
-
void NativeContextToCPUContext32(const Context32& context_record,
CPUContext* context,
CPUContextUnion* context_union) {
@@ -51,6 +42,25 @@
context->x86 = &context_union->x86;
InitializeX86Context(context_record, context->x86);
}
+#endif // ARCH_CPU_X86_FAMILY
+
+#if defined(ARCH_CPU_64_BITS)
+void NativeContextToCPUContext64(const CONTEXT& context_record,
+ CPUContext* context,
+ CPUContextUnion* context_union) {
+#if defined(ARCH_CPU_X86_64)
+ context->architecture = kCPUArchitectureX86_64;
+ context->x86_64 = &context_union->x86_64;
+ InitializeX64Context(context_record, context->x86_64);
+#elif defined(ARCH_CPU_ARM64)
+ context->architecture = kCPUArchitectureARM64;
+ context->arm64 = &context_union->arm64;
+ InitializeARM64Context(context_record, context->arm64);
+#else
+#error Unsupported Windows 64-bit Arch
+#endif
+}
+#endif
} // namespace
@@ -106,6 +116,8 @@
}
}
#endif
+
+#if !defined(ARCH_CPU_ARM64)
if (!is_64_bit) {
if (!InitializeFromExceptionPointers<EXCEPTION_RECORD32,
process_types::EXCEPTION_POINTERS32>(
@@ -116,6 +128,7 @@
return false;
}
}
+#endif
CaptureMemoryDelegateWin capture_memory_delegate(
process_reader, *thread, &extra_memory_, nullptr);
@@ -176,9 +189,9 @@
CPUContext* context,
CPUContextUnion* context_union)) {
ExceptionPointersType exception_pointers;
- if (!process_reader->ReadMemory(exception_pointers_address,
- sizeof(exception_pointers),
- &exception_pointers)) {
+ if (!process_reader->Memory()->Read(exception_pointers_address,
+ sizeof(exception_pointers),
+ &exception_pointers)) {
LOG(ERROR) << "EXCEPTION_POINTERS read failed";
return false;
}
@@ -188,7 +201,7 @@
}
ExceptionRecordType first_record;
- if (!process_reader->ReadMemory(
+ if (!process_reader->Memory()->Read(
static_cast<WinVMAddress>(exception_pointers.ExceptionRecord),
sizeof(first_record),
&first_record)) {
@@ -240,7 +253,7 @@
}
ContextType context_record;
- if (!process_reader->ReadMemory(
+ if (!process_reader->Memory()->Read(
static_cast<WinVMAddress>(exception_pointers.ContextRecord),
sizeof(context_record),
&context_record)) {
diff --git a/snapshot/win/exception_snapshot_win.h b/snapshot/win/exception_snapshot_win.h
index f6e29d9..fd4e854 100644
--- a/snapshot/win/exception_snapshot_win.h
+++ b/snapshot/win/exception_snapshot_win.h
@@ -36,14 +36,16 @@
namespace internal {
-class MemorySnapshotWin;
+class MemorySnapshotGeneric;
-#if defined(ARCH_CPU_X86_FAMILY)
union CPUContextUnion {
+#if defined(ARCH_CPU_X86_FAMILY)
CPUContextX86 x86;
CPUContextX86_64 x86_64;
-};
+#elif defined(ARCH_CPU_ARM64)
+ CPUContextARM64 arm64;
#endif
+};
class ExceptionSnapshotWin final : public ExceptionSnapshot {
public:
@@ -91,12 +93,10 @@
CPUContext* context,
CPUContextUnion* context_union));
-#if defined(ARCH_CPU_X86_FAMILY)
CPUContextUnion context_union_;
-#endif
CPUContext context_;
std::vector<uint64_t> codes_;
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>> extra_memory_;
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>> extra_memory_;
uint64_t thread_id_;
uint64_t exception_address_;
uint32_t exception_flags_;
diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc
index 537b4ca..07a25a8 100644
--- a/snapshot/win/exception_snapshot_win_test.cc
+++ b/snapshot/win/exception_snapshot_win_test.cc
@@ -23,7 +23,6 @@
#include "gtest/gtest.h"
#include "snapshot/win/process_snapshot_win.h"
#include "test/errors.h"
-#include "test/gtest_disabled.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
@@ -103,7 +102,12 @@
// Verify the exception happened at the expected location with a bit of
// slop space to allow for reading the current PC before the exception
// happens. See TestCrashingChild().
+#if !defined(NDEBUG)
+ // Debug build is likely not optimized and contains more instructions.
+ constexpr uint64_t kAllowedOffset = 200;
+#else
constexpr uint64_t kAllowedOffset = 100;
+#endif
EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_);
EXPECT_LT(snapshot.Exception()->ExceptionAddress(),
break_near_ + kAllowedOffset);
@@ -176,7 +180,7 @@
#if defined(ARCH_CPU_64_BITS)
TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestCrashingChild(TestPaths::Architecture::k32Bit);
@@ -214,6 +218,9 @@
// ASan instrumentation inserts more instructions between the expected
// location and what's reported. https://crbug.com/845011.
constexpr uint64_t kAllowedOffset = 500;
+#elif !defined(NDEBUG)
+ // Debug build is likely not optimized and contains more instructions.
+ constexpr uint64_t kAllowedOffset = 200;
#else
constexpr uint64_t kAllowedOffset = 100;
#endif
@@ -280,14 +287,20 @@
EXPECT_EQ(child.WaitForExit(), 0u);
}
-TEST(SimulateCrash, ChildDumpWithoutCrashing) {
+#if defined(ADDRESS_SANITIZER)
+// https://crbug.com/845011
+#define MAYBE_ChildDumpWithoutCrashing DISABLED_ChildDumpWithoutCrashing
+#else
+#define MAYBE_ChildDumpWithoutCrashing ChildDumpWithoutCrashing
+#endif
+TEST(SimulateCrash, MAYBE_ChildDumpWithoutCrashing) {
TestDumpWithoutCrashingChild(TestPaths::Architecture::kDefault);
}
#if defined(ARCH_CPU_64_BITS)
TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestDumpWithoutCrashingChild(TestPaths::Architecture::k32Bit);
diff --git a/snapshot/win/extra_memory_ranges_test.cc b/snapshot/win/extra_memory_ranges_test.cc
index dcef805..e0c17a2 100644
--- a/snapshot/win/extra_memory_ranges_test.cc
+++ b/snapshot/win/extra_memory_ranges_test.cc
@@ -25,7 +25,6 @@
#include "client/simple_address_range_bag.h"
#include "gtest/gtest.h"
#include "snapshot/win/process_snapshot_win.h"
-#include "test/gtest_disabled.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
@@ -110,7 +109,7 @@
#if defined(ARCH_CPU_64_BITS)
TEST(ExtraMemoryRanges, DontCrashWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestExtraMemoryRanges(kDontCrash, TestPaths::Architecture::k32Bit);
@@ -118,7 +117,7 @@
TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Architecture::k32Bit);
diff --git a/snapshot/win/memory_snapshot_win.cc b/snapshot/win/memory_snapshot_win.cc
deleted file mode 100644
index 17e8ac1..0000000
--- a/snapshot/win/memory_snapshot_win.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2015 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <memory>
-
-#include "snapshot/win/memory_snapshot_win.h"
-
-namespace crashpad {
-namespace internal {
-
-MemorySnapshotWin::MemorySnapshotWin()
- : MemorySnapshot(),
- process_reader_(nullptr),
- address_(0),
- size_(0),
- initialized_() {
-}
-
-MemorySnapshotWin::~MemorySnapshotWin() {
-}
-
-void MemorySnapshotWin::Initialize(ProcessReaderWin* process_reader,
- uint64_t address,
- uint64_t size) {
- INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- process_reader_ = process_reader;
- address_ = address;
- DLOG_IF(WARNING, size >= std::numeric_limits<size_t>::max())
- << "size overflow";
- size_ = static_cast<size_t>(size);
- INITIALIZATION_STATE_SET_VALID(initialized_);
-}
-
-uint64_t MemorySnapshotWin::Address() const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return address_;
-}
-
-size_t MemorySnapshotWin::Size() const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- return size_;
-}
-
-bool MemorySnapshotWin::Read(Delegate* delegate) const {
- INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-
- if (size_ == 0) {
- return delegate->MemorySnapshotDelegateRead(nullptr, size_);
- }
-
- std::unique_ptr<uint8_t[]> buffer(new uint8_t[size_]);
- if (!process_reader_->ReadMemory(address_, size_, buffer.get())) {
- return false;
- }
- return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
-}
-
-const MemorySnapshot* MemorySnapshotWin::MergeWithOtherSnapshot(
- const MemorySnapshot* other) const {
- return MergeWithOtherSnapshotImpl(this, other);
-}
-
-} // namespace internal
-} // namespace crashpad
diff --git a/snapshot/win/memory_snapshot_win.h b/snapshot/win/memory_snapshot_win.h
deleted file mode 100644
index ebc878b..0000000
--- a/snapshot/win/memory_snapshot_win.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2015 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_
-#define CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "base/macros.h"
-#include "snapshot/memory_snapshot.h"
-#include "snapshot/win/process_reader_win.h"
-#include "util/misc/initialization_state_dcheck.h"
-
-namespace crashpad {
-namespace internal {
-
-//! \brief A MemorySnapshot of a memory region in a process on the running
-//! system, when the system runs Windows.
-class MemorySnapshotWin final : public MemorySnapshot {
- public:
- MemorySnapshotWin();
- ~MemorySnapshotWin() override;
-
- //! \brief Initializes the object.
- //!
- //! Memory is read lazily. No attempt is made to read the memory snapshot data
- //! until Read() is called, and the memory snapshot data is discared when
- //! Read() returns.
- //!
- //! \param[in] process_reader A reader for the process being snapshotted.
- //! \param[in] address The base address of the memory region to snapshot, in
- //! the snapshot process' address space.
- //! \param[in] size The size of the memory region to snapshot.
- void Initialize(ProcessReaderWin* process_reader,
- uint64_t address,
- uint64_t size);
-
- // MemorySnapshot:
-
- uint64_t Address() const override;
- size_t Size() const override;
- bool Read(Delegate* delegate) const override;
- const MemorySnapshot* MergeWithOtherSnapshot(
- const MemorySnapshot* other) const override;
-
- private:
- template <class T>
- friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
- const T* self,
- const MemorySnapshot* other);
-
- ProcessReaderWin* process_reader_; // weak
- uint64_t address_;
- size_t size_;
- InitializationStateDcheck initialized_;
-
- DISALLOW_COPY_AND_ASSIGN(MemorySnapshotWin);
-};
-
-} // namespace internal
-} // namespace crashpad
-
-#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_
diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc
index 880e9da..c7aaad8 100644
--- a/snapshot/win/module_snapshot_win.cc
+++ b/snapshot/win/module_snapshot_win.cc
@@ -19,7 +19,7 @@
#include "base/strings/utf_string_conversions.h"
#include "client/crashpad_info.h"
#include "client/simple_address_range_bag.h"
-#include "snapshot/win/memory_snapshot_win.h"
+#include "snapshot/memory_snapshot_generic.h"
#include "snapshot/win/pe_image_annotations_reader.h"
#include "snapshot/win/pe_image_reader.h"
#include "util/misc/tri_state.h"
@@ -33,17 +33,18 @@
name_(),
pdb_name_(),
uuid_(),
- pe_image_reader_(),
+ memory_range_(),
+ streams_(),
+ vs_fixed_file_info_(),
+ initialized_vs_fixed_file_info_(),
process_reader_(nullptr),
+ pe_image_reader_(),
+ crashpad_info_(),
timestamp_(0),
age_(0),
- initialized_(),
- vs_fixed_file_info_(),
- initialized_vs_fixed_file_info_() {
-}
+ initialized_() {}
-ModuleSnapshotWin::~ModuleSnapshotWin() {
-}
+ModuleSnapshotWin::~ModuleSnapshotWin() {}
bool ModuleSnapshotWin::Initialize(
ProcessReaderWin* process_reader,
@@ -75,6 +76,26 @@
pdb_name_ = base::UTF16ToUTF8(name_);
}
+ if (!memory_range_.Initialize(process_reader_->Memory(),
+ process_reader_->Is64Bit())) {
+ return false;
+ }
+
+ WinVMAddress crashpad_info_address;
+ WinVMSize crashpad_info_size;
+ if (pe_image_reader_->GetCrashpadInfoSection(&crashpad_info_address,
+ &crashpad_info_size)) {
+ ProcessMemoryRange info_range;
+ info_range.Initialize(memory_range_);
+ info_range.RestrictRange(crashpad_info_address,
+ crashpad_info_address + crashpad_info_size);
+
+ auto info = std::make_unique<CrashpadInfoReader>();
+ if (info->Initialize(&info_range, crashpad_info_address)) {
+ crashpad_info_ = std::move(info);
+ }
+ }
+
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
@@ -173,6 +194,11 @@
return pdb_name_;
}
+std::vector<uint8_t> ModuleSnapshotWin::BuildID() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<uint8_t>();
+}
+
std::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// These correspond to system-logged things on Mac. We don't currently track
@@ -228,8 +254,7 @@
template <class Traits>
void ModuleSnapshotWin::GetCrashpadOptionsInternal(
CrashpadInfoClientOptions* options) {
- process_types::CrashpadInfo<Traits> crashpad_info;
- if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) {
+ if (!crashpad_info_) {
options->crashpad_handler_behavior = TriState::kUnset;
options->system_crash_reporter_forwarding = TriState::kUnset;
options->gather_indirectly_referenced_memory = TriState::kUnset;
@@ -238,19 +263,13 @@
}
options->crashpad_handler_behavior =
- CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
- crashpad_info.crashpad_handler_behavior);
-
+ crashpad_info_->CrashpadHandlerBehavior();
options->system_crash_reporter_forwarding =
- CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
- crashpad_info.system_crash_reporter_forwarding);
-
+ crashpad_info_->SystemCrashReporterForwarding();
options->gather_indirectly_referenced_memory =
- CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
- crashpad_info.gather_indirectly_referenced_memory);
-
+ crashpad_info_->GatherIndirectlyReferencedMemory();
options->indirectly_referenced_memory_cap =
- crashpad_info.indirectly_referenced_memory_cap;
+ crashpad_info_->IndirectlyReferencedMemoryCap();
}
const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {
@@ -270,16 +289,13 @@
template <class Traits>
void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges(
std::set<CheckedRange<uint64_t>>* ranges) const {
- process_types::CrashpadInfo<Traits> crashpad_info;
- if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) ||
- !crashpad_info.extra_address_ranges) {
+ if (!crashpad_info_ || !crashpad_info_->ExtraMemoryRanges())
return;
- }
std::vector<SimpleAddressRangeBag::Entry> simple_ranges(
SimpleAddressRangeBag::num_entries);
- if (!process_reader_->ReadMemory(
- crashpad_info.extra_address_ranges,
+ if (!process_reader_->Memory()->Read(
+ crashpad_info_->ExtraMemoryRanges(),
simple_ranges.size() * sizeof(simple_ranges[0]),
&simple_ranges[0])) {
LOG(WARNING) << "could not read simple address_ranges from "
@@ -298,24 +314,23 @@
template <class Traits>
void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams(
std::vector<std::unique_ptr<const UserMinidumpStream>>* streams) const {
- process_types::CrashpadInfo<Traits> crashpad_info;
- if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info))
+ if (!crashpad_info_)
return;
- for (uint64_t cur = crashpad_info.user_data_minidump_stream_head; cur;) {
+ for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) {
internal::UserDataMinidumpStreamListEntry list_entry;
- if (!process_reader_->ReadMemory(
- cur, sizeof(list_entry), &list_entry)) {
+ if (!process_reader_->Memory()->Read(
+ cur, sizeof(list_entry), &list_entry)) {
LOG(WARNING) << "could not read user data stream entry from "
<< base::UTF16ToUTF8(name_);
return;
}
if (list_entry.size != 0) {
- std::unique_ptr<internal::MemorySnapshotWin> memory(
- new internal::MemorySnapshotWin());
+ std::unique_ptr<internal::MemorySnapshotGeneric> memory(
+ new internal::MemorySnapshotGeneric());
memory->Initialize(
- process_reader_, list_entry.base_address, list_entry.size);
+ process_reader_->Memory(), list_entry.base_address, list_entry.size);
streams->push_back(std::make_unique<UserMinidumpStream>(
list_entry.stream_type, memory.release()));
}
diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h
index 693588d..b32216c 100644
--- a/snapshot/win/module_snapshot_win.h
+++ b/snapshot/win/module_snapshot_win.h
@@ -25,6 +25,7 @@
#include "base/macros.h"
#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/crashpad_types/crashpad_info_reader.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/win/process_reader_win.h"
#include "util/misc/initialization_state.h"
@@ -83,6 +84,7 @@
ModuleType GetModuleType() const override;
void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
std::string DebugFileName() const override;
+ std::vector<uint8_t> BuildID() const override;
std::vector<std::string> AnnotationsVector() const override;
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
std::vector<AnnotationSnapshot> AnnotationObjects() const override;
@@ -109,18 +111,19 @@
std::wstring name_;
std::string pdb_name_;
UUID uuid_;
- std::unique_ptr<PEImageReader> pe_image_reader_;
- ProcessReaderWin* process_reader_; // weak
- time_t timestamp_;
- uint32_t age_;
+ ProcessMemoryRange memory_range_;
// Too const-y: https://crashpad.chromium.org/bug/9.
mutable std::vector<std::unique_ptr<const UserMinidumpStream>> streams_;
- InitializationStateDcheck initialized_;
-
// VSFixedFileInfo() is logically const, but updates these members on the
// call. See https://crashpad.chromium.org/bug/9.
mutable VS_FIXEDFILEINFO vs_fixed_file_info_;
mutable InitializationState initialized_vs_fixed_file_info_;
+ ProcessReaderWin* process_reader_; // weak
+ std::unique_ptr<PEImageReader> pe_image_reader_;
+ std::unique_ptr<CrashpadInfoReader> crashpad_info_;
+ time_t timestamp_;
+ uint32_t age_;
+ InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotWin);
};
diff --git a/snapshot/win/pe_image_annotations_reader_test.cc b/snapshot/win/module_snapshot_win_test.cc
similarity index 86%
rename from snapshot/win/pe_image_annotations_reader_test.cc
rename to snapshot/win/module_snapshot_win_test.cc
index e102600..d9786cf 100644
--- a/snapshot/win/pe_image_annotations_reader_test.cc
+++ b/snapshot/win/module_snapshot_win_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "snapshot/win/pe_image_annotations_reader.h"
+#include "snapshot/win/module_snapshot_win.h"
#include <stdlib.h>
#include <string.h>
@@ -30,7 +30,6 @@
#include "snapshot/annotation_snapshot.h"
#include "snapshot/win/pe_image_reader.h"
#include "snapshot/win/process_reader_win.h"
-#include "test/gtest_disabled.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
@@ -74,20 +73,15 @@
std::map<std::string, std::string> all_annotations_simple_map;
std::vector<AnnotationSnapshot> all_annotation_objects;
for (const ProcessInfo::Module& module : modules) {
- PEImageReader pe_image_reader;
- pe_image_reader.Initialize(&process_reader,
- module.dll_base,
- module.size,
- base::UTF16ToUTF8(module.name));
- PEImageAnnotationsReader module_annotations_reader(
- &process_reader, &pe_image_reader, module.name);
+ internal::ModuleSnapshotWin module_snapshot;
+ module_snapshot.Initialize(&process_reader, module);
std::map<std::string, std::string> module_annotations_simple_map =
- module_annotations_reader.SimpleMap();
+ module_snapshot.AnnotationsSimpleMap();
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
module_annotations_simple_map.end());
- auto module_annotations_list = module_annotations_reader.AnnotationsList();
+ auto module_annotations_list = module_snapshot.AnnotationObjects();
all_annotation_objects.insert(all_annotation_objects.end(),
module_annotations_list.begin(),
module_annotations_list.end());
@@ -146,26 +140,26 @@
EXPECT_EQ(child.WaitForExit(), expected_exit_code);
}
-TEST(PEImageAnnotationsReader, DontCrash) {
+TEST(ModuleSnapshotWinTest, DontCrash) {
TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::kDefault);
}
-TEST(PEImageAnnotationsReader, CrashDebugBreak) {
+TEST(ModuleSnapshotWinTest, CrashDebugBreak) {
TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::kDefault);
}
#if defined(ARCH_CPU_64_BITS)
-TEST(PEImageAnnotationsReader, DontCrashWOW64) {
+TEST(ModuleSnapshotWinTest, DontCrashWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::k32Bit);
}
-TEST(PEImageAnnotationsReader, CrashDebugBreakWOW64) {
+TEST(ModuleSnapshotWinTest, CrashDebugBreakWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::k32Bit);
diff --git a/snapshot/win/pe_image_annotations_reader.cc b/snapshot/win/pe_image_annotations_reader.cc
index 6796145..058342d 100644
--- a/snapshot/win/pe_image_annotations_reader.cc
+++ b/snapshot/win/pe_image_annotations_reader.cc
@@ -17,6 +17,7 @@
#include <string.h>
#include <sys/types.h>
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "client/annotation.h"
#include "client/simple_string_dictionary.h"
@@ -92,7 +93,7 @@
std::vector<SimpleStringDictionary::Entry>
simple_annotations(SimpleStringDictionary::num_entries);
- if (!process_reader_->ReadMemory(
+ if (!process_reader_->Memory()->Read(
crashpad_info.simple_annotations,
simple_annotations.size() * sizeof(simple_annotations[0]),
&simple_annotations[0])) {
@@ -127,9 +128,9 @@
}
process_types::AnnotationList<Traits> annotation_list_object;
- if (!process_reader_->ReadMemory(crashpad_info.annotations_list,
- sizeof(annotation_list_object),
- &annotation_list_object)) {
+ if (!process_reader_->Memory()->Read(crashpad_info.annotations_list,
+ sizeof(annotation_list_object),
+ &annotation_list_object)) {
LOG(WARNING) << "could not read annotations list object in "
<< base::UTF16ToUTF8(name_);
return;
@@ -140,7 +141,7 @@
current.link_node != annotation_list_object.tail_pointer &&
index < kMaxNumberOfAnnotations;
++index) {
- if (!process_reader_->ReadMemory(
+ if (!process_reader_->Memory()->Read(
current.link_node, sizeof(current), ¤t)) {
LOG(WARNING) << "could not read annotation at index " << index << " in "
<< base::UTF16ToUTF8(name_);
@@ -155,7 +156,8 @@
snapshot.type = current.type;
char name[Annotation::kNameMaxLength];
- if (!process_reader_->ReadMemory(current.name, arraysize(name), name)) {
+ if (!process_reader_->Memory()->Read(
+ current.name, base::size(name), name)) {
LOG(WARNING) << "could not read annotation name at index " << index
<< " in " << base::UTF16ToUTF8(name_);
continue;
@@ -167,7 +169,7 @@
size_t value_length =
std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);
snapshot.value.resize(value_length);
- if (!process_reader_->ReadMemory(
+ if (!process_reader_->Memory()->Read(
current.value, value_length, snapshot.value.data())) {
LOG(WARNING) << "could not read annotation value at index " << index
<< " in " << base::UTF16ToUTF8(name_);
diff --git a/snapshot/win/pe_image_reader.cc b/snapshot/win/pe_image_reader.cc
index e705bb5..e69faef 100644
--- a/snapshot/win/pe_image_reader.cc
+++ b/snapshot/win/pe_image_reader.cc
@@ -21,6 +21,7 @@
#include <memory>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "snapshot/win/pe_image_resource_reader.h"
@@ -71,6 +72,18 @@
return true;
}
+bool PEImageReader::GetCrashpadInfoSection(WinVMAddress* address,
+ WinVMSize* size) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (module_subrange_reader_.Is64Bit()) {
+ return GetCrashpadInfoSectionInternal<process_types::internal::Traits64>(
+ address, size);
+ } else {
+ return GetCrashpadInfoSectionInternal<process_types::internal::Traits32>(
+ address, size);
+ }
+}
+
template <class Traits>
bool PEImageReader::GetCrashpadInfo(
process_types::CrashpadInfo<Traits>* crashpad_info) const {
@@ -275,7 +288,7 @@
version_info.wType != 0 ||
wcsncmp(version_info.szKey,
L"VS_VERSION_INFO",
- arraysize(version_info.szKey)) != 0) {
+ base::size(version_info.szKey)) != 0) {
LOG(WARNING) << "unexpected VS_VERSIONINFO in "
<< module_subrange_reader_.name();
return false;
@@ -293,6 +306,31 @@
return true;
}
+template <class Traits>
+bool PEImageReader::GetCrashpadInfoSectionInternal(WinVMAddress* address,
+ WinVMSize* size) const {
+ IMAGE_SECTION_HEADER section;
+ if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>("CPADinfo",
+ §ion)) {
+ return false;
+ }
+
+ process_types::CrashpadInfo<Traits> crashpad_info;
+ if (section.Misc.VirtualSize <
+ offsetof(process_types::CrashpadInfo<Traits>, size) +
+ sizeof(crashpad_info.size)) {
+ LOG(WARNING) << "small crashpad info section size "
+ << section.Misc.VirtualSize << ", "
+ << module_subrange_reader_.name();
+ return false;
+ }
+
+ *address = Address() + section.VirtualAddress;
+ *size = std::min<WinVMSize>(sizeof(crashpad_info), section.Misc.VirtualSize);
+
+ return true;
+}
+
template <class NtHeadersType>
bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,
WinVMAddress* nt_headers_address) const {
diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h
index 56a991b..ebdbe57 100644
--- a/snapshot/win/pe_image_reader.h
+++ b/snapshot/win/pe_image_reader.h
@@ -94,6 +94,16 @@
//! This is the value passed as \a size to Initialize().
WinVMSize Size() const { return module_subrange_reader_.Size(); }
+ //! \brief Obtains the module's CrashpadInfo structure address and size.
+ //!
+ //! \param[out] address The CrashpadInfo structure address.
+ //! \param[out] size The CrashpadInfo structure size.
+ //!
+ //! \return `true` on success, `false` on failure. If the module does not have
+ //! a `CPADinfo` section, this will return `false` without logging any
+ //! messages. Other failures will result in messages being logged.
+ bool GetCrashpadInfoSection(WinVMAddress* address, WinVMSize* size) const;
+
//! \brief Obtains the module's CrashpadInfo structure.
//!
//! \return `true` on success, `false` on failure. If the module does not have
@@ -137,6 +147,13 @@
bool VSFixedFileInfo(VS_FIXEDFILEINFO* vs_fixed_file_info) const;
private:
+ //! \brief Performs the internal logic for GetCrashpadInfoSection().
+ //!
+ //! \sa GetCrashpadInfoSection
+ template <class Traits>
+ bool GetCrashpadInfoSectionInternal(WinVMAddress* address,
+ WinVMSize* size) const;
+
//! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.
//!
//! \param[out] nt_headers The contents of the templated NtHeadersType
diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc
index ac456e2..161d9c8 100644
--- a/snapshot/win/pe_image_reader_test.cc
+++ b/snapshot/win/pe_image_reader_test.cc
@@ -14,7 +14,9 @@
#include "snapshot/win/pe_image_reader.h"
-#define PSAPI_VERSION 1
+#ifndef PSAPI_VERSION
+#define PSAPI_VERSION 2
+#endif
#include <psapi.h>
#include "base/files/file_path.h"
diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc
index 2fa0258..51940c0 100644
--- a/snapshot/win/process_reader_win.cc
+++ b/snapshot/win/process_reader_win.cc
@@ -151,11 +151,17 @@
PLOG(ERROR) << "SuspendThread";
return false;
}
- DCHECK(previous_suspend_count > 0 ||
- suspension_state == ProcessSuspensionState::kRunning);
- thread->suspend_count =
- previous_suspend_count -
- (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
+ if (previous_suspend_count <= 0 &&
+ suspension_state == ProcessSuspensionState::kSuspended) {
+ LOG(WARNING) << "Thread " << thread->id
+ << " should be suspended, but previous_suspend_count is "
+ << previous_suspend_count;
+ thread->suspend_count = 0;
+ } else {
+ thread->suspend_count =
+ previous_suspend_count -
+ (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
+ }
memset(&thread->context, 0, sizeof(thread->context));
#if defined(ARCH_CPU_32_BITS)
@@ -204,6 +210,7 @@
ProcessReaderWin::ProcessReaderWin()
: process_(INVALID_HANDLE_VALUE),
process_info_(),
+ process_memory_(),
threads_(),
modules_(),
suspension_state_(),
@@ -220,67 +227,15 @@
process_ = process;
suspension_state_ = suspension_state;
- process_info_.Initialize(process);
+ if (!process_info_.Initialize(process))
+ return false;
+ if (!process_memory_.Initialize(process))
+ return false;
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
-bool ProcessReaderWin::ReadMemory(WinVMAddress at,
- WinVMSize num_bytes,
- void* into) const {
- if (num_bytes == 0)
- return 0;
-
- SIZE_T bytes_read;
- if (!ReadProcessMemory(process_,
- reinterpret_cast<void*>(at),
- into,
- base::checked_cast<SIZE_T>(num_bytes),
- &bytes_read) ||
- num_bytes != bytes_read) {
- PLOG(ERROR) << "ReadMemory at 0x" << std::hex << at << std::dec << " of "
- << num_bytes << " bytes failed";
- return false;
- }
- return true;
-}
-
-WinVMSize ProcessReaderWin::ReadAvailableMemory(WinVMAddress at,
- WinVMSize num_bytes,
- void* into) const {
- if (num_bytes == 0)
- return 0;
-
- auto ranges = process_info_.GetReadableRanges(
- CheckedRange<WinVMAddress, WinVMSize>(at, num_bytes));
-
- // We only read up until the first unavailable byte, so we only read from the
- // first range. If we have no ranges, then no bytes were accessible anywhere
- // in the range.
- if (ranges.empty()) {
- LOG(ERROR) << base::StringPrintf(
- "range at 0x%llx, size 0x%llx completely inaccessible", at, num_bytes);
- return 0;
- }
-
- // If the start address was adjusted, we couldn't read even the first
- // requested byte.
- if (ranges.front().base() != at) {
- LOG(ERROR) << base::StringPrintf(
- "start of range at 0x%llx, size 0x%llx inaccessible", at, num_bytes);
- return 0;
- }
-
- DCHECK_LE(ranges.front().size(), num_bytes);
-
- // If we fail on a normal read, then something went very wrong.
- if (!ReadMemory(ranges.front().base(), ranges.front().size(), into))
- return 0;
-
- return ranges.front().size();
-}
-
bool ProcessReaderWin::StartTime(timeval* start_time) const {
FILETIME creation, exit, kernel, user;
if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {
@@ -398,7 +353,7 @@
process_types::NT_TIB<Traits> tib;
thread.teb_address = thread_basic_info.TebBaseAddress;
thread.teb_size = sizeof(process_types::TEB<Traits>);
- if (ReadMemory(thread.teb_address, sizeof(tib), &tib)) {
+ if (process_memory_.Read(thread.teb_address, sizeof(tib), &tib)) {
WinVMAddress base = 0;
WinVMAddress limit = 0;
// If we're reading a WOW64 process, then the TIB we just retrieved is the
@@ -409,7 +364,7 @@
thread.teb_address = tib.Wow64Teb;
thread.teb_size =
sizeof(process_types::TEB<process_types::internal::Traits32>);
- if (ReadMemory(thread.teb_address, sizeof(tib32), &tib32)) {
+ if (process_memory_.Read(thread.teb_address, sizeof(tib32), &tib32)) {
base = tib32.StackBase;
limit = tib32.StackLimit;
}
diff --git a/snapshot/win/process_reader_win.h b/snapshot/win/process_reader_win.h
index 1794a51..a4e32aa 100644
--- a/snapshot/win/process_reader_win.h
+++ b/snapshot/win/process_reader_win.h
@@ -23,6 +23,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_memory_win.h"
#include "util/win/address_types.h"
#include "util/win/process_info.h"
@@ -84,25 +85,8 @@
//! \return `true` if the target task is a 64-bit process.
bool Is64Bit() const { return process_info_.Is64Bit(); }
- //! \brief Attempts to read \a num_bytes bytes from the target process
- //! starting at address \a at into \a into.
- //!
- //! \return `true` if the entire region could be read, or `false` with an
- //! error logged.
- //!
- //! \sa ReadAvailableMemory
- bool ReadMemory(WinVMAddress at, WinVMSize num_bytes, void* into) const;
-
- //! \brief Attempts to read \a num_bytes bytes from the target process
- //! starting at address \a at into \a into. If some of the specified range
- //! is not accessible, reads up to the first inaccessible byte.
- //!
- //! \return The actual number of bytes read.
- //!
- //! \sa ReadMemory
- WinVMSize ReadAvailableMemory(WinVMAddress at,
- WinVMSize num_bytes,
- void* into) const;
+ //! \brief Return a memory reader for the target process.
+ const ProcessMemoryWin* Memory() const { return &process_memory_; }
//! \brief Determines the target process' start time.
//!
@@ -145,6 +129,7 @@
HANDLE process_;
ProcessInfo process_info_;
+ ProcessMemoryWin process_memory_;
std::vector<Thread> threads_;
std::vector<ProcessInfo::Module> modules_;
ProcessSuspensionState suspension_state_;
diff --git a/snapshot/win/process_reader_win_test.cc b/snapshot/win/process_reader_win_test.cc
index d0d4d96..de22008 100644
--- a/snapshot/win/process_reader_win_test.cc
+++ b/snapshot/win/process_reader_win_test.cc
@@ -17,11 +17,13 @@
#include <windows.h>
#include <string.h>
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "test/win/win_multiprocess.h"
#include "util/misc/from_pointer_cast.h"
#include "util/synchronization/semaphore.h"
#include "util/thread/thread.h"
+#include "util/win/context_wrappers.h"
#include "util/win/scoped_process_suspend.h"
namespace crashpad {
@@ -42,8 +44,8 @@
EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId());
static constexpr char kTestMemory[] = "Some test memory";
- char buffer[arraysize(kTestMemory)];
- ASSERT_TRUE(process_reader.ReadMemory(
+ char buffer[base::size(kTestMemory)];
+ ASSERT_TRUE(process_reader.Memory()->Read(
reinterpret_cast<uintptr_t>(kTestMemory), sizeof(kTestMemory), &buffer));
EXPECT_STREQ(kTestMemory, buffer);
}
@@ -72,7 +74,7 @@
char buffer[sizeof(kTestMemory)];
ASSERT_TRUE(
- process_reader.ReadMemory(address, sizeof(kTestMemory), &buffer));
+ process_reader.Memory()->Read(address, sizeof(kTestMemory), &buffer));
EXPECT_EQ(strcmp(kTestMemory, buffer), 0);
}
@@ -106,12 +108,7 @@
ASSERT_GE(threads.size(), 1u);
EXPECT_EQ(threads[0].id, GetCurrentThreadId());
-#if defined(ARCH_CPU_64_BITS)
- EXPECT_NE(threads[0].context.native.Rip, 0u);
-#else
- EXPECT_NE(threads[0].context.native.Eip, 0u);
-#endif
-
+ EXPECT_NE(ProgramCounterFromCONTEXT(&threads[0].context.native), nullptr);
EXPECT_EQ(threads[0].suspend_count, 0u);
}
@@ -133,7 +130,7 @@
void ThreadMain() override {
done_->Wait();
- };
+ }
private:
Semaphore* done_;
@@ -188,7 +185,7 @@
// the pipe.
CheckedReadFileAtEOF(ReadPipeHandle());
- for (size_t i = 0; i < arraysize(threads); ++i)
+ for (size_t i = 0; i < base::size(threads); ++i)
done.Signal();
for (auto& thread : threads)
thread.Join();
diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc
index 289f50c..cafe7b4 100644
--- a/snapshot/win/process_snapshot_win.cc
+++ b/snapshot/win/process_snapshot_win.cc
@@ -21,8 +21,10 @@
#include <utility>
#include "base/logging.h"
-#include "base/strings/stringprintf.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
#include "util/misc/from_pointer_cast.h"
#include "util/misc/time.h"
#include "util/win/nt_internals.h"
@@ -63,9 +65,9 @@
if (exception_information_address != 0) {
ExceptionInformation exception_information = {};
- if (!process_reader_.ReadMemory(exception_information_address,
- sizeof(exception_information),
- &exception_information)) {
+ if (!process_reader_.Memory()->Read(exception_information_address,
+ sizeof(exception_information),
+ &exception_information)) {
LOG(WARNING) << "ReadMemory ExceptionInformation failed";
return false;
}
@@ -121,12 +123,12 @@
*options = options_;
}
-pid_t ProcessSnapshotWin::ProcessID() const {
+crashpad::ProcessID ProcessSnapshotWin::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.GetProcessInfo().ProcessID();
}
-pid_t ProcessSnapshotWin::ParentProcessID() const {
+crashpad::ProcessID ProcessSnapshotWin::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.GetProcessInfo().ParentProcessID();
}
@@ -232,6 +234,11 @@
return extra_memory;
}
+const ProcessMemory* ProcessSnapshotWin::Memory() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return process_reader_.Memory();
+}
+
void ProcessSnapshotWin::InitializeThreads(
bool gather_indirectly_referenced_memory,
uint32_t indirectly_referenced_memory_cap) {
@@ -271,7 +278,7 @@
// and 32-reading-32, so at the moment, we simply do not retrieve unloaded
// modules for 64-reading-32. See https://crashpad.chromium.org/bug/89.
-#if defined(ARCH_CPU_X86_64)
+#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)
if (!process_reader_.Is64Bit()) {
LOG(ERROR)
<< "reading unloaded modules across bitness not currently supported";
@@ -298,9 +305,9 @@
FromPointerCast<WinVMAddress>(event_trace_address);
Traits::Pointer pointer_to_array;
- if (!process_reader_.ReadMemory(address_in_target_process,
- sizeof(pointer_to_array),
- &pointer_to_array)) {
+ if (!process_reader_.Memory()->Read(address_in_target_process,
+ sizeof(pointer_to_array),
+ &pointer_to_array)) {
LOG(ERROR) << "failed to read target address";
return;
}
@@ -311,7 +318,7 @@
const size_t data_size = *element_size * *element_count;
std::vector<uint8_t> data(data_size);
- if (!process_reader_.ReadMemory(pointer_to_array, data_size, &data[0])) {
+ if (!process_reader_.Memory()->Read(pointer_to_array, data_size, &data[0])) {
LOG(ERROR) << "failed to read unloaded module data";
return;
}
@@ -328,7 +335,7 @@
uet.TimeDateStamp,
base::UTF16ToUTF8(base::StringPiece16(
uet.ImageName,
- wcsnlen(uet.ImageName, arraysize(uet.ImageName))))));
+ wcsnlen(uet.ImageName, base::size(uet.ImageName))))));
}
}
}
@@ -377,14 +384,15 @@
AddMemorySnapshot(peb_address, peb_size, &extra_memory_);
process_types::PEB<Traits> peb_data;
- if (!process_reader_.ReadMemory(peb_address, peb_size, &peb_data)) {
+ if (!process_reader_.Memory()->Read(
+ peb_address, base::checked_cast<size_t>(peb_size), &peb_data)) {
LOG(ERROR) << "ReadMemory PEB";
return;
}
process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &extra_memory_);
- if (!process_reader_.ReadMemory(
+ if (!process_reader_.Memory()->Read(
peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) {
LOG(ERROR) << "ReadMemory PEB_LDR_DATA";
} else {
@@ -406,9 +414,9 @@
}
process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
- if (!process_reader_.ReadMemory(peb_data.ProcessParameters,
- sizeof(process_parameters),
- &process_parameters)) {
+ if (!process_reader_.Memory()->Read(peb_data.ProcessParameters,
+ sizeof(process_parameters),
+ &process_parameters)) {
LOG(ERROR) << "ReadMemory RTL_USER_PROCESS_PARAMETERS";
return;
}
@@ -451,7 +459,7 @@
void ProcessSnapshotWin::AddMemorySnapshot(
WinVMAddress address,
WinVMSize size,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into) {
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {
if (size == 0)
return;
@@ -472,14 +480,14 @@
}
}
- into->push_back(std::make_unique<internal::MemorySnapshotWin>());
- into->back()->Initialize(&process_reader_, address, size);
+ into->push_back(std::make_unique<internal::MemorySnapshotGeneric>());
+ into->back()->Initialize(process_reader_.Memory(), address, size);
}
template <class Traits>
void ProcessSnapshotWin::AddMemorySnapshotForUNICODE_STRING(
const process_types::UNICODE_STRING<Traits>& us,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into) {
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {
AddMemorySnapshot(us.Buffer, us.Length, into);
}
@@ -487,7 +495,7 @@
void ProcessSnapshotWin::AddMemorySnapshotForLdrLIST_ENTRY(
const process_types::LIST_ENTRY<Traits>& le,
size_t offset_of_member,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into) {
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {
// Walk the doubly-linked list of entries, adding the list memory itself, as
// well as pointed-to strings.
typename Traits::Pointer last = le.Blink;
@@ -496,7 +504,7 @@
for (;;) {
// |cur| is the pointer to LIST_ENTRY embedded in the LDR_DATA_TABLE_ENTRY.
// So we need to offset back to the beginning of the structure.
- if (!process_reader_.ReadMemory(
+ if (!process_reader_.Memory()->Read(
cur - offset_of_member, sizeof(entry), &entry)) {
return;
}
@@ -520,16 +528,16 @@
// be more than enough.
std::wstring env_block;
env_block.resize(32768);
- WinVMSize bytes_read = process_reader_.ReadAvailableMemory(
+ size_t bytes_read = process_reader_.Memory()->ReadAvailableMemory(
start_of_environment_block,
env_block.size() * sizeof(env_block[0]),
&env_block[0]);
env_block.resize(
static_cast<unsigned int>(bytes_read / sizeof(env_block[0])));
static constexpr wchar_t terminator[] = {0, 0};
- size_t at = env_block.find(std::wstring(terminator, arraysize(terminator)));
+ size_t at = env_block.find(std::wstring(terminator, base::size(terminator)));
if (at != std::wstring::npos)
- env_block.resize(at + arraysize(terminator));
+ env_block.resize(at + base::size(terminator));
return env_block.size() * sizeof(env_block[0]);
}
@@ -537,13 +545,13 @@
template <class Traits>
void ProcessSnapshotWin::ReadLock(
WinVMAddress start,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into) {
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {
// We're walking the RTL_CRITICAL_SECTION_DEBUG ProcessLocksList, but starting
// from an actual RTL_CRITICAL_SECTION, so start by getting to the first
// RTL_CRITICAL_SECTION_DEBUG.
process_types::RTL_CRITICAL_SECTION<Traits> critical_section;
- if (!process_reader_.ReadMemory(
+ if (!process_reader_.Memory()->Read(
start, sizeof(critical_section), &critical_section)) {
LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION";
return;
diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h
index 9250c13..8b0bf52 100644
--- a/snapshot/win/process_snapshot_win.h
+++ b/snapshot/win/process_snapshot_win.h
@@ -31,6 +31,7 @@
#include "snapshot/exception_snapshot.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/memory_snapshot.h"
+#include "snapshot/memory_snapshot_generic.h"
#include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h"
@@ -38,12 +39,12 @@
#include "snapshot/unloaded_module_snapshot.h"
#include "snapshot/win/exception_snapshot_win.h"
#include "snapshot/win/memory_map_region_snapshot_win.h"
-#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/module_snapshot_win.h"
#include "snapshot/win/system_snapshot_win.h"
#include "snapshot/win/thread_snapshot_win.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/misc/uuid.h"
+#include "util/process/process_id.h"
#include "util/win/address_types.h"
#include "util/win/process_structs.h"
@@ -112,8 +113,8 @@
// ProcessSnapshot:
- pid_t ProcessID() const override;
- pid_t ParentProcessID() const override;
+ crashpad::ProcessID ProcessID() const override;
+ crashpad::ProcessID ParentProcessID() const override;
void SnapshotTime(timeval* snapshot_time) const override;
void ProcessStartTime(timeval* start_time) const override;
void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;
@@ -129,6 +130,7 @@
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override;
+ const ProcessMemory* Memory() const override;
private:
// Initializes threads_ on behalf of Initialize().
@@ -152,18 +154,18 @@
void AddMemorySnapshot(
WinVMAddress address,
WinVMSize size,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into);
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);
template <class Traits>
void AddMemorySnapshotForUNICODE_STRING(
const process_types::UNICODE_STRING<Traits>& us,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into);
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);
template <class Traits>
void AddMemorySnapshotForLdrLIST_ENTRY(
const process_types::LIST_ENTRY<Traits>& le,
size_t offset_of_member,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into);
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);
WinVMSize DetermineSizeOfEnvironmentBlock(
WinVMAddress start_of_environment_block);
@@ -173,10 +175,10 @@
template <class Traits>
void ReadLock(
WinVMAddress start,
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>>* into);
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);
internal::SystemSnapshotWin system_;
- std::vector<std::unique_ptr<internal::MemorySnapshotWin>> extra_memory_;
+ std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>> extra_memory_;
std::vector<std::unique_ptr<internal::ThreadSnapshotWin>> threads_;
std::vector<std::unique_ptr<internal::ModuleSnapshotWin>> modules_;
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
diff --git a/snapshot/win/process_snapshot_win_test.cc b/snapshot/win/process_snapshot_win_test.cc
index 8a3e6a4..7192aa8 100644
--- a/snapshot/win/process_snapshot_win_test.cc
+++ b/snapshot/win/process_snapshot_win_test.cc
@@ -20,7 +20,6 @@
#include "snapshot/win/pe_image_reader.h"
#include "snapshot/win/process_reader_win.h"
#include "test/errors.h"
-#include "test/gtest_disabled.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
@@ -120,7 +119,7 @@
#if defined(ARCH_CPU_64_BITS)
TEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestImageReaderChild(TestPaths::Architecture::k32Bit);
diff --git a/snapshot/win/process_subrange_reader.cc b/snapshot/win/process_subrange_reader.cc
index 996fbdf..124c5d0 100644
--- a/snapshot/win/process_subrange_reader.cc
+++ b/snapshot/win/process_subrange_reader.cc
@@ -15,6 +15,7 @@
#include "snapshot/win/process_subrange_reader.h"
#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
#include "snapshot/win/process_reader_win.h"
namespace crashpad {
@@ -82,7 +83,8 @@
return false;
}
- return process_reader_->ReadMemory(address, size, into);
+ return process_reader_->Memory()->Read(
+ address, base::checked_cast<size_t>(size), into);
}
bool ProcessSubrangeReader::InitializeInternal(ProcessReaderWin* process_reader,
diff --git a/snapshot/win/system_snapshot_win.cc b/snapshot/win/system_snapshot_win.cc
index 74f34a7..f07c4f6 100644
--- a/snapshot/win/system_snapshot_win.cc
+++ b/snapshot/win/system_snapshot_win.cc
@@ -26,7 +26,9 @@
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
#include "util/win/module_version.h"
+#include "util/win/scoped_registry_key.h"
namespace crashpad {
@@ -75,11 +77,9 @@
os_version_minor_(0),
os_version_bugfix_(0),
os_server_(false),
- initialized_() {
-}
+ initialized_() {}
-SystemSnapshotWin::~SystemSnapshotWin() {
-}
+SystemSnapshotWin::~SystemSnapshotWin() {}
void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
@@ -125,13 +125,20 @@
CPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
: kCPUArchitectureX86;
+#elif defined(ARCH_CPU_ARM64)
+ return kCPUArchitectureARM64;
+#else
+#error Unsupported Windows Arch
+#endif
}
uint32_t SystemSnapshotWin::CPURevision() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
uint32_t raw = CPUX86Signature();
uint8_t stepping = raw & 0xf;
uint8_t model = (raw & 0xf0) >> 4;
@@ -149,6 +156,14 @@
uint16_t adjusted_family = family + extended_family;
uint8_t adjusted_model = model + (extended_model << 4);
return (adjusted_family << 16) | (adjusted_model << 8) | stepping;
+#elif defined(ARCH_CPU_ARM64)
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+
+ return system_info.wProcessorRevision;
+#else
+#error Unsupported Windows Arch
+#endif
}
uint8_t SystemSnapshotWin::CPUCount() const {
@@ -166,6 +181,7 @@
std::string SystemSnapshotWin::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4];
__cpuid(cpu_info, 0);
char vendor[12];
@@ -173,6 +189,42 @@
*reinterpret_cast<int*>(vendor + 4) = cpu_info[3];
*reinterpret_cast<int*>(vendor + 8) = cpu_info[2];
return std::string(vendor, sizeof(vendor));
+#elif defined(ARCH_CPU_ARM64)
+ HKEY key;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+ 0,
+ KEY_QUERY_VALUE,
+ &key) != ERROR_SUCCESS) {
+ return std::string();
+ }
+
+ crashpad::ScopedRegistryKey scoped_key(key);
+ DWORD type;
+ wchar_t vendor_identifier[1024];
+ DWORD vendor_identifier_size = sizeof(vendor_identifier);
+
+ if (RegQueryValueEx(key,
+ L"VendorIdentifier",
+ nullptr,
+ &type,
+ reinterpret_cast<BYTE*>(vendor_identifier),
+ &vendor_identifier_size) != ERROR_SUCCESS ||
+ type != REG_SZ) {
+ return std::string();
+ }
+
+ std::string return_value;
+ DCHECK_EQ(vendor_identifier_size % sizeof(wchar_t), 0u);
+ base::UTF16ToUTF8(vendor_identifier,
+ vendor_identifier_size / sizeof(wchar_t),
+ &return_value);
+
+ return return_value.c_str();
+#else
+#error Unsupported Windows Arch
+#endif
}
void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz,
@@ -212,36 +264,52 @@
uint32_t SystemSnapshotWin::CPUX86Signature() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4];
// We will never run on any processors that don't support at least function 1.
__cpuid(cpu_info, 1);
return cpu_info[0];
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
uint64_t SystemSnapshotWin::CPUX86Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4];
// We will never run on any processors that don't support at least function 1.
__cpuid(cpu_info, 1);
return (static_cast<uint64_t>(cpu_info[2]) << 32) |
static_cast<uint64_t>(cpu_info[3]);
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4];
// We will never run on any processors that don't support at least extended
// function 1.
__cpuid(cpu_info, 0x80000001);
return (static_cast<uint64_t>(cpu_info[2]) << 32) |
static_cast<uint64_t>(cpu_info[3]);
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4];
// Make sure leaf 7 can be called.
@@ -251,11 +319,16 @@
__cpuidex(cpu_info, 7, 0);
return cpu_info[1];
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
bool SystemSnapshotWin::CPUX86SupportsDAZ() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
// The correct way to check for denormals-as-zeros (DAZ) support is to examine
// mxcsr mask, which can be done with fxsave. See Intel Software Developer's
// Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 "Checking for the
@@ -277,6 +350,10 @@
// Test the DAZ bit.
return (mxcsr_mask & (1 << 6)) != 0;
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
SystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const {
diff --git a/snapshot/win/system_snapshot_win_test.cc b/snapshot/win/system_snapshot_win_test.cc
index c87a6a6..d93c654 100644
--- a/snapshot/win/system_snapshot_win_test.cc
+++ b/snapshot/win/system_snapshot_win_test.cc
@@ -60,6 +60,10 @@
EXPECT_EQ(cpu_architecture, kCPUArchitectureX86);
#elif defined(ARCH_CPU_X86_64)
EXPECT_EQ(cpu_architecture, kCPUArchitectureX86_64);
+#elif defined(ARCH_CPU_ARM64)
+ EXPECT_EQ(cpu_architecture, kCPUArchitectureARM64);
+#else
+#error Unsupported Windows Arch
#endif
}
@@ -69,17 +73,22 @@
TEST_F(SystemSnapshotWinTest, CPUVendor) {
std::string cpu_vendor = system_snapshot().CPUVendor();
-
- // There are a variety of other values, but we don't expect to run our tests
- // on them.
+#if defined(ARCH_CPU_X86_FAMILY)
EXPECT_TRUE(cpu_vendor == "GenuineIntel" || cpu_vendor == "AuthenticAMD");
+#elif defined(ARCH_CPU_ARM64)
+ EXPECT_FALSE(cpu_vendor.empty());
+#else
+#error Unsupported Windows Arch
+#endif
}
+#if defined(ARCH_CPU_X86_FAMILY)
TEST_F(SystemSnapshotWinTest, CPUX86SupportsDAZ) {
// Most SSE2+ machines support Denormals-Are-Zero. This may fail if run on
// older machines.
EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ());
}
+#endif
TEST_F(SystemSnapshotWinTest, GetOperatingSystem) {
EXPECT_EQ(system_snapshot().GetOperatingSystem(),
diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc
index 3b927f9..02cc63c 100644
--- a/snapshot/win/thread_snapshot_win.cc
+++ b/snapshot/win/thread_snapshot_win.cc
@@ -48,22 +48,27 @@
if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(
CheckedRange<WinVMAddress, WinVMSize>(thread_.stack_region_address,
thread_.stack_region_size))) {
- stack_.Initialize(process_reader,
+ stack_.Initialize(process_reader->Memory(),
thread_.stack_region_address,
thread_.stack_region_size);
} else {
- stack_.Initialize(process_reader, 0, 0);
+ stack_.Initialize(process_reader->Memory(), 0, 0);
}
if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(
CheckedRange<WinVMAddress, WinVMSize>(thread_.teb_address,
thread_.teb_size))) {
- teb_.Initialize(process_reader, thread_.teb_address, thread_.teb_size);
+ teb_.Initialize(
+ process_reader->Memory(), thread_.teb_address, thread_.teb_size);
} else {
- teb_.Initialize(process_reader, 0, 0);
+ teb_.Initialize(process_reader->Memory(), 0, 0);
}
-#if defined(ARCH_CPU_X86_64)
+#if defined(ARCH_CPU_X86)
+ context_.architecture = kCPUArchitectureX86;
+ context_.x86 = &context_union_.x86;
+ InitializeX86Context(process_reader_thread.context.native, context_.x86);
+#elif defined(ARCH_CPU_X86_64)
if (process_reader->Is64Bit()) {
context_.architecture = kCPUArchitectureX86_64;
context_.x86_64 = &context_union_.x86_64;
@@ -73,11 +78,13 @@
context_.x86 = &context_union_.x86;
InitializeX86Context(process_reader_thread.context.wow64, context_.x86);
}
+#elif defined(ARCH_CPU_ARM64)
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_union_.arm64;
+ InitializeARM64Context(process_reader_thread.context.native, context_.arm64);
#else
- context_.architecture = kCPUArchitectureX86;
- context_.x86 = &context_union_.x86;
- InitializeX86Context(process_reader_thread.context.native, context_.x86);
-#endif // ARCH_CPU_X86_64
+#error Unsupported Windows Arch
+#endif // ARCH_CPU_X86
CaptureMemoryDelegateWin capture_memory_delegate(
process_reader,
diff --git a/snapshot/win/thread_snapshot_win.h b/snapshot/win/thread_snapshot_win.h
index 273fc7f..64ec43d 100644
--- a/snapshot/win/thread_snapshot_win.h
+++ b/snapshot/win/thread_snapshot_win.h
@@ -24,8 +24,8 @@
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/memory_snapshot.h"
+#include "snapshot/memory_snapshot_generic.h"
#include "snapshot/thread_snapshot.h"
-#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/process_reader_win.h"
#include "util/misc/initialization_state_dcheck.h"
@@ -71,18 +71,22 @@
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private:
-#if defined(ARCH_CPU_X86_FAMILY)
union {
+#if defined(ARCH_CPU_X86_FAMILY)
CPUContextX86 x86;
CPUContextX86_64 x86_64;
- } context_union_;
+#elif defined(ARCH_CPU_ARM64)
+ CPUContextARM64 arm64;
+#else
+#error Unsupported Windows Arch
#endif
+ } context_union_;
CPUContext context_;
- MemorySnapshotWin stack_;
- MemorySnapshotWin teb_;
+ MemorySnapshotGeneric stack_;
+ MemorySnapshotGeneric teb_;
ProcessReaderWin::Thread thread_;
InitializationStateDcheck initialized_;
- std::vector<std::unique_ptr<MemorySnapshotWin>> pointed_to_memory_;
+ std::vector<std::unique_ptr<MemorySnapshotGeneric>> pointed_to_memory_;
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotWin);
};
diff --git a/test/BUILD.gn b/test/BUILD.gn
index d1e6297..047c786 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -25,8 +25,6 @@
"filesystem.cc",
"filesystem.h",
"gtest_death.h",
- "gtest_disabled.cc",
- "gtest_disabled.h",
"hex_string.cc",
"hex_string.h",
"main_arguments.cc",
@@ -36,6 +34,7 @@
"multiprocess_exec.h",
"process_type.cc",
"process_type.h",
+ "scoped_guarded_page.h",
"scoped_module_handle.cc",
"scoped_module_handle.h",
"scoped_temp_dir.cc",
@@ -45,9 +44,12 @@
]
if (crashpad_is_posix || crashpad_is_fuchsia) {
- sources += [ "scoped_temp_dir_posix.cc" ]
+ sources += [
+ "scoped_guarded_page_posix.cc",
+ "scoped_temp_dir_posix.cc",
+ ]
- if (!crashpad_is_fuchsia) {
+ if (!crashpad_is_fuchsia && !crashpad_is_ios) {
sources += [
"multiprocess_exec_posix.cc",
"multiprocess_posix.cc",
@@ -55,19 +57,32 @@
}
}
+ if (crashpad_is_mac || crashpad_is_ios) {
+ sources += [
+ "mac/mach_errors.cc",
+ "mac/mach_errors.h",
+ ]
+ }
+
if (crashpad_is_mac) {
sources += [
"mac/dyld.cc",
"mac/dyld.h",
"mac/exception_swallower.cc",
"mac/exception_swallower.h",
- "mac/mach_errors.cc",
- "mac/mach_errors.h",
"mac/mach_multiprocess.cc",
"mac/mach_multiprocess.h",
]
}
+ if (crashpad_is_ios) {
+ sources -= [
+ "multiprocess.h",
+ "multiprocess_exec.cc",
+ "multiprocess_exec.h",
+ ]
+ }
+
if (crashpad_is_linux || crashpad_is_android) {
set_sources_assignment_filter([])
sources += [
@@ -81,6 +96,7 @@
if (crashpad_is_win) {
sources += [
"multiprocess_exec_win.cc",
+ "scoped_guarded_page_win.cc",
"scoped_temp_dir_win.cc",
"win/child_launcher.cc",
"win/child_launcher.h",
@@ -101,12 +117,10 @@
configs += [
"../build:crashpad_is_in_chromium",
- "../build:crashpad_is_in_fuchsia"
+ "../build:crashpad_is_in_fuchsia",
]
- data = [
- "test_paths_test_data_root.txt",
- ]
+ data = [ "test_paths_test_data_root.txt" ]
deps = [
"../compat",
@@ -123,14 +137,30 @@
]
}
+ if (crashpad_is_ios) {
+ deps += [ ":test_bundle_data" ]
+ }
+
if (crashpad_is_win) {
libs = [ "shell32.lib" ]
}
- if (crashpad_is_fuchsia && crashpad_is_in_fuchsia) {
- deps += [
- "//zircon/public/lib/fdio",
- ]
+ if (crashpad_is_fuchsia) {
+ public_deps = [ "../third_party/fuchsia" ]
+ if (crashpad_is_in_fuchsia) {
+ deps += [ "//zircon/public/lib/fdio" ]
+ }
+ }
+}
+
+if (crashpad_is_ios) {
+ bundle_data("test_bundle_data") {
+ testonly = true
+
+ sources = [ "test_paths_test_data_root.txt" ]
+
+ outputs = [ "{{bundle_resources_dir}}/crashpad_test_data/" +
+ "{{source_root_relative_dir}}/{{source_file_part}}" ]
}
}
@@ -141,13 +171,14 @@
"hex_string_test.cc",
"main_arguments_test.cc",
"multiprocess_exec_test.cc",
+ "scoped_guarded_page_test.cc",
"scoped_temp_dir_test.cc",
"test_paths_test.cc",
]
# TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no
# longer treated as a posix platform.
- if (crashpad_is_posix && !crashpad_is_fuchsia) {
+ if (crashpad_is_posix && !crashpad_is_fuchsia && !crashpad_is_ios) {
sources += [ "multiprocess_posix_test.cc" ]
}
@@ -155,6 +186,13 @@
sources += [ "mac/mach_multiprocess_test.cc" ]
}
+ if (crashpad_is_ios) {
+ sources -= [
+ "multiprocess_exec_test.cc",
+ "scoped_guarded_page_test.cc",
+ ]
+ }
+
if (crashpad_is_win) {
sources += [
"win/win_child_process_test.cc",
@@ -171,26 +209,24 @@
"../util",
]
- data_deps = [
- ":crashpad_test_test_multiprocess_exec_test_child",
- ]
+ data_deps = [ ":crashpad_test_test_multiprocess_exec_test_child" ]
+
+ if (crashpad_is_ios) {
+ data_deps -= [ ":crashpad_test_test_multiprocess_exec_test_child" ]
+ }
}
-crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") {
- sources = [
- "multiprocess_exec_test_child.cc",
- ]
+if (!crashpad_is_ios) {
+ crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") {
+ sources = [ "multiprocess_exec_test_child.cc" ]
- deps = [
- "../third_party/mini_chromium:base",
- ]
+ deps = [ "../third_party/mini_chromium:base" ]
+ }
}
static_library("gmock_main") {
testonly = true
- sources = [
- "gtest_main.cc",
- ]
+ sources = [ "gtest_main.cc" ]
configs += [ "../build:crashpad_is_in_chromium" ]
defines = [ "CRASHPAD_TEST_LAUNCHER_GMOCK" ]
deps = [
@@ -200,13 +236,17 @@
"../third_party/mini_chromium:base",
"../third_party/mini_chromium:base_test_support",
]
+ if (crashpad_is_android) {
+ deps += [ "../util" ]
+ }
+ if (crashpad_is_ios) {
+ deps += [ "ios:google_test_setup" ]
+ }
}
static_library("gtest_main") {
testonly = true
- sources = [
- "gtest_main.cc",
- ]
+ sources = [ "gtest_main.cc" ]
configs += [ "../build:crashpad_is_in_chromium" ]
defines = [ "CRASHPAD_TEST_LAUNCHER_GTEST" ]
deps = [
@@ -215,4 +255,10 @@
"../third_party/mini_chromium:base",
"../third_party/mini_chromium:base_test_support",
]
+ if (crashpad_is_android) {
+ deps += [ "../util" ]
+ }
+ if (crashpad_is_ios) {
+ deps += [ "ios:google_test_setup" ]
+ }
}
diff --git a/test/fuchsia_crashpad_tests.cmx b/test/fuchsia_crashpad_tests.cmx
new file mode 100644
index 0000000..d187098
--- /dev/null
+++ b/test/fuchsia_crashpad_tests.cmx
@@ -0,0 +1,24 @@
+{
+ "facets": {
+ "fuchsia.test": {
+ "system-services": [
+ "fuchsia.net.NameLookup",
+ "fuchsia.posix.socket.Provider"
+ ]
+ }
+ },
+ "program": {
+ "binary": "test/crashpad_tests"
+ },
+ "sandbox": {
+ "features": [
+ "isolated-temp",
+ "deprecated-ambient-replace-as-executable"
+ ],
+ "services": [
+ "fuchsia.net.NameLookup",
+ "fuchsia.posix.socket.Provider",
+ "fuchsia.process.Launcher"
+ ]
+ }
+}
diff --git a/test/gtest_death.h b/test/gtest_death.h
index 69f6361..f205a2f 100644
--- a/test/gtest_death.h
+++ b/test/gtest_death.h
@@ -25,7 +25,7 @@
//! \file
-#if defined(OS_MACOSX) || DOXYGEN
+#if (defined(OS_MACOSX) && !defined(OS_IOS)) || DOXYGEN
//! \brief Wraps the gtest `ASSERT_DEATH_IF_SUPPORTED()` macro to make
//! assertions about death caused by crashes.
@@ -73,14 +73,14 @@
regex); \
} while (false)
-#else // OS_MACOSX
+#else // OS_MACOSX && !OS_IOS
#define ASSERT_DEATH_CRASH(statement, regex) \
ASSERT_DEATH_IF_SUPPORTED(statement, regex)
#define EXPECT_DEATH_CRASH(statement, regex) \
EXPECT_DEATH_IF_SUPPORTED(statement, regex)
-#endif // OS_MACOSX
+#endif // OS_MACOSX && !OS_IOS
#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \
defined(OFFICIAL_BUILD) && \
diff --git a/test/gtest_disabled.cc b/test/gtest_disabled.cc
deleted file mode 100644
index fab6802..0000000
--- a/test/gtest_disabled.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "test/gtest_disabled.h"
-
-#include <stdio.h>
-
-#include "base/format_macros.h"
-#include "base/logging.h"
-#include "base/strings/stringprintf.h"
-
-namespace crashpad {
-namespace test {
-
-namespace {
-
-DisabledTestGtestEnvironment* g_instance;
-
-} // namespace
-
-// static
-DisabledTestGtestEnvironment* DisabledTestGtestEnvironment::Get() {
- if (!g_instance) {
- g_instance = new DisabledTestGtestEnvironment();
- }
- return g_instance;
-}
-
-void DisabledTestGtestEnvironment::DisabledTest() {
- const testing::TestInfo* test_info =
- testing::UnitTest::GetInstance()->current_test_info();
- std::string disabled_test = base::StringPrintf(
- "%s.%s", test_info->test_case_name(), test_info->name());
-
- // Show a DISABLED message using a format similar to gtest, along with a hint
- // explaining that OK or FAILED will also appear.
- printf(
- "This test has been disabled dynamically.\n"
- "It will appear as both DISABLED and OK or FAILED.\n"
- "[ DISABLED ] %s\n",
- disabled_test.c_str());
-
- disabled_tests_.push_back(disabled_test);
-}
-
-DisabledTestGtestEnvironment::DisabledTestGtestEnvironment()
- : testing::Environment(),
- disabled_tests_() {
- DCHECK(!g_instance);
-}
-
-DisabledTestGtestEnvironment::~DisabledTestGtestEnvironment() {
- DCHECK_EQ(this, g_instance);
- g_instance = nullptr;
-}
-
-void DisabledTestGtestEnvironment::TearDown() {
- if (!disabled_tests_.empty()) {
- printf(
- "[ DISABLED ] %" PRIuS " dynamically disabled test%s, listed below:\n"
- "[ DISABLED ] %s also counted in PASSED or FAILED below.\n",
- disabled_tests_.size(),
- disabled_tests_.size() == 1 ? "" : "s",
- disabled_tests_.size() == 1 ? "This test is" : "These tests are");
- for (const std::string& disabled_test : disabled_tests_) {
- printf("[ DISABLED ] %s\n", disabled_test.c_str());
- }
- }
-}
-
-} // namespace test
-} // namespace crashpad
diff --git a/test/gtest_disabled.h b/test/gtest_disabled.h
deleted file mode 100644
index 9415cba..0000000
--- a/test/gtest_disabled.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_TEST_GTEST_DISABLED_H_
-#define CRASHPAD_TEST_GTEST_DISABLED_H_
-
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "gtest/gtest.h"
-
-//! \file
-
-namespace crashpad {
-namespace test {
-
-//! \brief Provides support for dynamically disabled gtest tests.
-//!
-//! A test runner must register this with gtest as follows prior to calling
-//! `RUN_ALL_TESTS()`:
-//! \code
-//! testing::AddGlobalTestEnvironment(
-//! crashpad::test::DisabledTestGtestEnvironment::Get());
-//! \endcode
-class DisabledTestGtestEnvironment final : public testing::Environment {
- public:
- //! \brief Returns the DisabledTestGtestEnvironment singleton instance,
- //! creating it if necessary.
- static DisabledTestGtestEnvironment* Get();
-
- //! \brief Displays a message about a test being disabled, and arranges for
- //! this information to be duplicated in TearDown().
- //!
- //! This method is for the internal use of the DISABLED_TEST() macro. Do not
- //! call it directly, use the macro instead.
- void DisabledTest();
-
- private:
- DisabledTestGtestEnvironment();
- ~DisabledTestGtestEnvironment() override;
-
- // testing::Environment:
- void TearDown() override;
-
- std::vector<std::string> disabled_tests_;
-
- DISALLOW_COPY_AND_ASSIGN(DisabledTestGtestEnvironment);
-};
-
-} // namespace test
-} // namespace crashpad
-
-//! \brief Displays a message about a test being disabled, and returns early.
-//!
-//! gtest only provides a mechanism for tests to be disabled statically, by
-//! prefixing test case names or test names with `DISABLED_`. When it is
-//! necessary to disable tests dynamically, gtest provides no assistance. This
-//! macro displays a message about the disabled test and returns early. The
-//! dynamically disabled test will also be displayed during gtest global test
-//! environment tear-down before the test executable exits.
-//!
-//! This macro may only be invoked from the context of a gtest test.
-//!
-//! There’s a long-standing <a
-//! href="https://groups.google.com/d/topic/googletestframework/Nwh3u7YFuN4">gtest
-//! feature request</a> to provide this functionality directly in gtest, but
-//! since it hasn’t been implemented, this macro provides a local mechanism to
-//! achieve it.
-#define DISABLED_TEST() \
- do { \
- ::crashpad::test::DisabledTestGtestEnvironment::Get()->DisabledTest(); \
- return; \
- } while (false)
-
-#endif // CRASHPAD_TEST_GTEST_DISABLED_H_
diff --git a/test/gtest_main.cc b/test/gtest_main.cc
index ebdbeb9..73cdddf 100644
--- a/test/gtest_main.cc
+++ b/test/gtest_main.cc
@@ -14,7 +14,6 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
-#include "test/gtest_disabled.h"
#include "test/main_arguments.h"
#include "test/multiprocess_exec.h"
@@ -22,6 +21,14 @@
#include "gmock/gmock.h"
#endif // CRASHPAD_TEST_LAUNCHER_GMOCK
+#if defined(OS_ANDROID)
+#include "util/linux/initial_signal_dispositions.h"
+#endif // OS_ANDROID
+
+#if defined(OS_IOS)
+#include "test/ios/google_test_setup.h"
+#endif
+
#if defined(OS_WIN)
#include "test/win/win_child_process.h"
#endif // OS_WIN
@@ -34,6 +41,7 @@
namespace {
+#if !defined(OS_IOS)
bool GetChildTestFunctionName(std::string* child_func_name) {
constexpr size_t arg_length =
sizeof(crashpad::test::internal::kChildTestFunction) - 1;
@@ -46,19 +54,24 @@
}
return false;
}
+#endif // !OS_IOS
} // namespace
int main(int argc, char* argv[]) {
- crashpad::test::InitializeMainArguments(argc, argv);
- testing::AddGlobalTestEnvironment(
- crashpad::test::DisabledTestGtestEnvironment::Get());
+#if defined(OS_ANDROID)
+ crashpad::InitializeSignalDispositions();
+#endif // OS_ANDROID
+ crashpad::test::InitializeMainArguments(argc, argv);
+
+#if !defined(OS_IOS)
std::string child_func_name;
if (GetChildTestFunctionName(&child_func_name)) {
return crashpad::test::internal::CheckedInvokeMultiprocessChild(
child_func_name);
}
+#endif // !OS_IOS
#if defined(CRASHPAD_IS_IN_CHROMIUM)
@@ -68,6 +81,8 @@
// runner.
const bool use_chromium_test_launcher =
!crashpad::test::WinChildProcess::IsChildProcess();
+#elif defined(OS_ANDROID)
+ constexpr bool use_chromium_test_launcher = false;
#else // OS_WIN
constexpr bool use_chromium_test_launcher = true;
#endif // OS_WIN
@@ -79,7 +94,7 @@
return base::LaunchUnitTests(
argc,
argv,
- base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+ base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
#endif // CRASHPAD_IS_IN_CHROMIUM
@@ -92,5 +107,12 @@
#error #define CRASHPAD_TEST_LAUNCHER_GTEST or CRASHPAD_TEST_LAUNCHER_GMOCK
#endif // CRASHPAD_TEST_LAUNCHER_GMOCK
+#if defined(OS_IOS)
+ // iOS needs to run tests within the context of an app, so call a helper that
+ // invokes UIApplicationMain(). The application delegate will call
+ // RUN_ALL_TESTS() and exit before returning control to this function.
+ crashpad::test::IOSLaunchApplicationAndRunTests(argc, argv);
+#else
return RUN_ALL_TESTS();
+#endif
}
diff --git a/test/hex_string.h b/test/hex_string.h
index 435a692..b0d4453 100644
--- a/test/hex_string.h
+++ b/test/hex_string.h
@@ -29,8 +29,8 @@
//! uint8_t expected[10];
//! uint8_t observed[10];
//! // …
-//! EXPECT_EQ(BytesToHexString(observed, arraysize(observed)),
-//! BytesToHexString(expected, arraysize(expected)));
+//! EXPECT_EQ(BytesToHexString(observed, base::size(observed)),
+//! BytesToHexString(expected, base::size(expected)));
//! \endcode
std::string BytesToHexString(const void* bytes, size_t length);
diff --git a/test/hex_string_test.cc b/test/hex_string_test.cc
index 68745e6..3a09eb7 100644
--- a/test/hex_string_test.cc
+++ b/test/hex_string_test.cc
@@ -14,7 +14,7 @@
#include "test/hex_string.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
namespace crashpad {
@@ -25,7 +25,7 @@
EXPECT_EQ(BytesToHexString(nullptr, 0), "");
static constexpr char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_";
- EXPECT_EQ(BytesToHexString(kBytes, arraysize(kBytes)),
+ EXPECT_EQ(BytesToHexString(kBytes, base::size(kBytes)),
"41626331323378797a200a7ff09f92a95f00");
}
diff --git a/test/ios/BUILD.gn b/test/ios/BUILD.gn
new file mode 100644
index 0000000..d548a27
--- /dev/null
+++ b/test/ios/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../build/crashpad_buildconfig.gni")
+
+if (crashpad_is_in_chromium) {
+ import("//build/config/ios/rules.gni")
+} else if (crashpad_is_standalone) {
+ import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni")
+}
+
+group("all_tests") {
+ testonly = true
+ deps = [
+ ":ios_crash_xcuitests_module",
+ "host:ios_crash_xcuitests",
+ ]
+}
+
+source_set("google_test_runner_shared_headers") {
+ testonly = true
+ sources = [ "cptest_google_test_runner_delegate.h" ]
+}
+
+source_set("google_test_runner") {
+ testonly = true
+ sources = [ "cptest_google_test_runner.mm" ]
+ configs += [ "../..:crashpad_config" ]
+ deps = [
+ "../../build:ios_enable_arc",
+ "../../build:ios_xctest",
+ "../../test/ios:google_test_runner_shared_headers",
+ "../../third_party/mini_chromium:base",
+ ]
+ libs = [ "UIKit.framework" ]
+}
+
+source_set("google_test_setup") {
+ testonly = true
+ sources = [
+ "google_test_setup.h",
+ "google_test_setup.mm",
+ ]
+ configs += [ "../..:crashpad_config" ]
+ deps = [
+ ":google_test_runner_shared_headers",
+ "../../build:ios_enable_arc",
+ "../../third_party/gtest:gtest",
+ "../../third_party/mini_chromium:base",
+ ]
+ libs = [ "UIKit.framework" ]
+}
+
+source_set("xcuitests") {
+ testonly = true
+ sources = [ "crash_type_xctest.mm" ]
+ configs += [ "../..:crashpad_config" ]
+ deps = [
+ "../../build:ios_enable_arc",
+ "../../build:ios_xctest",
+ "../../test/ios/host:app_shared_sources",
+ "../../third_party/edo",
+ ]
+}
+
+ios_xcuitest_test("ios_crash_xcuitests_module") {
+ xcode_test_application_name = "ios_crash_xcuitests"
+ deps = [ ":xcuitests" ]
+}
diff --git a/test/ios/cptest_google_test_runner.mm b/test/ios/cptest_google_test_runner.mm
new file mode 100644
index 0000000..ae933fc
--- /dev/null
+++ b/test/ios/cptest_google_test_runner.mm
@@ -0,0 +1,41 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <UIKit/UIKit.h>
+#import <XCTest/XCTest.h>
+
+#include "base/logging.h"
+#import "test/ios/cptest_google_test_runner_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface CPTestGoogleTestRunner : XCTestCase
+@end
+
+@implementation CPTestGoogleTestRunner
+
+- (void)testRunGoogleTests {
+ id appDelegate = UIApplication.sharedApplication.delegate;
+ DCHECK([appDelegate
+ conformsToProtocol:@protocol(CPTestGoogleTestRunnerDelegate)]);
+
+ id<CPTestGoogleTestRunnerDelegate> runnerDelegate =
+ static_cast<id<CPTestGoogleTestRunnerDelegate>>(appDelegate);
+ DCHECK(runnerDelegate.supportsRunningGoogleTestsWithXCTest);
+ XCTAssertTrue([runnerDelegate runGoogleTests] == 0);
+}
+
+@end
diff --git a/test/ios/cptest_google_test_runner_delegate.h b/test/ios/cptest_google_test_runner_delegate.h
new file mode 100644
index 0000000..f88d63d
--- /dev/null
+++ b/test/ios/cptest_google_test_runner_delegate.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_
+#define CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_
+
+@protocol CPTestGoogleTestRunnerDelegate
+
+// Returns YES if this delegate supports running GoogleTests via a call to
+// |runGoogleTests|.
+@property(nonatomic, readonly, assign)
+ BOOL supportsRunningGoogleTestsWithXCTest;
+
+// Runs GoogleTests and returns the final exit code.
+- (int)runGoogleTests;
+
+@end
+
+#endif // CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_H_
diff --git a/test/ios/crash_type_xctest.mm b/test/ios/crash_type_xctest.mm
new file mode 100644
index 0000000..4529124
--- /dev/null
+++ b/test/ios/crash_type_xctest.mm
@@ -0,0 +1,241 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <XCTest/XCTest.h>
+
+#include <objc/runtime.h>
+#import "Service/Sources/EDOClientService.h"
+#import "test/ios/host/cptest_shared_object.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface CPTestTestCase : XCTestCase {
+ XCUIApplication* _app;
+}
+
+@end
+
+@implementation CPTestTestCase
+
+- (void)handleCrashUnderSymbol:(id)arg1 {
+ // For now, do nothing. In the future this can be something testable.
+}
+
++ (void)setUp {
+ // Swizzle away the handleCrashUnderSymbol callback. Without this, any time
+ // the host app is intentionally crashed, the test is immediately failed.
+ SEL originalSelector = NSSelectorFromString(@"handleCrashUnderSymbol:");
+ SEL swizzledSelector = @selector(handleCrashUnderSymbol:);
+
+ Method originalMethod = class_getInstanceMethod(
+ objc_getClass("XCUIApplicationImpl"), originalSelector);
+ Method swizzledMethod =
+ class_getInstanceMethod([self class], swizzledSelector);
+
+ method_exchangeImplementations(originalMethod, swizzledMethod);
+
+ // Override EDO default error handler. Without this, the default EDO error
+ // handler will throw an error and fail the test.
+ [EDOClientService setErrorHandler:^(NSError* error){
+ // Do nothing.
+ }];
+}
+
+- (void)setUp {
+ _app = [[XCUIApplication alloc] init];
+ [_app launch];
+}
+
+- (void)testEDO {
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ NSString* result = [rootObject testEDO];
+ XCTAssertEqualObjects(result, @"crashpad");
+}
+
+- (void)testSegv {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashSegv];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testKillAbort {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashKillAbort];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testTrap {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashTrap];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testAbort {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashAbort];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testBadAccess {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashBadAccess];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testException {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashException];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testNSException {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashNSException];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testCrashUnreocgnizedSelectorAfterDelay {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashUnreocgnizedSelectorAfterDelay];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testCatchUIGestureEnvironmentNSException {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Tap the button with the string UIGestureEnvironmentException.
+ [_app.buttons[@"UIGestureEnvironmentException"] tap];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testCatchNSException {
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // The app should not crash
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject catchNSException];
+
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+- (void)testRecursion {
+ // TODO(justincohen): Crashpad iOS does not currently support stack type
+ // crashes.
+ return;
+
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+
+ // Crash the app.
+ CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
+ [rootObject crashRecursion];
+
+ // Confirm the app is not running.
+ XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
+ XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
+
+ // TODO: Query the app for crash data
+ [_app launch];
+ XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
+}
+
+@end
diff --git a/test/ios/google_test_setup.h b/test/ios/google_test_setup.h
new file mode 100644
index 0000000..98bb942
--- /dev/null
+++ b/test/ios/google_test_setup.h
@@ -0,0 +1,33 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_
+#define CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_
+
+namespace crashpad {
+namespace test {
+
+//! \brief Runs all registered tests in the context of a UIKit application.
+//!
+//! Invokes UIApplicationMain() to launch the iOS application and runs all
+//! registered tests after the application finishes
+//! launching. UIApplicationMain() brings up the main runloop and never returns,
+//! so therefore this function never returns either. It invokes _exit() to
+//! terminate the application after tests have completed.
+void IOSLaunchApplicationAndRunTests(int argc, char* argv[]);
+
+} // namespace test
+} // namespace crashpad
+
+#endif // CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_
diff --git a/test/ios/google_test_setup.mm b/test/ios/google_test_setup.mm
new file mode 100644
index 0000000..109c4b6
--- /dev/null
+++ b/test/ios/google_test_setup.mm
@@ -0,0 +1,134 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/ios/google_test_setup.h"
+
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "gtest/gtest.h"
+#include "test/ios/cptest_google_test_runner_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface UIApplication (Testing)
+- (void)_terminateWithStatus:(int)status;
+@end
+
+namespace {
+
+// The iOS watchdog timer will kill an app that doesn't spin the main event
+// loop often enough. This uses a Gtest TestEventListener to spin the current
+// loop after each test finishes. However, if any individual test takes too
+// long, it is still possible that the app will get killed.
+class IOSRunLoopListener : public testing::EmptyTestEventListener {
+ public:
+ virtual void OnTestEnd(const testing::TestInfo& test_info) {
+ @autoreleasepool {
+ // At the end of the test, spin the default loop for a moment.
+ NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001];
+ [[NSRunLoop currentRunLoop] runUntilDate:stop_date];
+ }
+ }
+};
+
+void RegisterTestEndListener() {
+ testing::TestEventListeners& listeners =
+ testing::UnitTest::GetInstance()->listeners();
+ listeners.Append(new IOSRunLoopListener);
+}
+
+} // namespace
+
+@interface CPTestUnitTestApplicationDelegate
+ : NSObject <CPTestGoogleTestRunnerDelegate>
+@property(nonatomic, readwrite, strong) UIWindow* window;
+- (void)runTests;
+@end
+
+@implementation CPTestUnitTestApplicationDelegate
+
+- (BOOL)application:(UIApplication*)application
+ didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+ self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ self.window.backgroundColor = UIColor.whiteColor;
+ [self.window makeKeyAndVisible];
+
+ UIViewController* controller = [[UIViewController alloc] init];
+ [self.window setRootViewController:controller];
+
+ // Add a label with the app name.
+ UILabel* label = [[UILabel alloc] initWithFrame:controller.view.bounds];
+ label.text = [[NSProcessInfo processInfo] processName];
+ label.textAlignment = NSTextAlignmentCenter;
+ label.textColor = UIColor.blackColor;
+ [controller.view addSubview:label];
+
+ // Queue up the test run.
+ if (![self supportsRunningGoogleTestsWithXCTest]) {
+ // When running in XCTest mode, XCTest will invoke |runGoogleTest| directly.
+ // Otherwise, schedule a call to |runTests|.
+ [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1];
+ }
+
+ return YES;
+}
+
+- (BOOL)supportsRunningGoogleTestsWithXCTest {
+ return getenv("XCTestConfigurationFilePath") != nullptr;
+}
+
+- (int)runGoogleTests {
+ RegisterTestEndListener();
+ int exitStatus = RUN_ALL_TESTS();
+ return exitStatus;
+}
+
+- (void)runTests {
+ DCHECK(![self supportsRunningGoogleTestsWithXCTest]);
+
+ int exitStatus = [self runGoogleTests];
+
+ // If a test app is too fast, it will exit before Instruments has has a
+ // a chance to initialize and no test results will be seen.
+ // TODO(crbug.com/137010): Figure out how much time is actually needed, and
+ // sleep only to make sure that much time has elapsed since launch.
+ [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
+ self.window = nil;
+
+ // Use the hidden selector to try and cleanly take down the app (otherwise
+ // things can think the app crashed even on a zero exit status).
+ UIApplication* application = [UIApplication sharedApplication];
+ [application _terminateWithStatus:exitStatus];
+
+ exit(exitStatus);
+}
+
+@end
+
+namespace crashpad {
+namespace test {
+
+void IOSLaunchApplicationAndRunTests(int argc, char* argv[]) {
+ @autoreleasepool {
+ int exit_status = UIApplicationMain(
+ argc, argv, nil, @"CPTestUnitTestApplicationDelegate");
+ exit(exit_status);
+ }
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/test/ios/host/BUILD.gn b/test/ios/host/BUILD.gn
new file mode 100644
index 0000000..cd6995a
--- /dev/null
+++ b/test/ios/host/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2020 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../build/crashpad_buildconfig.gni")
+
+if (crashpad_is_in_chromium) {
+ import("//build/config/ios/rules.gni")
+} else if (crashpad_is_standalone) {
+ import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni")
+}
+
+source_set("app_shared_sources") {
+ testonly = true
+ sources = [ "cptest_shared_object.h" ]
+ configs += [ "../../..:crashpad_config" ]
+ deps = [ "../../../build:ios_enable_arc" ]
+ libs = [ "UIKit.framework" ]
+}
+
+static_library("app_host_sources") {
+ testonly = true
+ sources = [
+ "cptest_application_delegate.h",
+ "cptest_application_delegate.mm",
+ "cptest_crash_view_controller.h",
+ "cptest_crash_view_controller.mm",
+ "main.mm",
+ ]
+ configs += [ "../../..:crashpad_config" ]
+ deps = [
+ ":app_shared_sources",
+ "../../../build:ios_enable_arc",
+ "../../../client",
+ "../../../third_party/edo",
+ ]
+ libs = [
+ "Foundation.framework",
+ "UIKit.framework",
+ ]
+}
+
+ios_app_bundle("ios_crash_xcuitests") {
+ info_plist = "Info.plist"
+ testonly = true
+ deps = [ ":app_host_sources" ]
+}
diff --git a/test/ios/host/Info.plist b/test/ios/host/Info.plist
new file mode 100644
index 0000000..8944a2e
--- /dev/null
+++ b/test/ios/host/Info.plist
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>${IOS_BUNDLE_ID_PREFIX}.gtest.${EXECUTABLE_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchImages</key>
+ <array>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{320, 480}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{320, 568}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{375, 667}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{414, 736}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{375, 812}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{414, 896}</string>
+ </dict>
+ </array>
+ <key>UILaunchImages~ipad</key>
+ <array>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{768, 1024}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{1024, 1366}</string>
+ </dict>
+ <dict>
+ <key>UILaunchImageMinimumOSVersion</key>
+ <string>9.0</string>
+ <key>UILaunchImageName</key>
+ <string>Default</string>
+ <key>UILaunchImageSize</key>
+ <string>{832, 1114}</string>
+ </dict>
+ </array>
+ <key>UISupportedInterfaceOrientation</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/util/net/http_transport_none.cc b/test/ios/host/cptest_application_delegate.h
similarity index 60%
copy from util/net/http_transport_none.cc
copy to test/ios/host/cptest_application_delegate.h
index 1c08c1f..ca835f1 100644
--- a/util/net/http_transport_none.cc
+++ b/test/ios/host/cptest_application_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_
+#define CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_
-#include "base/logging.h"
+#import <UIKit/UIKit.h>
-namespace crashpad {
+@interface CPTestApplicationDelegate : UIResponder <UIApplicationDelegate>
+@end
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_
diff --git a/test/ios/host/cptest_application_delegate.mm b/test/ios/host/cptest_application_delegate.mm
new file mode 100644
index 0000000..80baf8b
--- /dev/null
+++ b/test/ios/host/cptest_application_delegate.mm
@@ -0,0 +1,126 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "test/ios/host/cptest_application_delegate.h"
+
+#include <dispatch/dispatch.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vector>
+
+#import "Service/Sources/EDOHostNamingService.h"
+#import "Service/Sources/EDOHostService.h"
+#include "client/crashpad_client.h"
+#import "test/ios/host/cptest_crash_view_controller.h"
+#import "test/ios/host/cptest_shared_object.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CPTestApplicationDelegate
+
+@synthesize window = _window;
+
+- (BOOL)application:(UIApplication*)application
+ didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
+ // Start up crashpad.
+ crashpad::CrashpadClient client;
+ client.StartCrashpadInProcessHandler();
+
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ [self.window makeKeyAndVisible];
+ self.window.backgroundColor = UIColor.greenColor;
+
+ CPTestCrashViewController* controller =
+ [[CPTestCrashViewController alloc] init];
+ self.window.rootViewController = controller;
+
+ // Start up EDO.
+ [EDOHostService serviceWithPort:12345
+ rootObject:[[CPTestSharedObject alloc] init]
+ queue:dispatch_get_main_queue()];
+ return YES;
+}
+
+@end
+
+@implementation CPTestSharedObject
+
+- (NSString*)testEDO {
+ return @"crashpad";
+}
+
+- (void)crashBadAccess {
+ strcpy(nullptr, "bla");
+}
+
+- (void)crashKillAbort {
+ kill(getpid(), SIGABRT);
+}
+
+- (void)crashSegv {
+ long* zero = nullptr;
+ *zero = 0xc045004d;
+}
+
+- (void)crashTrap {
+ __builtin_trap();
+}
+
+- (void)crashAbort {
+ abort();
+}
+
+- (void)crashException {
+ std::vector<int> empty_vector = {};
+ empty_vector.at(42);
+}
+
+- (void)crashNSException {
+ // EDO has its own sinkhole.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSArray* empty_array = @[];
+ [empty_array objectAtIndex:42];
+ });
+}
+
+- (void)catchNSException {
+ @try {
+ NSArray* empty_array = @[];
+ [empty_array objectAtIndex:42];
+ } @catch (NSException* exception) {
+ } @finally {
+ }
+}
+
+- (void)crashUnreocgnizedSelectorAfterDelay {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+ [self performSelector:@selector(does_not_exist) withObject:nil afterDelay:1];
+#pragma clang diagnostic pop
+}
+
+- (void)recurse {
+ [self recurse];
+}
+
+- (void)crashRecursion {
+ [self recurse];
+}
+
+@end
diff --git a/util/net/http_transport_none.cc b/test/ios/host/cptest_crash_view_controller.h
similarity index 61%
copy from util/net/http_transport_none.cc
copy to test/ios/host/cptest_crash_view_controller.h
index 1c08c1f..2a41e12 100644
--- a/util/net/http_transport_none.cc
+++ b/test/ios/host/cptest_crash_view_controller.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
+#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_
+#define CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_
-#include "base/logging.h"
+#import <UIKit/UIKit.h>
-namespace crashpad {
+@interface CPTestCrashViewController : UIViewController
+@end
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
-
-} // namespace crashpad
+#endif // CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_
diff --git a/test/ios/host/cptest_crash_view_controller.mm b/test/ios/host/cptest_crash_view_controller.mm
new file mode 100644
index 0000000..c467722
--- /dev/null
+++ b/test/ios/host/cptest_crash_view_controller.mm
@@ -0,0 +1,65 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "test/ios/host/cptest_crash_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation CPTestCrashViewController
+
+- (void)loadView {
+ self.view = [[UIView alloc] init];
+
+ UIStackView* buttonStack = [[UIStackView alloc] init];
+ buttonStack.axis = UILayoutConstraintAxisVertical;
+ buttonStack.spacing = 6;
+
+ UIButton* button = [UIButton new];
+ [button setTitle:@"UIGestureEnvironmentException"
+ forState:UIControlStateNormal];
+ UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc]
+ initWithTarget:self
+ action:@selector(throwUIGestureEnvironmentException)];
+ [button addGestureRecognizer:tapGesture];
+ [button setTranslatesAutoresizingMaskIntoConstraints:NO];
+
+ [buttonStack addArrangedSubview:button];
+
+ [self.view addSubview:buttonStack];
+
+ [buttonStack setTranslatesAutoresizingMaskIntoConstraints:NO];
+
+ [NSLayoutConstraint activateConstraints:@[
+ [buttonStack.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
+ [buttonStack.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+ [buttonStack.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
+ [buttonStack.trailingAnchor
+ constraintEqualToAnchor:self.view.trailingAnchor],
+ ]];
+}
+
+- (void)throwUIGestureEnvironmentException {
+ NSArray* empty_array = @[];
+ [empty_array objectAtIndex:42];
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.view.backgroundColor = UIColor.redColor;
+}
+
+@end
diff --git a/test/ios/host/cptest_shared_object.h b/test/ios/host/cptest_shared_object.h
new file mode 100644
index 0000000..70c814b
--- /dev/null
+++ b/test/ios/host/cptest_shared_object.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_
+#define CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_
+
+#import <UIKit/UIKit.h>
+
+@interface CPTestSharedObject : NSObject
+// Returns the string "crashpad" for testing EDO.
+- (NSString*)testEDO;
+
+// Triggers an EXC_BAD_ACCESS exception and crash.
+- (void)crashBadAccess;
+
+// Triggers a crash with a call to kill(SIGABRT).
+- (void)crashKillAbort;
+
+// Triggers a segfault crash.
+- (void)crashSegv;
+
+// Trigger a crash with a __builtin_trap.
+- (void)crashTrap;
+
+// Trigger a crash with an abort().
+- (void)crashAbort;
+
+// Trigger a crash with an uncaught exception.
+- (void)crashException;
+
+// Trigger a crash with an uncaught NSException.
+- (void)crashNSException;
+
+// Trigger an unrecognized selector after delay.
+- (void)crashUnreocgnizedSelectorAfterDelay;
+
+// Trigger a caught NSxception.
+- (void)catchNSException;
+
+// Trigger a crash with an infinite recursion.
+- (void)crashRecursion;
+@end
+
+#endif // CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_
diff --git a/test/ios/host/main.mm b/test/ios/host/main.mm
new file mode 100644
index 0000000..a0ae5c4
--- /dev/null
+++ b/test/ios/host/main.mm
@@ -0,0 +1,30 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <UIKit/UIKit.h>
+
+#import "test/ios/host/cptest_application_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+int main(int argc, char* argv[]) {
+ NSString* appDelegateClassName;
+ @autoreleasepool {
+ // Setup code that might create autoreleased objects goes here.
+ appDelegateClassName = NSStringFromClass([CPTestApplicationDelegate class]);
+ }
+ return UIApplicationMain(argc, argv, nil, appDelegateClassName);
+}
diff --git a/test/linux/fake_ptrace_connection.cc b/test/linux/fake_ptrace_connection.cc
index 022b132..b03228a 100644
--- a/test/linux/fake_ptrace_connection.cc
+++ b/test/linux/fake_ptrace_connection.cc
@@ -92,5 +92,11 @@
return memory_.get();
}
+bool FakePtraceConnection::Threads(std::vector<pid_t>* threads) {
+ // TODO(jperaza): Implement this if/when it's needed.
+ NOTREACHED();
+ return false;
+}
+
} // namespace test
} // namespace crashpad
diff --git a/test/linux/fake_ptrace_connection.h b/test/linux/fake_ptrace_connection.h
index 6597c47..01a6f92 100644
--- a/test/linux/fake_ptrace_connection.h
+++ b/test/linux/fake_ptrace_connection.h
@@ -62,6 +62,9 @@
//! ADD_FAILURE() and returning `nullptr` on failure.
ProcessMemory* Memory() override;
+ //! \todo Not yet implemented.
+ bool Threads(std::vector<pid_t>* threads) override;
+
private:
std::set<pid_t> attachments_;
std::unique_ptr<ProcessMemoryLinux> memory_;
diff --git a/test/mac/exception_swallower.cc b/test/mac/exception_swallower.cc
index 18c5cf7..946938f 100644
--- a/test/mac/exception_swallower.cc
+++ b/test/mac/exception_swallower.cc
@@ -24,6 +24,7 @@
#include "base/mac/scoped_mach_port.h"
#include "base/strings/stringprintf.h"
#include "handler/mac/exception_handler_server.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
diff --git a/test/mac/mach_errors.cc b/test/mac/mach_errors.cc
index b68448f..db8ed66 100644
--- a/test/mac/mach_errors.cc
+++ b/test/mac/mach_errors.cc
@@ -14,8 +14,6 @@
#include "test/mac/mach_errors.h"
-#include <servers/bootstrap.h>
-
#include "base/strings/stringprintf.h"
namespace {
@@ -50,28 +48,5 @@
FormatMachErrorNumber(mach_err).c_str());
}
-std::string BootstrapErrorMessage(kern_return_t bootstrap_err,
- const std::string& base) {
- switch (bootstrap_err) {
- case BOOTSTRAP_SUCCESS:
- case BOOTSTRAP_NOT_PRIVILEGED:
- case BOOTSTRAP_NAME_IN_USE:
- case BOOTSTRAP_UNKNOWN_SERVICE:
- case BOOTSTRAP_SERVICE_ACTIVE:
- case BOOTSTRAP_BAD_COUNT:
- case BOOTSTRAP_NO_MEMORY:
- case BOOTSTRAP_NO_CHILDREN:
- // Show known bootstrap errors in decimal because that's how they're
- // defined in <servers/bootstrap.h>.
- return base::StringPrintf("%s%s (%d)",
- FormatBase(base).c_str(),
- bootstrap_strerror(bootstrap_err),
- bootstrap_err);
-
- default:
- return MachErrorMessage(bootstrap_err, base);
- }
-}
-
} // namespace test
} // namespace crashpad
diff --git a/test/mac/mach_errors.h b/test/mac/mach_errors.h
index 9e43ef2..0afcdad 100644
--- a/test/mac/mach_errors.h
+++ b/test/mac/mach_errors.h
@@ -22,9 +22,9 @@
namespace crashpad {
namespace test {
-// These functions format messages in a similar way to the logging macros in
-// base/mac/mach_logging.h. They exist to interoperate with gtest assertions,
-// which don’t interoperate with logging but can be streamed to.
+// This function formats messages in a similar way to the Mach error logging
+// macros in base/mac/mach_logging.h. It exists to interoperate with gtest
+// assertions, which don’t interoperate with logging but can be streamed to.
//
// Where non-test code could do:
// MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate";
@@ -47,23 +47,6 @@
std::string MachErrorMessage(mach_error_t mach_err,
const std::string& base = std::string());
-//! \brief Formats a bootstrap error message.
-//!
-//! The returned string will combine the \a base string, if supplied, with a
-//! textual and numeric description of the error.
-//!
-//! \param[in] bootstrap_err The bootstrap error code.
-//! \param[in] base A string to prepend to the error description.
-//!
-//! \return A string of the format `"Permission denied (1100)"` if \a
-//! bootstrap_err has the value `BOOTSTRAP_NOT_PRIVILEGED` on a system where
-//! this is defined to be 1100. If \a base is not empty, it will be
-//! prepended to this string, separated by a colon. If \a bootstrap_err is
-//! not a valid bootstrap error code, it will be interpreted as a Mach error
-//! code in the manner of MachErrorMessage().
-std::string BootstrapErrorMessage(kern_return_t bootstrap_err,
- const std::string& base = std::string());
-
} // namespace test
} // namespace crashpad
diff --git a/test/mac/mach_multiprocess.cc b/test/mac/mach_multiprocess.cc
index f29a8b0..59aa653 100644
--- a/test/mac/mach_multiprocess.cc
+++ b/test/mac/mach_multiprocess.cc
@@ -27,6 +27,7 @@
#include "test/errors.h"
#include "test/mac/mach_errors.h"
#include "util/file/file_io.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/mach_extensions.h"
#include "util/mach/mach_message.h"
#include "util/misc/implicit_cast.h"
diff --git a/test/multiprocess.h b/test/multiprocess.h
index d027502..61df98b 100644
--- a/test/multiprocess.h
+++ b/test/multiprocess.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_TEST_MULTIPROCESS_H_
#define CRASHPAD_TEST_MULTIPROCESS_H_
+#include <stdint.h>
#include <sys/types.h>
#include "base/macros.h"
@@ -26,7 +27,13 @@
namespace internal {
struct MultiprocessInfo;
-};
+} // namespace internal
+
+#if defined(OS_FUCHSIA)
+using ReturnCodeType = int64_t;
+#else
+using ReturnCodeType = int;
+#endif
//! \brief Manages a multiprocess test.
//!
@@ -76,7 +83,7 @@
//! \brief Sets the expected termination reason and code.
//!
- //! The default expected termination reasaon is
+ //! The default expected termination reason is
//! TerminationReason::kTerminationNormal, and the default expected
//! termination code is `EXIT_SUCCESS` (`0`).
//!
@@ -91,7 +98,8 @@
//! TerminationReason::kTerminationSignal, this is the signal that is
//! expected to kill the child. On Linux platforms, SIG_DFL will be
//! installed for \a code in the child process.
- void SetExpectedChildTermination(TerminationReason reason, int code);
+ void SetExpectedChildTermination(TerminationReason reason,
+ ReturnCodeType code);
#if !defined(OS_WIN)
//! \brief Sets termination reason and code appropriately for a child that
@@ -211,7 +219,7 @@
virtual void MultiprocessChild() = 0;
internal::MultiprocessInfo* info_;
- int code_;
+ ReturnCodeType code_;
TerminationReason reason_;
DISALLOW_COPY_AND_ASSIGN(Multiprocess);
diff --git a/test/multiprocess_exec.h b/test/multiprocess_exec.h
index 84335b2..1168f80 100644
--- a/test/multiprocess_exec.h
+++ b/test/multiprocess_exec.h
@@ -122,7 +122,9 @@
//!
//! This method is only valid during the body of MultiprocessParent().
//!
- //! \return A platform-specific type representing the child process.
+ //! \return A platform-specific type representing the child process. This
+ //! method can fail on macOS because access to a child's task port
+ //! requires the task_for_pid entitlement.
ProcessType ChildProcess();
protected:
diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc
index 95436b4..a1e2acf 100644
--- a/test/multiprocess_exec_fuchsia.cc
+++ b/test/multiprocess_exec_fuchsia.cc
@@ -16,13 +16,12 @@
#include <lib/fdio/io.h>
#include <lib/fdio/spawn.h>
-#include <zircon/process.h>
+#include <lib/zx/process.h>
+#include <lib/zx/time.h>
#include <zircon/processargs.h>
-#include <zircon/syscalls.h>
#include "base/files/scoped_file.h"
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/scoped_zx_handle.h"
#include "gtest/gtest.h"
namespace crashpad {
@@ -31,14 +30,12 @@
namespace {
void AddPipe(fdio_spawn_action_t* action, int target_fd, int* fd_out) {
- zx_handle_t handle;
- uint32_t id;
- zx_status_t status = fdio_pipe_half(&handle, &id);
- ZX_CHECK(status >= 0, status) << "fdio_pipe_half";
+ zx_handle_t handle = ZX_HANDLE_INVALID;
+ zx_status_t status = fdio_pipe_half(fd_out, &handle);
+ ZX_CHECK(status == ZX_OK, status) << "fdio_pipe_half";
action->action = FDIO_SPAWN_ACTION_ADD_HANDLE;
- action->h.id = PA_HND(PA_HND_TYPE(id), target_fd);
+ action->h.id = PA_HND(PA_FD, target_fd);
action->h.handle = handle;
- *fd_out = status;
}
} // namespace
@@ -49,7 +46,7 @@
MultiprocessInfo() {}
base::ScopedFD stdin_write;
base::ScopedFD stdout_read;
- base::ScopedZxHandle child;
+ zx::process child;
};
} // namespace internal
@@ -68,19 +65,14 @@
// Wait until the child exits.
zx_signals_t signals;
ASSERT_EQ(
- zx_object_wait_one(
- info_->child.get(), ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &signals),
+ info_->child.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), &signals),
ZX_OK);
ASSERT_EQ(signals, ZX_TASK_TERMINATED);
// Get the child's exit code.
zx_info_process_t proc_info;
- zx_status_t status = zx_object_get_info(info_->child.get(),
- ZX_INFO_PROCESS,
- &proc_info,
- sizeof(proc_info),
- nullptr,
- nullptr);
+ zx_status_t status = info_->child.get_info(
+ ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_info";
ADD_FAILURE() << "Unable to get exit code of child";
@@ -93,7 +85,7 @@
}
void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
- int code) {
+ ReturnCodeType code) {
EXPECT_EQ(info_, nullptr)
<< "SetExpectedChildTermination() must be called before Run()";
reason_ = reason;
@@ -101,7 +93,8 @@
}
void Multiprocess::SetExpectedChildTerminationBuiltinTrap() {
- SetExpectedChildTermination(kTerminationNormal, -1);
+ constexpr ReturnCodeType kExpectedReturnCode = ZX_TASK_RETCODE_EXCEPTION_KILL;
+ SetExpectedChildTermination(kTerminationNormal, kExpectedReturnCode);
}
Multiprocess::~Multiprocess() {
@@ -188,7 +181,7 @@
uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO;
char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
- zx_handle_t child;
+ zx::process child;
zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID,
flags,
command_.value().c_str(),
@@ -196,14 +189,14 @@
nullptr,
kActionCount,
actions,
- &child,
+ child.reset_and_get_address(),
error_message);
ZX_CHECK(status == ZX_OK, status) << "fdio_spawn_etc: " << error_message;
- info()->child.reset(child);
+ info()->child = std::move(child);
}
ProcessType MultiprocessExec::ChildProcess() {
- return info()->child.get();
+ return zx::unowned_process(info()->child);
}
} // namespace test
diff --git a/test/multiprocess_exec_posix.cc b/test/multiprocess_exec_posix.cc
index c91b7f7..534013c 100644
--- a/test/multiprocess_exec_posix.cc
+++ b/test/multiprocess_exec_posix.cc
@@ -20,6 +20,7 @@
#include <unistd.h>
#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "util/misc/scoped_forbid_return.h"
@@ -29,6 +30,10 @@
#include <stdio_ext.h>
#endif
+#if defined(OS_MACOSX)
+#include "util/mach/task_for_pid.h"
+#endif
+
namespace crashpad {
namespace test {
@@ -149,7 +154,11 @@
}
ProcessType MultiprocessExec::ChildProcess() {
+#if defined(OS_MACOSX)
+ return TaskForPID(ChildPID());
+#else
return ChildPID();
+#endif
}
} // namespace test
diff --git a/test/multiprocess_exec_test.cc b/test/multiprocess_exec_test.cc
index 99a1b01..d290efa 100644
--- a/test/multiprocess_exec_test.cc
+++ b/test/multiprocess_exec_test.cc
@@ -72,8 +72,7 @@
TestMultiprocessExec exec;
exec.SetChildTestMainFunction("SimpleMultiprocess");
exec.Run();
-};
-
+}
CRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocessReturnsNonZero) {
return 123;
@@ -96,7 +95,7 @@
exec.SetExpectedChildTermination(
Multiprocess::TerminationReason::kTerminationNormal, 123);
exec.Run();
-};
+}
#if !defined(OS_WIN)
diff --git a/test/multiprocess_exec_win.cc b/test/multiprocess_exec_win.cc
index 3897818..b75f8f5 100644
--- a/test/multiprocess_exec_win.cc
+++ b/test/multiprocess_exec_win.cc
@@ -58,7 +58,7 @@
}
void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
- int code) {
+ ReturnCodeType code) {
EXPECT_EQ(info_, nullptr)
<< "SetExpectedChildTermination() must be called before Run()";
reason_ = reason;
diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc
index 2e0c385..d2de318 100644
--- a/test/multiprocess_posix.cc
+++ b/test/multiprocess_posix.cc
@@ -154,7 +154,7 @@
}
void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
- int code) {
+ ReturnCodeType code) {
EXPECT_EQ(info_, nullptr)
<< "SetExpectedChildTermination() must be called before Run()";
reason_ = reason;
diff --git a/test/process_type.cc b/test/process_type.cc
index 0e3c9e6..94fd912 100644
--- a/test/process_type.cc
+++ b/test/process_type.cc
@@ -15,8 +15,8 @@
#include "test/process_type.h"
#if defined(OS_FUCHSIA)
-#include <zircon/process.h>
-#elif defined(OS_POSIX)
+#include <lib/zx/process.h>
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include <unistd.h>
#endif
@@ -25,11 +25,13 @@
ProcessType GetSelfProcess() {
#if defined(OS_FUCHSIA)
- return zx_process_self();
-#elif defined(OS_POSIX)
+ return zx::process::self();
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
return getpid();
#elif defined(OS_WIN)
return GetCurrentProcess();
+#elif defined(OS_MACOSX)
+ return mach_task_self();
#endif
}
diff --git a/test/process_type.h b/test/process_type.h
index a25b122..dc99687 100644
--- a/test/process_type.h
+++ b/test/process_type.h
@@ -18,23 +18,27 @@
#include "build/build_config.h"
#if defined(OS_FUCHSIA)
-#include <zircon/types.h>
-#elif defined(OS_POSIX)
+#include <lib/zx/process.h>
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include <sys/types.h>
#elif defined(OS_WIN)
#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <mach/mach.h>
#endif
namespace crashpad {
namespace test {
#if defined(OS_FUCHSIA)
-using ProcessType = zx_handle_t;
-#elif defined(OS_POSIX) || DOXYGEN
+using ProcessType = zx::unowned_process;
+#elif defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
//! \brief Alias for platform-specific type to represent a process.
using ProcessType = pid_t;
#elif defined(OS_WIN)
using ProcessType = HANDLE;
+#elif defined(OS_MACOSX)
+using ProcessType = task_t;
#else
#error Port.
#endif
diff --git a/test/scoped_guarded_page.h b/test/scoped_guarded_page.h
new file mode 100644
index 0000000..55ef272
--- /dev/null
+++ b/test/scoped_guarded_page.h
@@ -0,0 +1,49 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_TEST_SCOPED_GUARDED_PAGE_
+#define CRASHPAD_TEST_SCOPED_GUARDED_PAGE_
+
+#include "base/macros.h"
+
+namespace crashpad {
+namespace test {
+
+//! \brief A RAII object that allocates a read-write page with an inacessible
+//! page following it.
+//!
+//! Upon construction, a mapping will be created. Failure to create the mapping
+//! is fatal. On destruction, the mapping is freed.
+//!
+//! This object should not be used in multi-threded contexts, the POSIX
+//! implementation can not be made thread-safe.
+class ScopedGuardedPage {
+ public:
+ ScopedGuardedPage();
+ ~ScopedGuardedPage();
+
+ //! \brief Returns the address of the read-write page.
+ //!
+ //! \return The address of the read-write page.
+ void* Pointer() const { return ptr_; }
+
+ private:
+ void* ptr_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedGuardedPage);
+};
+
+} // namespace test
+} // namespace crashpad
+
+#endif // CRASHPAD_TEST_SCOPED_GUARDED_PAGE_
diff --git a/test/scoped_guarded_page_posix.cc b/test/scoped_guarded_page_posix.cc
new file mode 100644
index 0000000..750fe18
--- /dev/null
+++ b/test/scoped_guarded_page_posix.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/scoped_guarded_page.h"
+
+#include <sys/mman.h>
+
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+
+namespace crashpad {
+namespace test {
+
+ScopedGuardedPage::ScopedGuardedPage() {
+ ptr_ = mmap(nullptr,
+ base::GetPageSize() * 2,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0);
+ PCHECK(ptr_ != MAP_FAILED) << "mmap";
+
+ // Simply mprotect()ing the guard page PROT_NONE does not make it inaccessible
+ // using ptrace() or /proc/$pid/mem so we munmap() the following page instead.
+ // Unfortunately, this means that the guarded page is not thread safe from
+ // other threads mapping a single page into the empty region.
+ char* second_page = static_cast<char*>(ptr_) + base::GetPageSize();
+ PCHECK(munmap(second_page, base::GetPageSize()) >= 0) << "munmap";
+}
+
+ScopedGuardedPage::~ScopedGuardedPage() {
+ PCHECK(munmap(ptr_, base::GetPageSize()) >= 0) << "munmap";
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/test/scoped_guarded_page_test.cc b/test/scoped_guarded_page_test.cc
new file mode 100644
index 0000000..023d1ed
--- /dev/null
+++ b/test/scoped_guarded_page_test.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/scoped_guarded_page.h"
+
+#include "base/process/process_metrics.h"
+#include "gtest/gtest.h"
+#include "test/gtest_death.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ScopedGuardedPage, BasicFunctionality) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+
+ ScopedGuardedPage page;
+ char* address = (char*)page.Pointer();
+ EXPECT_NE(address, nullptr);
+ address[0] = 0;
+ address[base::GetPageSize() - 1] = 0;
+ EXPECT_DEATH_CRASH({ address[base::GetPageSize()] = 0; }, "");
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/test/scoped_guarded_page_win.cc b/test/scoped_guarded_page_win.cc
new file mode 100644
index 0000000..23a6897
--- /dev/null
+++ b/test/scoped_guarded_page_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/scoped_guarded_page.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+
+namespace crashpad {
+namespace test {
+
+ScopedGuardedPage::ScopedGuardedPage() {
+ const size_t page_size = base::GetPageSize();
+ ptr_ = VirtualAlloc(nullptr, page_size * 2, MEM_RESERVE, PAGE_NOACCESS);
+ PCHECK(ptr_ != nullptr) << "VirtualAlloc";
+
+ PCHECK(VirtualAlloc(ptr_, page_size, MEM_COMMIT, PAGE_READWRITE) != nullptr)
+ << "VirtualAlloc";
+}
+
+ScopedGuardedPage::~ScopedGuardedPage() {
+ PCHECK(VirtualFree(ptr_, 0, MEM_RELEASE)) << "VirtualFree";
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/test/scoped_temp_dir_win.cc b/test/scoped_temp_dir_win.cc
index ab4ff95..8031921 100644
--- a/test/scoped_temp_dir_win.cc
+++ b/test/scoped_temp_dir_win.cc
@@ -64,7 +64,7 @@
// the one we generate exists, keep trying another path name until we reach
// some limit.
base::FilePath path_to_create = GenerateCandidateName();
- if (CreateDirectory(path_to_create.value().c_str(), NULL))
+ if (CreateDirectory(path_to_create.value().c_str(), nullptr))
return path_to_create;
}
diff --git a/test/test.gyp b/test/test.gyp
index 2973708..d00256a 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -37,8 +37,6 @@
'filesystem.cc',
'filesystem.h',
'gtest_death.h',
- 'gtest_disabled.cc',
- 'gtest_disabled.h',
'hex_string.cc',
'hex_string.h',
'linux/fake_ptrace_connection.cc',
@@ -63,6 +61,8 @@
'multiprocess_posix.cc',
'process_type.cc',
'process_type.h',
+ 'scoped_guarded_page.h',
+ 'scoped_guarded_page_posix.cc',
'scoped_module_handle.cc',
'scoped_module_handle.h',
'scoped_temp_dir.cc',
diff --git a/test/test_paths.cc b/test/test_paths.cc
index 50165e4..475e2b0 100644
--- a/test/test_paths.cc
+++ b/test/test_paths.cc
@@ -44,16 +44,9 @@
base::FilePath TestDataRootInternal() {
#if defined(OS_FUCHSIA)
- base::FilePath asset_path("/pkg/assets");
-#if defined(CRASHPAD_IS_IN_FUCHSIA)
- // Tests are not yet packaged when running in the Fuchsia tree, so assets do
- // not appear as expected at /pkg/assets. Override the default so that tests
- // can find their data for now.
- // https://crashpad.chromium.org/bug/196.
- asset_path = base::FilePath("/system/data/crashpad_test_data");
-#endif
+ base::FilePath asset_path("/pkg/data");
if (!IsTestDataRoot(asset_path)) {
- LOG(WARNING) << "Test data root seems invalid, continuing anyway";
+ LOG(WARNING) << "test data root seems invalid, continuing anyway";
}
return asset_path;
#else // defined(OS_FUCHSIA)
@@ -72,14 +65,23 @@
return base::FilePath(environment_value);
}
- // In a standalone build, the test executable is usually at
- // out/{Debug,Release} relative to the Crashpad root.
base::FilePath executable_path;
if (Paths::Executable(&executable_path)) {
+#if defined(OS_IOS) || defined(OS_ANDROID)
+ // On Android and iOS, test data is in a crashpad_test_data directory
+ // adjacent to the main executable. On iOS, this refers to the main
+ // executable file inside the .app bundle, so crashpad_test_data is also
+ // inside the bundle.
+ base::FilePath candidate = executable_path.DirName()
+ .Append("crashpad_test_data");
+#else // OS_IOS || OS_ANDRID
+ // In a standalone build, the test executable is usually at
+ // out/{Debug,Release} relative to the Crashpad root.
base::FilePath candidate =
base::FilePath(executable_path.DirName()
.Append(base::FilePath::kParentDirectory)
.Append(base::FilePath::kParentDirectory));
+#endif // OS_IOS || OS_ANDROID
if (IsTestDataRoot(candidate)) {
return candidate;
}
@@ -129,11 +131,7 @@
base::FilePath executable_path;
CHECK(Paths::Executable(&executable_path));
#if defined(CRASHPAD_IS_IN_FUCHSIA)
- // Tests are not yet packaged when running in the Fuchsia tree, so binaries do
- // not appear as expected at /pkg/bin. Override the default of /pkg/bin/app
- // so that tests can find the correct location for now.
- // https://crashpad.chromium.org/bug/196.
- executable_path = base::FilePath("/system/test/crashpad_test_data/app");
+ executable_path = base::FilePath("/pkg/bin/app");
#endif
return executable_path;
}
@@ -202,16 +200,7 @@
#if defined(OS_WIN)
extension = FILE_PATH_LITERAL(".exe");
#elif defined(OS_FUCHSIA)
-#if defined(CRASHPAD_IS_IN_FUCHSIA)
- // Tests are not yet packaged when running in the Fuchsia tree, so
- // binaries do not appear as expected at /pkg/bin. Override the default of
- // /pkg/bin/app so that tests can find the correct location for now.
- // https://crashpad.chromium.org/bug/196.
- directory =
- base::FilePath(FILE_PATH_LITERAL("/system/test/crashpad_test_data"));
-#else
directory = base::FilePath(FILE_PATH_LITERAL("/pkg/bin"));
-#endif
#endif // OS_WIN
break;
@@ -233,12 +222,7 @@
case FileType::kCertificate:
#if defined(CRASHPAD_IS_IN_FUCHSIA)
- // When running in the Fuchsia tree, the .pem files are packaged as assets
- // into the test data folder. This will need to be rationalized when
- // things are actually run from a package.
- // https://crashpad.chromium.org/bug/196.
- directory =
- base::FilePath(FILE_PATH_LITERAL("/system/test/crashpad_test_data"));
+ directory = base::FilePath(FILE_PATH_LITERAL("/pkg/data"));
#endif
extension = FILE_PATH_LITERAL(".pem");
break;
diff --git a/test/test_test.gyp b/test/test_test.gyp
index 3c6d706..ddd920e 100644
--- a/test/test_test.gyp
+++ b/test/test_test.gyp
@@ -39,6 +39,7 @@
'main_arguments_test.cc',
'multiprocess_exec_test.cc',
'multiprocess_posix_test.cc',
+ 'scoped_guarded_page_test.cc',
'scoped_temp_dir_test.cc',
'test_paths_test.cc',
'win/win_child_process_test.cc',
diff --git a/test/win/win_multiprocess_with_temp_dir.cc b/test/win/win_multiprocess_with_temp_dir.cc
index b61ebf6..ca1e01a 100644
--- a/test/win/win_multiprocess_with_temp_dir.cc
+++ b/test/win/win_multiprocess_with_temp_dir.cc
@@ -17,6 +17,7 @@
#include <tlhelp32.h>
#include "test/errors.h"
+#include "util/process/process_id.h"
#include "util/win/process_info.h"
namespace crashpad {
@@ -28,20 +29,20 @@
// Returns the process IDs of all processes that have |parent_pid| as
// parent process ID.
-std::vector<pid_t> GetPotentialChildProcessesOf(pid_t parent_pid) {
+std::vector<ProcessID> GetPotentialChildProcessesOf(ProcessID parent_pid) {
ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (!snapshot.is_valid()) {
ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot");
- return std::vector<pid_t>();
+ return std::vector<ProcessID>();
}
PROCESSENTRY32 entry = {sizeof(entry)};
if (!Process32First(snapshot.get(), &entry)) {
ADD_FAILURE() << ErrorMessage("Process32First");
- return std::vector<pid_t>();
+ return std::vector<ProcessID>();
}
- std::vector<pid_t> child_pids;
+ std::vector<ProcessID> child_pids;
do {
if (entry.th32ParentProcessID == parent_pid)
child_pids.push_back(entry.th32ProcessID);
@@ -68,11 +69,11 @@
// not their offspring. For this to work without race, |parent| has to be
// suspended or have exited.
void WaitForAllChildProcessesOf(HANDLE parent) {
- pid_t parent_pid = GetProcessId(parent);
- std::vector<pid_t> child_pids = GetPotentialChildProcessesOf(parent_pid);
+ ProcessID parent_pid = GetProcessId(parent);
+ std::vector<ProcessID> child_pids = GetPotentialChildProcessesOf(parent_pid);
ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent);
- for (pid_t child_pid : child_pids) {
+ for (ProcessID child_pid : child_pids) {
// Try and open the process. This may fail for reasons such as:
// 1. The process isn't |parent|'s child process, but rather a
// higher-privilege sub-process of an earlier process that had
diff --git a/third_party/apple_cf/BUILD.gn b/third_party/apple_cf/BUILD.gn
new file mode 100644
index 0000000..9e37354
--- /dev/null
+++ b/third_party/apple_cf/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("apple_cf") {
+ sources = [ "CFStreamAbstract.h" ]
+}
diff --git a/third_party/apple_cf/CFStreamAbstract.h b/third_party/apple_cf/CFStreamAbstract.h
index bf12c92..ac26664 100644
--- a/third_party/apple_cf/CFStreamAbstract.h
+++ b/third_party/apple_cf/CFStreamAbstract.h
@@ -1,15 +1,15 @@
/*
- * Copyright (c) 2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,12 +17,12 @@
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
/* CFStreamAbstract.h
- Copyright (c) 2000-2009, Apple Inc. All rights reserved.
+ Copyright (c) 2000-2014, Apple Inc. All rights reserved.
*/
#if !defined(__COREFOUNDATION_CFSTREAMABSTRACT__)
@@ -134,11 +134,18 @@
void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream);
// Returns an array of the runloops and modes on which the stream is currently scheduled
+// Note that these are unretained mutable arrays - use the copy variant instead.
CF_EXPORT
CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream);
CF_EXPORT
CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream);
+// Returns an array of the runloops and modes on which the stream is currently scheduled
+CF_EXPORT
+CFArrayRef _CFReadStreamCopyRunLoopsAndModes(CFReadStreamRef readStream);
+CF_EXPORT
+CFArrayRef _CFWriteStreamCopyRunLoopsAndModes(CFWriteStreamRef writeStream);
+
/* Deprecated versions; here for backwards compatibility. */
typedef struct {
CFIndex version; /* == 1 */
diff --git a/third_party/apple_cf/README.crashpad b/third_party/apple_cf/README.crashpad
index b6c6cfa..2006648 100644
--- a/third_party/apple_cf/README.crashpad
+++ b/third_party/apple_cf/README.crashpad
@@ -2,7 +2,7 @@
Short Name: CF
URL: https://opensource.apple.com/source/CF/
URL: https://opensource.apple.com/tarballs/CF/
-Version: 550.43 (from Mac OS X 10.6.8)
+Version: 1153.18 (from OS X 10.10.3)
License: APSL 2.0
License File: APPLE_LICENSE
Security Critical: no
diff --git a/third_party/cpp-httplib/BUILD.gn b/third_party/cpp-httplib/BUILD.gn
index be2a6d7..2cbb72f 100644
--- a/third_party/cpp-httplib/BUILD.gn
+++ b/third_party/cpp-httplib/BUILD.gn
@@ -15,7 +15,5 @@
source_set("cpp-httplib") {
testonly = true
include_dirs = [ "cpp-httplib" ]
- sources = [
- "cpp-httplib/httplib.h",
- ]
+ sources = [ "cpp-httplib/httplib.h" ]
}
diff --git a/third_party/edo/BUILD.gn b/third_party/edo/BUILD.gn
new file mode 100644
index 0000000..0205489
--- /dev/null
+++ b/third_party/edo/BUILD.gn
@@ -0,0 +1,142 @@
+# Copyright 2018 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/crashpad_buildconfig.gni")
+
+if (crashpad_is_in_chromium) {
+ group("edo") {
+ testonly = true
+ public_deps = [
+ "//ios/third_party/edo",
+ ]
+ }
+} else {
+ config("config") {
+ include_dirs = [ "../../third_party/edo/edo" ]
+ }
+
+ source_set("edo") {
+ testonly = true
+
+ sources = [
+ "edo/Channel/Sources/EDOBlockingQueue.h",
+ "edo/Channel/Sources/EDOBlockingQueue.m",
+ "edo/Channel/Sources/EDOChannel.h",
+ "edo/Channel/Sources/EDOChannelErrors.h",
+ "edo/Channel/Sources/EDOChannelErrors.m",
+ "edo/Channel/Sources/EDOChannelForwarder.h",
+ "edo/Channel/Sources/EDOChannelForwarder.m",
+ "edo/Channel/Sources/EDOChannelMultiplexer.h",
+ "edo/Channel/Sources/EDOChannelMultiplexer.m",
+ "edo/Channel/Sources/EDOChannelPool.h",
+ "edo/Channel/Sources/EDOChannelPool.m",
+ "edo/Channel/Sources/EDOChannelUtil.h",
+ "edo/Channel/Sources/EDOChannelUtil.m",
+ "edo/Channel/Sources/EDOHostPort.h",
+ "edo/Channel/Sources/EDOHostPort.m",
+ "edo/Channel/Sources/EDOListenSocket.h",
+ "edo/Channel/Sources/EDOListenSocket.m",
+ "edo/Channel/Sources/EDOSocket.h",
+ "edo/Channel/Sources/EDOSocket.m",
+ "edo/Channel/Sources/EDOSocketChannel.h",
+ "edo/Channel/Sources/EDOSocketChannel.m",
+ "edo/Channel/Sources/EDOSocketPort.h",
+ "edo/Channel/Sources/EDOSocketPort.m",
+ "edo/Device/Sources/EDODeviceChannel.h",
+ "edo/Device/Sources/EDODeviceChannel.m",
+ "edo/Device/Sources/EDODeviceConnector.h",
+ "edo/Device/Sources/EDODeviceConnector.m",
+ "edo/Device/Sources/EDODeviceDetector.h",
+ "edo/Device/Sources/EDODeviceDetector.m",
+ "edo/Device/Sources/EDOUSBMuxUtil.h",
+ "edo/Device/Sources/EDOUSBMuxUtil.m",
+ "edo/DeviceForwarder/Sources/EDODeviceForwardersManager.h",
+ "edo/DeviceForwarder/Sources/EDODeviceForwardersManager.m",
+ "edo/Measure/Sources/EDONumericMeasure.h",
+ "edo/Measure/Sources/EDONumericMeasure.m",
+ "edo/Service/Sources/EDOBlockObject.h",
+ "edo/Service/Sources/EDOBlockObject.m",
+ "edo/Service/Sources/EDOClassMessage.h",
+ "edo/Service/Sources/EDOClassMessage.m",
+ "edo/Service/Sources/EDOClientService+Private.h",
+ "edo/Service/Sources/EDOClientService.h",
+ "edo/Service/Sources/EDOClientService.m",
+ "edo/Service/Sources/EDOClientServiceStatsCollector.h",
+ "edo/Service/Sources/EDOClientServiceStatsCollector.m",
+ "edo/Service/Sources/EDODeallocationTracker.h",
+ "edo/Service/Sources/EDODeallocationTracker.m",
+ "edo/Service/Sources/EDOExecutor.h",
+ "edo/Service/Sources/EDOExecutor.m",
+ "edo/Service/Sources/EDOExecutorMessage.h",
+ "edo/Service/Sources/EDOExecutorMessage.m",
+ "edo/Service/Sources/EDOHostNamingService+Private.h",
+ "edo/Service/Sources/EDOHostNamingService.h",
+ "edo/Service/Sources/EDOHostNamingService.m",
+ "edo/Service/Sources/EDOHostService+Handlers.h",
+ "edo/Service/Sources/EDOHostService+Handlers.m",
+ "edo/Service/Sources/EDOHostService+Private.h",
+ "edo/Service/Sources/EDOHostService.h",
+ "edo/Service/Sources/EDOHostService.m",
+ "edo/Service/Sources/EDOInvocationMessage.h",
+ "edo/Service/Sources/EDOInvocationMessage.m",
+ "edo/Service/Sources/EDOMessage.h",
+ "edo/Service/Sources/EDOMessage.m",
+ "edo/Service/Sources/EDOMethodSignatureMessage.h",
+ "edo/Service/Sources/EDOMethodSignatureMessage.m",
+ "edo/Service/Sources/EDOObject+EDOParameter.m",
+ "edo/Service/Sources/EDOObject+Invocation.m",
+ "edo/Service/Sources/EDOObject+Private.h",
+ "edo/Service/Sources/EDOObject.h",
+ "edo/Service/Sources/EDOObject.m",
+ "edo/Service/Sources/EDOObjectAliveMessage.h",
+ "edo/Service/Sources/EDOObjectAliveMessage.m",
+ "edo/Service/Sources/EDOObjectMessage.h",
+ "edo/Service/Sources/EDOObjectMessage.m",
+ "edo/Service/Sources/EDOObjectReleaseMessage.h",
+ "edo/Service/Sources/EDOObjectReleaseMessage.m",
+ "edo/Service/Sources/EDOParameter.h",
+ "edo/Service/Sources/EDOParameter.m",
+ "edo/Service/Sources/EDOProtocolObject.h",
+ "edo/Service/Sources/EDOProtocolObject.m",
+ "edo/Service/Sources/EDORemoteVariable.h",
+ "edo/Service/Sources/EDORemoteVariable.m",
+ "edo/Service/Sources/EDOServiceError.h",
+ "edo/Service/Sources/EDOServiceError.m",
+ "edo/Service/Sources/EDOServiceException.h",
+ "edo/Service/Sources/EDOServiceException.m",
+ "edo/Service/Sources/EDOServicePort.h",
+ "edo/Service/Sources/EDOServicePort.m",
+ "edo/Service/Sources/EDOServiceRequest.h",
+ "edo/Service/Sources/EDOServiceRequest.m",
+ "edo/Service/Sources/EDOTimingFunctions.h",
+ "edo/Service/Sources/EDOTimingFunctions.m",
+ "edo/Service/Sources/EDOValueObject+EDOParameter.m",
+ "edo/Service/Sources/EDOValueObject.h",
+ "edo/Service/Sources/EDOValueObject.m",
+ "edo/Service/Sources/EDOValueType.m",
+ "edo/Service/Sources/EDOWeakObject.h",
+ "edo/Service/Sources/EDOWeakObject.m",
+ "edo/Service/Sources/NSBlock+EDOInvocation.m",
+ "edo/Service/Sources/NSKeyedArchiver+EDOAdditions.h",
+ "edo/Service/Sources/NSKeyedArchiver+EDOAdditions.m",
+ "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.h",
+ "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.m",
+ "edo/Service/Sources/NSObject+EDOParameter.h",
+ "edo/Service/Sources/NSObject+EDOParameter.m",
+ "edo/Service/Sources/NSObject+EDOValue.h",
+ "edo/Service/Sources/NSObject+EDOValue.m",
+ "edo/Service/Sources/NSObject+EDOValueObject.h",
+ "edo/Service/Sources/NSObject+EDOValueObject.m",
+ "edo/Service/Sources/NSObject+EDOWeakObject.h",
+ "edo/Service/Sources/NSObject+EDOWeakObject.m",
+ "edo/Service/Sources/NSProxy+EDOParameter.h",
+ "edo/Service/Sources/NSProxy+EDOParameter.m",
+ ]
+
+ public_configs = [ ":config" ]
+ deps = [
+ "../../build:ios_enable_arc",
+ ]
+ }
+}
diff --git a/third_party/edo/README.crashpad b/third_party/edo/README.crashpad
new file mode 100644
index 0000000..fb83158
--- /dev/null
+++ b/third_party/edo/README.crashpad
@@ -0,0 +1,10 @@
+Name: eDistantObject
+Short Name: EDO
+URL: https://github.com/google/eDistantObject
+Revision: See DEPS
+License: Apache 2.0
+License File: edo/LICENSE
+Security Critical: no
+
+Description:
+iOS remote method invocations (distant object) over Inter-process communication layer.
diff --git a/third_party/fuchsia/BUILD.gn b/third_party/fuchsia/BUILD.gn
new file mode 100644
index 0000000..7e06289
--- /dev/null
+++ b/third_party/fuchsia/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright 2018 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../build/crashpad_buildconfig.gni")
+
+if (crashpad_is_in_fuchsia) {
+ group("fuchsia") {
+ public_deps = [
+ "//zircon/public/lib/fdio",
+ "//zircon/public/lib/zx",
+ ]
+ }
+} else if (crashpad_is_in_chromium) {
+ group("fuchsia") {
+ public_deps = [
+ "//third_party/fuchsia-sdk/sdk/pkg/fdio",
+ "//third_party/fuchsia-sdk/sdk/pkg/zx",
+ ]
+ }
+} else {
+ group("fuchsia") {
+ public_deps = [
+ "//third_party/fuchsia/sdk/$host_os-amd64/pkg/fdio",
+ "//third_party/fuchsia/sdk/$host_os-amd64/pkg/zx",
+ ]
+ }
+}
diff --git a/third_party/fuchsia/runner.py b/third_party/fuchsia/runner.py
new file mode 100755
index 0000000..229d8a4
--- /dev/null
+++ b/third_party/fuchsia/runner.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+# Copyright 2018 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+os.execv(sys.argv[1], sys.argv[1:])
diff --git a/third_party/glibc/BUILD.gn b/third_party/glibc/BUILD.gn
new file mode 100644
index 0000000..3f2b08b
--- /dev/null
+++ b/third_party/glibc/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("glibc") {
+ sources = [ "elf/elf.h" ]
+}
diff --git a/third_party/glibc/COPYING.LIB b/third_party/glibc/COPYING.LIB
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/third_party/glibc/COPYING.LIB
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/third_party/glibc/README.crashpad b/third_party/glibc/README.crashpad
new file mode 100644
index 0000000..b550ad9
--- /dev/null
+++ b/third_party/glibc/README.crashpad
@@ -0,0 +1,16 @@
+Name: GNU C Library
+Short Name: glibc
+URL: https://www.gnu.org/software/libc/
+URL: https://sourceware.org/git/?p=glibc.git
+Version: 2.29
+License: GNU LGPL 2.1
+License File: COPYING.LIB
+Security Critical: no
+
+Description:
+glibc is the GNU Project’s implementation of the C standard library.
+
+Local Modifications:
+ - Only elf/elf.h is included. Its #include of <features.h> has been removed,
+ and it uses of __BEGIN_DECLS and __END_DECLS have been replaced with inline
+ versions in the manner that misc/sys/cdefs.h defines those macros.
diff --git a/third_party/glibc/elf/elf.h b/third_party/glibc/elf/elf.h
new file mode 100644
index 0000000..331536b
--- /dev/null
+++ b/third_party/glibc/elf/elf.h
@@ -0,0 +1,4003 @@
+/* This file defines standard ELF types, structures, and macros.
+ Copyright (C) 1995-2019 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _ELF_H
+#define _ELF_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Standard ELF types. */
+
+#include <stdint.h>
+
+/* Type for a 16-bit quantity. */
+typedef uint16_t Elf32_Half;
+typedef uint16_t Elf64_Half;
+
+/* Types for signed and unsigned 32-bit quantities. */
+typedef uint32_t Elf32_Word;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf64_Word;
+typedef int32_t Elf64_Sword;
+
+/* Types for signed and unsigned 64-bit quantities. */
+typedef uint64_t Elf32_Xword;
+typedef int64_t Elf32_Sxword;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+
+/* Type of addresses. */
+typedef uint32_t Elf32_Addr;
+typedef uint64_t Elf64_Addr;
+
+/* Type of file offsets. */
+typedef uint32_t Elf32_Off;
+typedef uint64_t Elf64_Off;
+
+/* Type for section indices, which are 16-bit quantities. */
+typedef uint16_t Elf32_Section;
+typedef uint16_t Elf64_Section;
+
+/* Type for version symbol information. */
+typedef Elf32_Half Elf32_Versym;
+typedef Elf64_Half Elf64_Versym;
+
+
+/* The ELF file header. This appears at the start of every ELF file. */
+
+#define EI_NIDENT (16)
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf32_Half e_type; /* Object file type */
+ Elf32_Half e_machine; /* Architecture */
+ Elf32_Word e_version; /* Object file version */
+ Elf32_Addr e_entry; /* Entry point virtual address */
+ Elf32_Off e_phoff; /* Program header table file offset */
+ Elf32_Off e_shoff; /* Section header table file offset */
+ Elf32_Word e_flags; /* Processor-specific flags */
+ Elf32_Half e_ehsize; /* ELF header size in bytes */
+ Elf32_Half e_phentsize; /* Program header table entry size */
+ Elf32_Half e_phnum; /* Program header table entry count */
+ Elf32_Half e_shentsize; /* Section header table entry size */
+ Elf32_Half e_shnum; /* Section header table entry count */
+ Elf32_Half e_shstrndx; /* Section header string table index */
+} Elf32_Ehdr;
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf64_Half e_type; /* Object file type */
+ Elf64_Half e_machine; /* Architecture */
+ Elf64_Word e_version; /* Object file version */
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags; /* Processor-specific flags */
+ Elf64_Half e_ehsize; /* ELF header size in bytes */
+ Elf64_Half e_phentsize; /* Program header table entry size */
+ Elf64_Half e_phnum; /* Program header table entry count */
+ Elf64_Half e_shentsize; /* Section header table entry size */
+ Elf64_Half e_shnum; /* Section header table entry count */
+ Elf64_Half e_shstrndx; /* Section header string table index */
+} Elf64_Ehdr;
+
+/* Fields in the e_ident array. The EI_* macros are indices into the
+ array. The macros under each EI_* macro are the values the byte
+ may have. */
+
+#define EI_MAG0 0 /* File identification byte 0 index */
+#define ELFMAG0 0x7f /* Magic number byte 0 */
+
+#define EI_MAG1 1 /* File identification byte 1 index */
+#define ELFMAG1 'E' /* Magic number byte 1 */
+
+#define EI_MAG2 2 /* File identification byte 2 index */
+#define ELFMAG2 'L' /* Magic number byte 2 */
+
+#define EI_MAG3 3 /* File identification byte 3 index */
+#define ELFMAG3 'F' /* Magic number byte 3 */
+
+/* Conglomeration of the identification bytes, for easy testing as a word. */
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define EI_CLASS 4 /* File class byte index */
+#define ELFCLASSNONE 0 /* Invalid class */
+#define ELFCLASS32 1 /* 32-bit objects */
+#define ELFCLASS64 2 /* 64-bit objects */
+#define ELFCLASSNUM 3
+
+#define EI_DATA 5 /* Data encoding byte index */
+#define ELFDATANONE 0 /* Invalid data encoding */
+#define ELFDATA2LSB 1 /* 2's complement, little endian */
+#define ELFDATA2MSB 2 /* 2's complement, big endian */
+#define ELFDATANUM 3
+
+#define EI_VERSION 6 /* File version byte index */
+ /* Value must be EV_CURRENT */
+
+#define EI_OSABI 7 /* OS ABI identification */
+#define ELFOSABI_NONE 0 /* UNIX System V ABI */
+#define ELFOSABI_SYSV 0 /* Alias. */
+#define ELFOSABI_HPUX 1 /* HP-UX */
+#define ELFOSABI_NETBSD 2 /* NetBSD. */
+#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */
+#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */
+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
+#define ELFOSABI_AIX 7 /* IBM AIX. */
+#define ELFOSABI_IRIX 8 /* SGI Irix. */
+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
+#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
+#define ELFOSABI_ARM 97 /* ARM */
+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
+
+#define EI_ABIVERSION 8 /* ABI version */
+
+#define EI_PAD 9 /* Byte index of padding bytes */
+
+/* Legal values for e_type (object file type). */
+
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* Relocatable file */
+#define ET_EXEC 2 /* Executable file */
+#define ET_DYN 3 /* Shared object file */
+#define ET_CORE 4 /* Core file */
+#define ET_NUM 5 /* Number of defined types */
+#define ET_LOOS 0xfe00 /* OS-specific range start */
+#define ET_HIOS 0xfeff /* OS-specific range end */
+#define ET_LOPROC 0xff00 /* Processor-specific range start */
+#define ET_HIPROC 0xffff /* Processor-specific range end */
+
+/* Legal values for e_machine (architecture). */
+
+#define EM_NONE 0 /* No machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SUN SPARC */
+#define EM_386 3 /* Intel 80386 */
+#define EM_68K 4 /* Motorola m68k family */
+#define EM_88K 5 /* Motorola m88k family */
+#define EM_IAMCU 6 /* Intel MCU */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 big-endian */
+#define EM_S370 9 /* IBM System/370 */
+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
+ /* reserved 11-14 */
+#define EM_PARISC 15 /* HPPA */
+ /* reserved 16 */
+#define EM_VPP500 17 /* Fujitsu VPP500 */
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+#define EM_960 19 /* Intel 80960 */
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC 64-bit */
+#define EM_S390 22 /* IBM S390 */
+#define EM_SPU 23 /* IBM SPU/SPC */
+ /* reserved 24-35 */
+#define EM_V800 36 /* NEC V800 series */
+#define EM_FR20 37 /* Fujitsu FR20 */
+#define EM_RH32 38 /* TRW RH-32 */
+#define EM_RCE 39 /* Motorola RCE */
+#define EM_ARM 40 /* ARM */
+#define EM_FAKE_ALPHA 41 /* Digital Alpha */
+#define EM_SH 42 /* Hitachi SH */
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+#define EM_TRICORE 44 /* Siemens Tricore */
+#define EM_ARC 45 /* Argonaut RISC Core */
+#define EM_H8_300 46 /* Hitachi H8/300 */
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+#define EM_H8_500 49 /* Hitachi H8/500 */
+#define EM_IA_64 50 /* Intel Merced */
+#define EM_MIPS_X 51 /* Stanford MIPS-X */
+#define EM_COLDFIRE 52 /* Motorola Coldfire */
+#define EM_68HC12 53 /* Motorola M68HC12 */
+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */
+#define EM_PCP 55 /* Siemens PCP */
+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
+#define EM_NDR1 57 /* Denso NDR1 microprocessor */
+#define EM_STARCORE 58 /* Motorola Start*Core processor */
+#define EM_ME16 59 /* Toyota ME16 processor */
+#define EM_ST100 60 /* STMicroelectronic ST100 processor */
+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam */
+#define EM_X86_64 62 /* AMD x86-64 architecture */
+#define EM_PDSP 63 /* Sony DSP Processor */
+#define EM_PDP10 64 /* Digital PDP-10 */
+#define EM_PDP11 65 /* Digital PDP-11 */
+#define EM_FX66 66 /* Siemens FX66 microcontroller */
+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
+#define EM_SVX 73 /* Silicon Graphics SVx */
+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
+#define EM_VAX 75 /* Digital VAX */
+#define EM_CRIS 76 /* Axis Communications 32-bit emb.proc */
+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit emb.proc */
+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc */
+#define EM_HUANY 81 /* Harvard University machine-independent object files */
+#define EM_PRISM 82 /* SiTera Prism */
+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
+#define EM_FR30 84 /* Fujitsu FR30 */
+#define EM_D10V 85 /* Mitsubishi D10V */
+#define EM_D30V 86 /* Mitsubishi D30V */
+#define EM_V850 87 /* NEC v850 */
+#define EM_M32R 88 /* Mitsubishi M32R */
+#define EM_MN10300 89 /* Matsushita MN10300 */
+#define EM_MN10200 90 /* Matsushita MN10200 */
+#define EM_PJ 91 /* picoJava */
+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
+#define EM_ARC_COMPACT 93 /* ARC International ARCompact */
+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
+#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore */
+#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Proc */
+#define EM_NS32K 97 /* National Semi. 32000 */
+#define EM_TPC 98 /* Tenor Network TPC */
+#define EM_SNP1K 99 /* Trebia SNP 1000 */
+#define EM_ST200 100 /* STMicroelectronics ST200 */
+#define EM_IP2K 101 /* Ubicom IP2xxx */
+#define EM_MAX 102 /* MAX processor */
+#define EM_CR 103 /* National Semi. CompactRISC */
+#define EM_F2MC16 104 /* Fujitsu F2MC16 */
+#define EM_MSP430 105 /* Texas Instruments msp430 */
+#define EM_BLACKFIN 106 /* Analog Devices Blackfin DSP */
+#define EM_SE_C33 107 /* Seiko Epson S1C33 family */
+#define EM_SEP 108 /* Sharp embedded microprocessor */
+#define EM_ARCA 109 /* Arca RISC */
+#define EM_UNICORE 110 /* PKU-Unity & MPRC Peking Uni. mc series */
+#define EM_EXCESS 111 /* eXcess configurable cpu */
+#define EM_DXP 112 /* Icera Semi. Deep Execution Processor */
+#define EM_ALTERA_NIOS2 113 /* Altera Nios II */
+#define EM_CRX 114 /* National Semi. CompactRISC CRX */
+#define EM_XGATE 115 /* Motorola XGATE */
+#define EM_C166 116 /* Infineon C16x/XC16x */
+#define EM_M16C 117 /* Renesas M16C */
+#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F */
+#define EM_CE 119 /* Freescale Communication Engine RISC */
+#define EM_M32C 120 /* Renesas M32C */
+ /* reserved 121-130 */
+#define EM_TSK3000 131 /* Altium TSK3000 */
+#define EM_RS08 132 /* Freescale RS08 */
+#define EM_SHARC 133 /* Analog Devices SHARC family */
+#define EM_ECOG2 134 /* Cyan Technology eCOG2 */
+#define EM_SCORE7 135 /* Sunplus S+core7 RISC */
+#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP */
+#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III */
+#define EM_LATTICEMICO32 138 /* RISC for Lattice FPGA */
+#define EM_SE_C17 139 /* Seiko Epson C17 */
+#define EM_TI_C6000 140 /* Texas Instruments TMS320C6000 DSP */
+#define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP */
+#define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP */
+#define EM_TI_ARP32 143 /* Texas Instruments App. Specific RISC */
+#define EM_TI_PRU 144 /* Texas Instruments Prog. Realtime Unit */
+ /* reserved 145-159 */
+#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW DSP */
+#define EM_CYPRESS_M8C 161 /* Cypress M8C */
+#define EM_R32C 162 /* Renesas R32C */
+#define EM_TRIMEDIA 163 /* NXP Semi. TriMedia */
+#define EM_QDSP6 164 /* QUALCOMM DSP6 */
+#define EM_8051 165 /* Intel 8051 and variants */
+#define EM_STXP7X 166 /* STMicroelectronics STxP7x */
+#define EM_NDS32 167 /* Andes Tech. compact code emb. RISC */
+#define EM_ECOG1X 168 /* Cyan Technology eCOG1X */
+#define EM_MAXQ30 169 /* Dallas Semi. MAXQ30 mc */
+#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP */
+#define EM_MANIK 171 /* M2000 Reconfigurable RISC */
+#define EM_CRAYNV2 172 /* Cray NV2 vector architecture */
+#define EM_RX 173 /* Renesas RX */
+#define EM_METAG 174 /* Imagination Tech. META */
+#define EM_MCST_ELBRUS 175 /* MCST Elbrus */
+#define EM_ECOG16 176 /* Cyan Technology eCOG16 */
+#define EM_CR16 177 /* National Semi. CompactRISC CR16 */
+#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */
+#define EM_SLE9X 179 /* Infineon Tech. SLE9X */
+#define EM_L10M 180 /* Intel L10M */
+#define EM_K10M 181 /* Intel K10M */
+ /* reserved 182 */
+#define EM_AARCH64 183 /* ARM AARCH64 */
+ /* reserved 184 */
+#define EM_AVR32 185 /* Amtel 32-bit microprocessor */
+#define EM_STM8 186 /* STMicroelectronics STM8 */
+#define EM_TILE64 187 /* Tileta TILE64 */
+#define EM_TILEPRO 188 /* Tilera TILEPro */
+#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */
+#define EM_CUDA 190 /* NVIDIA CUDA */
+#define EM_TILEGX 191 /* Tilera TILE-Gx */
+#define EM_CLOUDSHIELD 192 /* CloudShield */
+#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st gen. */
+#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd gen. */
+#define EM_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */
+#define EM_OPEN8 196 /* Open8 RISC */
+#define EM_RL78 197 /* Renesas RL78 */
+#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V */
+#define EM_78KOR 199 /* Renesas 78KOR */
+#define EM_56800EX 200 /* Freescale 56800EX DSC */
+#define EM_BA1 201 /* Beyond BA1 */
+#define EM_BA2 202 /* Beyond BA2 */
+#define EM_XCORE 203 /* XMOS xCORE */
+#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) */
+ /* reserved 205-209 */
+#define EM_KM32 210 /* KM211 KM32 */
+#define EM_KMX32 211 /* KM211 KMX32 */
+#define EM_EMX16 212 /* KM211 KMX16 */
+#define EM_EMX8 213 /* KM211 KMX8 */
+#define EM_KVARC 214 /* KM211 KVARC */
+#define EM_CDP 215 /* Paneve CDP */
+#define EM_COGE 216 /* Cognitive Smart Memory Processor */
+#define EM_COOL 217 /* Bluechip CoolEngine */
+#define EM_NORC 218 /* Nanoradio Optimized RISC */
+#define EM_CSR_KALIMBA 219 /* CSR Kalimba */
+#define EM_Z80 220 /* Zilog Z80 */
+#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore */
+#define EM_FT32 222 /* FTDI Chip FT32 */
+#define EM_MOXIE 223 /* Moxie processor */
+#define EM_AMDGPU 224 /* AMD GPU */
+ /* reserved 225-242 */
+#define EM_RISCV 243 /* RISC-V */
+
+#define EM_BPF 247 /* Linux BPF -- in-kernel virtual machine */
+#define EM_CSKY 252 /* C_SKY */
+
+#define EM_NUM 253
+
+/* Old spellings/synonyms. */
+
+#define EM_ARC_A5 EM_ARC_COMPACT
+
+/* If it is necessary to assign new unofficial EM_* values, please
+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
+ chances of collision with official or non-GNU unofficial values. */
+
+#define EM_ALPHA 0x9026
+
+/* Legal values for e_version (version). */
+
+#define EV_NONE 0 /* Invalid ELF version */
+#define EV_CURRENT 1 /* Current version */
+#define EV_NUM 2
+
+/* Section header. */
+
+typedef struct
+{
+ Elf32_Word sh_name; /* Section name (string tbl index) */
+ Elf32_Word sh_type; /* Section type */
+ Elf32_Word sh_flags; /* Section flags */
+ Elf32_Addr sh_addr; /* Section virtual addr at execution */
+ Elf32_Off sh_offset; /* Section file offset */
+ Elf32_Word sh_size; /* Section size in bytes */
+ Elf32_Word sh_link; /* Link to another section */
+ Elf32_Word sh_info; /* Additional section information */
+ Elf32_Word sh_addralign; /* Section alignment */
+ Elf32_Word sh_entsize; /* Entry size if section holds table */
+} Elf32_Shdr;
+
+typedef struct
+{
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+/* Special section indices. */
+
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
+#define SHN_LOPROC 0xff00 /* Start of processor-specific */
+#define SHN_BEFORE 0xff00 /* Order section before all others
+ (Solaris). */
+#define SHN_AFTER 0xff01 /* Order section after all others
+ (Solaris). */
+#define SHN_HIPROC 0xff1f /* End of processor-specific */
+#define SHN_LOOS 0xff20 /* Start of OS-specific */
+#define SHN_HIOS 0xff3f /* End of OS-specific */
+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xfff2 /* Associated symbol is common */
+#define SHN_XINDEX 0xffff /* Index is in extra table. */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
+
+/* Legal values for sh_type (section type). */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program data */
+#define SHT_SYMTAB 2 /* Symbol table */
+#define SHT_STRTAB 3 /* String table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* Symbol hash table */
+#define SHT_DYNAMIC 6 /* Dynamic linking information */
+#define SHT_NOTE 7 /* Notes */
+#define SHT_NOBITS 8 /* Program space with no data (bss) */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY 14 /* Array of constructors */
+#define SHT_FINI_ARRAY 15 /* Array of destructors */
+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
+#define SHT_GROUP 17 /* Section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
+#define SHT_NUM 19 /* Number of defined types. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
+#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
+#define SHT_SUNW_move 0x6ffffffa
+#define SHT_SUNW_COMDAT 0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
+#define SHT_HIOS 0x6fffffff /* End OS-specific type */
+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
+#define SHT_LOUSER 0x80000000 /* Start of application-specific */
+#define SHT_HIUSER 0x8fffffff /* End of application-specific */
+
+/* Legal values for sh_flags (section flags). */
+
+#define SHF_WRITE (1 << 0) /* Writable */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable */
+#define SHF_MERGE (1 << 4) /* Might be merged */
+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
+ required */
+#define SHF_GROUP (1 << 9) /* Section is member of a group. */
+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
+#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */
+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
+#define SHF_ORDERED (1 << 30) /* Special ordering requirement
+ (Solaris). */
+#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless
+ referenced or allocated (Solaris).*/
+
+/* Section compression header. Used when SHF_COMPRESSED is set. */
+
+typedef struct
+{
+ Elf32_Word ch_type; /* Compression format. */
+ Elf32_Word ch_size; /* Uncompressed data size. */
+ Elf32_Word ch_addralign; /* Uncompressed data alignment. */
+} Elf32_Chdr;
+
+typedef struct
+{
+ Elf64_Word ch_type; /* Compression format. */
+ Elf64_Word ch_reserved;
+ Elf64_Xword ch_size; /* Uncompressed data size. */
+ Elf64_Xword ch_addralign; /* Uncompressed data alignment. */
+} Elf64_Chdr;
+
+/* Legal values for ch_type (compression algorithm). */
+#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */
+#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */
+#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */
+#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */
+#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */
+
+/* Section group handling. */
+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */
+
+/* Symbol table entry. */
+
+typedef struct
+{
+ Elf32_Word st_name; /* Symbol name (string tbl index) */
+ Elf32_Addr st_value; /* Symbol value */
+ Elf32_Word st_size; /* Symbol size */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf32_Section st_shndx; /* Section index */
+} Elf32_Sym;
+
+typedef struct
+{
+ Elf64_Word st_name; /* Symbol name (string tbl index) */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf64_Section st_shndx; /* Section index */
+ Elf64_Addr st_value; /* Symbol value */
+ Elf64_Xword st_size; /* Symbol size */
+} Elf64_Sym;
+
+/* The syminfo section if available contains additional information about
+ every dynamic symbol. */
+
+typedef struct
+{
+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf32_Half si_flags; /* Per symbol flags */
+} Elf32_Syminfo;
+
+typedef struct
+{
+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf64_Half si_flags; /* Per symbol flags */
+} Elf64_Syminfo;
+
+/* Possible values for si_boundto. */
+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
+
+/* Possible bitmasks for si_flags. */
+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
+ loaded */
+/* Syminfo version values. */
+#define SYMINFO_NONE 0
+#define SYMINFO_CURRENT 1
+#define SYMINFO_NUM 2
+
+
+/* How to extract and insert information held in the st_info field. */
+
+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val) ((val) & 0xf)
+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+
+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding). */
+
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+#define STB_NUM 3 /* Number of defined types. */
+#define STB_LOOS 10 /* Start of OS-specific */
+#define STB_GNU_UNIQUE 10 /* Unique symbol. */
+#define STB_HIOS 12 /* End of OS-specific */
+#define STB_LOPROC 13 /* Start of processor-specific */
+#define STB_HIPROC 15 /* End of processor-specific */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_NOTYPE 0 /* Symbol type is unspecified */
+#define STT_OBJECT 1 /* Symbol is a data object */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STT_SECTION 3 /* Symbol associated with a section */
+#define STT_FILE 4 /* Symbol's name is file name */
+#define STT_COMMON 5 /* Symbol is a common data object */
+#define STT_TLS 6 /* Symbol is thread-local data object*/
+#define STT_NUM 7 /* Number of defined types. */
+#define STT_LOOS 10 /* Start of OS-specific */
+#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */
+#define STT_HIOS 12 /* End of OS-specific */
+#define STT_LOPROC 13 /* Start of processor-specific */
+#define STT_HIPROC 15 /* End of processor-specific */
+
+
+/* Symbol table indices are found in the hash buckets and chain table
+ of a symbol hash table section. This special index value indicates
+ the end of a chain, meaning no further symbols are found in that bucket. */
+
+#define STN_UNDEF 0 /* End of a chain. */
+
+
+/* How to extract and insert information held in the st_other field. */
+
+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
+
+/* For ELF64 the definitions are the same. */
+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
+
+/* Symbol visibility specification encoded in the st_other field. */
+#define STV_DEFAULT 0 /* Default symbol visibility rules */
+#define STV_INTERNAL 1 /* Processor specific hidden class */
+#define STV_HIDDEN 2 /* Sym unavailable in other modules */
+#define STV_PROTECTED 3 /* Not preemptible, not exported */
+
+
+/* Relocation table entry without addend (in section of type SHT_REL). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+} Elf32_Rel;
+
+/* I have seen two different definitions of the Elf64_Rel and
+ Elf64_Rela structures, so we'll leave them out until Novell (or
+ whoever) gets their act together. */
+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+} Elf64_Rel;
+
+/* Relocation table entry with addend (in section of type SHT_RELA). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+ Elf32_Sword r_addend; /* Addend */
+} Elf32_Rela;
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+ Elf64_Sxword r_addend; /* Addend */
+} Elf64_Rela;
+
+/* How to extract and insert information held in the r_info field. */
+
+#define ELF32_R_SYM(val) ((val) >> 8)
+#define ELF32_R_TYPE(val) ((val) & 0xff)
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
+
+/* Program segment header. */
+
+typedef struct
+{
+ Elf32_Word p_type; /* Segment type */
+ Elf32_Off p_offset; /* Segment file offset */
+ Elf32_Addr p_vaddr; /* Segment virtual address */
+ Elf32_Addr p_paddr; /* Segment physical address */
+ Elf32_Word p_filesz; /* Segment size in file */
+ Elf32_Word p_memsz; /* Segment size in memory */
+ Elf32_Word p_flags; /* Segment flags */
+ Elf32_Word p_align; /* Segment alignment */
+} Elf32_Phdr;
+
+typedef struct
+{
+ Elf64_Word p_type; /* Segment type */
+ Elf64_Word p_flags; /* Segment flags */
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment */
+} Elf64_Phdr;
+
+/* Special value for e_phnum. This indicates that the real number of
+ program headers is too large to fit into e_phnum. Instead the real
+ value is in the field sh_info of section 0. */
+
+#define PN_XNUM 0xffff
+
+/* Legal values for p_type (segment type). */
+
+#define PT_NULL 0 /* Program header table entry unused */
+#define PT_LOAD 1 /* Loadable program segment */
+#define PT_DYNAMIC 2 /* Dynamic linking information */
+#define PT_INTERP 3 /* Program interpreter */
+#define PT_NOTE 4 /* Auxiliary information */
+#define PT_SHLIB 5 /* Reserved */
+#define PT_PHDR 6 /* Entry for header table itself */
+#define PT_TLS 7 /* Thread-local storage segment */
+#define PT_NUM 8 /* Number of defined types */
+#define PT_LOOS 0x60000000 /* Start of OS-specific */
+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
+#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
+#define PT_LOSUNW 0x6ffffffa
+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
+#define PT_HISUNW 0x6fffffff
+#define PT_HIOS 0x6fffffff /* End of OS-specific */
+#define PT_LOPROC 0x70000000 /* Start of processor-specific */
+#define PT_HIPROC 0x7fffffff /* End of processor-specific */
+
+/* Legal values for p_flags (segment flags). */
+
+#define PF_X (1 << 0) /* Segment is executable */
+#define PF_W (1 << 1) /* Segment is writable */
+#define PF_R (1 << 2) /* Segment is readable */
+#define PF_MASKOS 0x0ff00000 /* OS-specific */
+#define PF_MASKPROC 0xf0000000 /* Processor-specific */
+
+/* Legal values for note segment descriptor types for core files. */
+
+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
+#define NT_PRFPREG 2 /* Contains copy of fpregset
+ struct. */
+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
+#define NT_PRXREG 4 /* Contains copy of prxregset struct */
+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */
+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
+#define NT_AUXV 6 /* Contains copy of auxv array */
+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
+#define NT_ASRS 8 /* Contains copy of asrset struct */
+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
+#define NT_PSINFO 13 /* Contains copy of psinfo struct */
+#define NT_PRCRED 14 /* Contains copy of prcred struct */
+#define NT_UTSNAME 15 /* Contains copy of utsname struct */
+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */
+#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t,
+ size might increase */
+#define NT_FILE 0x46494c45 /* Contains information about mapped
+ files */
+#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */
+#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
+#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
+#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
+#define NT_PPC_TAR 0x103 /* Target Address Register */
+#define NT_PPC_PPR 0x104 /* Program Priority Register */
+#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */
+#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */
+#define NT_PPC_PMU 0x107 /* Performance Monitor Registers */
+#define NT_PPC_TM_CGPR 0x108 /* TM checkpointed GPR Registers */
+#define NT_PPC_TM_CFPR 0x109 /* TM checkpointed FPR Registers */
+#define NT_PPC_TM_CVMX 0x10a /* TM checkpointed VMX Registers */
+#define NT_PPC_TM_CVSX 0x10b /* TM checkpointed VSX Registers */
+#define NT_PPC_TM_SPR 0x10c /* TM Special Purpose Registers */
+#define NT_PPC_TM_CTAR 0x10d /* TM checkpointed Target Address
+ Register */
+#define NT_PPC_TM_CPPR 0x10e /* TM checkpointed Program Priority
+ Register */
+#define NT_PPC_TM_CDSCR 0x10f /* TM checkpointed Data Stream Control
+ Register */
+#define NT_PPC_PKEY 0x110 /* Memory Protection Keys
+ registers. */
+#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
+#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
+#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
+#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */
+#define NT_S390_TIMER 0x301 /* s390 timer register */
+#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
+#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
+#define NT_S390_CTRS 0x304 /* s390 control registers */
+#define NT_S390_PREFIX 0x305 /* s390 prefix register */
+#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
+#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
+#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */
+#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15
+ upper half. */
+#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31. */
+#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers. */
+#define NT_S390_GS_BC 0x30c /* s390 guarded storage
+ broadcast control block. */
+#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation. */
+#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
+#define NT_ARM_TLS 0x401 /* ARM TLS register */
+#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */
+#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */
+#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */
+#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension
+ registers */
+#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */
+#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */
+#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */
+
+/* Legal values for the note segment descriptor types for object files. */
+
+#define NT_VERSION 1 /* Contains a version string. */
+
+
+/* Dynamic section entry. */
+
+typedef struct
+{
+ Elf32_Sword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf32_Word d_val; /* Integer value */
+ Elf32_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+ Elf64_Sxword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf64_Xword d_val; /* Integer value */
+ Elf64_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf64_Dyn;
+
+/* Legal values for d_tag (dynamic entry type). */
+
+#define DT_NULL 0 /* Marks end of dynamic section */
+#define DT_NEEDED 1 /* Name of needed library */
+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
+#define DT_PLTGOT 3 /* Processor defined value */
+#define DT_HASH 4 /* Address of symbol hash table */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_RELA 7 /* Address of Rela relocs */
+#define DT_RELASZ 8 /* Total size of Rela relocs */
+#define DT_RELAENT 9 /* Size of one Rela reloc */
+#define DT_STRSZ 10 /* Size of string table */
+#define DT_SYMENT 11 /* Size of one symbol table entry */
+#define DT_INIT 12 /* Address of init function */
+#define DT_FINI 13 /* Address of termination function */
+#define DT_SONAME 14 /* Name of shared object */
+#define DT_RPATH 15 /* Library search path (deprecated) */
+#define DT_SYMBOLIC 16 /* Start symbol search here */
+#define DT_REL 17 /* Address of Rel relocs */
+#define DT_RELSZ 18 /* Total size of Rel relocs */
+#define DT_RELENT 19 /* Size of one Rel reloc */
+#define DT_PLTREL 20 /* Type of reloc in PLT */
+#define DT_DEBUG 21 /* For debugging; unspecified */
+#define DT_TEXTREL 22 /* Reloc might modify .text */
+#define DT_JMPREL 23 /* Address of PLT relocs */
+#define DT_BIND_NOW 24 /* Process relocations of object */
+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
+#define DT_RUNPATH 29 /* Library search path */
+#define DT_FLAGS 30 /* Flags for the object being loaded */
+#define DT_ENCODING 32 /* Start of encoded range */
+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
+#define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */
+#define DT_NUM 35 /* Number used */
+#define DT_LOOS 0x6000000d /* Start of OS-specific */
+#define DT_HIOS 0x6ffff000 /* End of OS-specific */
+#define DT_LOPROC 0x70000000 /* Start of processor-specific */
+#define DT_HIPROC 0x7fffffff /* End of processor-specific */
+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
+
+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
+ approach. */
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
+#define DT_CHECKSUM 0x6ffffdf8
+#define DT_PLTPADSZ 0x6ffffdf9
+#define DT_MOVEENT 0x6ffffdfa
+#define DT_MOVESZ 0x6ffffdfb
+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
+ the following DT_* entry. */
+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
+#define DT_VALRNGHI 0x6ffffdff
+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
+#define DT_VALNUM 12
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+ If any adjustment is made to the ELF object after it has been
+ built these entries will need to be adjusted. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
+#define DT_TLSDESC_PLT 0x6ffffef6
+#define DT_TLSDESC_GOT 0x6ffffef7
+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
+#define DT_CONFIG 0x6ffffefa /* Configuration information. */
+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
+#define DT_AUDIT 0x6ffffefc /* Object auditing. */
+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
+#define DT_MOVETAB 0x6ffffefe /* Move table. */
+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
+#define DT_ADDRRNGHI 0x6ffffeff
+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
+#define DT_ADDRNUM 11
+
+/* The versioning entry types. The next are defined as part of the
+ GNU extension. */
+#define DT_VERSYM 0x6ffffff0
+
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+
+/* These were chosen by Sun. */
+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
+#define DT_VERDEF 0x6ffffffc /* Address of version definition
+ table */
+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
+#define DT_VERNEED 0x6ffffffe /* Address of table with needed
+ versions */
+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+ range. Be compatible. */
+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
+#define DT_FILTER 0x7fffffff /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM 3
+
+/* Values of `d_un.d_val' in the DT_FLAGS entry. */
+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */
+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */
+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */
+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */
+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */
+
+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
+ entry in the dynamic section. */
+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */
+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */
+#define DF_1_TRANS 0x00000200
+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */
+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */
+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */
+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/
+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */
+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */
+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */
+#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */
+#define DF_1_IGNMULDEF 0x00040000
+#define DF_1_NOKSYMS 0x00080000
+#define DF_1_NOHDR 0x00100000
+#define DF_1_EDITED 0x00200000 /* Object is modified after built. */
+#define DF_1_NORELOC 0x00400000
+#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */
+#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */
+#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */
+#define DF_1_STUB 0x04000000
+#define DF_1_PIE 0x08000000
+
+/* Flags for the feature selection in DT_FEATURE_1. */
+#define DTF_1_PARINIT 0x00000001
+#define DTF_1_CONFEXP 0x00000002
+
+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */
+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */
+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not
+ generally available. */
+
+/* Version definition sections. */
+
+typedef struct
+{
+ Elf32_Half vd_version; /* Version revision */
+ Elf32_Half vd_flags; /* Version information */
+ Elf32_Half vd_ndx; /* Version Index */
+ Elf32_Half vd_cnt; /* Number of associated aux entries */
+ Elf32_Word vd_hash; /* Version name hash value */
+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf32_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf32_Verdef;
+
+typedef struct
+{
+ Elf64_Half vd_version; /* Version revision */
+ Elf64_Half vd_flags; /* Version information */
+ Elf64_Half vd_ndx; /* Version Index */
+ Elf64_Half vd_cnt; /* Number of associated aux entries */
+ Elf64_Word vd_hash; /* Version name hash value */
+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf64_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf64_Verdef;
+
+
+/* Legal values for vd_version (version revision). */
+#define VER_DEF_NONE 0 /* No version */
+#define VER_DEF_CURRENT 1 /* Current version */
+#define VER_DEF_NUM 2 /* Given version number */
+
+/* Legal values for vd_flags (version information flags). */
+#define VER_FLG_BASE 0x1 /* Version definition of file itself */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+/* Versym symbol index values. */
+#define VER_NDX_LOCAL 0 /* Symbol is local. */
+#define VER_NDX_GLOBAL 1 /* Symbol is global. */
+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */
+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */
+
+/* Auxialiary version information. */
+
+typedef struct
+{
+ Elf32_Word vda_name; /* Version or dependency names */
+ Elf32_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf32_Verdaux;
+
+typedef struct
+{
+ Elf64_Word vda_name; /* Version or dependency names */
+ Elf64_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf64_Verdaux;
+
+
+/* Version dependency section. */
+
+typedef struct
+{
+ Elf32_Half vn_version; /* Version of structure */
+ Elf32_Half vn_cnt; /* Number of associated aux entries */
+ Elf32_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf32_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf32_Verneed;
+
+typedef struct
+{
+ Elf64_Half vn_version; /* Version of structure */
+ Elf64_Half vn_cnt; /* Number of associated aux entries */
+ Elf64_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf64_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf64_Verneed;
+
+
+/* Legal values for vn_version (version revision). */
+#define VER_NEED_NONE 0 /* No version */
+#define VER_NEED_CURRENT 1 /* Current version */
+#define VER_NEED_NUM 2 /* Given version number */
+
+/* Auxiliary needed version information. */
+
+typedef struct
+{
+ Elf32_Word vna_hash; /* Hash value of dependency name */
+ Elf32_Half vna_flags; /* Dependency specific information */
+ Elf32_Half vna_other; /* Unused */
+ Elf32_Word vna_name; /* Dependency name string offset */
+ Elf32_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf32_Vernaux;
+
+typedef struct
+{
+ Elf64_Word vna_hash; /* Hash value of dependency name */
+ Elf64_Half vna_flags; /* Dependency specific information */
+ Elf64_Half vna_other; /* Unused */
+ Elf64_Word vna_name; /* Dependency name string offset */
+ Elf64_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf64_Vernaux;
+
+
+/* Legal values for vna_flags. */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+
+/* Auxiliary vector. */
+
+/* This vector is normally only used by the program interpreter. The
+ usual definition in an ABI supplement uses the name auxv_t. The
+ vector is not usually defined in a standard <elf.h> file, but it
+ can't hurt. We rename it to avoid conflicts. The sizes of these
+ types are an arrangement between the exec server and the program
+ interpreter, so we don't fully specify them here. */
+
+typedef struct
+{
+ uint32_t a_type; /* Entry type */
+ union
+ {
+ uint32_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf32_auxv_t;
+
+typedef struct
+{
+ uint64_t a_type; /* Entry type */
+ union
+ {
+ uint64_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf64_auxv_t;
+
+/* Legal values for a_type (entry type). */
+
+#define AT_NULL 0 /* End of vector */
+#define AT_IGNORE 1 /* Entry should be ignored */
+#define AT_EXECFD 2 /* File descriptor of program */
+#define AT_PHDR 3 /* Program headers for program */
+#define AT_PHENT 4 /* Size of program header entry */
+#define AT_PHNUM 5 /* Number of program headers */
+#define AT_PAGESZ 6 /* System page size */
+#define AT_BASE 7 /* Base address of interpreter */
+#define AT_FLAGS 8 /* Flags */
+#define AT_ENTRY 9 /* Entry point of program */
+#define AT_NOTELF 10 /* Program is not ELF */
+#define AT_UID 11 /* Real uid */
+#define AT_EUID 12 /* Effective uid */
+#define AT_GID 13 /* Real gid */
+#define AT_EGID 14 /* Effective gid */
+#define AT_CLKTCK 17 /* Frequency of times() */
+
+/* Some more special a_type values describing the hardware. */
+#define AT_PLATFORM 15 /* String identifying platform. */
+#define AT_HWCAP 16 /* Machine-dependent hints about
+ processor capabilities. */
+
+/* This entry gives some information about the FPU initialization
+ performed by the kernel. */
+#define AT_FPUCW 18 /* Used FPU control word. */
+
+/* Cache block sizes. */
+#define AT_DCACHEBSIZE 19 /* Data cache block size. */
+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */
+
+/* A special ignored value for PPC, used by the kernel to control the
+ interpretation of the AUXV. Must be > 16. */
+#define AT_IGNOREPPC 22 /* Entry should be ignored. */
+
+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
+
+#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/
+
+#define AT_RANDOM 25 /* Address of 16 random bytes. */
+
+#define AT_HWCAP2 26 /* More machine-dependent hints about
+ processor capabilities. */
+
+#define AT_EXECFN 31 /* Filename of executable. */
+
+/* Pointer to the global system page used for system calls and other
+ nice things. */
+#define AT_SYSINFO 32
+#define AT_SYSINFO_EHDR 33
+
+/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains
+ log2 of line size; mask those to get cache size. */
+#define AT_L1I_CACHESHAPE 34
+#define AT_L1D_CACHESHAPE 35
+#define AT_L2_CACHESHAPE 36
+#define AT_L3_CACHESHAPE 37
+
+/* Shapes of the caches, with more room to describe them.
+ *GEOMETRY are comprised of cache line size in bytes in the bottom 16 bits
+ and the cache associativity in the next 16 bits. */
+#define AT_L1I_CACHESIZE 40
+#define AT_L1I_CACHEGEOMETRY 41
+#define AT_L1D_CACHESIZE 42
+#define AT_L1D_CACHEGEOMETRY 43
+#define AT_L2_CACHESIZE 44
+#define AT_L2_CACHEGEOMETRY 45
+#define AT_L3_CACHESIZE 46
+#define AT_L3_CACHEGEOMETRY 47
+
+#define AT_MINSIGSTKSZ 51 /* Stack needed for signal delivery
+ (AArch64). */
+
+/* Note section contents. Each entry in the note section begins with
+ a header of a fixed form. */
+
+typedef struct
+{
+ Elf32_Word n_namesz; /* Length of the note's name. */
+ Elf32_Word n_descsz; /* Length of the note's descriptor. */
+ Elf32_Word n_type; /* Type of the note. */
+} Elf32_Nhdr;
+
+typedef struct
+{
+ Elf64_Word n_namesz; /* Length of the note's name. */
+ Elf64_Word n_descsz; /* Length of the note's descriptor. */
+ Elf64_Word n_type; /* Type of the note. */
+} Elf64_Nhdr;
+
+/* Known names of notes. */
+
+/* Solaris entries in the note section have this name. */
+#define ELF_NOTE_SOLARIS "SUNW Solaris"
+
+/* Note entries for GNU systems have this name. */
+#define ELF_NOTE_GNU "GNU"
+
+
+/* Defined types of notes for Solaris. */
+
+/* Value of descriptor (one word) is desired pagesize for the binary. */
+#define ELF_NOTE_PAGESIZE_HINT 1
+
+
+/* Defined note types for GNU systems. */
+
+/* ABI information. The descriptor consists of words:
+ word 0: OS descriptor
+ word 1: major version of the ABI
+ word 2: minor version of the ABI
+ word 3: subminor version of the ABI
+*/
+#define NT_GNU_ABI_TAG 1
+#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */
+
+/* Known OSes. These values can appear in word 0 of an
+ NT_GNU_ABI_TAG note section entry. */
+#define ELF_NOTE_OS_LINUX 0
+#define ELF_NOTE_OS_GNU 1
+#define ELF_NOTE_OS_SOLARIS2 2
+#define ELF_NOTE_OS_FREEBSD 3
+
+/* Synthetic hwcap information. The descriptor begins with two words:
+ word 0: number of entries
+ word 1: bitmask of enabled entries
+ Then follow variable-length entries, one byte followed by a
+ '\0'-terminated hwcap name string. The byte gives the bit
+ number to test if enabled, (1U << bit) & bitmask. */
+#define NT_GNU_HWCAP 2
+
+/* Build ID bits as generated by ld --build-id.
+ The descriptor consists of any nonzero number of bytes. */
+#define NT_GNU_BUILD_ID 3
+
+/* Version note generated by GNU gold containing a version string. */
+#define NT_GNU_GOLD_VERSION 4
+
+/* Program property. */
+#define NT_GNU_PROPERTY_TYPE_0 5
+
+/* Note section name of program property. */
+#define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property"
+
+/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */
+
+/* Stack size. */
+#define GNU_PROPERTY_STACK_SIZE 1
+/* No copy relocation on protected data symbol. */
+#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2
+
+/* Processor-specific semantics, lo */
+#define GNU_PROPERTY_LOPROC 0xc0000000
+/* Processor-specific semantics, hi */
+#define GNU_PROPERTY_HIPROC 0xdfffffff
+/* Application-specific semantics, lo */
+#define GNU_PROPERTY_LOUSER 0xe0000000
+/* Application-specific semantics, hi */
+#define GNU_PROPERTY_HIUSER 0xffffffff
+
+/* The x86 instruction sets indicated by the corresponding bits are
+ used in program. Their support in the hardware is optional. */
+#define GNU_PROPERTY_X86_ISA_1_USED 0xc0000000
+/* The x86 instruction sets indicated by the corresponding bits are
+ used in program and they must be supported by the hardware. */
+#define GNU_PROPERTY_X86_ISA_1_NEEDED 0xc0000001
+/* X86 processor-specific features used in program. */
+#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002
+
+#define GNU_PROPERTY_X86_ISA_1_486 (1U << 0)
+#define GNU_PROPERTY_X86_ISA_1_586 (1U << 1)
+#define GNU_PROPERTY_X86_ISA_1_686 (1U << 2)
+#define GNU_PROPERTY_X86_ISA_1_SSE (1U << 3)
+#define GNU_PROPERTY_X86_ISA_1_SSE2 (1U << 4)
+#define GNU_PROPERTY_X86_ISA_1_SSE3 (1U << 5)
+#define GNU_PROPERTY_X86_ISA_1_SSSE3 (1U << 6)
+#define GNU_PROPERTY_X86_ISA_1_SSE4_1 (1U << 7)
+#define GNU_PROPERTY_X86_ISA_1_SSE4_2 (1U << 8)
+#define GNU_PROPERTY_X86_ISA_1_AVX (1U << 9)
+#define GNU_PROPERTY_X86_ISA_1_AVX2 (1U << 10)
+#define GNU_PROPERTY_X86_ISA_1_AVX512F (1U << 11)
+#define GNU_PROPERTY_X86_ISA_1_AVX512CD (1U << 12)
+#define GNU_PROPERTY_X86_ISA_1_AVX512ER (1U << 13)
+#define GNU_PROPERTY_X86_ISA_1_AVX512PF (1U << 14)
+#define GNU_PROPERTY_X86_ISA_1_AVX512VL (1U << 15)
+#define GNU_PROPERTY_X86_ISA_1_AVX512DQ (1U << 16)
+#define GNU_PROPERTY_X86_ISA_1_AVX512BW (1U << 17)
+
+/* This indicates that all executable sections are compatible with
+ IBT. */
+#define GNU_PROPERTY_X86_FEATURE_1_IBT (1U << 0)
+/* This indicates that all executable sections are compatible with
+ SHSTK. */
+#define GNU_PROPERTY_X86_FEATURE_1_SHSTK (1U << 1)
+
+/* Move records. */
+typedef struct
+{
+ Elf32_Xword m_value; /* Symbol value. */
+ Elf32_Word m_info; /* Size and index. */
+ Elf32_Word m_poffset; /* Symbol offset. */
+ Elf32_Half m_repeat; /* Repeat count. */
+ Elf32_Half m_stride; /* Stride info. */
+} Elf32_Move;
+
+typedef struct
+{
+ Elf64_Xword m_value; /* Symbol value. */
+ Elf64_Xword m_info; /* Size and index. */
+ Elf64_Xword m_poffset; /* Symbol offset. */
+ Elf64_Half m_repeat; /* Repeat count. */
+ Elf64_Half m_stride; /* Stride info. */
+} Elf64_Move;
+
+/* Macro to construct move records. */
+#define ELF32_M_SYM(info) ((info) >> 8)
+#define ELF32_M_SIZE(info) ((unsigned char) (info))
+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
+
+#define ELF64_M_SYM(info) ELF32_M_SYM (info)
+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
+
+
+/* Motorola 68k specific definitions. */
+
+/* Values for Elf32_Ehdr.e_flags. */
+#define EF_CPU32 0x00810000
+
+/* m68k relocs. */
+
+#define R_68K_NONE 0 /* No reloc */
+#define R_68K_32 1 /* Direct 32 bit */
+#define R_68K_16 2 /* Direct 16 bit */
+#define R_68K_8 3 /* Direct 8 bit */
+#define R_68K_PC32 4 /* PC relative 32 bit */
+#define R_68K_PC16 5 /* PC relative 16 bit */
+#define R_68K_PC8 6 /* PC relative 8 bit */
+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
+#define R_68K_GOT32O 10 /* 32 bit GOT offset */
+#define R_68K_GOT16O 11 /* 16 bit GOT offset */
+#define R_68K_GOT8O 12 /* 8 bit GOT offset */
+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
+#define R_68K_PLT32O 16 /* 32 bit PLT offset */
+#define R_68K_PLT16O 17 /* 16 bit PLT offset */
+#define R_68K_PLT8O 18 /* 8 bit PLT offset */
+#define R_68K_COPY 19 /* Copy symbol at runtime */
+#define R_68K_GLOB_DAT 20 /* Create GOT entry */
+#define R_68K_JMP_SLOT 21 /* Create PLT entry */
+#define R_68K_RELATIVE 22 /* Adjust by program base */
+#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */
+#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */
+#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */
+#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */
+#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */
+#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */
+#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */
+#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */
+#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */
+#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */
+#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */
+#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */
+#define R_68K_TLS_LE32 37 /* 32 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_LE16 38 /* 16 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_LE8 39 /* 8 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */
+#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */
+#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */
+/* Keep this the last entry. */
+#define R_68K_NUM 43
+
+/* Intel 80386 specific definitions. */
+
+/* i386 relocs. */
+
+#define R_386_NONE 0 /* No reloc */
+#define R_386_32 1 /* Direct 32 bit */
+#define R_386_PC32 2 /* PC relative 32 bit */
+#define R_386_GOT32 3 /* 32 bit GOT entry */
+#define R_386_PLT32 4 /* 32 bit PLT address */
+#define R_386_COPY 5 /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6 /* Create GOT entry */
+#define R_386_JMP_SLOT 7 /* Create PLT entry */
+#define R_386_RELATIVE 8 /* Adjust by program base */
+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
+#define R_386_32PLT 11
+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
+ block offset */
+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
+ offset */
+#define R_386_TLS_LE 17 /* Offset relative to static TLS
+ block */
+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
+ general dynamic thread local data */
+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
+ local dynamic thread local data
+ in LE code */
+#define R_386_16 20
+#define R_386_PC16 21
+#define R_386_8 22
+#define R_386_PC8 23
+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
+ thread local data */
+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL 26 /* Relocation for call to
+ __tls_get_addr() */
+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
+ thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
+ __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
+ block offset */
+#define R_386_TLS_LE_32 34 /* Negated offset relative to static
+ TLS block */
+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
+#define R_386_SIZE32 38 /* 32-bit symbol size */
+#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */
+#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS
+ descriptor for
+ relaxation. */
+#define R_386_TLS_DESC 41 /* TLS descriptor containing
+ pointer to code and to
+ argument, returning the TLS
+ offset for the symbol. */
+#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */
+#define R_386_GOT32X 43 /* Load from 32 bit GOT entry,
+ relaxable. */
+/* Keep this the last entry. */
+#define R_386_NUM 44
+
+/* SUN SPARC specific definitions. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */
+
+/* Values for Elf64_Ehdr.e_flags. */
+
+#define EF_SPARCV9_MM 3
+#define EF_SPARCV9_TSO 0
+#define EF_SPARCV9_PSO 1
+#define EF_SPARCV9_RMO 2
+#define EF_SPARC_LEDATA 0x800000 /* little endian data */
+#define EF_SPARC_EXT_MASK 0xFFFF00
+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */
+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */
+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */
+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */
+
+/* SPARC relocs. */
+
+#define R_SPARC_NONE 0 /* No reloc */
+#define R_SPARC_8 1 /* Direct 8 bit */
+#define R_SPARC_16 2 /* Direct 16 bit */
+#define R_SPARC_32 3 /* Direct 32 bit */
+#define R_SPARC_DISP8 4 /* PC relative 8 bit */
+#define R_SPARC_DISP16 5 /* PC relative 16 bit */
+#define R_SPARC_DISP32 6 /* PC relative 32 bit */
+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
+#define R_SPARC_HI22 9 /* High 22 bit */
+#define R_SPARC_22 10 /* Direct 22 bit */
+#define R_SPARC_13 11 /* Direct 13 bit */
+#define R_SPARC_LO10 12 /* Truncated 10 bit */
+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
+#define R_SPARC_COPY 19 /* Copy symbol at runtime */
+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
+#define R_SPARC_RELATIVE 22 /* Adjust by program base */
+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
+
+/* Additional Sparc64 relocs. */
+
+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
+#define R_SPARC_10 30 /* Direct 10 bit */
+#define R_SPARC_11 31 /* Direct 11 bit */
+#define R_SPARC_64 32 /* Direct 64 bit */
+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */
+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
+#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */
+#define R_SPARC_7 43 /* Direct 7 bit */
+#define R_SPARC_5 44 /* Direct 5 bit */
+#define R_SPARC_6 45 /* Direct 6 bit */
+#define R_SPARC_DISP64 46 /* PC relative 64 bit */
+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
+#define R_SPARC_HIX22 48 /* High 22 bit complemented */
+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
+#define R_SPARC_REGISTER 53 /* Global register usage */
+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
+#define R_SPARC_TLS_GD_HI22 56
+#define R_SPARC_TLS_GD_LO10 57
+#define R_SPARC_TLS_GD_ADD 58
+#define R_SPARC_TLS_GD_CALL 59
+#define R_SPARC_TLS_LDM_HI22 60
+#define R_SPARC_TLS_LDM_LO10 61
+#define R_SPARC_TLS_LDM_ADD 62
+#define R_SPARC_TLS_LDM_CALL 63
+#define R_SPARC_TLS_LDO_HIX22 64
+#define R_SPARC_TLS_LDO_LOX10 65
+#define R_SPARC_TLS_LDO_ADD 66
+#define R_SPARC_TLS_IE_HI22 67
+#define R_SPARC_TLS_IE_LO10 68
+#define R_SPARC_TLS_IE_LD 69
+#define R_SPARC_TLS_IE_LDX 70
+#define R_SPARC_TLS_IE_ADD 71
+#define R_SPARC_TLS_LE_HIX22 72
+#define R_SPARC_TLS_LE_LOX10 73
+#define R_SPARC_TLS_DTPMOD32 74
+#define R_SPARC_TLS_DTPMOD64 75
+#define R_SPARC_TLS_DTPOFF32 76
+#define R_SPARC_TLS_DTPOFF64 77
+#define R_SPARC_TLS_TPOFF32 78
+#define R_SPARC_TLS_TPOFF64 79
+#define R_SPARC_GOTDATA_HIX22 80
+#define R_SPARC_GOTDATA_LOX10 81
+#define R_SPARC_GOTDATA_OP_HIX22 82
+#define R_SPARC_GOTDATA_OP_LOX10 83
+#define R_SPARC_GOTDATA_OP 84
+#define R_SPARC_H34 85
+#define R_SPARC_SIZE32 86
+#define R_SPARC_SIZE64 87
+#define R_SPARC_WDISP10 88
+#define R_SPARC_JMP_IREL 248
+#define R_SPARC_IRELATIVE 249
+#define R_SPARC_GNU_VTINHERIT 250
+#define R_SPARC_GNU_VTENTRY 251
+#define R_SPARC_REV32 252
+/* Keep this the last entry. */
+#define R_SPARC_NUM 253
+
+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
+
+#define DT_SPARC_REGISTER 0x70000001
+#define DT_SPARC_NUM 2
+
+/* MIPS R3000 specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */
+#define EF_MIPS_PIC 2 /* Contains PIC code. */
+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */
+#define EF_MIPS_XGOT 8
+#define EF_MIPS_64BIT_WHIRL 16
+#define EF_MIPS_ABI2 32
+#define EF_MIPS_ABI_ON32 64
+#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */
+#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */
+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */
+
+/* Legal values for MIPS architecture level. */
+
+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */
+#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */
+#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */
+#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */
+
+/* The following are unofficial names and should not be used. */
+
+#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1
+#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2
+#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3
+#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4
+#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5
+#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32
+#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64
+
+/* Special section indices. */
+
+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */
+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */
+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */
+#define SHT_MIPS_MSYM 0x70000001
+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */
+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */
+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */
+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */
+#define SHT_MIPS_PACKAGE 0x70000007
+#define SHT_MIPS_PACKSYM 0x70000008
+#define SHT_MIPS_RELD 0x70000009
+#define SHT_MIPS_IFACE 0x7000000b
+#define SHT_MIPS_CONTENT 0x7000000c
+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
+#define SHT_MIPS_SHDR 0x70000010
+#define SHT_MIPS_FDESC 0x70000011
+#define SHT_MIPS_EXTSYM 0x70000012
+#define SHT_MIPS_DENSE 0x70000013
+#define SHT_MIPS_PDESC 0x70000014
+#define SHT_MIPS_LOCSYM 0x70000015
+#define SHT_MIPS_AUXSYM 0x70000016
+#define SHT_MIPS_OPTSYM 0x70000017
+#define SHT_MIPS_LOCSTR 0x70000018
+#define SHT_MIPS_LINE 0x70000019
+#define SHT_MIPS_RFDESC 0x7000001a
+#define SHT_MIPS_DELTASYM 0x7000001b
+#define SHT_MIPS_DELTAINST 0x7000001c
+#define SHT_MIPS_DELTACLASS 0x7000001d
+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
+#define SHT_MIPS_DELTADECL 0x7000001f
+#define SHT_MIPS_SYMBOL_LIB 0x70000020
+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
+#define SHT_MIPS_TRANSLATE 0x70000022
+#define SHT_MIPS_PIXIE 0x70000023
+#define SHT_MIPS_XLATE 0x70000024
+#define SHT_MIPS_XLATE_DEBUG 0x70000025
+#define SHT_MIPS_WHIRL 0x70000026
+#define SHT_MIPS_EH_REGION 0x70000027
+#define SHT_MIPS_XLATE_OLD 0x70000028
+#define SHT_MIPS_PDR_EXCEPTION 0x70000029
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */
+#define SHF_MIPS_MERGE 0x20000000
+#define SHF_MIPS_ADDR 0x40000000
+#define SHF_MIPS_STRINGS 0x80000000
+#define SHF_MIPS_NOSTRIP 0x08000000
+#define SHF_MIPS_LOCAL 0x04000000
+#define SHF_MIPS_NAMES 0x02000000
+#define SHF_MIPS_NODUPE 0x01000000
+
+
+/* Symbol tables. */
+
+/* MIPS specific values for `st_other'. */
+#define STO_MIPS_DEFAULT 0x0
+#define STO_MIPS_INTERNAL 0x1
+#define STO_MIPS_HIDDEN 0x2
+#define STO_MIPS_PROTECTED 0x3
+#define STO_MIPS_PLT 0x8
+#define STO_MIPS_SC_ALIGN_UNUSED 0xff
+
+/* MIPS specific values for `st_info'. */
+#define STB_MIPS_SPLIT_COMMON 13
+
+/* Entries found in sections of type SHT_MIPS_GPTAB. */
+
+typedef union
+{
+ struct
+ {
+ Elf32_Word gt_current_g_value; /* -G value used for compilation. */
+ Elf32_Word gt_unused; /* Not used. */
+ } gt_header; /* First entry in section. */
+ struct
+ {
+ Elf32_Word gt_g_value; /* If this value were used for -G. */
+ Elf32_Word gt_bytes; /* This many bytes would be used. */
+ } gt_entry; /* Subsequent entries in section. */
+} Elf32_gptab;
+
+/* Entry found in sections of type SHT_MIPS_REGINFO. */
+
+typedef struct
+{
+ Elf32_Word ri_gprmask; /* General registers used. */
+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */
+ Elf32_Sword ri_gp_value; /* $gp register value. */
+} Elf32_RegInfo;
+
+/* Entries found in sections of type SHT_MIPS_OPTIONS. */
+
+typedef struct
+{
+ unsigned char kind; /* Determines interpretation of the
+ variable part of descriptor. */
+ unsigned char size; /* Size of descriptor, including header. */
+ Elf32_Section section; /* Section header index of section affected,
+ 0 for global options. */
+ Elf32_Word info; /* Kind-specific information. */
+} Elf_Options;
+
+/* Values for `kind' field in Elf_Options. */
+
+#define ODK_NULL 0 /* Undefined. */
+#define ODK_REGINFO 1 /* Register usage information. */
+#define ODK_EXCEPTIONS 2 /* Exception processing options. */
+#define ODK_PAD 3 /* Section padding options. */
+#define ODK_HWPATCH 4 /* Hardware workarounds performed */
+#define ODK_FILL 5 /* record the fill value used by the linker. */
+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
+
+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
+
+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
+#define OEX_SMM 0x20000 /* Force sequential memory mode? */
+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
+#define OEX_PRECISEFP OEX_FPDBUG
+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
+
+#define OEX_FPU_INVAL 0x10
+#define OEX_FPU_DIV0 0x08
+#define OEX_FPU_OFLO 0x04
+#define OEX_FPU_UFLO 0x02
+#define OEX_FPU_INEX 0x01
+
+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
+
+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
+
+#define OPAD_PREFIX 0x1
+#define OPAD_POSTFIX 0x2
+#define OPAD_SYMBOL 0x4
+
+/* Entry found in `.options' section. */
+
+typedef struct
+{
+ Elf32_Word hwp_flags1; /* Extra flags. */
+ Elf32_Word hwp_flags2; /* Extra flags. */
+} Elf_Options_Hw;
+
+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
+
+#define OHWA0_R4KEOP_CHECKED 0x00000001
+#define OHWA1_R4KEOP_CLEAN 0x00000002
+
+/* MIPS relocs. */
+
+#define R_MIPS_NONE 0 /* No reloc */
+#define R_MIPS_16 1 /* Direct 16 bit */
+#define R_MIPS_32 2 /* Direct 32 bit */
+#define R_MIPS_REL32 3 /* PC relative 32 bit */
+#define R_MIPS_26 4 /* Direct 26 bit shifted */
+#define R_MIPS_HI16 5 /* High 16 bit */
+#define R_MIPS_LO16 6 /* Low 16 bit */
+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
+#define R_MIPS_PC16 10 /* PC relative 16 bit */
+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
+
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+#define R_MIPS_GOT_HI16 22
+#define R_MIPS_GOT_LO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+#define R_MIPS_CALL_HI16 30
+#define R_MIPS_CALL_LO16 31
+#define R_MIPS_SCN_DISP 32
+#define R_MIPS_REL16 33
+#define R_MIPS_ADD_IMMEDIATE 34
+#define R_MIPS_PJUMP 35
+#define R_MIPS_RELGOT 36
+#define R_MIPS_JALR 37
+#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */
+#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */
+#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */
+#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */
+#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */
+#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */
+#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */
+#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */
+#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */
+#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */
+#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
+#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
+#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
+#define R_MIPS_GLOB_DAT 51
+#define R_MIPS_COPY 126
+#define R_MIPS_JUMP_SLOT 127
+/* Keep this the last entry. */
+#define R_MIPS_NUM 128
+
+/* Legal values for p_type field of Elf32_Phdr. */
+
+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */
+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
+#define PT_MIPS_OPTIONS 0x70000002
+#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */
+
+/* Special program header types. */
+
+#define PF_MIPS_LOCAL 0x10000000
+
+/* Legal values for d_tag field of Elf32_Dyn. */
+
+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
+#define DT_MIPS_FLAGS 0x70000005 /* Flags */
+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
+#define DT_MIPS_MSYM 0x70000007
+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
+ DT_MIPS_DELTA_CLASS. */
+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
+ DT_MIPS_DELTA_INSTANCE. */
+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
+ DT_MIPS_DELTA_RELOC. */
+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
+ relocations refer to. */
+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
+ DT_MIPS_DELTA_SYM. */
+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
+ class declaration. */
+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
+ DT_MIPS_DELTA_CLASSSYM. */
+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
+#define DT_MIPS_PIXIE_INIT 0x70000023
+#define DT_MIPS_SYMBOL_LIB 0x70000024
+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
+#define DT_MIPS_LOCAL_GOTIDX 0x70000026
+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
+ function stored in GOT. */
+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
+ by rld on dlopen() calls. */
+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
+/* The address of .got.plt in an executable using the new non-PIC ABI. */
+#define DT_MIPS_PLTGOT 0x70000032
+/* The base of the PLT in an executable using the new non-PIC ABI if that
+ PLT is writable. For a non-writable PLT, this is omitted or has a zero
+ value. */
+#define DT_MIPS_RWPLT 0x70000034
+/* An alternative description of the classic MIPS RLD_MAP that is usable
+ in a PIE as it stores a relative offset from the address of the tag
+ rather than an absolute address. */
+#define DT_MIPS_RLD_MAP_REL 0x70000035
+#define DT_MIPS_NUM 0x36
+
+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
+
+#define RHF_NONE 0 /* No flags */
+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
+#define RHF_NO_MOVE (1 << 3)
+#define RHF_SGI_ONLY (1 << 4)
+#define RHF_GUARANTEE_INIT (1 << 5)
+#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
+#define RHF_GUARANTEE_START_INIT (1 << 7)
+#define RHF_PIXIE (1 << 8)
+#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
+#define RHF_REQUICKSTART (1 << 10)
+#define RHF_REQUICKSTARTED (1 << 11)
+#define RHF_CORD (1 << 12)
+#define RHF_NO_UNRES_UNDEF (1 << 13)
+#define RHF_RLD_ORDER_SAFE (1 << 14)
+
+/* Entries found in sections of type SHT_MIPS_LIBLIST. */
+
+typedef struct
+{
+ Elf32_Word l_name; /* Name (string table index) */
+ Elf32_Word l_time_stamp; /* Timestamp */
+ Elf32_Word l_checksum; /* Checksum */
+ Elf32_Word l_version; /* Interface version */
+ Elf32_Word l_flags; /* Flags */
+} Elf32_Lib;
+
+typedef struct
+{
+ Elf64_Word l_name; /* Name (string table index) */
+ Elf64_Word l_time_stamp; /* Timestamp */
+ Elf64_Word l_checksum; /* Checksum */
+ Elf64_Word l_version; /* Interface version */
+ Elf64_Word l_flags; /* Flags */
+} Elf64_Lib;
+
+
+/* Legal values for l_flags. */
+
+#define LL_NONE 0
+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
+#define LL_REQUIRE_MINOR (1 << 2)
+#define LL_EXPORTS (1 << 3)
+#define LL_DELAY_LOAD (1 << 4)
+#define LL_DELTA (1 << 5)
+
+/* Entries found in sections of type SHT_MIPS_CONFLICT. */
+
+typedef Elf32_Addr Elf32_Conflict;
+
+typedef struct
+{
+ /* Version of flags structure. */
+ Elf32_Half version;
+ /* The level of the ISA: 1-5, 32, 64. */
+ unsigned char isa_level;
+ /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
+ unsigned char isa_rev;
+ /* The size of general purpose registers. */
+ unsigned char gpr_size;
+ /* The size of co-processor 1 registers. */
+ unsigned char cpr1_size;
+ /* The size of co-processor 2 registers. */
+ unsigned char cpr2_size;
+ /* The floating-point ABI. */
+ unsigned char fp_abi;
+ /* Processor-specific extension. */
+ Elf32_Word isa_ext;
+ /* Mask of ASEs used. */
+ Elf32_Word ases;
+ /* Mask of general flags. */
+ Elf32_Word flags1;
+ Elf32_Word flags2;
+} Elf_MIPS_ABIFlags_v0;
+
+/* Values for the register size bytes of an abi flags structure. */
+
+#define MIPS_AFL_REG_NONE 0x00 /* No registers. */
+#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */
+#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */
+#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */
+
+/* Masks for the ases word of an ABI flags structure. */
+
+#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */
+#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */
+#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */
+#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */
+#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */
+#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */
+#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */
+#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */
+#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */
+#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */
+#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */
+#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */
+#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */
+#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */
+
+/* Values for the isa_ext word of an ABI flags structure. */
+
+#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */
+#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */
+#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */
+#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */
+#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */
+#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */
+#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */
+#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */
+#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */
+#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */
+#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */
+#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */
+#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */
+#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */
+#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */
+#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */
+#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */
+#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */
+
+/* Masks for the flags1 word of an ABI flags structure. */
+#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */
+
+/* Object attribute values. */
+enum
+{
+ /* Not tagged or not using any ABIs affected by the differences. */
+ Val_GNU_MIPS_ABI_FP_ANY = 0,
+ /* Using hard-float -mdouble-float. */
+ Val_GNU_MIPS_ABI_FP_DOUBLE = 1,
+ /* Using hard-float -msingle-float. */
+ Val_GNU_MIPS_ABI_FP_SINGLE = 2,
+ /* Using soft-float. */
+ Val_GNU_MIPS_ABI_FP_SOFT = 3,
+ /* Using -mips32r2 -mfp64. */
+ Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
+ /* Using -mfpxx. */
+ Val_GNU_MIPS_ABI_FP_XX = 5,
+ /* Using -mips32r2 -mfp64. */
+ Val_GNU_MIPS_ABI_FP_64 = 6,
+ /* Using -mips32r2 -mfp64 -mno-odd-spreg. */
+ Val_GNU_MIPS_ABI_FP_64A = 7,
+ /* Maximum allocated FP ABI value. */
+ Val_GNU_MIPS_ABI_FP_MAX = 7
+};
+
+/* HPPA specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
+ prediction. */
+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
+
+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
+
+/* Additional section indeces. */
+
+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
+ symbols in ANSI C. */
+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
+
+#define STT_HP_OPAQUE (STT_LOOS + 0x1)
+#define STT_HP_STUB (STT_LOOS + 0x2)
+
+/* HPPA relocs. */
+
+#define R_PARISC_NONE 0 /* No reloc. */
+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64 64 /* 64 bits function address. */
+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
+#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */
+#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */
+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LORESERVE 128
+#define R_PARISC_COPY 128 /* Copy relocation. */
+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_GNU_VTENTRY 232
+#define R_PARISC_GNU_VTINHERIT 233
+#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */
+#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */
+#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */
+#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */
+#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */
+#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */
+#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */
+#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */
+#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */
+#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */
+#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */
+#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */
+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L
+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R
+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L
+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R
+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32
+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64
+#define R_PARISC_HIRESERVE 255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PT_HP_TLS (PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
+#define PT_HP_PARALLEL (PT_LOOS + 0x10)
+#define PT_HP_FASTBIND (PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
+#define PT_HP_STACK (PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT 0x70000000
+#define PT_PARISC_UNWIND 0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PF_PARISC_SBP 0x08000000
+
+#define PF_HP_PAGE_SIZE 0x00100000
+#define PF_HP_FAR_SHARED 0x00200000
+#define PF_HP_NEAR_SHARED 0x00400000
+#define PF_HP_CODE 0x01000000
+#define PF_HP_MODIFY 0x02000000
+#define PF_HP_LAZYSWAP 0x04000000
+#define PF_HP_SBP 0x08000000
+
+
+/* Alpha specific definitions. */
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
+
+/* Legal values for sh_type field of Elf64_Shdr. */
+
+/* These two are primerily concerned with ECOFF debugging info. */
+#define SHT_ALPHA_DEBUG 0x70000001
+#define SHT_ALPHA_REGINFO 0x70000002
+
+/* Legal values for sh_flags field of Elf64_Shdr. */
+
+#define SHF_ALPHA_GPREL 0x10000000
+
+/* Legal values for st_other field of Elf64_Sym. */
+#define STO_ALPHA_NOPV 0x80 /* No PV required. */
+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
+
+/* Alpha relocs. */
+
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+#define R_ALPHA_TLS_GD_HI 28
+#define R_ALPHA_TLSGD 29
+#define R_ALPHA_TLS_LDM 30
+#define R_ALPHA_DTPMOD64 31
+#define R_ALPHA_GOTDTPREL 32
+#define R_ALPHA_DTPREL64 33
+#define R_ALPHA_DTPRELHI 34
+#define R_ALPHA_DTPRELLO 35
+#define R_ALPHA_DTPREL16 36
+#define R_ALPHA_GOTTPREL 37
+#define R_ALPHA_TPREL64 38
+#define R_ALPHA_TPRELHI 39
+#define R_ALPHA_TPRELLO 40
+#define R_ALPHA_TPREL16 41
+/* Keep this the last entry. */
+#define R_ALPHA_NUM 46
+
+/* Magic values of the LITUSE relocation addend. */
+#define LITUSE_ALPHA_ADDR 0
+#define LITUSE_ALPHA_BASE 1
+#define LITUSE_ALPHA_BYTOFF 2
+#define LITUSE_ALPHA_JSR 3
+#define LITUSE_ALPHA_TLS_GD 4
+#define LITUSE_ALPHA_TLS_LDM 5
+
+/* Legal values for d_tag of Elf64_Dyn. */
+#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
+#define DT_ALPHA_NUM 1
+
+/* PowerPC specific declarations */
+
+/* Values for Elf32/64_Ehdr.e_flags. */
+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
+
+/* Cygnus local bits below */
+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
+ flag */
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE 0
+#define R_PPC_ADDR32 1 /* 32bit absolute address */
+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
+#define R_PPC_ADDR16 3 /* 16bit absolute address */
+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN 8
+#define R_PPC_ADDR14_BRNTAKEN 9
+#define R_PPC_REL24 10 /* PC relative 26 bit */
+#define R_PPC_REL14 11 /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN 12
+#define R_PPC_REL14_BRNTAKEN 13
+#define R_PPC_GOT16 14
+#define R_PPC_GOT16_LO 15
+#define R_PPC_GOT16_HI 16
+#define R_PPC_GOT16_HA 17
+#define R_PPC_PLTREL24 18
+#define R_PPC_COPY 19
+#define R_PPC_GLOB_DAT 20
+#define R_PPC_JMP_SLOT 21
+#define R_PPC_RELATIVE 22
+#define R_PPC_LOCAL24PC 23
+#define R_PPC_UADDR32 24
+#define R_PPC_UADDR16 25
+#define R_PPC_REL32 26
+#define R_PPC_PLT32 27
+#define R_PPC_PLTREL32 28
+#define R_PPC_PLT16_LO 29
+#define R_PPC_PLT16_HI 30
+#define R_PPC_PLT16_HA 31
+#define R_PPC_SDAREL16 32
+#define R_PPC_SECTOFF 33
+#define R_PPC_SECTOFF_LO 34
+#define R_PPC_SECTOFF_HI 35
+#define R_PPC_SECTOFF_HA 36
+
+/* PowerPC relocations defined for the TLS access ABI. */
+#define R_PPC_TLS 67 /* none (sym+add)@tls */
+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */
+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */
+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */
+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */
+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */
+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */
+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */
+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
+#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */
+#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */
+
+/* The remaining relocs are from the Embedded ELF ABI, and are not
+ in the SVR4 ELF ABI. */
+#define R_PPC_EMB_NADDR32 101
+#define R_PPC_EMB_NADDR16 102
+#define R_PPC_EMB_NADDR16_LO 103
+#define R_PPC_EMB_NADDR16_HI 104
+#define R_PPC_EMB_NADDR16_HA 105
+#define R_PPC_EMB_SDAI16 106
+#define R_PPC_EMB_SDA2I16 107
+#define R_PPC_EMB_SDA2REL 108
+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
+#define R_PPC_EMB_MRKREF 110
+#define R_PPC_EMB_RELSEC16 111
+#define R_PPC_EMB_RELST_LO 112
+#define R_PPC_EMB_RELST_HI 113
+#define R_PPC_EMB_RELST_HA 114
+#define R_PPC_EMB_BIT_FLD 115
+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
+
+/* Diab tool relocations. */
+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
+
+/* GNU extension to support local ifunc. */
+#define R_PPC_IRELATIVE 248
+
+/* GNU relocs used in PIC code sequences. */
+#define R_PPC_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */
+
+/* This is a phony reloc to handle any old fashioned TOC16 references
+ that may still be in object files. */
+#define R_PPC_TOC16 255
+
+/* PowerPC specific values for the Dyn d_tag field. */
+#define DT_PPC_GOT (DT_LOPROC + 0)
+#define DT_PPC_OPT (DT_LOPROC + 1)
+#define DT_PPC_NUM 2
+
+/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */
+#define PPC_OPT_TLS 1
+
+/* PowerPC64 relocations defined by the ABIs */
+#define R_PPC64_NONE R_PPC_NONE
+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */
+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */
+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */
+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */
+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */
+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */
+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */
+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */
+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
+#define R_PPC64_GOT16 R_PPC_GOT16
+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
+
+#define R_PPC64_COPY R_PPC_COPY
+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
+#define R_PPC64_RELATIVE R_PPC_RELATIVE
+
+#define R_PPC64_UADDR32 R_PPC_UADDR32
+#define R_PPC64_UADDR16 R_PPC_UADDR16
+#define R_PPC64_REL32 R_PPC_REL32
+#define R_PPC64_PLT32 R_PPC_PLT32
+#define R_PPC64_PLTREL32 R_PPC_PLTREL32
+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
+
+#define R_PPC64_SECTOFF R_PPC_SECTOFF
+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */
+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */
+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */
+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */
+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */
+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */
+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */
+#define R_PPC64_PLT64 45 /* doubleword64 L + A */
+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */
+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */
+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */
+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */
+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */
+#define R_PPC64_TOC 51 /* doubleword64 .TOC */
+#define R_PPC64_PLTGOT16 52 /* half16* M + A */
+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */
+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */
+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */
+
+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */
+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */
+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */
+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */
+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */
+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */
+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */
+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */
+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */
+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */
+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */
+
+/* PowerPC64 relocations defined for the TLS access ABI. */
+#define R_PPC64_TLS 67 /* none (sym+add)@tls */
+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */
+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */
+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */
+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */
+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */
+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */
+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */
+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */
+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */
+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */
+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */
+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */
+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */
+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
+#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */
+#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */
+#define R_PPC64_TOCSAVE 109 /* none */
+
+/* Added when HA and HI relocs were changed to report overflows. */
+#define R_PPC64_ADDR16_HIGH 110
+#define R_PPC64_ADDR16_HIGHA 111
+#define R_PPC64_TPREL16_HIGH 112
+#define R_PPC64_TPREL16_HIGHA 113
+#define R_PPC64_DTPREL16_HIGH 114
+#define R_PPC64_DTPREL16_HIGHA 115
+
+/* GNU extension to support local ifunc. */
+#define R_PPC64_JMP_IREL 247
+#define R_PPC64_IRELATIVE 248
+#define R_PPC64_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */
+
+/* e_flags bits specifying ABI.
+ 1 for original function descriptor using ABI,
+ 2 for revised ABI without function descriptors,
+ 0 for unspecified or not using any features affected by the differences. */
+#define EF_PPC64_ABI 3
+
+/* PowerPC64 specific values for the Dyn d_tag field. */
+#define DT_PPC64_GLINK (DT_LOPROC + 0)
+#define DT_PPC64_OPD (DT_LOPROC + 1)
+#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
+#define DT_PPC64_OPT (DT_LOPROC + 3)
+#define DT_PPC64_NUM 4
+
+/* PowerPC64 specific bits in the DT_PPC64_OPT Dyn entry. */
+#define PPC64_OPT_TLS 1
+#define PPC64_OPT_MULTI_TOC 2
+#define PPC64_OPT_LOCALENTRY 4
+
+/* PowerPC64 specific values for the Elf64_Sym st_other field. */
+#define STO_PPC64_LOCAL_BIT 5
+#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
+#define PPC64_LOCAL_ENTRY_OFFSET(other) \
+ (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2)
+
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_ARM_RELEXEC 0x01
+#define EF_ARM_HASENTRY 0x02
+#define EF_ARM_INTERWORK 0x04
+#define EF_ARM_APCS_26 0x08
+#define EF_ARM_APCS_FLOAT 0x10
+#define EF_ARM_PIC 0x20
+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */
+#define EF_ARM_NEW_ABI 0x80
+#define EF_ARM_OLD_ABI 0x100
+#define EF_ARM_SOFT_FLOAT 0x200
+#define EF_ARM_VFP_FLOAT 0x400
+#define EF_ARM_MAVERICK_FLOAT 0x800
+
+#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */
+#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */
+
+
+/* Other constants defined in the ARM ELF spec. version B-01. */
+/* NB. These conflict with values defined above. */
+#define EF_ARM_SYMSARESORTED 0x04
+#define EF_ARM_DYNSYMSUSESEGIDX 0x08
+#define EF_ARM_MAPSYMSFIRST 0x10
+#define EF_ARM_EABIMASK 0XFF000000
+
+/* Constants defined in AAELF. */
+#define EF_ARM_BE8 0x00800000
+#define EF_ARM_LE8 0x00400000
+
+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
+#define EF_ARM_EABI_UNKNOWN 0x00000000
+#define EF_ARM_EABI_VER1 0x01000000
+#define EF_ARM_EABI_VER2 0x02000000
+#define EF_ARM_EABI_VER3 0x03000000
+#define EF_ARM_EABI_VER4 0x04000000
+#define EF_ARM_EABI_VER5 0x05000000
+
+/* Additional symbol types for Thumb. */
+#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */
+#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
+ in the input to a link step. */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB 0x10000000 /* Segment contains the location
+ addressed by the static base. */
+#define PF_ARM_PI 0x20000000 /* Position-independent segment. */
+#define PF_ARM_ABS 0x40000000 /* Absolute segment. */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */
+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */
+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
+
+
+/* AArch64 relocs. */
+
+#define R_AARCH64_NONE 0 /* No relocation. */
+
+/* ILP32 AArch64 relocs. */
+#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */
+#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */
+#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */
+#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */
+#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */
+#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */
+#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */
+#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */
+#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */
+#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */
+
+/* LP64 AArch64 relocs. */
+#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
+#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
+#define R_AARCH64_ABS16 259 /* Direct 16-bit. */
+#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */
+#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */
+#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */
+#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */
+#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */
+#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */
+#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */
+#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */
+#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */
+#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */
+#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */
+#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */
+#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */
+#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */
+#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */
+#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */
+#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */
+#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */
+#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */
+#define R_AARCH64_CALL26 283 /* Likewise for CALL. */
+#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */
+#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */
+#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */
+#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */
+#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */
+#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */
+#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */
+#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */
+#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */
+#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */
+#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */
+#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */
+#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */
+#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */
+#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */
+#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */
+#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */
+#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */
+#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */
+#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */
+#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */
+#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */
+#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */
+#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */
+#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */
+#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */
+#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */
+#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */
+#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */
+#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */
+#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */
+#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */
+#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */
+#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */
+#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */
+#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */
+#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */
+#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */
+#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */
+#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */
+#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */
+#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */
+#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */
+#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */
+#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */
+#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */
+#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */
+#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */
+#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */
+#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */
+#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */
+#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */
+#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */
+#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */
+#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */
+#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */
+#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */
+#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */
+#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */
+#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */
+#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */
+#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */
+#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */
+#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */
+#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */
+#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
+#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
+
+/* ARM relocs. */
+
+#define R_ARM_NONE 0 /* No reloc */
+#define R_ARM_PC24 1 /* Deprecated PC relative 26
+ bit branch. */
+#define R_ARM_ABS32 2 /* Direct 32 bit */
+#define R_ARM_REL32 3 /* PC relative 32 bit */
+#define R_ARM_PC13 4
+#define R_ARM_ABS16 5 /* Direct 16 bit */
+#define R_ARM_ABS12 6 /* Direct 12 bit */
+#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */
+#define R_ARM_ABS8 8 /* Direct 8 bit */
+#define R_ARM_SBREL32 9
+#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */
+#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC
+ (Thumb16 LDR, ADD, ADR). */
+#define R_ARM_AMP_VCALL9 12
+#define R_ARM_SWI24 13 /* Obsolete static relocation. */
+#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */
+#define R_ARM_THM_SWI8 14 /* Reserved. */
+#define R_ARM_XPC25 15 /* Reserved. */
+#define R_ARM_THM_XPC22 16 /* Reserved. */
+#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
+#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
+#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
+#define R_ARM_COPY 20 /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
+#define R_ARM_RELATIVE 23 /* Adjust by program base */
+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32 26 /* 32 bit GOT entry */
+#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */
+#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */
+#define R_ARM_JUMP24 29 /* PC relative 24 bit
+ (B, BL<cond>). */
+#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */
+#define R_ARM_BASE_ABS 31 /* Adjust by program base. */
+#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */
+#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */
+#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */
+#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */
+#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */
+#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */
+#define R_ARM_TARGET1 38
+#define R_ARM_SBREL31 39 /* Program base relative. */
+#define R_ARM_V4BX 40
+#define R_ARM_TARGET2 41
+#define R_ARM_PREL31 42 /* 32 bit PC relative. */
+#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */
+#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */
+#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */
+#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */
+#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */
+#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit
+ (Thumb32 MOVT). */
+#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit
+ (Thumb32 MOVW). */
+#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit
+ (Thumb32 MOVT). */
+#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit
+ (Thumb32 B<cond>.W). */
+#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E
+ (Thumb16 CBZ, CBNZ). */
+#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit
+ (Thumb32 ADR.W). */
+#define R_ARM_THM_PC12 54 /* PC relative 12 bit
+ (Thumb32 LDR{D,SB,H,SH}). */
+#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */
+#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */
+#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */
+#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */
+#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */
+#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */
+#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */
+#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */
+#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */
+#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H},
+ LDR{D,SB,H,SH}). */
+#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H},
+ LDR{D,SB,H,SH}). */
+#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H},
+ LDR{D,SB,H,SH}). */
+#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */
+#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */
+#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */
+#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */
+#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */
+#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */
+#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */
+#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */
+#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR,
+ STR, LDRB, STRB). */
+#define R_ARM_LDR_SB_G1 76 /* Program base relative
+ (LDR, STR, LDRB, STRB). */
+#define R_ARM_LDR_SB_G2 77 /* Program base relative
+ (LDR, STR, LDRB, STRB). */
+#define R_ARM_LDRS_SB_G0 78 /* Program base relative
+ (LDR, STR, LDRB, STRB). */
+#define R_ARM_LDRS_SB_G1 79 /* Program base relative
+ (LDR, STR, LDRB, STRB). */
+#define R_ARM_LDRS_SB_G2 80 /* Program base relative
+ (LDR, STR, LDRB, STRB). */
+#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */
+#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */
+#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */
+#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16
+ bit (MOVW). */
+#define R_ARM_MOVT_BREL 85 /* Program base relative high
+ 16 bit (MOVT). */
+#define R_ARM_MOVW_BREL 86 /* Program base relative 16
+ bit (MOVW). */
+#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16
+ bit (Thumb32 MOVW). */
+#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high
+ 16 bit (Thumb32 MOVT). */
+#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16
+ bit (Thumb32 MOVW). */
+#define R_ARM_TLS_GOTDESC 90
+#define R_ARM_TLS_CALL 91
+#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */
+#define R_ARM_THM_TLS_CALL 93
+#define R_ARM_PLT32_ABS 94
+#define R_ARM_GOT_ABS 95 /* GOT entry. */
+#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */
+#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT
+ origin (LDR). */
+#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative
+ to GOT origin (LDR, STR). */
+#define R_ARM_GOTRELAX 99
+#define R_ARM_GNU_VTENTRY 100
+#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */
+#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE
+ (Thumb16 B/B<cond>). */
+#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic
+ thread local data */
+#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic
+ thread local data */
+#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS
+ block */
+#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of
+ static TLS block offset */
+#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static
+ TLS block */
+#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS
+ block (LDR, STR). */
+#define R_ARM_TLS_LE12 110 /* 12 bit relative to static
+ TLS block (LDR, STR). */
+#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative
+ to GOT origin (LDR). */
+#define R_ARM_ME_TOO 128 /* Obsolete. */
+#define R_ARM_THM_TLS_DESCSEQ 129
+#define R_ARM_THM_TLS_DESCSEQ16 129
+#define R_ARM_THM_TLS_DESCSEQ32 130
+#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT
+ origin, 12 bit (Thumb32 LDR). */
+#define R_ARM_IRELATIVE 160
+#define R_ARM_RXPC25 249
+#define R_ARM_RSBREL32 250
+#define R_ARM_THM_RPC22 251
+#define R_ARM_RREL32 252
+#define R_ARM_RABS22 253
+#define R_ARM_RPC24 254
+#define R_ARM_RBASE 255
+/* Keep this the last entry. */
+#define R_ARM_NUM 256
+
+/* csky */
+#define R_CKCORE_NONE 0 /* no reloc */
+#define R_CKCORE_ADDR32 1 /* direct 32 bit (S + A) */
+#define R_CKCORE_PCRELIMM8BY4 2 /* disp ((S + A - P) >> 2) & 0xff */
+#define R_CKCORE_PCRELIMM11BY2 3 /* disp ((S + A - P) >> 1) & 0x7ff */
+#define R_CKCORE_PCREL32 5 /* 32-bit rel (S + A - P) */
+#define R_CKCORE_PCRELJSR_IMM11BY2 6 /* disp ((S + A - P) >>1) & 0x7ff */
+#define R_CKCORE_RELATIVE 9 /* 32 bit adjust program base(B + A)*/
+#define R_CKCORE_COPY 10 /* 32 bit adjust by program base */
+#define R_CKCORE_GLOB_DAT 11 /* off between got and sym (S) */
+#define R_CKCORE_JUMP_SLOT 12 /* PLT entry (S) */
+#define R_CKCORE_GOTOFF 13 /* offset to GOT (S + A - GOT) */
+#define R_CKCORE_GOTPC 14 /* PC offset to GOT (GOT + A - P) */
+#define R_CKCORE_GOT32 15 /* 32 bit GOT entry (G) */
+#define R_CKCORE_PLT32 16 /* 32 bit PLT entry (G) */
+#define R_CKCORE_ADDRGOT 17 /* GOT entry in GLOB_DAT (GOT + G) */
+#define R_CKCORE_ADDRPLT 18 /* PLT entry in GLOB_DAT (GOT + G) */
+#define R_CKCORE_PCREL_IMM26BY2 19 /* ((S + A - P) >> 1) & 0x3ffffff */
+#define R_CKCORE_PCREL_IMM16BY2 20 /* disp ((S + A - P) >> 1) & 0xffff */
+#define R_CKCORE_PCREL_IMM16BY4 21 /* disp ((S + A - P) >> 2) & 0xffff */
+#define R_CKCORE_PCREL_IMM10BY2 22 /* disp ((S + A - P) >> 1) & 0x3ff */
+#define R_CKCORE_PCREL_IMM10BY4 23 /* disp ((S + A - P) >> 2) & 0x3ff */
+#define R_CKCORE_ADDR_HI16 24 /* high & low 16 bit ADDR */
+ /* ((S + A) >> 16) & 0xffff */
+#define R_CKCORE_ADDR_LO16 25 /* (S + A) & 0xffff */
+#define R_CKCORE_GOTPC_HI16 26 /* high & low 16 bit GOTPC */
+ /* ((GOT + A - P) >> 16) & 0xffff */
+#define R_CKCORE_GOTPC_LO16 27 /* (GOT + A - P) & 0xffff */
+#define R_CKCORE_GOTOFF_HI16 28 /* high & low 16 bit GOTOFF */
+ /* ((S + A - GOT) >> 16) & 0xffff */
+#define R_CKCORE_GOTOFF_LO16 29 /* (S + A - GOT) & 0xffff */
+#define R_CKCORE_GOT12 30 /* 12 bit disp GOT entry (G) */
+#define R_CKCORE_GOT_HI16 31 /* high & low 16 bit GOT */
+ /* (G >> 16) & 0xffff */
+#define R_CKCORE_GOT_LO16 32 /* (G & 0xffff) */
+#define R_CKCORE_PLT12 33 /* 12 bit disp PLT entry (G) */
+#define R_CKCORE_PLT_HI16 34 /* high & low 16 bit PLT */
+ /* (G >> 16) & 0xffff */
+#define R_CKCORE_PLT_LO16 35 /* G & 0xffff */
+#define R_CKCORE_ADDRGOT_HI16 36 /* high & low 16 bit ADDRGOT */
+ /* (GOT + G * 4) & 0xffff */
+#define R_CKCORE_ADDRGOT_LO16 37 /* (GOT + G * 4) & 0xffff */
+#define R_CKCORE_ADDRPLT_HI16 38 /* high & low 16 bit ADDRPLT */
+ /* ((GOT + G * 4) >> 16) & 0xFFFF */
+#define R_CKCORE_ADDRPLT_LO16 39 /* (GOT+G*4) & 0xffff */
+#define R_CKCORE_PCREL_JSR_IMM26BY2 40 /* disp ((S+A-P) >>1) & x3ffffff */
+#define R_CKCORE_TOFFSET_LO16 41 /* (S+A-BTEXT) & 0xffff */
+#define R_CKCORE_DOFFSET_LO16 42 /* (S+A-BTEXT) & 0xffff */
+#define R_CKCORE_PCREL_IMM18BY2 43 /* disp ((S+A-P) >>1) & 0x3ffff */
+#define R_CKCORE_DOFFSET_IMM18 44 /* disp (S+A-BDATA) & 0x3ffff */
+#define R_CKCORE_DOFFSET_IMM18BY2 45 /* disp ((S+A-BDATA)>>1) & 0x3ffff */
+#define R_CKCORE_DOFFSET_IMM18BY4 46 /* disp ((S+A-BDATA)>>2) & 0x3ffff */
+#define R_CKCORE_GOT_IMM18BY4 48 /* disp (G >> 2) */
+#define R_CKCORE_PLT_IMM18BY4 49 /* disp (G >> 2) */
+#define R_CKCORE_PCREL_IMM7BY4 50 /* disp ((S+A-P) >>2) & 0x7f */
+#define R_CKCORE_TLS_LE32 51 /* 32 bit offset to TLS block */
+#define R_CKCORE_TLS_IE32 52
+#define R_CKCORE_TLS_GD32 53
+#define R_CKCORE_TLS_LDM32 54
+#define R_CKCORE_TLS_LDO32 55
+#define R_CKCORE_TLS_DTPMOD32 56
+#define R_CKCORE_TLS_DTPOFF32 57
+#define R_CKCORE_TLS_TPOFF32 58
+
+/* IA-64 specific declarations. */
+
+/* Processor specific flags for the Ehdr e_flags field. */
+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
+
+/* Processor specific flags for the Phdr p_flags field. */
+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field. */
+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field. */
+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
+#define DT_IA_64_NUM 1
+
+/* IA-64 relocations. */
+#define R_IA64_NONE 0x00 /* none */
+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY 0x84 /* copy relocation */
+#define R_IA64_SUB 0x85 /* Addend and symbol difference */
+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+
+/* SH specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_SH_MACH_MASK 0x1f
+#define EF_SH_UNKNOWN 0x0
+#define EF_SH1 0x1
+#define EF_SH2 0x2
+#define EF_SH3 0x3
+#define EF_SH_DSP 0x4
+#define EF_SH3_DSP 0x5
+#define EF_SH4AL_DSP 0x6
+#define EF_SH3E 0x8
+#define EF_SH4 0x9
+#define EF_SH2E 0xb
+#define EF_SH4A 0xc
+#define EF_SH2A 0xd
+#define EF_SH4_NOFPU 0x10
+#define EF_SH4A_NOFPU 0x11
+#define EF_SH4_NOMMU_NOFPU 0x12
+#define EF_SH2A_NOFPU 0x13
+#define EF_SH3_NOMMU 0x14
+#define EF_SH2A_SH4_NOFPU 0x15
+#define EF_SH2A_SH3_NOFPU 0x16
+#define EF_SH2A_SH4 0x17
+#define EF_SH2A_SH3E 0x18
+
+/* SH relocs. */
+#define R_SH_NONE 0
+#define R_SH_DIR32 1
+#define R_SH_REL32 2
+#define R_SH_DIR8WPN 3
+#define R_SH_IND12W 4
+#define R_SH_DIR8WPL 5
+#define R_SH_DIR8WPZ 6
+#define R_SH_DIR8BP 7
+#define R_SH_DIR8W 8
+#define R_SH_DIR8L 9
+#define R_SH_SWITCH16 25
+#define R_SH_SWITCH32 26
+#define R_SH_USES 27
+#define R_SH_COUNT 28
+#define R_SH_ALIGN 29
+#define R_SH_CODE 30
+#define R_SH_DATA 31
+#define R_SH_LABEL 32
+#define R_SH_SWITCH8 33
+#define R_SH_GNU_VTINHERIT 34
+#define R_SH_GNU_VTENTRY 35
+#define R_SH_TLS_GD_32 144
+#define R_SH_TLS_LD_32 145
+#define R_SH_TLS_LDO_32 146
+#define R_SH_TLS_IE_32 147
+#define R_SH_TLS_LE_32 148
+#define R_SH_TLS_DTPMOD32 149
+#define R_SH_TLS_DTPOFF32 150
+#define R_SH_TLS_TPOFF32 151
+#define R_SH_GOT32 160
+#define R_SH_PLT32 161
+#define R_SH_COPY 162
+#define R_SH_GLOB_DAT 163
+#define R_SH_JMP_SLOT 164
+#define R_SH_RELATIVE 165
+#define R_SH_GOTOFF 166
+#define R_SH_GOTPC 167
+/* Keep this the last entry. */
+#define R_SH_NUM 256
+
+/* S/390 specific definitions. */
+
+/* Valid values for the e_flags field. */
+
+#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */
+
+/* Additional s390 relocs */
+
+#define R_390_NONE 0 /* No reloc. */
+#define R_390_8 1 /* Direct 8 bit. */
+#define R_390_12 2 /* Direct 12 bit. */
+#define R_390_16 3 /* Direct 16 bit. */
+#define R_390_32 4 /* Direct 32 bit. */
+#define R_390_PC32 5 /* PC relative 32 bit. */
+#define R_390_GOT12 6 /* 12 bit GOT offset. */
+#define R_390_GOT32 7 /* 32 bit GOT offset. */
+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
+#define R_390_COPY 9 /* Copy symbol at runtime. */
+#define R_390_GLOB_DAT 10 /* Create GOT entry. */
+#define R_390_JMP_SLOT 11 /* Create PLT entry. */
+#define R_390_RELATIVE 12 /* Adjust by program base. */
+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
+#define R_390_GOT16 15 /* 16 bit GOT offset. */
+#define R_390_PC16 16 /* PC relative 16 bit. */
+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
+#define R_390_64 22 /* Direct 64 bit. */
+#define R_390_PC64 23 /* PC relative 64 bit. */
+#define R_390_GOT64 24 /* 64 bit GOT offset. */
+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL 38 /* Tag for function call in general
+ dynamic TLS code. */
+#define R_390_TLS_LDCALL 39 /* Tag for function call in local
+ dynamic TLS code. */
+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
+ block. */
+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
+ block. */
+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS
+ block. */
+#define R_390_20 57 /* Direct 20 bit. */
+#define R_390_GOT20 58 /* 20 bit GOT offset. */
+#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */
+#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS
+ block offset. */
+#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */
+/* Keep this the last entry. */
+#define R_390_NUM 62
+
+
+/* CRIS relocations. */
+#define R_CRIS_NONE 0
+#define R_CRIS_8 1
+#define R_CRIS_16 2
+#define R_CRIS_32 3
+#define R_CRIS_8_PCREL 4
+#define R_CRIS_16_PCREL 5
+#define R_CRIS_32_PCREL 6
+#define R_CRIS_GNU_VTINHERIT 7
+#define R_CRIS_GNU_VTENTRY 8
+#define R_CRIS_COPY 9
+#define R_CRIS_GLOB_DAT 10
+#define R_CRIS_JUMP_SLOT 11
+#define R_CRIS_RELATIVE 12
+#define R_CRIS_16_GOT 13
+#define R_CRIS_32_GOT 14
+#define R_CRIS_16_GOTPLT 15
+#define R_CRIS_32_GOTPLT 16
+#define R_CRIS_32_GOTREL 17
+#define R_CRIS_32_PLT_GOTREL 18
+#define R_CRIS_32_PLT_PCREL 19
+
+#define R_CRIS_NUM 20
+
+
+/* AMD x86-64 relocations. */
+#define R_X86_64_NONE 0 /* No reloc */
+#define R_X86_64_64 1 /* Direct 64 bit */
+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
+#define R_X86_64_PLT32 4 /* 32 bit PLT address */
+#define R_X86_64_COPY 5 /* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
+#define R_X86_64_RELATIVE 8 /* Adjust by program base */
+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative
+ offset to GOT */
+#define R_X86_64_32 10 /* Direct 32 bit zero extended */
+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
+#define R_X86_64_16 12 /* Direct 16 bit zero extended */
+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
+#define R_X86_64_8 14 /* Direct 8 bit sign extended */
+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */
+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */
+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */
+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset
+ to two GOT entries for GD symbol */
+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset
+ to two GOT entries for LD symbol */
+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */
+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset
+ to GOT entry for IE symbol */
+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */
+#define R_X86_64_PC64 24 /* PC relative 64 bit */
+#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */
+#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative
+ offset to GOT */
+#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */
+#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset
+ to GOT entry */
+#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */
+#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */
+#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset
+ to PLT entry */
+#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */
+#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */
+#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */
+#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS
+ descriptor. */
+#define R_X86_64_TLSDESC 36 /* TLS descriptor. */
+#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */
+#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */
+ /* 39 Reserved was R_X86_64_PC32_BND */
+ /* 40 Reserved was R_X86_64_PLT32_BND */
+#define R_X86_64_GOTPCRELX 41 /* Load from 32 bit signed pc relative
+ offset to GOT entry without REX
+ prefix, relaxable. */
+#define R_X86_64_REX_GOTPCRELX 42 /* Load from 32 bit signed pc relative
+ offset to GOT entry with REX prefix,
+ relaxable. */
+#define R_X86_64_NUM 43
+
+/* x86-64 sh_type values. */
+#define SHT_X86_64_UNWIND 0x70000001 /* Unwind information. */
+
+
+/* AM33 relocations. */
+#define R_MN10300_NONE 0 /* No reloc. */
+#define R_MN10300_32 1 /* Direct 32 bit. */
+#define R_MN10300_16 2 /* Direct 16 bit. */
+#define R_MN10300_8 3 /* Direct 8 bit. */
+#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */
+#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */
+#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */
+#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */
+#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */
+#define R_MN10300_24 9 /* Direct 24 bit. */
+#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */
+#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */
+#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */
+#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */
+#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */
+#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */
+#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */
+#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */
+#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */
+#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */
+#define R_MN10300_COPY 20 /* Copy symbol at runtime. */
+#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */
+#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */
+#define R_MN10300_RELATIVE 23 /* Adjust by program base. */
+#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */
+#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */
+#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */
+#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block
+ offset. */
+#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block
+ offset. */
+#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS
+ block. */
+#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */
+#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */
+#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */
+#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed
+ by linker relaxation. */
+#define R_MN10300_ALIGN 34 /* Alignment requirement for linker
+ relaxation. */
+#define R_MN10300_NUM 35
+
+
+/* M32R relocs. */
+#define R_M32R_NONE 0 /* No reloc. */
+#define R_M32R_16 1 /* Direct 16 bit. */
+#define R_M32R_32 2 /* Direct 32 bit. */
+#define R_M32R_24 3 /* Direct 24 bit. */
+#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */
+#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */
+#define R_M32R_LO16 9 /* Low 16 bit. */
+#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */
+#define R_M32R_GNU_VTINHERIT 11
+#define R_M32R_GNU_VTENTRY 12
+/* M32R relocs use SHT_RELA. */
+#define R_M32R_16_RELA 33 /* Direct 16 bit. */
+#define R_M32R_32_RELA 34 /* Direct 32 bit. */
+#define R_M32R_24_RELA 35 /* Direct 24 bit. */
+#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */
+#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */
+#define R_M32R_LO16_RELA 41 /* Low 16 bit */
+#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */
+#define R_M32R_RELA_GNU_VTINHERIT 43
+#define R_M32R_RELA_GNU_VTENTRY 44
+#define R_M32R_REL32 45 /* PC relative 32 bit. */
+
+#define R_M32R_GOT24 48 /* 24 bit GOT entry */
+#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */
+#define R_M32R_COPY 50 /* Copy symbol at runtime */
+#define R_M32R_GLOB_DAT 51 /* Create GOT entry */
+#define R_M32R_JMP_SLOT 52 /* Create PLT entry */
+#define R_M32R_RELATIVE 53 /* Adjust by program base */
+#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */
+#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */
+#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned
+ low */
+#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed
+ low */
+#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */
+#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to
+ GOT with unsigned low */
+#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to
+ GOT with signed low */
+#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to
+ GOT */
+#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT
+ with unsigned low */
+#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT
+ with signed low */
+#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */
+#define R_M32R_NUM 256 /* Keep this the last entry. */
+
+/* MicroBlaze relocations */
+#define R_MICROBLAZE_NONE 0 /* No reloc. */
+#define R_MICROBLAZE_32 1 /* Direct 32 bit. */
+#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */
+#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */
+#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */
+#define R_MICROBLAZE_64 5 /* Direct 64 bit. */
+#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */
+#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */
+#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */
+#define R_MICROBLAZE_64_NONE 9 /* No reloc. */
+#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */
+#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */
+#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */
+#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */
+#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */
+#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */
+#define R_MICROBLAZE_REL 16 /* Adjust by program base. */
+#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */
+#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */
+#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */
+#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */
+#define R_MICROBLAZE_COPY 21 /* Runtime copy. */
+#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */
+#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */
+#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */
+#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */
+#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */
+#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */
+#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */
+#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */
+
+/* Legal values for d_tag (dynamic entry type). */
+#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */
+
+/* Nios II relocations. */
+#define R_NIOS2_NONE 0 /* No reloc. */
+#define R_NIOS2_S16 1 /* Direct signed 16 bit. */
+#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */
+#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */
+#define R_NIOS2_CALL26 4 /* Direct call. */
+#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */
+#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */
+#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */
+#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */
+#define R_NIOS2_HI16 9 /* High 16 bit. */
+#define R_NIOS2_LO16 10 /* Low 16 bit. */
+#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */
+#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */
+#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */
+#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */
+#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */
+#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */
+#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */
+#define R_NIOS2_UJMP 18 /* Unconditional branch. */
+#define R_NIOS2_CJMP 19 /* Conditional branch. */
+#define R_NIOS2_CALLR 20 /* Indirect call through register. */
+#define R_NIOS2_ALIGN 21 /* Alignment requirement for
+ linker relaxation. */
+#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */
+#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */
+#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */
+#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */
+#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */
+#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */
+#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */
+#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */
+#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */
+#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */
+#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */
+#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */
+#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */
+#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */
+#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */
+#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */
+#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */
+#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */
+#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */
+#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */
+#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */
+#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */
+#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */
+#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */
+
+/* TILEPro relocations. */
+#define R_TILEPRO_NONE 0 /* No reloc */
+#define R_TILEPRO_32 1 /* Direct 32 bit */
+#define R_TILEPRO_16 2 /* Direct 16 bit */
+#define R_TILEPRO_8 3 /* Direct 8 bit */
+#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */
+#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */
+#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */
+#define R_TILEPRO_LO16 7 /* Low 16 bit */
+#define R_TILEPRO_HI16 8 /* High 16 bit */
+#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */
+#define R_TILEPRO_COPY 10 /* Copy relocation */
+#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */
+#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */
+#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */
+#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */
+#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */
+#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */
+#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */
+#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */
+#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */
+#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */
+#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */
+#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */
+#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */
+#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */
+#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */
+#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */
+#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */
+#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */
+#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */
+#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */
+#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */
+#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */
+#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */
+#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */
+#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */
+#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */
+#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */
+#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */
+#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */
+#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */
+#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */
+#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */
+#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */
+#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */
+#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */
+#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */
+#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */
+#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */
+/* Relocs 56-59 are currently not defined. */
+#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */
+#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */
+#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */
+#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */
+#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */
+#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */
+#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */
+#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */
+
+#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
+#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
+
+#define R_TILEPRO_NUM 130
+
+
+/* TILE-Gx relocations. */
+#define R_TILEGX_NONE 0 /* No reloc */
+#define R_TILEGX_64 1 /* Direct 64 bit */
+#define R_TILEGX_32 2 /* Direct 32 bit */
+#define R_TILEGX_16 3 /* Direct 16 bit */
+#define R_TILEGX_8 4 /* Direct 8 bit */
+#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */
+#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */
+#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */
+#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */
+#define R_TILEGX_HW0 9 /* hword 0 16-bit */
+#define R_TILEGX_HW1 10 /* hword 1 16-bit */
+#define R_TILEGX_HW2 11 /* hword 2 16-bit */
+#define R_TILEGX_HW3 12 /* hword 3 16-bit */
+#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */
+#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */
+#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */
+#define R_TILEGX_COPY 16 /* Copy relocation */
+#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */
+#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */
+#define R_TILEGX_RELATIVE 19 /* Adjust by program base */
+#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */
+#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */
+#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */
+#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */
+#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */
+#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */
+#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */
+#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */
+#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */
+#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */
+#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */
+#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */
+#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */
+#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */
+#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */
+#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */
+#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */
+#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */
+#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */
+#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */
+#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */
+#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */
+#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */
+#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */
+#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */
+#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */
+#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */
+#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */
+#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */
+#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */
+#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */
+#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */
+#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */
+#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */
+/* Relocs 90-91 are currently not defined. */
+#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */
+#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */
+/* Relocs 104-105 are currently not defined. */
+#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */
+#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */
+#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */
+#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */
+#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */
+#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */
+#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */
+#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */
+#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */
+#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */
+
+#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
+#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
+
+#define R_TILEGX_NUM 130
+
+/* RISC-V ELF Flags */
+#define EF_RISCV_RVC 0x0001
+#define EF_RISCV_FLOAT_ABI 0x0006
+#define EF_RISCV_FLOAT_ABI_SOFT 0x0000
+#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002
+#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004
+#define EF_RISCV_FLOAT_ABI_QUAD 0x0006
+
+/* RISC-V relocations. */
+#define R_RISCV_NONE 0
+#define R_RISCV_32 1
+#define R_RISCV_64 2
+#define R_RISCV_RELATIVE 3
+#define R_RISCV_COPY 4
+#define R_RISCV_JUMP_SLOT 5
+#define R_RISCV_TLS_DTPMOD32 6
+#define R_RISCV_TLS_DTPMOD64 7
+#define R_RISCV_TLS_DTPREL32 8
+#define R_RISCV_TLS_DTPREL64 9
+#define R_RISCV_TLS_TPREL32 10
+#define R_RISCV_TLS_TPREL64 11
+#define R_RISCV_BRANCH 16
+#define R_RISCV_JAL 17
+#define R_RISCV_CALL 18
+#define R_RISCV_CALL_PLT 19
+#define R_RISCV_GOT_HI20 20
+#define R_RISCV_TLS_GOT_HI20 21
+#define R_RISCV_TLS_GD_HI20 22
+#define R_RISCV_PCREL_HI20 23
+#define R_RISCV_PCREL_LO12_I 24
+#define R_RISCV_PCREL_LO12_S 25
+#define R_RISCV_HI20 26
+#define R_RISCV_LO12_I 27
+#define R_RISCV_LO12_S 28
+#define R_RISCV_TPREL_HI20 29
+#define R_RISCV_TPREL_LO12_I 30
+#define R_RISCV_TPREL_LO12_S 31
+#define R_RISCV_TPREL_ADD 32
+#define R_RISCV_ADD8 33
+#define R_RISCV_ADD16 34
+#define R_RISCV_ADD32 35
+#define R_RISCV_ADD64 36
+#define R_RISCV_SUB8 37
+#define R_RISCV_SUB16 38
+#define R_RISCV_SUB32 39
+#define R_RISCV_SUB64 40
+#define R_RISCV_GNU_VTINHERIT 41
+#define R_RISCV_GNU_VTENTRY 42
+#define R_RISCV_ALIGN 43
+#define R_RISCV_RVC_BRANCH 44
+#define R_RISCV_RVC_JUMP 45
+#define R_RISCV_RVC_LUI 46
+#define R_RISCV_GPREL_I 47
+#define R_RISCV_GPREL_S 48
+#define R_RISCV_TPREL_I 49
+#define R_RISCV_TPREL_S 50
+#define R_RISCV_RELAX 51
+#define R_RISCV_SUB6 52
+#define R_RISCV_SET6 53
+#define R_RISCV_SET8 54
+#define R_RISCV_SET16 55
+#define R_RISCV_SET32 56
+#define R_RISCV_32_PCREL 57
+
+#define R_RISCV_NUM 58
+
+/* BPF specific declarations. */
+
+#define R_BPF_NONE 0 /* No reloc */
+#define R_BPF_64_64 1
+#define R_BPF_64_32 10
+
+/* Imagination Meta specific relocations. */
+
+#define R_METAG_HIADDR16 0
+#define R_METAG_LOADDR16 1
+#define R_METAG_ADDR32 2 /* 32bit absolute address */
+#define R_METAG_NONE 3 /* No reloc */
+#define R_METAG_RELBRANCH 4
+#define R_METAG_GETSETOFF 5
+
+/* Backward compatability */
+#define R_METAG_REG32OP1 6
+#define R_METAG_REG32OP2 7
+#define R_METAG_REG32OP3 8
+#define R_METAG_REG16OP1 9
+#define R_METAG_REG16OP2 10
+#define R_METAG_REG16OP3 11
+#define R_METAG_REG32OP4 12
+
+#define R_METAG_HIOG 13
+#define R_METAG_LOOG 14
+
+#define R_METAG_REL8 15
+#define R_METAG_REL16 16
+
+/* GNU */
+#define R_METAG_GNU_VTINHERIT 30
+#define R_METAG_GNU_VTENTRY 31
+
+/* PIC relocations */
+#define R_METAG_HI16_GOTOFF 32
+#define R_METAG_LO16_GOTOFF 33
+#define R_METAG_GETSET_GOTOFF 34
+#define R_METAG_GETSET_GOT 35
+#define R_METAG_HI16_GOTPC 36
+#define R_METAG_LO16_GOTPC 37
+#define R_METAG_HI16_PLT 38
+#define R_METAG_LO16_PLT 39
+#define R_METAG_RELBRANCH_PLT 40
+#define R_METAG_GOTOFF 41
+#define R_METAG_PLT 42
+#define R_METAG_COPY 43
+#define R_METAG_JMP_SLOT 44
+#define R_METAG_RELATIVE 45
+#define R_METAG_GLOB_DAT 46
+
+/* TLS relocations */
+#define R_METAG_TLS_GD 47
+#define R_METAG_TLS_LDM 48
+#define R_METAG_TLS_LDO_HI16 49
+#define R_METAG_TLS_LDO_LO16 50
+#define R_METAG_TLS_LDO 51
+#define R_METAG_TLS_IE 52
+#define R_METAG_TLS_IENONPIC 53
+#define R_METAG_TLS_IENONPIC_HI16 54
+#define R_METAG_TLS_IENONPIC_LO16 55
+#define R_METAG_TLS_TPOFF 56
+#define R_METAG_TLS_DTPMOD 57
+#define R_METAG_TLS_DTPOFF 58
+#define R_METAG_TLS_LE 59
+#define R_METAG_TLS_LE_HI16 60
+#define R_METAG_TLS_LE_LO16 61
+
+/* NDS32 relocations. */
+#define R_NDS32_NONE 0
+#define R_NDS32_32_RELA 20
+#define R_NDS32_COPY 39
+#define R_NDS32_GLOB_DAT 40
+#define R_NDS32_JMP_SLOT 41
+#define R_NDS32_RELATIVE 42
+#define R_NDS32_TLS_TPOFF 102
+#define R_NDS32_TLS_DESC 119
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* elf.h */
diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn
index db5f4a5..4764604 100644
--- a/third_party/gtest/BUILD.gn
+++ b/third_party/gtest/BUILD.gn
@@ -28,12 +28,13 @@
"//testing/gmock",
]
}
-} else if (crashpad_is_in_fuchsia) {
+} else if (crashpad_is_in_dart || crashpad_is_in_fuchsia) {
group("gtest") {
testonly = true
public_deps = [
"//third_party/googletest:gtest",
]
+ public_configs = [ "../..:disable_ubsan" ]
}
group("gmock") {
testonly = true
@@ -56,6 +57,7 @@
testonly = true
sources = [
"gtest/googletest/include/gtest/gtest-death-test.h",
+ "gtest/googletest/include/gtest/gtest-matchers.h",
"gtest/googletest/include/gtest/gtest-message.h",
"gtest/googletest/include/gtest/gtest-param-test.h",
"gtest/googletest/include/gtest/gtest-printers.h",
@@ -71,18 +73,16 @@
"gtest/googletest/include/gtest/internal/gtest-death-test-internal.h",
"gtest/googletest/include/gtest/internal/gtest-filepath.h",
"gtest/googletest/include/gtest/internal/gtest-internal.h",
- "gtest/googletest/include/gtest/internal/gtest-linked_ptr.h",
- "gtest/googletest/include/gtest/internal/gtest-param-util-generated.h",
"gtest/googletest/include/gtest/internal/gtest-param-util.h",
"gtest/googletest/include/gtest/internal/gtest-port-arch.h",
"gtest/googletest/include/gtest/internal/gtest-port.h",
"gtest/googletest/include/gtest/internal/gtest-string.h",
- "gtest/googletest/include/gtest/internal/gtest-tuple.h",
"gtest/googletest/include/gtest/internal/gtest-type-util.h",
"gtest/googletest/src/gtest-all.cc",
"gtest/googletest/src/gtest-death-test.cc",
"gtest/googletest/src/gtest-filepath.cc",
"gtest/googletest/src/gtest-internal-inl.h",
+ "gtest/googletest/src/gtest-matchers.cc",
"gtest/googletest/src/gtest-port.cc",
"gtest/googletest/src/gtest-printers.cc",
"gtest/googletest/src/gtest-test-part.cc",
@@ -95,6 +95,11 @@
"//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
]
configs += [ ":gtest_private_config" ]
+ if (crashpad_is_fuchsia) {
+ deps = [
+ "../fuchsia",
+ ]
+ }
}
static_library("gtest_main") {
@@ -112,20 +117,20 @@
test("gtest_all_test") {
sources = [
- "gtest/googletest/test/gtest-death-test_test.cc",
- "gtest/googletest/test/gtest-filepath_test.cc",
- "gtest/googletest/test/gtest-linked_ptr_test.cc",
- "gtest/googletest/test/gtest-message_test.cc",
- "gtest/googletest/test/gtest-options_test.cc",
- "gtest/googletest/test/gtest-port_test.cc",
- "gtest/googletest/test/gtest-printers_test.cc",
- "gtest/googletest/test/gtest-test-part_test.cc",
+ "gtest/googletest/test/googletest-death-test-test.cc",
+ "gtest/googletest/test/googletest-filepath-test.cc",
+ "gtest/googletest/test/googletest-message-test.cc",
+ "gtest/googletest/test/googletest-options-test.cc",
+ "gtest/googletest/test/googletest-port-test.cc",
+ "gtest/googletest/test/googletest-printers-test.cc",
+ "gtest/googletest/test/googletest-test-part-test.cc",
"gtest/googletest/test/gtest-typed-test2_test.cc",
"gtest/googletest/test/gtest-typed-test_test.cc",
"gtest/googletest/test/gtest-typed-test_test.h",
"gtest/googletest/test/gtest_main_unittest.cc",
"gtest/googletest/test/gtest_pred_impl_unittest.cc",
"gtest/googletest/test/gtest_prod_test.cc",
+ "gtest/googletest/test/gtest_skip_test.cc",
"gtest/googletest/test/gtest_unittest.cc",
"gtest/googletest/test/production.cc",
"gtest/googletest/test/production.h",
@@ -156,7 +161,7 @@
test("gtest_listener_test") {
sources = [
- "gtest/googletest/test/gtest-listener_test.cc",
+ "gtest/googletest/test/googletest-listener-test.cc",
]
deps = [
":gtest",
@@ -174,9 +179,9 @@
test("gtest_param_test") {
sources = [
- "gtest/googletest/test/gtest-param-test2_test.cc",
- "gtest/googletest/test/gtest-param-test_test.cc",
- "gtest/googletest/test/gtest-param-test_test.h",
+ "gtest/googletest/test/googletest-param-test-test.cc",
+ "gtest/googletest/test/googletest-param-test-test.h",
+ "gtest/googletest/test/googletest-param-test2-test.cc",
]
configs -= [
"//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
@@ -284,21 +289,20 @@
sources = [
"gtest/googlemock/include/gmock/gmock-actions.h",
"gtest/googlemock/include/gmock/gmock-cardinalities.h",
+ "gtest/googlemock/include/gmock/gmock-function-mocker.h",
"gtest/googlemock/include/gmock/gmock-generated-actions.h",
- "gtest/googlemock/include/gmock/gmock-generated-function-mockers.h",
- "gtest/googlemock/include/gmock/gmock-generated-matchers.h",
- "gtest/googlemock/include/gmock/gmock-generated-nice-strict.h",
"gtest/googlemock/include/gmock/gmock-matchers.h",
"gtest/googlemock/include/gmock/gmock-more-actions.h",
"gtest/googlemock/include/gmock/gmock-more-matchers.h",
+ "gtest/googlemock/include/gmock/gmock-nice-strict.h",
"gtest/googlemock/include/gmock/gmock-spec-builders.h",
"gtest/googlemock/include/gmock/gmock.h",
"gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h",
"gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h",
"gtest/googlemock/include/gmock/internal/custom/gmock-port.h",
- "gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h",
"gtest/googlemock/include/gmock/internal/gmock-internal-utils.h",
"gtest/googlemock/include/gmock/internal/gmock-port.h",
+ "gtest/googlemock/include/gmock/internal/gmock-pp.h",
"gtest/googlemock/src/gmock-all.cc",
"gtest/googlemock/src/gmock-cardinalities.cc",
"gtest/googlemock/src/gmock-internal-utils.cc",
@@ -334,15 +338,16 @@
sources = [
"gtest/googlemock/test/gmock-actions_test.cc",
"gtest/googlemock/test/gmock-cardinalities_test.cc",
+ "gtest/googlemock/test/gmock-function-mocker_test.cc",
"gtest/googlemock/test/gmock-generated-actions_test.cc",
- "gtest/googlemock/test/gmock-generated-function-mockers_test.cc",
- "gtest/googlemock/test/gmock-generated-internal-utils_test.cc",
"gtest/googlemock/test/gmock-generated-matchers_test.cc",
"gtest/googlemock/test/gmock-internal-utils_test.cc",
"gtest/googlemock/test/gmock-matchers_test.cc",
"gtest/googlemock/test/gmock-more-actions_test.cc",
"gtest/googlemock/test/gmock-nice-strict_test.cc",
"gtest/googlemock/test/gmock-port_test.cc",
+ "gtest/googlemock/test/gmock-pp-string_test.cc",
+ "gtest/googlemock/test/gmock-pp_test.cc",
"gtest/googlemock/test/gmock-spec-builders_test.cc",
"gtest/googlemock/test/gmock_test.cc",
]
diff --git a/third_party/gtest/gmock.gyp b/third_party/gtest/gmock.gyp
index d53c925..10c0879 100644
--- a/third_party/gtest/gmock.gyp
+++ b/third_party/gtest/gmock.gyp
@@ -56,18 +56,18 @@
'sources': [
'<(gmock_dir)/include/gmock/gmock-actions.h',
'<(gmock_dir)/include/gmock/gmock-cardinalities.h',
+ '<(gmock_dir)/include/gmock/gmock-function-mocker.h',
'<(gmock_dir)/include/gmock/gmock-generated-actions.h',
- '<(gmock_dir)/include/gmock/gmock-generated-function-mockers.h',
- '<(gmock_dir)/include/gmock/gmock-generated-matchers.h',
- '<(gmock_dir)/include/gmock/gmock-generated-nice-strict.h',
'<(gmock_dir)/include/gmock/gmock-matchers.h',
'<(gmock_dir)/include/gmock/gmock-more-actions.h',
'<(gmock_dir)/include/gmock/gmock-more-matchers.h',
+ '<(gmock_dir)/include/gmock/gmock-nice-strict.h',
'<(gmock_dir)/include/gmock/gmock-spec-builders.h',
'<(gmock_dir)/include/gmock/gmock.h',
'<(gmock_dir)/include/gmock/internal/custom/gmock-generated-actions.h',
'<(gmock_dir)/include/gmock/internal/custom/gmock-matchers.h',
'<(gmock_dir)/include/gmock/internal/custom/gmock-port.h',
+ '<(gmock_dir)/include/gmock/internal/custom/gmock-pp.h',
'<(gmock_dir)/include/gmock/internal/gmock-generated-internal-utils.h',
'<(gmock_dir)/include/gmock/internal/gmock-internal-utils.h',
'<(gmock_dir)/include/gmock/internal/gmock-port.h',
@@ -156,15 +156,16 @@
'sources': [
'<(gmock_dir)/test/gmock-actions_test.cc',
'<(gmock_dir)/test/gmock-cardinalities_test.cc',
+ '<(gmock_dir)/test/gmock-function-mocker_test.cc',
'<(gmock_dir)/test/gmock-generated-actions_test.cc',
- '<(gmock_dir)/test/gmock-generated-function-mockers_test.cc',
- '<(gmock_dir)/test/gmock-generated-internal-utils_test.cc',
'<(gmock_dir)/test/gmock-generated-matchers_test.cc',
'<(gmock_dir)/test/gmock-internal-utils_test.cc',
'<(gmock_dir)/test/gmock-matchers_test.cc',
'<(gmock_dir)/test/gmock-more-actions_test.cc',
'<(gmock_dir)/test/gmock-nice-strict_test.cc',
'<(gmock_dir)/test/gmock-port_test.cc',
+ '<(gmock_dir)/test/gmock-pp-string_test.cc',
+ '<(gmock_dir)/test/gmock-pp_test.cc',
'<(gmock_dir)/test/gmock-spec-builders_test.cc',
'<(gmock_dir)/test/gmock_test.cc',
],
diff --git a/third_party/gtest/gtest.gyp b/third_party/gtest/gtest.gyp
index 5d93feb..aa6399b 100644
--- a/third_party/gtest/gtest.gyp
+++ b/third_party/gtest/gtest.gyp
@@ -67,6 +67,7 @@
],
'sources': [
'<(gtest_dir)/include/gtest/gtest-death-test.h',
+ '<(gtest_dir)/include/gtest/gtest-matchers.h',
'<(gtest_dir)/include/gtest/gtest-message.h',
'<(gtest_dir)/include/gtest/gtest-param-test.h',
'<(gtest_dir)/include/gtest/gtest-printers.h',
@@ -82,18 +83,17 @@
'<(gtest_dir)/include/gtest/internal/gtest-death-test-internal.h',
'<(gtest_dir)/include/gtest/internal/gtest-filepath.h',
'<(gtest_dir)/include/gtest/internal/gtest-internal.h',
- '<(gtest_dir)/include/gtest/internal/gtest-linked_ptr.h',
'<(gtest_dir)/include/gtest/internal/gtest-param-util-generated.h',
'<(gtest_dir)/include/gtest/internal/gtest-param-util.h',
'<(gtest_dir)/include/gtest/internal/gtest-port-arch.h',
'<(gtest_dir)/include/gtest/internal/gtest-port.h',
'<(gtest_dir)/include/gtest/internal/gtest-string.h',
- '<(gtest_dir)/include/gtest/internal/gtest-tuple.h',
'<(gtest_dir)/include/gtest/internal/gtest-type-util.h',
'<(gtest_dir)/src/gtest-all.cc',
'<(gtest_dir)/src/gtest-death-test.cc',
'<(gtest_dir)/src/gtest-filepath.cc',
'<(gtest_dir)/src/gtest-internal-inl.h',
+ '<(gtest_dir)/src/gtest-matchers.cc',
'<(gtest_dir)/src/gtest-port.cc',
'<(gtest_dir)/src/gtest-printers.cc',
'<(gtest_dir)/src/gtest-test-part.cc',
@@ -174,6 +174,7 @@
'<(gtest_dir)/test/gtest_main_unittest.cc',
'<(gtest_dir)/test/gtest_pred_impl_unittest.cc',
'<(gtest_dir)/test/gtest_prod_test.cc',
+ '<(gtest_dir)/test/gtest_skip_test.cc',
'<(gtest_dir)/test/gtest_unittest.cc',
'<(gtest_dir)/test/production.cc',
'<(gtest_dir)/test/production.h',
diff --git a/third_party/lss/BUILD.gn b/third_party/lss/BUILD.gn
new file mode 100644
index 0000000..c0652cd
--- /dev/null
+++ b/third_party/lss/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../build/crashpad_buildconfig.gni")
+
+config("lss_config") {
+ if (crashpad_is_in_chromium) {
+ defines = [ "CRASHPAD_LSS_SOURCE_EXTERNAL" ]
+ } else {
+ defines = [ "CRASHPAD_LSS_SOURCE_EMBEDDED" ]
+ }
+}
+
+source_set("lss") {
+ public_configs = [ ":lss_config" ]
+
+ sources = [ "lss.h" ]
+}
diff --git a/third_party/lss/README.crashpad b/third_party/lss/README.crashpad
new file mode 100644
index 0000000..c036ae0
--- /dev/null
+++ b/third_party/lss/README.crashpad
@@ -0,0 +1,16 @@
+Name: linux-syscall-support
+Short Name: lss
+URL: https://chromium.googlesource.com/linux-syscall-support/
+Revision: See DEPS
+License: BSD 3-clause
+License File: lss/linux-syscall-support.h
+Security Critical: yes
+
+Description:
+Every so often, projects need to directly embed Linux system calls instead of
+calling the implementations in the system runtime library. This project
+provides a header file that can be included into your application whenever you
+need to make direct system calls.
+
+Local Modifications:
+None.
diff --git a/third_party/lss/lss.gyp b/third_party/lss/lss.gyp
new file mode 100644
index 0000000..63b9b3a
--- /dev/null
+++ b/third_party/lss/lss.gyp
@@ -0,0 +1,27 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lss',
+ 'type': 'none',
+ 'sources': [ 'lss.h' ],
+ 'direct_dependent_settings': {
+ # gyp is only used in circumstances when the embedded lss is used.
+ 'defines': [ 'CRASHPAD_LSS_SOURCE_EMBEDDED' ]
+ },
+ }
+ ]
+}
diff --git a/third_party/lss/lss.h b/third_party/lss/lss.h
new file mode 100644
index 0000000..11209ff
--- /dev/null
+++ b/third_party/lss/lss.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_THIRD_PARTY_LSS_LSS_H_
+#define CRASHPAD_THIRD_PARTY_LSS_LSS_H_
+
+#if defined(CRASHPAD_LSS_SOURCE_EXTERNAL)
+#include "third_party/lss/linux_syscall_support.h"
+#elif defined(CRASHPAD_LSS_SOURCE_EMBEDDED)
+#include "third_party/lss/lss/linux_syscall_support.h"
+#else
+#error Unknown lss source
+#endif
+
+#endif // CRASHPAD_THIRD_PARTY_LSS_LSS_H_
diff --git a/third_party/mini_chromium/BUILD.gn b/third_party/mini_chromium/BUILD.gn
index e11d011..50d1513 100644
--- a/third_party/mini_chromium/BUILD.gn
+++ b/third_party/mini_chromium/BUILD.gn
@@ -16,13 +16,11 @@
group("base") {
if (crashpad_is_in_chromium) {
- public_deps = [
- "//base",
- ]
+ public_deps = [ "//base" ]
} else if (crashpad_is_standalone || crashpad_is_in_fuchsia) {
- public_deps = [
- "mini_chromium/base",
- ]
+ public_deps = [ "mini_chromium/base" ]
+ } else if (crashpad_is_in_dart) {
+ public_deps = [ "//third_party/mini_chromium/mini_chromium/base" ]
}
}
@@ -30,8 +28,6 @@
testonly = true
if (crashpad_is_in_chromium) {
- public_deps = [
- "//base/test:test_support",
- ]
+ public_deps = [ "//base/test:test_support" ]
}
}
diff --git a/third_party/xnu/APPLE_LICENSE b/third_party/xnu/APPLE_LICENSE
new file mode 100644
index 0000000..fe81a60
--- /dev/null
+++ b/third_party/xnu/APPLE_LICENSE
@@ -0,0 +1,367 @@
+APPLE PUBLIC SOURCE LICENSE
+Version 2.0 - August 6, 2003
+
+Please read this License carefully before downloading this software.
+By downloading or using this software, you are agreeing to be bound by
+the terms of this License. If you do not or cannot agree to the terms
+of this License, please do not download or use the software.
+
+1. General; Definitions. This License applies to any program or other
+work which Apple Computer, Inc. ("Apple") makes publicly available and
+which contains a notice placed by Apple identifying such program or
+work as "Original Code" and stating that it is subject to the terms of
+this Apple Public Source License version 2.0 ("License"). As used in
+this License:
+
+1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
+the grantor of rights, (i) claims of patents that are now or hereafter
+acquired, owned by or assigned to Apple and (ii) that cover subject
+matter contained in the Original Code, but only to the extent
+necessary to use, reproduce and/or distribute the Original Code
+without infringement; and (b) in the case where You are the grantor of
+rights, (i) claims of patents that are now or hereafter acquired,
+owned by or assigned to You and (ii) that cover subject matter in Your
+Modifications, taken alone or in combination with Original Code.
+
+1.2 "Contributor" means any person or entity that creates or
+contributes to the creation of Modifications.
+
+1.3 "Covered Code" means the Original Code, Modifications, the
+combination of Original Code and any Modifications, and/or any
+respective portions thereof.
+
+1.4 "Externally Deploy" means: (a) to sublicense, distribute or
+otherwise make Covered Code available, directly or indirectly, to
+anyone other than You; and/or (b) to use Covered Code, alone or as
+part of a Larger Work, in any way to provide a service, including but
+not limited to delivery of content, through electronic communication
+with a client other than You.
+
+1.5 "Larger Work" means a work which combines Covered Code or portions
+thereof with code not governed by the terms of this License.
+
+1.6 "Modifications" mean any addition to, deletion from, and/or change
+to, the substance and/or structure of the Original Code, any previous
+Modifications, the combination of Original Code and any previous
+Modifications, and/or any respective portions thereof. When code is
+released as a series of files, a Modification is: (a) any addition to
+or deletion from the contents of a file containing Covered Code;
+and/or (b) any new file or other representation of computer program
+statements that contains any part of Covered Code.
+
+1.7 "Original Code" means (a) the Source Code of a program or other
+work as originally made available by Apple under this License,
+including the Source Code of any updates or upgrades to such programs
+or works made available by Apple under this License, and that has been
+expressly identified by Apple as such in the header file(s) of such
+work; and (b) the object code compiled from such Source Code and
+originally made available by Apple under this License.
+
+1.8 "Source Code" means the human readable form of a program or other
+work that is suitable for making modifications to it, including all
+modules it contains, plus any associated interface definition files,
+scripts used to control compilation and installation of an executable
+(object code).
+
+1.9 "You" or "Your" means an individual or a legal entity exercising
+rights under this License. For legal entities, "You" or "Your"
+includes any entity which controls, is controlled by, or is under
+common control with, You, where "control" means (a) the power, direct
+or indirect, to cause the direction or management of such entity,
+whether by contract or otherwise, or (b) ownership of fifty percent
+(50%) or more of the outstanding shares or beneficial ownership of
+such entity.
+
+2. Permitted Uses; Conditions & Restrictions. Subject to the terms
+and conditions of this License, Apple hereby grants You, effective on
+the date You accept this License and download the Original Code, a
+world-wide, royalty-free, non-exclusive license, to the extent of
+Apple's Applicable Patent Rights and copyrights covering the Original
+Code, to do the following:
+
+2.1 Unmodified Code. You may use, reproduce, display, perform,
+internally distribute within Your organization, and Externally Deploy
+verbatim, unmodified copies of the Original Code, for commercial or
+non-commercial purposes, provided that in each instance:
+
+(a) You must retain and reproduce in all copies of Original Code the
+copyright and other proprietary notices and disclaimers of Apple as
+they appear in the Original Code, and keep intact all notices in the
+Original Code that refer to this License; and
+
+(b) You must include a copy of this License with every copy of Source
+Code of Covered Code and documentation You distribute or Externally
+Deploy, and You may not offer or impose any terms on such Source Code
+that alter or restrict this License or the recipients' rights
+hereunder, except as permitted under Section 6.
+
+2.2 Modified Code. You may modify Covered Code and use, reproduce,
+display, perform, internally distribute within Your organization, and
+Externally Deploy Your Modifications and Covered Code, for commercial
+or non-commercial purposes, provided that in each instance You also
+meet all of these conditions:
+
+(a) You must satisfy all the conditions of Section 2.1 with respect to
+the Source Code of the Covered Code;
+
+(b) You must duplicate, to the extent it does not already exist, the
+notice in Exhibit A in each file of the Source Code of all Your
+Modifications, and cause the modified files to carry prominent notices
+stating that You changed the files and the date of any change; and
+
+(c) If You Externally Deploy Your Modifications, You must make
+Source Code of all Your Externally Deployed Modifications either
+available to those to whom You have Externally Deployed Your
+Modifications, or publicly available. Source Code of Your Externally
+Deployed Modifications must be released under the terms set forth in
+this License, including the license grants set forth in Section 3
+below, for as long as you Externally Deploy the Covered Code or twelve
+(12) months from the date of initial External Deployment, whichever is
+longer. You should preferably distribute the Source Code of Your
+Externally Deployed Modifications electronically (e.g. download from a
+web site).
+
+2.3 Distribution of Executable Versions. In addition, if You
+Externally Deploy Covered Code (Original Code and/or Modifications) in
+object code, executable form only, You must include a prominent
+notice, in the code itself as well as in related documentation,
+stating that Source Code of the Covered Code is available under the
+terms of this License with information on how and where to obtain such
+Source Code.
+
+2.4 Third Party Rights. You expressly acknowledge and agree that
+although Apple and each Contributor grants the licenses to their
+respective portions of the Covered Code set forth herein, no
+assurances are provided by Apple or any Contributor that the Covered
+Code does not infringe the patent or other intellectual property
+rights of any other entity. Apple and each Contributor disclaim any
+liability to You for claims brought by any other entity based on
+infringement of intellectual property rights or otherwise. As a
+condition to exercising the rights and licenses granted hereunder, You
+hereby assume sole responsibility to secure any other intellectual
+property rights needed, if any. For example, if a third party patent
+license is required to allow You to distribute the Covered Code, it is
+Your responsibility to acquire that license before distributing the
+Covered Code.
+
+3. Your Grants. In consideration of, and as a condition to, the
+licenses granted to You under this License, You hereby grant to any
+person or entity receiving or distributing Covered Code under this
+License a non-exclusive, royalty-free, perpetual, irrevocable license,
+under Your Applicable Patent Rights and other intellectual property
+rights (other than patent) owned or controlled by You, to use,
+reproduce, display, perform, modify, sublicense, distribute and
+Externally Deploy Your Modifications of the same scope and extent as
+Apple's licenses under Sections 2.1 and 2.2 above.
+
+4. Larger Works. You may create a Larger Work by combining Covered
+Code with other code not governed by the terms of this License and
+distribute the Larger Work as a single product. In each such instance,
+You must make sure the requirements of this License are fulfilled for
+the Covered Code or any portion thereof.
+
+5. Limitations on Patent License. Except as expressly stated in
+Section 2, no other patent rights, express or implied, are granted by
+Apple herein. Modifications and/or Larger Works may require additional
+patent licenses from Apple which Apple may grant in its sole
+discretion.
+
+6. Additional Terms. You may choose to offer, and to charge a fee for,
+warranty, support, indemnity or liability obligations and/or other
+rights consistent with the scope of the license granted herein
+("Additional Terms") to one or more recipients of Covered Code.
+However, You may do so only on Your own behalf and as Your sole
+responsibility, and not on behalf of Apple or any Contributor. You
+must obtain the recipient's agreement that any such Additional Terms
+are offered by You alone, and You hereby agree to indemnify, defend
+and hold Apple and every Contributor harmless for any liability
+incurred by or claims asserted against Apple or such Contributor by
+reason of any such Additional Terms.
+
+7. Versions of the License. Apple may publish revised and/or new
+versions of this License from time to time. Each version will be given
+a distinguishing version number. Once Original Code has been published
+under a particular version of this License, You may continue to use it
+under the terms of that version. You may also choose to use such
+Original Code under the terms of any subsequent version of this
+License published by Apple. No one other than Apple has the right to
+modify the terms applicable to Covered Code created under this
+License.
+
+8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
+part pre-release, untested, or not fully tested works. The Covered
+Code may contain errors that could cause failures or loss of data, and
+may be incomplete or contain inaccuracies. You expressly acknowledge
+and agree that use of the Covered Code, or any portion thereof, is at
+Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
+WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
+APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
+PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
+ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
+MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
+PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
+PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
+INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
+FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
+THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
+ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
+ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
+AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
+You acknowledge that the Covered Code is not intended for use in the
+operation of nuclear facilities, aircraft navigation, communication
+systems, or air traffic control machines in which case the failure of
+the Covered Code could lead to death, personal injury, or severe
+physical or environmental damage.
+
+9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
+EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
+TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
+ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
+TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
+APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
+REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
+INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
+TO YOU. In no event shall Apple's total liability to You for all
+damages (other than as may be required by applicable law) under this
+License exceed the amount of fifty dollars ($50.00).
+
+10. Trademarks. This License does not grant any rights to use the
+trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
+"QuickTime", "QuickTime Streaming Server" or any other trademarks,
+service marks, logos or trade names belonging to Apple (collectively
+"Apple Marks") or to any trademark, service mark, logo or trade name
+belonging to any Contributor. You agree not to use any Apple Marks in
+or as part of the name of products derived from the Original Code or
+to endorse or promote products derived from the Original Code other
+than as expressly permitted by and in strict compliance at all times
+with Apple's third party trademark usage guidelines which are posted
+at http://www.apple.com/legal/guidelinesfor3rdparties.html.
+
+11. Ownership. Subject to the licenses granted under this License,
+each Contributor retains all rights, title and interest in and to any
+Modifications made by such Contributor. Apple retains all rights,
+title and interest in and to the Original Code and any Modifications
+made by or on behalf of Apple ("Apple Modifications"), and such Apple
+Modifications will not be automatically subject to this License. Apple
+may, at its sole discretion, choose to license such Apple
+Modifications under this License, or on different terms from those
+contained in this License or may choose not to license them at all.
+
+12. Termination.
+
+12.1 Termination. This License and the rights granted hereunder will
+terminate:
+
+(a) automatically without notice from Apple if You fail to comply with
+any term(s) of this License and fail to cure such breach within 30
+days of becoming aware of such breach;
+
+(b) immediately in the event of the circumstances described in Section
+13.5(b); or
+
+(c) automatically without notice from Apple if You, at any time during
+the term of this License, commence an action for patent infringement
+against Apple; provided that Apple did not first commence
+an action for patent infringement against You in that instance.
+
+12.2 Effect of Termination. Upon termination, You agree to immediately
+stop any further use, reproduction, modification, sublicensing and
+distribution of the Covered Code. All sublicenses to the Covered Code
+which have been properly granted prior to termination shall survive
+any termination of this License. Provisions which, by their nature,
+should remain in effect beyond the termination of this License shall
+survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
+12.2 and 13. No party will be liable to any other for compensation,
+indemnity or damages of any sort solely as a result of terminating
+this License in accordance with its terms, and termination of this
+License will be without prejudice to any other right or remedy of
+any party.
+
+13. Miscellaneous.
+
+13.1 Government End Users. The Covered Code is a "commercial item" as
+defined in FAR 2.101. Government software and technical data rights in
+the Covered Code include only those rights customarily provided to the
+public as defined in this License. This customary commercial license
+in technical data and software is provided in accordance with FAR
+12.211 (Technical Data) and 12.212 (Computer Software) and, for
+Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
+Commercial Items) and 227.7202-3 (Rights in Commercial Computer
+Software or Computer Software Documentation). Accordingly, all U.S.
+Government End Users acquire Covered Code with only those rights set
+forth herein.
+
+13.2 Relationship of Parties. This License will not be construed as
+creating an agency, partnership, joint venture or any other form of
+legal association between or among You, Apple or any Contributor, and
+You will not represent to the contrary, whether expressly, by
+implication, appearance or otherwise.
+
+13.3 Independent Development. Nothing in this License will impair
+Apple's right to acquire, license, develop, have others develop for
+it, market and/or distribute technology or products that perform the
+same or similar functions as, or otherwise compete with,
+Modifications, Larger Works, technology or products that You may
+develop, produce, market or distribute.
+
+13.4 Waiver; Construction. Failure by Apple or any Contributor to
+enforce any provision of this License will not be deemed a waiver of
+future enforcement of that or any other provision. Any law or
+regulation which provides that the language of a contract shall be
+construed against the drafter will not apply to this License.
+
+13.5 Severability. (a) If for any reason a court of competent
+jurisdiction finds any provision of this License, or portion thereof,
+to be unenforceable, that provision of the License will be enforced to
+the maximum extent permissible so as to effect the economic benefits
+and intent of the parties, and the remainder of this License will
+continue in full force and effect. (b) Notwithstanding the foregoing,
+if applicable law prohibits or restricts You from fully and/or
+specifically complying with Sections 2 and/or 3 or prevents the
+enforceability of either of those Sections, this License will
+immediately terminate and You must immediately discontinue any use of
+the Covered Code and destroy all copies of it that are in your
+possession or control.
+
+13.6 Dispute Resolution. Any litigation or other dispute resolution
+between You and Apple relating to this License shall take place in the
+Northern District of California, and You and Apple hereby consent to
+the personal jurisdiction of, and venue in, the state and federal
+courts within that District with respect to this License. The
+application of the United Nations Convention on Contracts for the
+International Sale of Goods is expressly excluded.
+
+13.7 Entire Agreement; Governing Law. This License constitutes the
+entire agreement between the parties with respect to the subject
+matter hereof. This License shall be governed by the laws of the
+United States and the State of California, except that body of
+California law concerning conflicts of law.
+
+Where You are located in the province of Quebec, Canada, the following
+clause applies: The parties hereby confirm that they have requested
+that this License and all related documents be drafted in English. Les
+parties ont exige que le present contrat et tous les documents
+connexes soient rediges en anglais.
+
+EXHIBIT A.
+
+"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
+Reserved.
+
+This file contains Original Code and/or Modifications of Original Code
+as defined in and that are subject to the Apple Public Source License
+Version 2.0 (the 'License'). You may not use this file except in
+compliance with the License. Please obtain a copy of the License at
+http://www.opensource.apple.com/apsl/ and read it before using this
+file.
+
+The Original Code and all software distributed under the License are
+distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+Please see the License for the specific language governing rights and
+limitations under the License."
diff --git a/third_party/xnu/BUILD.gn b/third_party/xnu/BUILD.gn
new file mode 100644
index 0000000..2079ecf
--- /dev/null
+++ b/third_party/xnu/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("xnu") {
+ sources = [ "EXTERNAL_HEADERS/mach-o/loader.h" ]
+}
diff --git a/third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h b/third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h
new file mode 100644
index 0000000..72e8c39
--- /dev/null
+++ b/third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h
@@ -0,0 +1,1574 @@
+/*
+ * Copyright (c) 1999-2010 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef _MACHO_LOADER_H_
+#define _MACHO_LOADER_H_
+
+/*
+ * This file describes the format of mach object files.
+ */
+#include <stdint.h>
+
+/*
+ * <mach/machine.h> is needed here for the cpu_type_t and cpu_subtype_t types
+ * and contains the constants for the possible values of these types.
+ */
+#include <mach/machine.h>
+
+/*
+ * <mach/vm_prot.h> is needed here for the vm_prot_t type and contains the
+ * constants that are or'ed together for the possible values of this type.
+ */
+#include <mach/vm_prot.h>
+
+/*
+ * <machine/thread_status.h> is expected to define the flavors of the thread
+ * states and the structures of those flavors for each machine.
+#include <mach/machine/thread_status.h>
+#include <architecture/byte_order.h>
+ */
+
+/*
+ * The 32-bit mach header appears at the very beginning of the object file for
+ * 32-bit architectures.
+ */
+struct mach_header {
+ uint32_t magic; /* mach magic number identifier */
+ cpu_type_t cputype; /* cpu specifier */
+ cpu_subtype_t cpusubtype; /* machine specifier */
+ uint32_t filetype; /* type of file */
+ uint32_t ncmds; /* number of load commands */
+ uint32_t sizeofcmds; /* the size of all the load commands */
+ uint32_t flags; /* flags */
+};
+
+/* Constant for the magic field of the mach_header (32-bit architectures) */
+#define MH_MAGIC 0xfeedface /* the mach magic number */
+#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
+
+/*
+ * The 64-bit mach header appears at the very beginning of object files for
+ * 64-bit architectures.
+ */
+struct mach_header_64 {
+ uint32_t magic; /* mach magic number identifier */
+ cpu_type_t cputype; /* cpu specifier */
+ cpu_subtype_t cpusubtype; /* machine specifier */
+ uint32_t filetype; /* type of file */
+ uint32_t ncmds; /* number of load commands */
+ uint32_t sizeofcmds; /* the size of all the load commands */
+ uint32_t flags; /* flags */
+ uint32_t reserved; /* reserved */
+};
+
+/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
+#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
+#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
+
+/*
+ * The layout of the file depends on the filetype. For all but the MH_OBJECT
+ * file type the segments are padded out and aligned on a segment alignment
+ * boundary for efficient demand pageing. The MH_EXECUTE, MH_FVMLIB, MH_DYLIB,
+ * MH_DYLINKER and MH_BUNDLE file types also have the headers included as part
+ * of their first segment.
+ *
+ * The file type MH_OBJECT is a compact format intended as output of the
+ * assembler and input (and possibly output) of the link editor (the .o
+ * format). All sections are in one unnamed segment with no segment padding.
+ * This format is used as an executable format when the file is so small the
+ * segment padding greatly increases its size.
+ *
+ * The file type MH_PRELOAD is an executable format intended for things that
+ * are not executed under the kernel (proms, stand alones, kernels, etc). The
+ * format can be executed under the kernel but may demand paged it and not
+ * preload it before execution.
+ *
+ * A core file is in MH_CORE format and can be any in an arbritray legal
+ * Mach-O file.
+ *
+ * Constants for the filetype field of the mach_header
+ */
+#define MH_OBJECT 0x1 /* relocatable object file */
+#define MH_EXECUTE 0x2 /* demand paged executable file */
+#define MH_FVMLIB 0x3 /* fixed VM shared library file */
+#define MH_CORE 0x4 /* core file */
+#define MH_PRELOAD 0x5 /* preloaded executable file */
+#define MH_DYLIB 0x6 /* dynamically bound shared library */
+#define MH_DYLINKER 0x7 /* dynamic link editor */
+#define MH_BUNDLE 0x8 /* dynamically bound bundle file */
+#define MH_DYLIB_STUB 0x9 /* shared library stub for static */
+ /* linking only, no section contents */
+#define MH_DSYM 0xa /* companion file with only debug */
+ /* sections */
+#define MH_KEXT_BUNDLE 0xb /* x86_64 kexts */
+
+/* Constants for the flags field of the mach_header */
+#define MH_NOUNDEFS 0x1 /* the object file has no undefined
+ references */
+#define MH_INCRLINK 0x2 /* the object file is the output of an
+ incremental link against a base file
+ and can't be link edited again */
+#define MH_DYLDLINK 0x4 /* the object file is input for the
+ dynamic linker and can't be staticly
+ link edited again */
+#define MH_BINDATLOAD 0x8 /* the object file's undefined
+ references are bound by the dynamic
+ linker when loaded. */
+#define MH_PREBOUND 0x10 /* the file has its dynamic undefined
+ references prebound. */
+#define MH_SPLIT_SEGS 0x20 /* the file has its read-only and
+ read-write segments split */
+#define MH_LAZY_INIT 0x40 /* the shared library init routine is
+ to be run lazily via catching memory
+ faults to its writeable segments
+ (obsolete) */
+#define MH_TWOLEVEL 0x80 /* the image is using two-level name
+ space bindings */
+#define MH_FORCE_FLAT 0x100 /* the executable is forcing all images
+ to use flat name space bindings */
+#define MH_NOMULTIDEFS 0x200 /* this umbrella guarantees no multiple
+ defintions of symbols in its
+ sub-images so the two-level namespace
+ hints can always be used. */
+#define MH_NOFIXPREBINDING 0x400 /* do not have dyld notify the
+ prebinding agent about this
+ executable */
+#define MH_PREBINDABLE 0x800 /* the binary is not prebound but can
+ have its prebinding redone. only used
+ when MH_PREBOUND is not set. */
+#define MH_ALLMODSBOUND 0x1000 /* indicates that this binary binds to
+ all two-level namespace modules of
+ its dependent libraries. only used
+ when MH_PREBINDABLE and MH_TWOLEVEL
+ are both set. */
+#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000/* safe to divide up the sections into
+ sub-sections via symbols for dead
+ code stripping */
+#define MH_CANONICAL 0x4000 /* the binary has been canonicalized
+ via the unprebind operation */
+#define MH_WEAK_DEFINES 0x8000 /* the final linked image contains
+ external weak symbols */
+#define MH_BINDS_TO_WEAK 0x10000 /* the final linked image uses
+ weak symbols */
+
+#define MH_ALLOW_STACK_EXECUTION 0x20000/* When this bit is set, all stacks
+ in the task will be given stack
+ execution privilege. Only used in
+ MH_EXECUTE filetypes. */
+#define MH_ROOT_SAFE 0x40000 /* When this bit is set, the binary
+ declares it is safe for use in
+ processes with uid zero */
+
+#define MH_SETUID_SAFE 0x80000 /* When this bit is set, the binary
+ declares it is safe for use in
+ processes when issetugid() is true */
+
+#define MH_NO_REEXPORTED_DYLIBS 0x100000 /* When this bit is set on a dylib,
+ the static linker does not need to
+ examine dependent dylibs to see
+ if any are re-exported */
+#define MH_PIE 0x200000 /* When this bit is set, the OS will
+ load the main executable at a
+ random address. Only used in
+ MH_EXECUTE filetypes. */
+#define MH_DEAD_STRIPPABLE_DYLIB 0x400000 /* Only for use on dylibs. When
+ linking against a dylib that
+ has this bit set, the static linker
+ will automatically not create a
+ LC_LOAD_DYLIB load command to the
+ dylib if no symbols are being
+ referenced from the dylib. */
+#define MH_HAS_TLV_DESCRIPTORS 0x800000 /* Contains a section of type
+ S_THREAD_LOCAL_VARIABLES */
+
+#define MH_NO_HEAP_EXECUTION 0x1000000 /* When this bit is set, the OS will
+ run the main executable with
+ a non-executable heap even on
+ platforms (e.g. i386) that don't
+ require it. Only used in MH_EXECUTE
+ filetypes. */
+
+#define MH_APP_EXTENSION_SAFE 0x02000000 /* The code was linked for use in an
+ application extension. */
+
+#define MH_NLIST_OUTOFSYNC_WITH_DYLDINFO 0x04000000 /* The external symbols
+ listed in the nlist symbol table do
+ not include all the symbols listed in
+ the dyld info. */
+
+#define MH_SIM_SUPPORT 0x08000000 /* Allow LC_MIN_VERSION_MACOS and
+ LC_BUILD_VERSION load commands with
+ the platforms macOS, iOSMac,
+ iOSSimulator, tvOSSimulator and
+ watchOSSimulator. */
+
+#define MH_DYLIB_IN_CACHE 0x80000000 /* Only for use on dylibs. When this bit
+ is set, the dylib is part of the dyld
+ shared cache, rather than loose in
+ the filesystem. */
+
+/*
+ * The load commands directly follow the mach_header. The total size of all
+ * of the commands is given by the sizeofcmds field in the mach_header. All
+ * load commands must have as their first two fields cmd and cmdsize. The cmd
+ * field is filled in with a constant for that command type. Each command type
+ * has a structure specifically for it. The cmdsize field is the size in bytes
+ * of the particular load command structure plus anything that follows it that
+ * is a part of the load command (i.e. section structures, strings, etc.). To
+ * advance to the next load command the cmdsize can be added to the offset or
+ * pointer of the current load command. The cmdsize for 32-bit architectures
+ * MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple
+ * of 8 bytes (these are forever the maximum alignment of any load commands).
+ * The padded bytes must be zero. All tables in the object file must also
+ * follow these rules so the file can be memory mapped. Otherwise the pointers
+ * to these tables will not work well or at all on some machines. With all
+ * padding zeroed like objects will compare byte for byte.
+ */
+struct load_command {
+ uint32_t cmd; /* type of load command */
+ uint32_t cmdsize; /* total size of command in bytes */
+};
+
+/*
+ * After MacOS X 10.1 when a new load command is added that is required to be
+ * understood by the dynamic linker for the image to execute properly the
+ * LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic
+ * linker sees such a load command it it does not understand will issue a
+ * "unknown load command required for execution" error and refuse to use the
+ * image. Other load commands without this bit that are not understood will
+ * simply be ignored.
+ */
+#define LC_REQ_DYLD 0x80000000
+
+/* Constants for the cmd field of all load commands, the type */
+#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
+#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
+#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
+#define LC_THREAD 0x4 /* thread */
+#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
+#define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */
+#define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */
+#define LC_IDENT 0x8 /* object identification info (obsolete) */
+#define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */
+#define LC_PREPAGE 0xa /* prepage command (internal use) */
+#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */
+#define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */
+#define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */
+#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */
+#define LC_ID_DYLINKER 0xf /* dynamic linker identification */
+#define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */
+ /* linked shared library */
+#define LC_ROUTINES 0x11 /* image routines */
+#define LC_SUB_FRAMEWORK 0x12 /* sub framework */
+#define LC_SUB_UMBRELLA 0x13 /* sub umbrella */
+#define LC_SUB_CLIENT 0x14 /* sub client */
+#define LC_SUB_LIBRARY 0x15 /* sub library */
+#define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */
+#define LC_PREBIND_CKSUM 0x17 /* prebind checksum */
+
+/*
+ * load a dynamically linked shared library that is allowed to be missing
+ * (all symbols are weak imported).
+ */
+#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
+
+#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be
+ mapped */
+#define LC_ROUTINES_64 0x1a /* 64-bit image routines */
+#define LC_UUID 0x1b /* the uuid */
+#define LC_RPATH (0x1c | LC_REQ_DYLD) /* runpath additions */
+#define LC_CODE_SIGNATURE 0x1d /* local of code signature */
+#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */
+#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
+#define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */
+#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */
+#define LC_DYLD_INFO 0x22 /* compressed dyld information */
+#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
+#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
+#define LC_VERSION_MIN_MACOSX 0x24 /* build for MacOSX min OS version */
+#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */
+#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */
+#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat
+ like environment variable */
+#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
+#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
+#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */
+#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
+#define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */
+#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
+#define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */
+#define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */
+#define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */
+#define LC_NOTE 0x31 /* arbitrary data included within a Mach-O file */
+#define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
+#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */
+#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */
+
+/*
+ * A variable length string in a load command is represented by an lc_str
+ * union. The strings are stored just after the load command structure and
+ * the offset is from the start of the load command structure. The size
+ * of the string is reflected in the cmdsize field of the load command.
+ * Once again any padded bytes to bring the cmdsize field to a multiple
+ * of 4 bytes must be zero.
+ */
+union lc_str {
+ uint32_t offset; /* offset to the string */
+#ifndef __LP64__
+ char *ptr; /* pointer to the string */
+#endif
+};
+
+/*
+ * The segment load command indicates that a part of this file is to be
+ * mapped into the task's address space. The size of this segment in memory,
+ * vmsize, maybe equal to or larger than the amount to map from this file,
+ * filesize. The file is mapped starting at fileoff to the beginning of
+ * the segment in memory, vmaddr. The rest of the memory of the segment,
+ * if any, is allocated zero fill on demand. The segment's maximum virtual
+ * memory protection and initial virtual memory protection are specified
+ * by the maxprot and initprot fields. If the segment has sections then the
+ * section structures directly follow the segment command and their size is
+ * reflected in cmdsize.
+ */
+struct segment_command { /* for 32-bit architectures */
+ uint32_t cmd; /* LC_SEGMENT */
+ uint32_t cmdsize; /* includes sizeof section structs */
+ char segname[16]; /* segment name */
+ uint32_t vmaddr; /* memory address of this segment */
+ uint32_t vmsize; /* memory size of this segment */
+ uint32_t fileoff; /* file offset of this segment */
+ uint32_t filesize; /* amount to map from the file */
+ vm_prot_t maxprot; /* maximum VM protection */
+ vm_prot_t initprot; /* initial VM protection */
+ uint32_t nsects; /* number of sections in segment */
+ uint32_t flags; /* flags */
+};
+
+/*
+ * The 64-bit segment load command indicates that a part of this file is to be
+ * mapped into a 64-bit task's address space. If the 64-bit segment has
+ * sections then section_64 structures directly follow the 64-bit segment
+ * command and their size is reflected in cmdsize.
+ */
+struct segment_command_64 { /* for 64-bit architectures */
+ uint32_t cmd; /* LC_SEGMENT_64 */
+ uint32_t cmdsize; /* includes sizeof section_64 structs */
+ char segname[16]; /* segment name */
+ uint64_t vmaddr; /* memory address of this segment */
+ uint64_t vmsize; /* memory size of this segment */
+ uint64_t fileoff; /* file offset of this segment */
+ uint64_t filesize; /* amount to map from the file */
+ vm_prot_t maxprot; /* maximum VM protection */
+ vm_prot_t initprot; /* initial VM protection */
+ uint32_t nsects; /* number of sections in segment */
+ uint32_t flags; /* flags */
+};
+
+/* Constants for the flags field of the segment_command */
+#define SG_HIGHVM 0x1 /* the file contents for this segment is for
+ the high part of the VM space, the low part
+ is zero filled (for stacks in core files) */
+#define SG_FVMLIB 0x2 /* this segment is the VM that is allocated by
+ a fixed VM library, for overlap checking in
+ the link editor */
+#define SG_NORELOC 0x4 /* this segment has nothing that was relocated
+ in it and nothing relocated to it, that is
+ it maybe safely replaced without relocation*/
+#define SG_PROTECTED_VERSION_1 0x8 /* This segment is protected. If the
+ segment starts at file offset 0, the
+ first page of the segment is not
+ protected. All other pages of the
+ segment are protected. */
+#define SG_READ_ONLY 0x10 /* This segment is made read-only after fixups */
+
+
+
+/*
+ * A segment is made up of zero or more sections. Non-MH_OBJECT files have
+ * all of their segments with the proper sections in each, and padded to the
+ * specified segment alignment when produced by the link editor. The first
+ * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header
+ * and load commands of the object file before its first section. The zero
+ * fill sections are always last in their segment (in all formats). This
+ * allows the zeroed segment padding to be mapped into memory where zero fill
+ * sections might be. The gigabyte zero fill sections, those with the section
+ * type S_GB_ZEROFILL, can only be in a segment with sections of this type.
+ * These segments are then placed after all other segments.
+ *
+ * The MH_OBJECT format has all of its sections in one segment for
+ * compactness. There is no padding to a specified segment boundary and the
+ * mach_header and load commands are not part of the segment.
+ *
+ * Sections with the same section name, sectname, going into the same segment,
+ * segname, are combined by the link editor. The resulting section is aligned
+ * to the maximum alignment of the combined sections and is the new section's
+ * alignment. The combined sections are aligned to their original alignment in
+ * the combined section. Any padded bytes to get the specified alignment are
+ * zeroed.
+ *
+ * The format of the relocation entries referenced by the reloff and nreloc
+ * fields of the section structure for mach object files is described in the
+ * header file <reloc.h>.
+ */
+struct section { /* for 32-bit architectures */
+ char sectname[16]; /* name of this section */
+ char segname[16]; /* segment this section goes in */
+ uint32_t addr; /* memory address of this section */
+ uint32_t size; /* size in bytes of this section */
+ uint32_t offset; /* file offset of this section */
+ uint32_t align; /* section alignment (power of 2) */
+ uint32_t reloff; /* file offset of relocation entries */
+ uint32_t nreloc; /* number of relocation entries */
+ uint32_t flags; /* flags (section type and attributes)*/
+ uint32_t reserved1; /* reserved (for offset or index) */
+ uint32_t reserved2; /* reserved (for count or sizeof) */
+};
+
+struct section_64 { /* for 64-bit architectures */
+ char sectname[16]; /* name of this section */
+ char segname[16]; /* segment this section goes in */
+ uint64_t addr; /* memory address of this section */
+ uint64_t size; /* size in bytes of this section */
+ uint32_t offset; /* file offset of this section */
+ uint32_t align; /* section alignment (power of 2) */
+ uint32_t reloff; /* file offset of relocation entries */
+ uint32_t nreloc; /* number of relocation entries */
+ uint32_t flags; /* flags (section type and attributes)*/
+ uint32_t reserved1; /* reserved (for offset or index) */
+ uint32_t reserved2; /* reserved (for count or sizeof) */
+ uint32_t reserved3; /* reserved */
+};
+
+/*
+ * The flags field of a section structure is separated into two parts a section
+ * type and section attributes. The section types are mutually exclusive (it
+ * can only have one type) but the section attributes are not (it may have more
+ * than one attribute).
+ */
+#define SECTION_TYPE 0x000000ff /* 256 section types */
+#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */
+
+/* Constants for the type of a section */
+#define S_REGULAR 0x0 /* regular section */
+#define S_ZEROFILL 0x1 /* zero fill on demand section */
+#define S_CSTRING_LITERALS 0x2 /* section with only literal C strings*/
+#define S_4BYTE_LITERALS 0x3 /* section with only 4 byte literals */
+#define S_8BYTE_LITERALS 0x4 /* section with only 8 byte literals */
+#define S_LITERAL_POINTERS 0x5 /* section with only pointers to */
+ /* literals */
+/*
+ * For the two types of symbol pointers sections and the symbol stubs section
+ * they have indirect symbol table entries. For each of the entries in the
+ * section the indirect symbol table entries, in corresponding order in the
+ * indirect symbol table, start at the index stored in the reserved1 field
+ * of the section structure. Since the indirect symbol table entries
+ * correspond to the entries in the section the number of indirect symbol table
+ * entries is inferred from the size of the section divided by the size of the
+ * entries in the section. For symbol pointers sections the size of the entries
+ * in the section is 4 bytes and for symbol stubs sections the byte size of the
+ * stubs is stored in the reserved2 field of the section structure.
+ */
+#define S_NON_LAZY_SYMBOL_POINTERS 0x6 /* section with only non-lazy
+ symbol pointers */
+#define S_LAZY_SYMBOL_POINTERS 0x7 /* section with only lazy symbol
+ pointers */
+#define S_SYMBOL_STUBS 0x8 /* section with only symbol
+ stubs, byte size of stub in
+ the reserved2 field */
+#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function
+ pointers for initialization*/
+#define S_MOD_TERM_FUNC_POINTERS 0xa /* section with only function
+ pointers for termination */
+#define S_COALESCED 0xb /* section contains symbols that
+ are to be coalesced */
+#define S_GB_ZEROFILL 0xc /* zero fill on demand section
+ (that can be larger than 4
+ gigabytes) */
+#define S_INTERPOSING 0xd /* section with only pairs of
+ function pointers for
+ interposing */
+#define S_16BYTE_LITERALS 0xe /* section with only 16 byte
+ literals */
+#define S_DTRACE_DOF 0xf /* section contains
+ DTrace Object Format */
+#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 /* section with only lazy
+ symbol pointers to lazy
+ loaded dylibs */
+/*
+ * Section types to support thread local variables
+ */
+#define S_THREAD_LOCAL_REGULAR 0x11 /* template of initial
+ values for TLVs */
+#define S_THREAD_LOCAL_ZEROFILL 0x12 /* template of initial
+ values for TLVs */
+#define S_THREAD_LOCAL_VARIABLES 0x13 /* TLV descriptors */
+#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 /* pointers to TLV
+ descriptors */
+#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 /* functions to call
+ to initialize TLV
+ values */
+#define S_INIT_FUNC_OFFSETS 0x16 /* 32-bit offsets to
+ initializers */
+
+/*
+ * Constants for the section attributes part of the flags field of a section
+ * structure.
+ */
+#define SECTION_ATTRIBUTES_USR 0xff000000 /* User setable attributes */
+#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 /* section contains only true
+ machine instructions */
+#define S_ATTR_NO_TOC 0x40000000 /* section contains coalesced
+ symbols that are not to be
+ in a ranlib table of
+ contents */
+#define S_ATTR_STRIP_STATIC_SYMS 0x20000000 /* ok to strip static symbols
+ in this section in files
+ with the MH_DYLDLINK flag */
+#define S_ATTR_NO_DEAD_STRIP 0x10000000 /* no dead stripping */
+#define S_ATTR_LIVE_SUPPORT 0x08000000 /* blocks are live if they
+ reference live blocks */
+#define S_ATTR_SELF_MODIFYING_CODE 0x04000000 /* Used with i386 code stubs
+ written on by dyld */
+/*
+ * If a segment contains any sections marked with S_ATTR_DEBUG then all
+ * sections in that segment must have this attribute. No section other than
+ * a section marked with this attribute may reference the contents of this
+ * section. A section with this attribute may contain no symbols and must have
+ * a section type S_REGULAR. The static linker will not copy section contents
+ * from sections with this attribute into its output file. These sections
+ * generally contain DWARF debugging info.
+ */
+#define S_ATTR_DEBUG 0x02000000 /* a debug section */
+#define SECTION_ATTRIBUTES_SYS 0x00ffff00 /* system setable attributes */
+#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some
+ machine instructions */
+#define S_ATTR_EXT_RELOC 0x00000200 /* section has external
+ relocation entries */
+#define S_ATTR_LOC_RELOC 0x00000100 /* section has local
+ relocation entries */
+
+
+/*
+ * The names of segments and sections in them are mostly meaningless to the
+ * link-editor. But there are few things to support traditional UNIX
+ * executables that require the link-editor and assembler to use some names
+ * agreed upon by convention.
+ *
+ * The initial protection of the "__TEXT" segment has write protection turned
+ * off (not writeable).
+ *
+ * The link-editor will allocate common symbols at the end of the "__common"
+ * section in the "__DATA" segment. It will create the section and segment
+ * if needed.
+ */
+
+/* The currently known segment names and the section names in those segments */
+
+#define SEG_PAGEZERO "__PAGEZERO" /* the pagezero segment which has no */
+ /* protections and catches NULL */
+ /* references for MH_EXECUTE files */
+
+
+#define SEG_TEXT "__TEXT" /* the tradition UNIX text segment */
+#define SECT_TEXT "__text" /* the real text part of the text */
+ /* section no headers, and no padding */
+#define SECT_FVMLIB_INIT0 "__fvmlib_init0" /* the fvmlib initialization */
+ /* section */
+#define SECT_FVMLIB_INIT1 "__fvmlib_init1" /* the section following the */
+ /* fvmlib initialization */
+ /* section */
+
+#define SEG_DATA "__DATA" /* the tradition UNIX data segment */
+#define SECT_DATA "__data" /* the real initialized data section */
+ /* no padding, no bss overlap */
+#define SECT_BSS "__bss" /* the real uninitialized data section*/
+ /* no padding */
+#define SECT_COMMON "__common" /* the section common symbols are */
+ /* allocated in by the link editor */
+
+#define SEG_OBJC "__OBJC" /* objective-C runtime segment */
+#define SECT_OBJC_SYMBOLS "__symbol_table" /* symbol table */
+#define SECT_OBJC_MODULES "__module_info" /* module information */
+#define SECT_OBJC_STRINGS "__selector_strs" /* string table */
+#define SECT_OBJC_REFS "__selector_refs" /* string table */
+
+#define SEG_ICON "__ICON" /* the icon segment */
+#define SECT_ICON_HEADER "__header" /* the icon headers */
+#define SECT_ICON_TIFF "__tiff" /* the icons in tiff format */
+
+#define SEG_LINKEDIT "__LINKEDIT" /* the segment containing all structs */
+ /* created and maintained by the link */
+ /* editor. Created with -seglinkedit */
+ /* option to ld(1) for MH_EXECUTE and */
+ /* FVMLIB file types only */
+
+#define SEG_UNIXSTACK "__UNIXSTACK" /* the unix stack segment */
+
+#define SEG_IMPORT "__IMPORT" /* the segment for the self (dyld) */
+ /* modifing code stubs that has read, */
+ /* write and execute permissions */
+
+/*
+ * Fixed virtual memory shared libraries are identified by two things. The
+ * target pathname (the name of the library as found for execution), and the
+ * minor version number. The address of where the headers are loaded is in
+ * header_addr. (THIS IS OBSOLETE and no longer supported).
+ */
+struct fvmlib {
+ union lc_str name; /* library's target pathname */
+ uint32_t minor_version; /* library's minor version number */
+ uint32_t header_addr; /* library's header address */
+};
+
+/*
+ * A fixed virtual shared library (filetype == MH_FVMLIB in the mach header)
+ * contains a fvmlib_command (cmd == LC_IDFVMLIB) to identify the library.
+ * An object that uses a fixed virtual shared library also contains a
+ * fvmlib_command (cmd == LC_LOADFVMLIB) for each library it uses.
+ * (THIS IS OBSOLETE and no longer supported).
+ */
+struct fvmlib_command {
+ uint32_t cmd; /* LC_IDFVMLIB or LC_LOADFVMLIB */
+ uint32_t cmdsize; /* includes pathname string */
+ struct fvmlib fvmlib; /* the library identification */
+};
+
+/*
+ * Dynamicly linked shared libraries are identified by two things. The
+ * pathname (the name of the library as found for execution), and the
+ * compatibility version number. The pathname must match and the compatibility
+ * number in the user of the library must be greater than or equal to the
+ * library being used. The time stamp is used to record the time a library was
+ * built and copied into user so it can be use to determined if the library used
+ * at runtime is exactly the same as used to built the program.
+ */
+struct dylib {
+ union lc_str name; /* library's path name */
+ uint32_t timestamp; /* library's build time stamp */
+ uint32_t current_version; /* library's current version number */
+ uint32_t compatibility_version; /* library's compatibility vers number*/
+};
+
+/*
+ * A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
+ * contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
+ * An object that uses a dynamically linked shared library also contains a
+ * dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
+ * LC_REEXPORT_DYLIB) for each library it uses.
+ */
+struct dylib_command {
+ uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,
+ LC_REEXPORT_DYLIB */
+ uint32_t cmdsize; /* includes pathname string */
+ struct dylib dylib; /* the library identification */
+};
+
+/*
+ * A dynamically linked shared library may be a subframework of an umbrella
+ * framework. If so it will be linked with "-umbrella umbrella_name" where
+ * Where "umbrella_name" is the name of the umbrella framework. A subframework
+ * can only be linked against by its umbrella framework or other subframeworks
+ * that are part of the same umbrella framework. Otherwise the static link
+ * editor produces an error and states to link against the umbrella framework.
+ * The name of the umbrella framework for subframeworks is recorded in the
+ * following structure.
+ */
+struct sub_framework_command {
+ uint32_t cmd; /* LC_SUB_FRAMEWORK */
+ uint32_t cmdsize; /* includes umbrella string */
+ union lc_str umbrella; /* the umbrella framework name */
+};
+
+/*
+ * For dynamically linked shared libraries that are subframework of an umbrella
+ * framework they can allow clients other than the umbrella framework or other
+ * subframeworks in the same umbrella framework. To do this the subframework
+ * is built with "-allowable_client client_name" and an LC_SUB_CLIENT load
+ * command is created for each -allowable_client flag. The client_name is
+ * usually a framework name. It can also be a name used for bundles clients
+ * where the bundle is built with "-client_name client_name".
+ */
+struct sub_client_command {
+ uint32_t cmd; /* LC_SUB_CLIENT */
+ uint32_t cmdsize; /* includes client string */
+ union lc_str client; /* the client name */
+};
+
+/*
+ * A dynamically linked shared library may be a sub_umbrella of an umbrella
+ * framework. If so it will be linked with "-sub_umbrella umbrella_name" where
+ * Where "umbrella_name" is the name of the sub_umbrella framework. When
+ * staticly linking when -twolevel_namespace is in effect a twolevel namespace
+ * umbrella framework will only cause its subframeworks and those frameworks
+ * listed as sub_umbrella frameworks to be implicited linked in. Any other
+ * dependent dynamic libraries will not be linked it when -twolevel_namespace
+ * is in effect. The primary library recorded by the static linker when
+ * resolving a symbol in these libraries will be the umbrella framework.
+ * Zero or more sub_umbrella frameworks may be use by an umbrella framework.
+ * The name of a sub_umbrella framework is recorded in the following structure.
+ */
+struct sub_umbrella_command {
+ uint32_t cmd; /* LC_SUB_UMBRELLA */
+ uint32_t cmdsize; /* includes sub_umbrella string */
+ union lc_str sub_umbrella; /* the sub_umbrella framework name */
+};
+
+/*
+ * A dynamically linked shared library may be a sub_library of another shared
+ * library. If so it will be linked with "-sub_library library_name" where
+ * Where "library_name" is the name of the sub_library shared library. When
+ * staticly linking when -twolevel_namespace is in effect a twolevel namespace
+ * shared library will only cause its subframeworks and those frameworks
+ * listed as sub_umbrella frameworks and libraries listed as sub_libraries to
+ * be implicited linked in. Any other dependent dynamic libraries will not be
+ * linked it when -twolevel_namespace is in effect. The primary library
+ * recorded by the static linker when resolving a symbol in these libraries
+ * will be the umbrella framework (or dynamic library). Zero or more sub_library
+ * shared libraries may be use by an umbrella framework or (or dynamic library).
+ * The name of a sub_library framework is recorded in the following structure.
+ * For example /usr/lib/libobjc_profile.A.dylib would be recorded as "libobjc".
+ */
+struct sub_library_command {
+ uint32_t cmd; /* LC_SUB_LIBRARY */
+ uint32_t cmdsize; /* includes sub_library string */
+ union lc_str sub_library; /* the sub_library name */
+};
+
+/*
+ * A program (filetype == MH_EXECUTE) that is
+ * prebound to its dynamic libraries has one of these for each library that
+ * the static linker used in prebinding. It contains a bit vector for the
+ * modules in the library. The bits indicate which modules are bound (1) and
+ * which are not (0) from the library. The bit for module 0 is the low bit
+ * of the first byte. So the bit for the Nth module is:
+ * (linked_modules[N/8] >> N%8) & 1
+ */
+struct prebound_dylib_command {
+ uint32_t cmd; /* LC_PREBOUND_DYLIB */
+ uint32_t cmdsize; /* includes strings */
+ union lc_str name; /* library's path name */
+ uint32_t nmodules; /* number of modules in library */
+ union lc_str linked_modules; /* bit vector of linked modules */
+};
+
+/*
+ * A program that uses a dynamic linker contains a dylinker_command to identify
+ * the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker
+ * contains a dylinker_command to identify the dynamic linker (LC_ID_DYLINKER).
+ * A file can have at most one of these.
+ * This struct is also used for the LC_DYLD_ENVIRONMENT load command and
+ * contains string for dyld to treat like environment variable.
+ */
+struct dylinker_command {
+ uint32_t cmd; /* LC_ID_DYLINKER, LC_LOAD_DYLINKER or
+ LC_DYLD_ENVIRONMENT */
+ uint32_t cmdsize; /* includes pathname string */
+ union lc_str name; /* dynamic linker's path name */
+};
+
+/*
+ * Thread commands contain machine-specific data structures suitable for
+ * use in the thread state primitives. The machine specific data structures
+ * follow the struct thread_command as follows.
+ * Each flavor of machine specific data structure is preceded by an uint32_t
+ * constant for the flavor of that data structure, an uint32_t that is the
+ * count of uint32_t's of the size of the state data structure and then
+ * the state data structure follows. This triple may be repeated for many
+ * flavors. The constants for the flavors, counts and state data structure
+ * definitions are expected to be in the header file <machine/thread_status.h>.
+ * These machine specific data structures sizes must be multiples of
+ * 4 bytes. The cmdsize reflects the total size of the thread_command
+ * and all of the sizes of the constants for the flavors, counts and state
+ * data structures.
+ *
+ * For executable objects that are unix processes there will be one
+ * thread_command (cmd == LC_UNIXTHREAD) created for it by the link-editor.
+ * This is the same as a LC_THREAD, except that a stack is automatically
+ * created (based on the shell's limit for the stack size). Command arguments
+ * and environment variables are copied onto that stack.
+ */
+struct thread_command {
+ uint32_t cmd; /* LC_THREAD or LC_UNIXTHREAD */
+ uint32_t cmdsize; /* total size of this command */
+ /* uint32_t flavor flavor of thread state */
+ /* uint32_t count count of uint32_t's in thread state */
+ /* struct XXX_thread_state state thread state for this flavor */
+ /* ... */
+};
+
+/*
+ * The routines command contains the address of the dynamic shared library
+ * initialization routine and an index into the module table for the module
+ * that defines the routine. Before any modules are used from the library the
+ * dynamic linker fully binds the module that defines the initialization routine
+ * and then calls it. This gets called before any module initialization
+ * routines (used for C++ static constructors) in the library.
+ */
+struct routines_command { /* for 32-bit architectures */
+ uint32_t cmd; /* LC_ROUTINES */
+ uint32_t cmdsize; /* total size of this command */
+ uint32_t init_address; /* address of initialization routine */
+ uint32_t init_module; /* index into the module table that */
+ /* the init routine is defined in */
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t reserved3;
+ uint32_t reserved4;
+ uint32_t reserved5;
+ uint32_t reserved6;
+};
+
+/*
+ * The 64-bit routines command. Same use as above.
+ */
+struct routines_command_64 { /* for 64-bit architectures */
+ uint32_t cmd; /* LC_ROUTINES_64 */
+ uint32_t cmdsize; /* total size of this command */
+ uint64_t init_address; /* address of initialization routine */
+ uint64_t init_module; /* index into the module table that */
+ /* the init routine is defined in */
+ uint64_t reserved1;
+ uint64_t reserved2;
+ uint64_t reserved3;
+ uint64_t reserved4;
+ uint64_t reserved5;
+ uint64_t reserved6;
+};
+
+/*
+ * The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
+ * "stab" style symbol table information as described in the header files
+ * <nlist.h> and <stab.h>.
+ */
+struct symtab_command {
+ uint32_t cmd; /* LC_SYMTAB */
+ uint32_t cmdsize; /* sizeof(struct symtab_command) */
+ uint32_t symoff; /* symbol table offset */
+ uint32_t nsyms; /* number of symbol table entries */
+ uint32_t stroff; /* string table offset */
+ uint32_t strsize; /* string table size in bytes */
+};
+
+/*
+ * This is the second set of the symbolic information which is used to support
+ * the data structures for the dynamically link editor.
+ *
+ * The original set of symbolic information in the symtab_command which contains
+ * the symbol and string tables must also be present when this load command is
+ * present. When this load command is present the symbol table is organized
+ * into three groups of symbols:
+ * local symbols (static and debugging symbols) - grouped by module
+ * defined external symbols - grouped by module (sorted by name if not lib)
+ * undefined external symbols (sorted by name if MH_BINDATLOAD is not set,
+ * and in order the were seen by the static
+ * linker if MH_BINDATLOAD is set)
+ * In this load command there are offsets and counts to each of the three groups
+ * of symbols.
+ *
+ * This load command contains a the offsets and sizes of the following new
+ * symbolic information tables:
+ * table of contents
+ * module table
+ * reference symbol table
+ * indirect symbol table
+ * The first three tables above (the table of contents, module table and
+ * reference symbol table) are only present if the file is a dynamically linked
+ * shared library. For executable and object modules, which are files
+ * containing only one module, the information that would be in these three
+ * tables is determined as follows:
+ * table of contents - the defined external symbols are sorted by name
+ * module table - the file contains only one module so everything in the
+ * file is part of the module.
+ * reference symbol table - is the defined and undefined external symbols
+ *
+ * For dynamically linked shared library files this load command also contains
+ * offsets and sizes to the pool of relocation entries for all sections
+ * separated into two groups:
+ * external relocation entries
+ * local relocation entries
+ * For executable and object modules the relocation entries continue to hang
+ * off the section structures.
+ */
+struct dysymtab_command {
+ uint32_t cmd; /* LC_DYSYMTAB */
+ uint32_t cmdsize; /* sizeof(struct dysymtab_command) */
+
+ /*
+ * The symbols indicated by symoff and nsyms of the LC_SYMTAB load command
+ * are grouped into the following three groups:
+ * local symbols (further grouped by the module they are from)
+ * defined external symbols (further grouped by the module they are from)
+ * undefined symbols
+ *
+ * The local symbols are used only for debugging. The dynamic binding
+ * process may have to use them to indicate to the debugger the local
+ * symbols for a module that is being bound.
+ *
+ * The last two groups are used by the dynamic binding process to do the
+ * binding (indirectly through the module table and the reference symbol
+ * table when this is a dynamically linked shared library file).
+ */
+ uint32_t ilocalsym; /* index to local symbols */
+ uint32_t nlocalsym; /* number of local symbols */
+
+ uint32_t iextdefsym;/* index to externally defined symbols */
+ uint32_t nextdefsym;/* number of externally defined symbols */
+
+ uint32_t iundefsym; /* index to undefined symbols */
+ uint32_t nundefsym; /* number of undefined symbols */
+
+ /*
+ * For the for the dynamic binding process to find which module a symbol
+ * is defined in the table of contents is used (analogous to the ranlib
+ * structure in an archive) which maps defined external symbols to modules
+ * they are defined in. This exists only in a dynamically linked shared
+ * library file. For executable and object modules the defined external
+ * symbols are sorted by name and is use as the table of contents.
+ */
+ uint32_t tocoff; /* file offset to table of contents */
+ uint32_t ntoc; /* number of entries in table of contents */
+
+ /*
+ * To support dynamic binding of "modules" (whole object files) the symbol
+ * table must reflect the modules that the file was created from. This is
+ * done by having a module table that has indexes and counts into the merged
+ * tables for each module. The module structure that these two entries
+ * refer to is described below. This exists only in a dynamically linked
+ * shared library file. For executable and object modules the file only
+ * contains one module so everything in the file belongs to the module.
+ */
+ uint32_t modtaboff; /* file offset to module table */
+ uint32_t nmodtab; /* number of module table entries */
+
+ /*
+ * To support dynamic module binding the module structure for each module
+ * indicates the external references (defined and undefined) each module
+ * makes. For each module there is an offset and a count into the
+ * reference symbol table for the symbols that the module references.
+ * This exists only in a dynamically linked shared library file. For
+ * executable and object modules the defined external symbols and the
+ * undefined external symbols indicates the external references.
+ */
+ uint32_t extrefsymoff; /* offset to referenced symbol table */
+ uint32_t nextrefsyms; /* number of referenced symbol table entries */
+
+ /*
+ * The sections that contain "symbol pointers" and "routine stubs" have
+ * indexes and (implied counts based on the size of the section and fixed
+ * size of the entry) into the "indirect symbol" table for each pointer
+ * and stub. For every section of these two types the index into the
+ * indirect symbol table is stored in the section header in the field
+ * reserved1. An indirect symbol table entry is simply a 32bit index into
+ * the symbol table to the symbol that the pointer or stub is referring to.
+ * The indirect symbol table is ordered to match the entries in the section.
+ */
+ uint32_t indirectsymoff; /* file offset to the indirect symbol table */
+ uint32_t nindirectsyms; /* number of indirect symbol table entries */
+
+ /*
+ * To support relocating an individual module in a library file quickly the
+ * external relocation entries for each module in the library need to be
+ * accessed efficiently. Since the relocation entries can't be accessed
+ * through the section headers for a library file they are separated into
+ * groups of local and external entries further grouped by module. In this
+ * case the presents of this load command who's extreloff, nextrel,
+ * locreloff and nlocrel fields are non-zero indicates that the relocation
+ * entries of non-merged sections are not referenced through the section
+ * structures (and the reloff and nreloc fields in the section headers are
+ * set to zero).
+ *
+ * Since the relocation entries are not accessed through the section headers
+ * this requires the r_address field to be something other than a section
+ * offset to identify the item to be relocated. In this case r_address is
+ * set to the offset from the vmaddr of the first LC_SEGMENT command.
+ * For MH_SPLIT_SEGS images r_address is set to the the offset from the
+ * vmaddr of the first read-write LC_SEGMENT command.
+ *
+ * The relocation entries are grouped by module and the module table
+ * entries have indexes and counts into them for the group of external
+ * relocation entries for that the module.
+ *
+ * For sections that are merged across modules there must not be any
+ * remaining external relocation entries for them (for merged sections
+ * remaining relocation entries must be local).
+ */
+ uint32_t extreloff; /* offset to external relocation entries */
+ uint32_t nextrel; /* number of external relocation entries */
+
+ /*
+ * All the local relocation entries are grouped together (they are not
+ * grouped by their module since they are only used if the object is moved
+ * from it staticly link edited address).
+ */
+ uint32_t locreloff; /* offset to local relocation entries */
+ uint32_t nlocrel; /* number of local relocation entries */
+
+};
+
+/*
+ * An indirect symbol table entry is simply a 32bit index into the symbol table
+ * to the symbol that the pointer or stub is refering to. Unless it is for a
+ * non-lazy symbol pointer section for a defined symbol which strip(1) as
+ * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the
+ * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.
+ */
+#define INDIRECT_SYMBOL_LOCAL 0x80000000
+#define INDIRECT_SYMBOL_ABS 0x40000000
+
+
+/* a table of contents entry */
+struct dylib_table_of_contents {
+ uint32_t symbol_index; /* the defined external symbol
+ (index into the symbol table) */
+ uint32_t module_index; /* index into the module table this symbol
+ is defined in */
+};
+
+/* a module table entry */
+struct dylib_module {
+ uint32_t module_name; /* the module name (index into string table) */
+
+ uint32_t iextdefsym; /* index into externally defined symbols */
+ uint32_t nextdefsym; /* number of externally defined symbols */
+ uint32_t irefsym; /* index into reference symbol table */
+ uint32_t nrefsym; /* number of reference symbol table entries */
+ uint32_t ilocalsym; /* index into symbols for local symbols */
+ uint32_t nlocalsym; /* number of local symbols */
+
+ uint32_t iextrel; /* index into external relocation entries */
+ uint32_t nextrel; /* number of external relocation entries */
+
+ uint32_t iinit_iterm; /* low 16 bits are the index into the init
+ section, high 16 bits are the index into
+ the term section */
+ uint32_t ninit_nterm; /* low 16 bits are the number of init section
+ entries, high 16 bits are the number of
+ term section entries */
+
+ uint32_t /* for this module address of the start of */
+ objc_module_info_addr; /* the (__OBJC,__module_info) section */
+ uint32_t /* for this module size of */
+ objc_module_info_size; /* the (__OBJC,__module_info) section */
+};
+
+/* a 64-bit module table entry */
+struct dylib_module_64 {
+ uint32_t module_name; /* the module name (index into string table) */
+
+ uint32_t iextdefsym; /* index into externally defined symbols */
+ uint32_t nextdefsym; /* number of externally defined symbols */
+ uint32_t irefsym; /* index into reference symbol table */
+ uint32_t nrefsym; /* number of reference symbol table entries */
+ uint32_t ilocalsym; /* index into symbols for local symbols */
+ uint32_t nlocalsym; /* number of local symbols */
+
+ uint32_t iextrel; /* index into external relocation entries */
+ uint32_t nextrel; /* number of external relocation entries */
+
+ uint32_t iinit_iterm; /* low 16 bits are the index into the init
+ section, high 16 bits are the index into
+ the term section */
+ uint32_t ninit_nterm; /* low 16 bits are the number of init section
+ entries, high 16 bits are the number of
+ term section entries */
+
+ uint32_t /* for this module size of */
+ objc_module_info_size; /* the (__OBJC,__module_info) section */
+ uint64_t /* for this module address of the start of */
+ objc_module_info_addr; /* the (__OBJC,__module_info) section */
+};
+
+/*
+ * The entries in the reference symbol table are used when loading the module
+ * (both by the static and dynamic link editors) and if the module is unloaded
+ * or replaced. Therefore all external symbols (defined and undefined) are
+ * listed in the module's reference table. The flags describe the type of
+ * reference that is being made. The constants for the flags are defined in
+ * <mach-o/nlist.h> as they are also used for symbol table entries.
+ */
+struct dylib_reference {
+ uint32_t isym:24, /* index into the symbol table */
+ flags:8; /* flags to indicate the type of reference */
+};
+
+/*
+ * The twolevel_hints_command contains the offset and number of hints in the
+ * two-level namespace lookup hints table.
+ */
+struct twolevel_hints_command {
+ uint32_t cmd; /* LC_TWOLEVEL_HINTS */
+ uint32_t cmdsize; /* sizeof(struct twolevel_hints_command) */
+ uint32_t offset; /* offset to the hint table */
+ uint32_t nhints; /* number of hints in the hint table */
+};
+
+/*
+ * The entries in the two-level namespace lookup hints table are twolevel_hint
+ * structs. These provide hints to the dynamic link editor where to start
+ * looking for an undefined symbol in a two-level namespace image. The
+ * isub_image field is an index into the sub-images (sub-frameworks and
+ * sub-umbrellas list) that made up the two-level image that the undefined
+ * symbol was found in when it was built by the static link editor. If
+ * isub-image is 0 the the symbol is expected to be defined in library and not
+ * in the sub-images. If isub-image is non-zero it is an index into the array
+ * of sub-images for the umbrella with the first index in the sub-images being
+ * 1. The array of sub-images is the ordered list of sub-images of the umbrella
+ * that would be searched for a symbol that has the umbrella recorded as its
+ * primary library. The table of contents index is an index into the
+ * library's table of contents. This is used as the starting point of the
+ * binary search or a directed linear search.
+ */
+struct twolevel_hint {
+ uint32_t
+ isub_image:8, /* index into the sub images */
+ itoc:24; /* index into the table of contents */
+};
+
+/*
+ * The prebind_cksum_command contains the value of the original check sum for
+ * prebound files or zero. When a prebound file is first created or modified
+ * for other than updating its prebinding information the value of the check sum
+ * is set to zero. When the file has it prebinding re-done and if the value of
+ * the check sum is zero the original check sum is calculated and stored in
+ * cksum field of this load command in the output file. If when the prebinding
+ * is re-done and the cksum field is non-zero it is left unchanged from the
+ * input file.
+ */
+struct prebind_cksum_command {
+ uint32_t cmd; /* LC_PREBIND_CKSUM */
+ uint32_t cmdsize; /* sizeof(struct prebind_cksum_command) */
+ uint32_t cksum; /* the check sum or zero */
+};
+
+/*
+ * The uuid load command contains a single 128-bit unique random number that
+ * identifies an object produced by the static link editor.
+ */
+struct uuid_command {
+ uint32_t cmd; /* LC_UUID */
+ uint32_t cmdsize; /* sizeof(struct uuid_command) */
+ uint8_t uuid[16]; /* the 128-bit uuid */
+};
+
+/*
+ * The rpath_command contains a path which at runtime should be added to
+ * the current run path used to find @rpath prefixed dylibs.
+ */
+struct rpath_command {
+ uint32_t cmd; /* LC_RPATH */
+ uint32_t cmdsize; /* includes string */
+ union lc_str path; /* path to add to run path */
+};
+
+/*
+ * The linkedit_data_command contains the offsets and sizes of a blob
+ * of data in the __LINKEDIT segment.
+ */
+struct linkedit_data_command {
+ uint32_t cmd; /* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO,
+ LC_FUNCTION_STARTS, LC_DATA_IN_CODE,
+ LC_DYLIB_CODE_SIGN_DRS,
+ LC_LINKER_OPTIMIZATION_HINT,
+ LC_DYLD_EXPORTS_TRIE, or
+ LC_DYLD_CHAINED_FIXUPS. */
+ uint32_t cmdsize; /* sizeof(struct linkedit_data_command) */
+ uint32_t dataoff; /* file offset of data in __LINKEDIT segment */
+ uint32_t datasize; /* file size of data in __LINKEDIT segment */
+};
+
+/*
+ * The encryption_info_command contains the file offset and size of an
+ * of an encrypted segment.
+ */
+struct encryption_info_command {
+ uint32_t cmd; /* LC_ENCRYPTION_INFO */
+ uint32_t cmdsize; /* sizeof(struct encryption_info_command) */
+ uint32_t cryptoff; /* file offset of encrypted range */
+ uint32_t cryptsize; /* file size of encrypted range */
+ uint32_t cryptid; /* which enryption system,
+ 0 means not-encrypted yet */
+};
+
+/*
+ * The encryption_info_command_64 contains the file offset and size of an
+ * of an encrypted segment (for use in x86_64 targets).
+ */
+struct encryption_info_command_64 {
+ uint32_t cmd; /* LC_ENCRYPTION_INFO_64 */
+ uint32_t cmdsize; /* sizeof(struct encryption_info_command_64) */
+ uint32_t cryptoff; /* file offset of encrypted range */
+ uint32_t cryptsize; /* file size of encrypted range */
+ uint32_t cryptid; /* which enryption system,
+ 0 means not-encrypted yet */
+ uint32_t pad; /* padding to make this struct's size a multiple
+ of 8 bytes */
+};
+
+/*
+ * The version_min_command contains the min OS version on which this
+ * binary was built to run.
+ */
+struct version_min_command {
+ uint32_t cmd; /* LC_VERSION_MIN_MACOSX or
+ LC_VERSION_MIN_IPHONEOS or
+ LC_VERSION_MIN_WATCHOS or
+ LC_VERSION_MIN_TVOS */
+ uint32_t cmdsize; /* sizeof(struct min_version_command) */
+ uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+};
+
+/*
+ * The build_version_command contains the min OS version on which this
+ * binary was built to run for its platform. The list of known platforms and
+ * tool values following it.
+ */
+struct build_version_command {
+ uint32_t cmd; /* LC_BUILD_VERSION */
+ uint32_t cmdsize; /* sizeof(struct build_version_command) plus */
+ /* ntools * sizeof(struct build_tool_version) */
+ uint32_t platform; /* platform */
+ uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
+ uint32_t ntools; /* number of tool entries following this */
+};
+
+struct build_tool_version {
+ uint32_t tool; /* enum for the tool */
+ uint32_t version; /* version number of the tool */
+};
+
+/* Known values for the platform field above. */
+#define PLATFORM_MACOS 1
+#define PLATFORM_IOS 2
+#define PLATFORM_TVOS 3
+#define PLATFORM_WATCHOS 4
+#define PLATFORM_BRIDGEOS 5
+#define PLATFORM_IOSMAC 6
+#define PLATFORM_IOSSIMULATOR 7
+#define PLATFORM_TVOSSIMULATOR 8
+#define PLATFORM_WATCHOSSIMULATOR 9
+#define PLATFORM_DRIVERKIT 10
+
+/* Known values for the tool field above. */
+#define TOOL_CLANG 1
+#define TOOL_SWIFT 2
+#define TOOL_LD 3
+
+/*
+ * The dyld_info_command contains the file offsets and sizes of
+ * the new compressed form of the information dyld needs to
+ * load the image. This information is used by dyld on Mac OS X
+ * 10.6 and later. All information pointed to by this command
+ * is encoded using byte streams, so no endian swapping is needed
+ * to interpret it.
+ */
+struct dyld_info_command {
+ uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
+ uint32_t cmdsize; /* sizeof(struct dyld_info_command) */
+
+ /*
+ * Dyld rebases an image whenever dyld loads it at an address different
+ * from its preferred address. The rebase information is a stream
+ * of byte sized opcodes whose symbolic names start with REBASE_OPCODE_.
+ * Conceptually the rebase information is a table of tuples:
+ * <seg-index, seg-offset, type>
+ * The opcodes are a compressed way to encode the table by only
+ * encoding when a column changes. In addition simple patterns
+ * like "every n'th offset for m times" can be encoded in a few
+ * bytes.
+ */
+ uint32_t rebase_off; /* file offset to rebase info */
+ uint32_t rebase_size; /* size of rebase info */
+
+ /*
+ * Dyld binds an image during the loading process, if the image
+ * requires any pointers to be initialized to symbols in other images.
+ * The bind information is a stream of byte sized
+ * opcodes whose symbolic names start with BIND_OPCODE_.
+ * Conceptually the bind information is a table of tuples:
+ * <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>
+ * The opcodes are a compressed way to encode the table by only
+ * encoding when a column changes. In addition simple patterns
+ * like for runs of pointers initialzed to the same value can be
+ * encoded in a few bytes.
+ */
+ uint32_t bind_off; /* file offset to binding info */
+ uint32_t bind_size; /* size of binding info */
+
+ /*
+ * Some C++ programs require dyld to unique symbols so that all
+ * images in the process use the same copy of some code/data.
+ * This step is done after binding. The content of the weak_bind
+ * info is an opcode stream like the bind_info. But it is sorted
+ * alphabetically by symbol name. This enable dyld to walk
+ * all images with weak binding information in order and look
+ * for collisions. If there are no collisions, dyld does
+ * no updating. That means that some fixups are also encoded
+ * in the bind_info. For instance, all calls to "operator new"
+ * are first bound to libstdc++.dylib using the information
+ * in bind_info. Then if some image overrides operator new
+ * that is detected when the weak_bind information is processed
+ * and the call to operator new is then rebound.
+ */
+ uint32_t weak_bind_off; /* file offset to weak binding info */
+ uint32_t weak_bind_size; /* size of weak binding info */
+
+ /*
+ * Some uses of external symbols do not need to be bound immediately.
+ * Instead they can be lazily bound on first use. The lazy_bind
+ * are contains a stream of BIND opcodes to bind all lazy symbols.
+ * Normal use is that dyld ignores the lazy_bind section when
+ * loading an image. Instead the static linker arranged for the
+ * lazy pointer to initially point to a helper function which
+ * pushes the offset into the lazy_bind area for the symbol
+ * needing to be bound, then jumps to dyld which simply adds
+ * the offset to lazy_bind_off to get the information on what
+ * to bind.
+ */
+ uint32_t lazy_bind_off; /* file offset to lazy binding info */
+ uint32_t lazy_bind_size; /* size of lazy binding infs */
+
+ /*
+ * The symbols exported by a dylib are encoded in a trie. This
+ * is a compact representation that factors out common prefixes.
+ * It also reduces LINKEDIT pages in RAM because it encodes all
+ * information (name, address, flags) in one small, contiguous range.
+ * The export area is a stream of nodes. The first node sequentially
+ * is the start node for the trie.
+ *
+ * Nodes for a symbol start with a uleb128 that is the length of
+ * the exported symbol information for the string so far.
+ * If there is no exported symbol, the node starts with a zero byte.
+ * If there is exported info, it follows the length.
+ *
+ * First is a uleb128 containing flags. Normally, it is followed by
+ * a uleb128 encoded offset which is location of the content named
+ * by the symbol from the mach_header for the image. If the flags
+ * is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is
+ * a uleb128 encoded library ordinal, then a zero terminated
+ * UTF8 string. If the string is zero length, then the symbol
+ * is re-export from the specified dylib with the same name.
+ * If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following
+ * the flags is two uleb128s: the stub offset and the resolver offset.
+ * The stub is used by non-lazy pointers. The resolver is used
+ * by lazy pointers and must be called to get the actual address to use.
+ *
+ * After the optional exported symbol information is a byte of
+ * how many edges (0-255) that this node has leaving it,
+ * followed by each edge.
+ * Each edge is a zero terminated UTF8 of the addition chars
+ * in the symbol, followed by a uleb128 offset for the node that
+ * edge points to.
+ *
+ */
+ uint32_t export_off; /* file offset to lazy binding info */
+ uint32_t export_size; /* size of lazy binding infs */
+};
+
+/*
+ * The following are used to encode rebasing information
+ */
+#define REBASE_TYPE_POINTER 1
+#define REBASE_TYPE_TEXT_ABSOLUTE32 2
+#define REBASE_TYPE_TEXT_PCREL32 3
+
+#define REBASE_OPCODE_MASK 0xF0
+#define REBASE_IMMEDIATE_MASK 0x0F
+#define REBASE_OPCODE_DONE 0x00
+#define REBASE_OPCODE_SET_TYPE_IMM 0x10
+#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20
+#define REBASE_OPCODE_ADD_ADDR_ULEB 0x30
+#define REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40
+#define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50
+#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60
+#define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70
+#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80
+
+
+/*
+ * The following are used to encode binding information
+ */
+#define BIND_TYPE_POINTER 1
+#define BIND_TYPE_TEXT_ABSOLUTE32 2
+#define BIND_TYPE_TEXT_PCREL32 3
+
+#define BIND_SPECIAL_DYLIB_SELF 0
+#define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1
+#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2
+#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP -3
+
+#define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1
+#define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 0x8
+
+#define BIND_OPCODE_MASK 0xF0
+#define BIND_IMMEDIATE_MASK 0x0F
+#define BIND_OPCODE_DONE 0x00
+#define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10
+#define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20
+#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30
+#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40
+#define BIND_OPCODE_SET_TYPE_IMM 0x50
+#define BIND_OPCODE_SET_ADDEND_SLEB 0x60
+#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70
+#define BIND_OPCODE_ADD_ADDR_ULEB 0x80
+#define BIND_OPCODE_DO_BIND 0x90
+#define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xA0
+#define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xB0
+#define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xC0
+#define BIND_OPCODE_THREADED 0xD0
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
+#define BIND_SUBOPCODE_THREADED_APPLY 0x01
+
+
+/*
+ * The following are used on the flags byte of a terminal node
+ * in the export information.
+ */
+#define EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03
+#define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00
+#define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01
+#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
+#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04
+#define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
+#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
+
+/*
+ * The linker_option_command contains linker options embedded in object files.
+ */
+struct linker_option_command {
+ uint32_t cmd; /* LC_LINKER_OPTION only used in MH_OBJECT filetypes */
+ uint32_t cmdsize;
+ uint32_t count; /* number of strings */
+ /* concatenation of zero terminated UTF8 strings.
+ Zero filled at end to align */
+};
+
+/*
+ * The symseg_command contains the offset and size of the GNU style
+ * symbol table information as described in the header file <symseg.h>.
+ * The symbol roots of the symbol segments must also be aligned properly
+ * in the file. So the requirement of keeping the offsets aligned to a
+ * multiple of a 4 bytes translates to the length field of the symbol
+ * roots also being a multiple of a long. Also the padding must again be
+ * zeroed. (THIS IS OBSOLETE and no longer supported).
+ */
+struct symseg_command {
+ uint32_t cmd; /* LC_SYMSEG */
+ uint32_t cmdsize; /* sizeof(struct symseg_command) */
+ uint32_t offset; /* symbol segment offset */
+ uint32_t size; /* symbol segment size in bytes */
+};
+
+/*
+ * The ident_command contains a free format string table following the
+ * ident_command structure. The strings are null terminated and the size of
+ * the command is padded out with zero bytes to a multiple of 4 bytes/
+ * (THIS IS OBSOLETE and no longer supported).
+ */
+struct ident_command {
+ uint32_t cmd; /* LC_IDENT */
+ uint32_t cmdsize; /* strings that follow this command */
+};
+
+/*
+ * The fvmfile_command contains a reference to a file to be loaded at the
+ * specified virtual address. (Presently, this command is reserved for
+ * internal use. The kernel ignores this command when loading a program into
+ * memory).
+ */
+struct fvmfile_command {
+ uint32_t cmd; /* LC_FVMFILE */
+ uint32_t cmdsize; /* includes pathname string */
+ union lc_str name; /* files pathname */
+ uint32_t header_addr; /* files virtual address */
+};
+
+
+/*
+ * The entry_point_command is a replacement for thread_command.
+ * It is used for main executables to specify the location (file offset)
+ * of main(). If -stack_size was used at link time, the stacksize
+ * field will contain the stack size need for the main thread.
+ */
+struct entry_point_command {
+ uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */
+ uint32_t cmdsize; /* 24 */
+ uint64_t entryoff; /* file (__TEXT) offset of main() */
+ uint64_t stacksize;/* if not zero, initial stack size */
+};
+
+
+/*
+ * The source_version_command is an optional load command containing
+ * the version of the sources used to build the binary.
+ */
+struct source_version_command {
+ uint32_t cmd; /* LC_SOURCE_VERSION */
+ uint32_t cmdsize; /* 16 */
+ uint64_t version; /* A.B.C.D.E packed as a24.b10.c10.d10.e10 */
+};
+
+
+/*
+ * The LC_DATA_IN_CODE load commands uses a linkedit_data_command
+ * to point to an array of data_in_code_entry entries. Each entry
+ * describes a range of data in a code section.
+ */
+struct data_in_code_entry {
+ uint32_t offset; /* from mach_header to start of data range*/
+ uint16_t length; /* number of bytes in data range */
+ uint16_t kind; /* a DICE_KIND_* value */
+};
+#define DICE_KIND_DATA 0x0001
+#define DICE_KIND_JUMP_TABLE8 0x0002
+#define DICE_KIND_JUMP_TABLE16 0x0003
+#define DICE_KIND_JUMP_TABLE32 0x0004
+#define DICE_KIND_ABS_JUMP_TABLE32 0x0005
+
+
+
+/*
+ * Sections of type S_THREAD_LOCAL_VARIABLES contain an array
+ * of tlv_descriptor structures.
+ */
+struct tlv_descriptor
+{
+ void* (*thunk)(struct tlv_descriptor*);
+ unsigned long key;
+ unsigned long offset;
+};
+
+/*
+ * LC_NOTE commands describe a region of arbitrary data included in a Mach-O
+ * file. Its initial use is to record extra data in MH_CORE files.
+ */
+struct note_command {
+ uint32_t cmd; /* LC_NOTE */
+ uint32_t cmdsize; /* sizeof(struct note_command) */
+ char data_owner[16]; /* owner name for this LC_NOTE */
+ uint64_t offset; /* file offset of this data */
+ uint64_t size; /* length of data region */
+};
+
+#endif /* _MACHO_LOADER_H_ */
diff --git a/third_party/xnu/README.crashpad b/third_party/xnu/README.crashpad
new file mode 100644
index 0000000..f8471d3
--- /dev/null
+++ b/third_party/xnu/README.crashpad
@@ -0,0 +1,25 @@
+Name: XNU
+Short Name: xnu
+URL: https://opensource.apple.com/source/xnu/
+URL: https://opensource.apple.com/tarballs/xnu/
+Version: 6153.11.26 (from macOS 10.15.0)
+License: APSL 2.0
+License File: APPLE_LICENSE
+Security Critical: no
+
+Description:
+XNU is the operating system kernel used on macOS and other Apple systems.
+
+Local Modifications:
+ - EXTERNAL_HEADERS/mach-o/loader.h is present. Its #includes of
+ <mach/machine/thread_status.h> and <architecture/byte_order.h> have been
+ commented out as unnecessary. Note that its #includes of <mach/machine.h> and
+ <mach/vm_prot.h> have been retained but these headers have not been provided.
+ External headers must be made available to provide the cpu_type_t,
+ cpu_subtype_t, and vm_prot_t types.
+ - osfmk/mach/exc.defs and osfmk/mach/mach_exc.defs are present, to fill in
+ for <mach/exc.defs> and <mach/mach_exc.defs> on iOS, where they are missing.
+ The .defs files they depend on, <mach/mach_types.defs>,
+ <mach/machine/machine_types.defs>, and <mach/std_types.defs> are also
+ included.
+ - Anything not listed above is omitted.
diff --git a/third_party/xnu/osfmk/mach/exc.defs b/third_party/xnu/osfmk/mach/exc.defs
new file mode 100644
index 0000000..734e740
--- /dev/null
+++ b/third_party/xnu/osfmk/mach/exc.defs
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+ * @OSF_COPYRIGHT@
+ */
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * Abstract:
+ * MiG definitions file for Mach exception interface.
+ */
+
+subsystem
+#if KERNEL_SERVER
+ KernelServer
+#endif /* KERNEL_SERVER */
+
+#if KERNEL_USER
+ KernelUser
+#endif
+ exc 2401;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+ServerPrefix catch_;
+
+type exception_data_t = array[*:2] of integer_t;
+type exception_type_t = int;
+
+routine exception_raise(
+ exception_port : mach_port_t;
+ thread : mach_port_t;
+ task : mach_port_t;
+ exception : exception_type_t;
+ code : exception_data_t
+#if EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+routine exception_raise_state(
+ exception_port : mach_port_t;
+ exception : exception_type_t;
+ code : exception_data_t, const;
+ inout flavor : int;
+ old_state : thread_state_t, const;
+ out new_state : thread_state_t
+#if EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+routine exception_raise_state_identity(
+ exception_port : mach_port_t;
+ thread : mach_port_t;
+ task : mach_port_t;
+ exception : exception_type_t;
+ code : exception_data_t;
+ inout flavor : int;
+ old_state : thread_state_t;
+ out new_state : thread_state_t
+#if EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+/* vim: set ft=c : */
diff --git a/third_party/xnu/osfmk/mach/mach_exc.defs b/third_party/xnu/osfmk/mach/mach_exc.defs
new file mode 100644
index 0000000..5ce6427
--- /dev/null
+++ b/third_party/xnu/osfmk/mach/mach_exc.defs
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+ * @OSF_COPYRIGHT@
+ */
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * Abstract:
+ * MiG definitions file for Mach exception interface.
+ */
+
+subsystem
+#if KERNEL_USER
+ KernelUser
+#endif
+#if KERNEL_SERVER
+ KernelServer
+#endif /* KERNEL_SERVER */
+
+ mach_exc 2405;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+
+ServerPrefix catch_;
+
+type mach_exception_data_t = array[*:2] of int64_t;
+type exception_type_t = int;
+
+routine mach_exception_raise(
+ exception_port : mach_port_t;
+ thread : mach_port_t;
+ task : mach_port_t;
+ exception : exception_type_t;
+ code : mach_exception_data_t
+#if MACH_EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if MACH_EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+routine mach_exception_raise_state(
+ exception_port : mach_port_t;
+ exception : exception_type_t;
+ code : mach_exception_data_t, const;
+ inout flavor : int;
+ old_state : thread_state_t, const;
+ out new_state : thread_state_t
+#if MACH_EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if MACH_EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+routine mach_exception_raise_state_identity(
+ exception_port : mach_port_t;
+ thread : mach_port_t;
+ task : mach_port_t;
+ exception : exception_type_t;
+ code : mach_exception_data_t;
+ inout flavor : int;
+ old_state : thread_state_t;
+ out new_state : thread_state_t
+#if MACH_EXC_SERVER_SECTOKEN
+ ;
+ ServerSecToken stoken : security_token_t
+#endif
+#if MACH_EXC_SERVER_AUDITTOKEN
+ ;
+ ServerAuditToken atoken: audit_token_t
+#endif
+ );
+
+/* vim: set ft=c : */
diff --git a/third_party/xnu/osfmk/mach/mach_types.defs b/third_party/xnu/osfmk/mach/mach_types.defs
new file mode 100644
index 0000000..d2e9fb0
--- /dev/null
+++ b/third_party/xnu/osfmk/mach/mach_types.defs
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+ * @OSF_COPYRIGHT@
+ */
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * NOTICE: This file was modified by McAfee Research in 2004 to introduce
+ * support for mandatory and extensible security protections. This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ */
+/*
+ */
+/*
+ * Mach kernel interface type declarations
+ */
+
+#ifndef _MACH_MACH_TYPES_DEFS_
+#define _MACH_MACH_TYPES_DEFS_
+
+
+#include <mach/std_types.defs>
+
+type memory_object_offset_t = uint64_t;
+type memory_object_size_t = uint64_t;
+type memory_object_cluster_size_t = uint32_t;
+type memory_object_fault_info_t = array[16] of integer_t;
+
+#ifdef KERNEL_PRIVATE
+
+/* Universal Page Lists - restricted to (in-kernel) pagers for now */
+type upl_size_t = uint32_t;
+type upl_offset_t = uint32_t;
+type upl_page_info_t = struct[2] of integer_t;
+type upl_page_info_array_t = array[*:256] of upl_page_info_t;
+
+type upl_t = mach_port_t
+ intran: upl_t convert_port_to_upl(mach_port_t)
+ outtran: mach_port_t convert_upl_to_port(upl_t)
+ destructor: upl_deallocate(upl_t)
+ ;
+
+#endif /* KERNEL_PRIVATE */
+
+type mach_port_status_t = struct[10] of integer_t; /* obsolete */
+type mach_port_info_ext_t = struct[17] of integer_t;
+
+ /* mach_port_info_t: can hold either a
+ * mach_port_status_t (9 ints) or a
+ * mach_port_limits_t (1 int) or a
+ * mach_port_info_ext_t (17 ints). If new flavors of
+ * mach_port_{get,set}_attributes are added, the size of
+ * this array may have to be increased. (See mach/port.h)
+ */
+type mach_port_flavor_t = int;
+type mach_port_info_t = array[*:17] of integer_t;
+
+ /*
+ * mach_msg_max_trailer_t: can hold
+ * mach_msg_trailer_type_t (1 int)
+ * mach_msg_trailer_size_t (1 int)
+ * mach_port_seqno_t (1 int)
+ * security_token_t (2 ints)
+ * audit_token_t (8 ints)
+ * mach_port_context_t (2 ints)
+ * msgh_ad (1 int)
+ * msg_labels_t (1 int)
+ */
+type mach_msg_trailer_type_t = int;
+type mach_msg_trailer_info_t = array[*:68] of char;
+
+type task_t = mach_port_t
+#if KERNEL_SERVER
+ intran: task_t convert_port_to_task(mach_port_t)
+ outtran: mach_port_t convert_task_to_port(task_t)
+ destructor: task_deallocate(task_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type task_name_t = mach_port_t
+#if KERNEL_SERVER
+ intran: task_name_t convert_port_to_task_name(mach_port_t)
+ outtran: mach_port_t convert_task_name_to_port(task_name_t)
+ destructor: task_name_deallocate(task_name_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type task_inspect_t = mach_port_t
+#if KERNEL_SERVER
+ intran: task_inspect_t convert_port_to_task_inspect(mach_port_t)
+ outtran: mach_port_t convert_task_inspect_to_port(task_inspect_t)
+ destructor: task_inspect_deallocate(task_inspect_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type thread_t = mach_port_t
+#if KERNEL_SERVER
+ intran: thread_t convert_port_to_thread(mach_port_t)
+ outtran: mach_port_t convert_thread_to_port(thread_t)
+ destructor: thread_deallocate(thread_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type thread_inspect_t = mach_port_t
+#if KERNEL_SERVER
+ intran: thread_inspect_t convert_port_to_thread_inspect(mach_port_t)
+ outtran: mach_port_t convert_thread_inspect_to_port(thread_inspect_t)
+ destructor: thread_inspect_deallocate(thread_inspect_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type thread_act_t = mach_port_t
+#if KERNEL_SERVER
+ intran: thread_act_t convert_port_to_thread(mach_port_t)
+ outtran: mach_port_t convert_thread_to_port(thread_act_t)
+ destructor: thread_deallocate(thread_act_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type thread_act_consume_ref_t = mach_port_move_send_t
+ cusertype: thread_act_t
+#if KERNEL_SERVER
+ intran: thread_act_t convert_port_to_thread(mach_port_t)
+ destructor: thread_deallocate(thread_act_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+ /* thread_state_t: This inline array can hold
+ * a machine-dependent amount of data, defined in
+ * mach/machine/???? (currently THREAD_STATE_MAX,
+ * in mach/thread_state.h)
+ */
+#include <mach/machine/thread_state.h>
+type thread_state_flavor_t = int;
+type thread_state_t = array[*:THREAD_STATE_MAX] of natural_t;
+
+type task_array_t = ^array[] of task_t;
+type thread_array_t = ^array[] of thread_t;
+type thread_act_array_t = ^array[] of thread_act_t;
+type act_params_t = array[6] of int;
+
+type vm_map_t = mach_port_t
+#if KERNEL_SERVER
+ intran: vm_map_t convert_port_to_map(mach_port_t)
+ destructor: vm_map_deallocate(vm_map_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type vm_task_entry_t = mach_port_t
+ cusertype: vm_map_t
+#if KERNEL_SERVER
+ intran: vm_map_t convert_port_entry_to_map(mach_port_t)
+ destructor: vm_map_deallocate(vm_map_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type ipc_space_t = mach_port_t
+#if KERNEL_SERVER
+ intran: ipc_space_t convert_port_to_space(mach_port_t)
+ destructor: space_deallocate(ipc_space_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type ipc_space_inspect_t = mach_port_t
+#if KERNEL_SERVER
+ intran: ipc_space_inspect_t convert_port_to_space_inspect(mach_port_t)
+ destructor: space_inspect_deallocate(ipc_space_inspect_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type arcade_register_t = mach_port_t
+#if KERNEL_SERVER
+ intran: arcade_register_t convert_port_to_arcade_register(mach_port_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type vm_prot_t = int;
+type vm_inherit_t = int;
+type vm_purgable_t = int;
+type xxx_vm_statistics_data_t = struct[13] of integer_t;
+type vm_behavior_t = int;
+type vm_statistics_data_t = struct[15] of integer_t;
+type vm_machine_attribute_t = int;
+type vm_machine_attribute_val_t = int;
+type vm_sync_t = int;
+
+ /* thread_info_t: this inline array can hold any of:
+ * thread_basic_info_t (10 ints)
+ * policy_timeshare_info_t (5 ints)
+ * policy_fifo_info_t (4 ints)
+ * policy_rr_info_t (5 ints)
+ * thread_extended_info (12 ints + 64 chars)
+ * if other thread_info flavors are added, this
+ * definition may need to be changed. (See
+ * mach/thread_info.h and mach/policy.h) */
+type thread_flavor_t = int;
+type thread_info_t = array[*:32] of integer_t;
+
+type thread_policy_flavor_t = natural_t;
+type thread_policy_t = array[*:16] of integer_t;
+
+ /* task_info_t: this inline array can hold any of:
+ * task_basic_info_32_t (8 ints)
+ * task_basic_info_64_t (10 ints)
+ * task_events_info_t (8 ints)
+ * task_thread_times_info_t (4 ints)
+ * policy_timeshare_info_t (5 ints)
+ * policy_fifo_info_t (4 ints)
+ * policy_rr_info_t (5 ints)
+ * task security token (2 ints)
+ * task audit token (8 ints)
+ * dyld info (2 64-bit ints and 1 int)
+ * task_extmod_info_t (8 64-bit ints)
+ * task_basic_info_64_2_t
+ * mach_task_basic_info_t (12 ints)
+ * task_power_info_t (18 ints)
+ * task_vm_info_t (87 ints)
+ * If other task_info flavors are added, this
+ * definition may need to be changed. (See
+ * mach/task_info.h and mach/policy.h) */
+type task_flavor_t = int;
+type task_info_t = array[*:87] of integer_t;
+
+type task_purgable_info_t = struct[68] of integer_t;
+
+type task_policy_flavor_t = natural_t;
+type task_policy_t = array[*:16] of integer_t;
+
+type task_inspect_flavor_t = natural_t;
+type task_inspect_info_t = array[*:4] of integer_t;
+
+type task_exc_guard_behavior_t = uint32_t;
+
+type mem_entry_name_port_t = mach_port_t
+#if KERNEL_SERVER
+ intran: mem_entry_name_port_t null_conversion(mach_port_t)
+ outtran: mach_port_t null_conversion(mem_entry_name_port_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type mem_entry_name_port_move_send_t = mach_port_move_send_t
+ cusertype: mem_entry_name_port_t
+#if KERNEL_SERVER
+ intran: mem_entry_name_port_t null_conversion(mach_port_t)
+ outtran: mach_port_t null_conversion(mem_entry_name_port_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type memory_object_default_t = mach_port_t
+#if KERNEL_PRIVATE
+ intran: memory_object_default_t null_conversion(mach_port_t)
+ outtran: mach_port_t null_conversion(memory_object_default_t)
+#endif /* KERNEL_PRIVATE */
+ ;
+
+type memory_object_t = mach_port_t
+#if KERNEL_PRIVATE
+ intran: memory_object_t convert_port_to_memory_object(mach_port_t)
+ outtran: mach_port_t convert_memory_object_to_port(memory_object_t)
+#endif /* KERNEL_PRIVATE */
+ ;
+
+
+type memory_object_control_t = mach_port_t
+#if KERNEL_PRIVATE
+ intran: memory_object_control_t convert_port_to_mo_control(mach_port_t)
+ outtran: mach_port_t convert_mo_control_to_port(memory_object_control_t)
+ destructor: memory_object_control_deallocate(memory_object_control_t)
+#endif /* KERNEL_PRIVATE */
+ ;
+
+type memory_object_name_t = mach_port_t
+ ctype: mach_port_t
+ ;
+
+
+type memory_object_copy_strategy_t = int;
+type memory_object_return_t = int;
+
+type machine_info_data_t = struct[5] of integer_t;
+type machine_slot_data_t = struct[8] of integer_t;
+
+type host_t = mach_port_t
+#if KERNEL_SERVER
+ intran: host_t convert_port_to_host(mach_port_t)
+ outtran: mach_port_t convert_host_to_port(host_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type host_priv_t = mach_port_t
+#if KERNEL_SERVER
+ intran: host_priv_t convert_port_to_host_priv(mach_port_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type host_security_t = mach_port_t
+#if KERNEL_SERVER
+ intran: host_security_t convert_port_to_host_security(mach_port_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+ /*
+ * host_info_t: variable-sized inline array that can contain:
+ *
+ * host_basic_info_old_t (5 ints)
+ * host_basic_info_t (12 ints)
+ * host_sched_info_t (2 ints)
+ * kernel_resource_sizes_t (5 ints)
+ * host_load_info_t (6 ints)
+ * vm_statistics32_t (15 ints)
+ * host_purgable_info_t (68 ints)
+ * host_expired_task_info uses a task_power_info (18 ints)
+ *
+ * If other host_info flavors are added, this definition may
+ * need to be changed. (See mach/{host_info,vm_statistics}.h)
+ */
+type host_flavor_t = int;
+type host_info_t = array[*:68] of integer_t;
+ /*
+ * host_info64_t: variable-sized inline array that can contain:
+ *
+ * vm_statistics_t (6 ints and 9 longs)
+ * vm_extmod_statistics_t (6 64-bit ints)
+ */
+type host_info64_t = array[*:256] of integer_t;
+
+type processor_t = mach_port_t
+#if KERNEL_SERVER
+ intran: processor_t convert_port_to_processor(mach_port_t)
+ outtran: mach_port_t convert_processor_to_port(processor_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type processor_array_t = ^array[] of processor_t;
+
+ /*
+ * processor_info_t: variable-sized inline array that can
+ * contain:
+ *
+ * - processor_basic_info_t: (5 ints)
+ * - processor_cpu_load_info_t: (4 ints)
+ * - processor_machine_info_t: (12 ints)
+ * - processor_cpu_stat_t: (10 ints)
+ * - processor_cpu_stat64_t: (20 ints)
+ *
+ * If other processor_info flavors are added, this definition
+ * may need to be changed.
+ *
+ * See mach/processor_info.h and mach/arm/processor_info.h.
+ */
+
+type processor_flavor_t = int;
+type processor_info_t = array[*:20] of integer_t;
+type processor_info_array_t = ^array[] of integer_t;
+
+type processor_set_t = mach_port_t
+#if KERNEL_SERVER
+ intran: processor_set_t convert_port_to_pset(mach_port_t)
+ outtran: mach_port_t convert_pset_to_port(processor_set_t)
+ destructor: pset_deallocate(processor_set_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type processor_set_array_t = ^array[] of processor_set_t;
+
+type processor_set_name_t = mach_port_t
+#if KERNEL_SERVER
+ intran: processor_set_name_t convert_port_to_pset_name(mach_port_t)
+ outtran: mach_port_t convert_pset_name_to_port(processor_set_name_t)
+ destructor: pset_deallocate(processor_set_name_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type processor_set_name_array_t = ^array[] of processor_set_name_t;
+
+ /* processor_set_info_t: variable-size inline array
+ * that can hold:
+ * processor_set_basic_info (5 ints)
+ * processor_set_load_info (4 ints)
+ * policy_timeshare_base_t (1 int)
+ * policy_fifo_base_t (1 int)
+ * policy_rr_base_t (1 int)
+ * policy_timeshare_base_t (1 int)
+ * policy_fifo_base_t (1 int)
+ * policy_rr_base_t (1 int)
+ * policy_t (1 int)
+ * If other flavors are added, this definition may
+ * need to be changed. (see mach/processor.h) */
+type processor_set_flavor_t = int;
+type processor_set_info_t = array[*:5] of integer_t;
+
+type bootstrap_t = mach_port_t;
+
+type kernel_version_t = c_string[*:512];
+type kernel_boot_info_t = c_string[*:4096];
+
+type time_value_t = struct[2] of integer_t;
+
+type mach_port_qos_t = struct[2] of integer_t;
+
+type mach_port_options_t = struct[3] of uint64_t;
+type mach_port_options_ptr_t = ^ mach_port_options_t;
+
+type emulation_vector_t = ^array[] of vm_offset_t;
+
+type inline_existence_map_t = array[*:512] of char;
+
+type policy_t = int;
+ /* policy_info_t: variable-size inline array. Can hold:
+ * policy_timeshare_info_t (5 ints)
+ * policy_fifo_info_t (4 ints)
+ * policy_rr_info_t (5 ints) */
+type policy_base_t = array[*:5] of integer_t;
+type policy_info_t = array[*:2] of integer_t;
+type policy_limit_t = array[*:1] of integer_t;
+
+type ledger_t = mach_port_t
+#if KERNEL_SERVER
+ intran: ledger_t convert_port_to_ledger(mach_port_t)
+ outtran: mach_port_t convert_ledger_to_port(ledger_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type ledger_array_t = ^array[] of ledger_t;
+type ledger_item_t = integer_t;
+ /* DEPRECATED */
+
+type ledger_amount_t = int64_t;
+
+type security_token_t = struct[2] of uint32_t;
+type audit_token_t = struct[8] of uint32_t;
+
+type msg_labels_t = mach_port_t;
+
+ /* memory_object_info_t: variable-size inline array:
+ * memory_object_attr_info_t (5 ints)
+ * XXX actually it's 6 ints temporarily (object_ready!)
+ * memory_object_behave_info_t (4 ints)
+ * memory_object_perf_info_t (2 ints)
+ * old_memory_object_attr_info_t (3 ints)
+ * If other flavors are added, this definition may
+ * need to be changed. (see mach/memory_object.h) */
+type memory_object_flavor_t = int;
+type memory_object_info_t = array[*:6] of int;
+
+ /* vm_region_info_t: variable-size inline array that can hold:
+ * vm_region_basic_info_t (8 ints)
+ * If other flavors are added, this definition may
+ * need to be changed. (see mach/vm_region.h) */
+type vm_region_flavor_t = int;
+type vm_region_info_t = array[*:10] of int;
+type vm_region_recurse_info_t = array[*:19] of int;
+
+type vm_page_info_flavor_t = int;
+type vm_page_info_t = array[*:32] of int;
+
+type mach_vm_read_entry_t = array[512] of mach_vm_offset_t;
+type vm_read_entry_t = array[512] of vm_offset_t;
+#ifdef VM32_SUPPORT
+type vm32_read_entry_t = array[512] of vm32_offset_t;
+#endif
+
+type exception_mask_t = int;
+type exception_behavior_t = int;
+
+type exception_handler_t = mach_port_t;
+
+type exception_handler_array_t =
+ array[*:32] of exception_handler_t;
+
+type exception_behavior_array_t =
+ array[*:32] of exception_behavior_t;
+
+type exception_flavor_array_t =
+ array[*:32] of thread_state_flavor_t;
+
+type exception_mask_array_t =
+ array[*:32] of exception_mask_t;
+
+type semaphore_t = mach_port_t
+#if KERNEL_SERVER
+ intran: semaphore_t convert_port_to_semaphore(mach_port_t)
+ outtran: mach_port_t convert_semaphore_to_port(semaphore_t)
+ destructor: semaphore_dereference(semaphore_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type semaphore_consume_ref_t = mach_port_move_send_t
+ cusertype: semaphore_t
+#if KERNEL_SERVER
+ intran: semaphore_t convert_port_to_semaphore(mach_port_t)
+ outtran: mach_port_t convert_semaphore_to_port(semaphore_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type lock_set_t = mach_port_t
+#if KERNEL_SERVER
+ intran: lock_set_t convert_port_to_lock_set(mach_port_t)
+ outtran: mach_port_t convert_lock_set_to_port(lock_set_t)
+ destructor: lock_set_dereference(lock_set_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type task_suspension_token_t = mach_port_move_send_once_t
+#if KERNEL_SERVER
+ intran: task_suspension_token_t convert_port_to_task_suspension_token(mach_port_t)
+ outtran: mach_port_t convert_task_suspension_token_to_port(task_suspension_token_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type vfs_path_t = c_string[4096];
+type nspace_path_t = c_string[1024]; /* 1024 == PATH_MAX */
+
+/* public voucher types */
+
+/* Mach voucher object */
+type mach_voucher_t = mach_port_t;
+type mach_voucher_name_t = mach_port_name_t;
+
+type mach_voucher_attr_manager_t = mach_port_t;
+type mach_voucher_attr_control_t = mach_port_t;
+
+/* IPC voucher internal object */
+type ipc_voucher_t = mach_port_t
+#if KERNEL_SERVER
+ intran: ipc_voucher_t convert_port_to_voucher(mach_port_t)
+ outtran: mach_port_t convert_voucher_to_port(ipc_voucher_t)
+ destructor: ipc_voucher_release(ipc_voucher_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+/* IPC voucher attribute control internal object */
+type ipc_voucher_attr_control_t = mach_port_t
+#if KERNEL_SERVER
+ intran: ipc_voucher_attr_control_t convert_port_to_voucher_attr_control(mach_port_t)
+ outtran: mach_port_t convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t)
+ destructor: ipc_voucher_attr_control_release(ipc_voucher_attr_control_t)
+#endif /* KERNEL_SERVER */
+ ;
+
+type mach_voucher_attr_key_t = uint32_t;
+
+type mach_voucher_attr_command_t = uint32_t;
+type mach_voucher_attr_recipe_command_t = uint32_t;
+
+type mach_voucher_attr_content_size_t = uint32_t;
+type mach_voucher_attr_content_t = array[*:4096] of uint8_t;
+type mach_voucher_attr_content_array_t = array[*:5120] of uint8_t;
+
+type mach_voucher_attr_raw_recipe_size_t = uint32_t;
+type mach_voucher_attr_raw_recipe_t = array[*:4096] of uint8_t;
+type mach_voucher_attr_raw_recipe_array_t = array[*:5120] of uint8_t;
+
+type mach_voucher_selector_t = uint32_t;
+
+type mach_voucher_attr_value_handle_t = uint64_t;
+type mach_voucher_attr_value_handle_array_t = array[*:4] of mach_voucher_attr_value_handle_t;
+type mach_voucher_attr_value_reference_t = uint32_t;
+
+/* kernel module loader */
+type kmod_t = int;
+type kmod_control_flavor_t = int;
+
+type kmod_args_t = ^array[] of MACH_MSG_TYPE_BYTE
+ ctype: kmod_args_t;
+
+type io_master_t = mach_port_t;
+type UNDServerRef = mach_port_t;
+
+/* These must be kept in sync with definitions in osfmk/mach/dyld_kernel.h */
+type dyld_kernel_image_info_t = struct[40] of MACH_MSG_TYPE_BYTE;
+type dyld_kernel_image_info_array_t = ^array[] of dyld_kernel_image_info_t;
+type dyld_kernel_process_info_t = struct[64] of MACH_MSG_TYPE_BYTE;
+
+#if KERNEL_SERVER
+#ifdef MACH_KERNEL_PRIVATE
+simport <ipc/ipc_voucher.h>; /* for voucher conversions */
+simport <kern/ipc_kobject.h>; /* for null conversion */
+simport <kern/ipc_tt.h>; /* for task/thread conversion */
+simport <kern/ipc_host.h>; /* for host/processor/pset conversions */
+simport <kern/ipc_sync.h>; /* for lock_set and semaphore conversions */
+simport <kern/ledger.h>; /* for ledger conversions */
+simport <kern/processor.h>; /* for processor conversions */
+simport <kern/sync_lock.h>; /* for lock-set conversions */
+simport <kern/sync_sema.h>; /* for semaphore conversions */
+simport <vm/memory_object.h>; /* for memory object type conversions */
+simport <vm/vm_map.h>; /* for vm_map conversions */
+#if CONFIG_ARCADE
+simport <kern/arcade.h>; /* for arcade_register conversions */
+#endif
+#endif /* MACH_KERNEL_PRIVATE */
+
+simport <kern/ipc_mig.h>; /* pick up kernel-specific MIG things */
+
+#endif /* KERNEL_SERVER */
+
+import <mach/mig.h>;
+import <mach/mach_types.h>;
+
+#endif /* _MACH_MACH_TYPES_DEFS_ */
+
+/* vim: set ft=c : */
diff --git a/third_party/xnu/osfmk/mach/machine/machine_types.defs b/third_party/xnu/osfmk/mach/machine/machine_types.defs
new file mode 100644
index 0000000..f481394
--- /dev/null
+++ b/third_party/xnu/osfmk/mach/machine/machine_types.defs
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2000-2007 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+ * @OSF_COPYRIGHT@
+ */
+
+/*
+ * Header file for basic, machine-dependent data types. arm+i386 version.
+ */
+
+#ifndef _MACH_MACHINE_MACHNINE_TYPES_DEFS
+#define _MACH_MACHINE_MACHNINE_TYPES_DEFS
+
+type short = int16_t;
+type int = int32_t;
+type unsigned = uint32_t;
+
+type float = MACH_MSG_TYPE_REAL_32;
+type double = MACH_MSG_TYPE_REAL_64;
+
+
+/* from ISO/IEC 988:1999 spec */
+/* 7.18.1.4 Integer types capable of hgolding object pointers */
+/*
+ * The [u]intptr_t types for the native
+ * integer type, e.g. 32 or 64 or.. whatever
+ * register size the machine has. They are
+ * used for entities that might be either
+ * [unsigned] integers or pointers, and for
+ * type-casting between the two.
+ *
+ * For instance, the IPC system represents
+ * a port in user space as an integer and
+ * in kernel space as a pointer.
+ */
+#if defined(__LP64__)
+type uintptr_t = uint64_t;
+type intptr_t = int64_t;
+#else
+type uintptr_t = uint32_t;
+type intptr_t = int32_t;
+#endif
+
+/*
+ * These are the legacy Mach types that are
+ * the [rough] equivalents of the standards above.
+ * They were defined in terms of int, not
+ * long int, so they remain separate.
+ */
+#if defined(__LP64__)
+type register_t = int64_t;
+#else
+type register_t = int32_t;
+#endif
+type integer_t = int32_t;
+type natural_t = uint32_t;
+
+/*
+ * These are the VM types that scale with the address
+ * space size of a given process.
+ */
+
+#if defined(__LP64__)
+type vm_address_t = uint64_t;
+type vm_offset_t = uint64_t;
+type vm_size_t = uint64_t;
+#else
+type vm_address_t = natural_t;
+type vm_offset_t = natural_t;
+type vm_size_t = natural_t;
+#endif
+
+/* This is a bit of a hack for arm. We implement the backend with a wide type, but present a native-sized type to callers */
+type mach_port_context_t = uint64_t;
+
+/*
+ * The mach_vm_xxx_t types are sized to hold the
+ * maximum pointer, offset, etc... supported on the
+ * platform.
+ */
+type mach_vm_address_t = uint64_t;
+type mach_vm_offset_t = uint64_t;
+type mach_vm_size_t = uint64_t;
+
+#if MACH_IPC_COMPAT
+/*
+ * For the old IPC interface
+ */
+#define MSG_TYPE_PORT_NAME natural_t
+
+#endif /* MACH_IPC_COMPAT */
+
+/*
+ * These are types used internal to Mach to implement the
+ * legacy 32-bit VM APIs published by the kernel.
+ */
+#define VM32_SUPPORT 1
+
+type vm32_address_t = uint32_t;
+type vm32_offset_t = uint32_t;
+type vm32_size_t = uint32_t;
+
+#endif /* _MACH_MACHINE_MACHNINE_TYPES_DEFS */
+
+/* vim: set ft=c : */
diff --git a/third_party/xnu/osfmk/mach/std_types.defs b/third_party/xnu/osfmk/mach/std_types.defs
new file mode 100644
index 0000000..0b48383
--- /dev/null
+++ b/third_party/xnu/osfmk/mach/std_types.defs
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2002,2000 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+ * @OSF_COPYRIGHT@
+ */
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * Mach kernel standard interface type declarations
+ */
+
+#ifndef _MACH_STD_TYPES_DEFS_
+#define _MACH_STD_TYPES_DEFS_
+
+/* from ISO/IEC 988:1999 spec */
+/* 7.18.1.1 Exact-width integer types */
+
+type int8_t = MACH_MSG_TYPE_INTEGER_8;
+type uint8_t = MACH_MSG_TYPE_INTEGER_8;
+type int16_t = MACH_MSG_TYPE_INTEGER_16;
+type uint16_t = MACH_MSG_TYPE_INTEGER_16;
+type int32_t = MACH_MSG_TYPE_INTEGER_32;
+type uint32_t = MACH_MSG_TYPE_INTEGER_32;
+type int64_t = MACH_MSG_TYPE_INTEGER_64;
+type uint64_t = MACH_MSG_TYPE_INTEGER_64;
+
+/*
+ * Legacy fixed-length Mach types which should
+ * be replaced with the Standard types from above.
+ */
+type int32 = int32_t;
+type unsigned32 = uint32_t;
+type int64 = int64_t;
+type unsigned64 = uint64_t;
+
+/*
+ * Other fixed length Mach types.
+ */
+type char = MACH_MSG_TYPE_CHAR;
+type boolean_t = MACH_MSG_TYPE_BOOLEAN;
+
+#include <mach/machine/machine_types.defs>
+
+type kern_return_t = int;
+
+type pointer_t = ^array[] of MACH_MSG_TYPE_BYTE
+ ctype: vm_offset_t;
+
+
+type mach_port_t = MACH_MSG_TYPE_COPY_SEND;
+type mach_port_array_t = array[] of mach_port_t;
+
+type mach_port_name_t = MACH_MSG_TYPE_PORT_NAME;
+type mach_port_name_array_t = array[] of mach_port_name_t;
+
+type mach_port_right_t = natural_t;
+
+type mach_port_type_t = natural_t;
+type mach_port_type_array_t = array[] of mach_port_type_t;
+
+type mach_port_urefs_t = natural_t;
+type mach_port_delta_t = integer_t;
+type mach_port_seqno_t = natural_t;
+type mach_port_mscount_t = unsigned;
+type mach_port_msgcount_t = unsigned;
+type mach_port_rights_t = unsigned;
+type mach_msg_id_t = integer_t;
+type mach_msg_size_t = natural_t;
+type mach_msg_type_name_t = unsigned;
+type mach_msg_options_t = integer_t;
+
+type mach_port_move_receive_t = MACH_MSG_TYPE_MOVE_RECEIVE
+ ctype: mach_port_t;
+type mach_port_copy_send_t = MACH_MSG_TYPE_COPY_SEND
+ ctype: mach_port_t;
+type mach_port_make_send_t = MACH_MSG_TYPE_MAKE_SEND
+ ctype: mach_port_t;
+type mach_port_move_send_t = MACH_MSG_TYPE_MOVE_SEND
+ ctype: mach_port_t;
+type mach_port_make_send_once_t = MACH_MSG_TYPE_MAKE_SEND_ONCE
+ ctype: mach_port_t;
+type mach_port_move_send_once_t = MACH_MSG_TYPE_MOVE_SEND_ONCE
+ ctype: mach_port_t;
+
+type mach_port_receive_t = MACH_MSG_TYPE_PORT_RECEIVE
+ ctype: mach_port_t;
+type mach_port_send_t = MACH_MSG_TYPE_PORT_SEND
+ ctype: mach_port_t;
+type mach_port_send_once_t = MACH_MSG_TYPE_PORT_SEND_ONCE
+ ctype: mach_port_t;
+
+type mach_port_poly_t = polymorphic
+ ctype: mach_port_t;
+
+import <mach/std_types.h>;
+import <mach/mig.h>;
+
+#endif /* _MACH_STD_TYPES_DEFS_ */
+
+/* vim: set ft=c : */
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 0723ba3..e5a2ad3 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -14,7 +14,7 @@
import("../../build/crashpad_buildconfig.gni")
-if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
+if (crashpad_is_in_chromium || crashpad_is_in_fuchsia || crashpad_is_in_dart) {
zlib_source = "external"
} else if (!crashpad_is_win && !crashpad_is_fuchsia) {
zlib_source = "system"
@@ -36,9 +36,7 @@
if (zlib_source == "external") {
group("zlib") {
public_configs = [ ":zlib_config" ]
- public_deps = [
- "//third_party/zlib",
- ]
+ public_deps = [ "//third_party/zlib" ]
}
} else if (zlib_source == "system") {
source_set("zlib") {
@@ -73,9 +71,9 @@
"zlib/uncompr.c",
"zlib/zconf.h",
"zlib/zlib.h",
- "zlib/zlib_crashpad.h",
"zlib/zutil.c",
"zlib/zutil.h",
+ "zlib_crashpad.h",
]
cflags = []
@@ -91,6 +89,12 @@
"/wd4324", # structure was padded due to alignment specifier
"/wd4702", # unreachable code
]
+ if (current_cpu == "arm64" && !crashpad_is_clang) {
+ # Select code path for clang in zlib to avoid using MSVC x86/x64
+ # intrinsics for Windows ARM64.
+ # TODO: https://crashpad.chromium.org/bug/267
+ defines += [ "__clang__" ]
+ }
} else {
defines += [
"HAVE_HIDDEN",
@@ -98,6 +102,10 @@
]
}
+ configs -= [
+ "//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
+ ]
+
if (current_cpu == "x86" || current_cpu == "x64") {
sources += [
"zlib/crc_folding.c",
diff --git a/tools/BUILD.gn b/tools/BUILD.gn
index cd1e95f..5e0228b 100644
--- a/tools/BUILD.gn
+++ b/tools/BUILD.gn
@@ -22,80 +22,83 @@
public_configs = [ "..:crashpad_config" ]
- deps = [
- "../third_party/mini_chromium:base",
- ]
+ deps = [ "../third_party/mini_chromium:base" ]
}
-crashpad_executable("crashpad_database_util") {
- sources = [
- "crashpad_database_util.cc",
- ]
+if (!crashpad_is_ios) {
+ crashpad_executable("crashpad_database_util") {
+ sources = [ "crashpad_database_util.cc" ]
- deps = [
- ":tool_support",
- "../build:default_exe_manifest_win",
- "../client",
- "../compat",
- "../third_party/mini_chromium:base",
- "../util",
- ]
-}
-
-crashpad_executable("crashpad_http_upload") {
- sources = [
- "crashpad_http_upload.cc",
- ]
-
- deps = [
- ":tool_support",
- "../build:default_exe_manifest_win",
- "../compat",
- "../third_party/mini_chromium:base",
- "../util",
- ]
-}
-
-crashpad_executable("generate_dump") {
- sources = [
- "generate_dump.cc",
- ]
-
- deps = [
- ":tool_support",
- "../build:default_exe_manifest_win",
- "../compat",
- "../minidump",
- "../snapshot",
- "../third_party/mini_chromium:base",
- "../util",
- ]
-
- if (crashpad_is_mac) {
- # This would be better as a config so that it could be shared with
- # exception_port_tool, but configs can’t alter “inputs”.
- # https://crbug.com/781858.
- inputs = [
- "mac/sectaskaccess_info.plist",
- ]
- ldflags = [
- "-sectcreate",
- "__TEXT",
- "__info_plist",
- rebase_path(inputs[0], root_build_dir),
+ deps = [
+ ":tool_support",
+ "../build:default_exe_manifest_win",
+ "../client",
+ "../compat",
+ "../third_party/mini_chromium:base",
+ "../util",
]
}
- if (crashpad_is_win) {
- cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
+ crashpad_executable("crashpad_http_upload") {
+ sources = [ "crashpad_http_upload.cc" ]
+
+ deps = [
+ ":tool_support",
+ "../build:default_exe_manifest_win",
+ "../compat",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+ }
+}
+
+crashpad_executable("base94_encoder") {
+ sources = [ "base94_encoder.cc" ]
+ deps = [
+ ":tool_support",
+ "../build:default_exe_manifest_win",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+}
+
+if (!crashpad_is_fuchsia && !crashpad_is_ios) {
+ crashpad_executable("generate_dump") {
+ sources = [ "generate_dump.cc" ]
+
+ deps = [
+ ":tool_support",
+ "../build:default_exe_manifest_win",
+ "../compat",
+ "../minidump",
+ "../snapshot",
+ "../third_party/mini_chromium:base",
+ "../util",
+ ]
+
+ if (crashpad_is_mac) {
+ # This would be better as a config so that it could be shared with
+ # exception_port_tool, but configs can’t alter “inputs”.
+ # https://crbug.com/781858.
+ inputs = [ "mac/sectaskaccess_info.plist" ]
+ ldflags = [
+ "-sectcreate",
+ "__TEXT",
+ "__info_plist",
+ rebase_path(inputs[0], root_build_dir),
+ ]
+ }
+
+ if (crashpad_is_win) {
+ cflags =
+ [ "/wd4201" ] # nonstandard extension used : nameless struct/union
+ }
}
}
if (crashpad_is_mac || crashpad_is_fuchsia) {
crashpad_executable("run_with_crashpad") {
- sources = [
- "run_with_crashpad.cc",
- ]
+ sources = [ "run_with_crashpad.cc" ]
deps = [
":tool_support",
@@ -109,9 +112,7 @@
if (crashpad_is_mac) {
crashpad_executable("catch_exception_tool") {
- sources = [
- "mac/catch_exception_tool.cc",
- ]
+ sources = [ "mac/catch_exception_tool.cc" ]
deps = [
":tool_support",
@@ -122,15 +123,11 @@
}
crashpad_executable("exception_port_tool") {
- sources = [
- "mac/exception_port_tool.cc",
- ]
+ sources = [ "mac/exception_port_tool.cc" ]
# This would be better as a config so that it could be shared with
# generate_dump, but configs can’t alter “inputs”. https://crbug.com/781858.
- inputs = [
- "mac/sectaskaccess_info.plist",
- ]
+ inputs = [ "mac/sectaskaccess_info.plist" ]
ldflags = [
"-sectcreate",
"__TEXT",
@@ -147,9 +144,7 @@
}
crashpad_executable("on_demand_service_tool") {
- sources = [
- "mac/on_demand_service_tool.mm",
- ]
+ sources = [ "mac/on_demand_service_tool.mm" ]
libs = [
"CoreFoundation.framework",
diff --git a/tools/base94_encoder.cc b/tools/base94_encoder.cc
new file mode 100644
index 0000000..1b184e9
--- /dev/null
+++ b/tools/base94_encoder.cc
@@ -0,0 +1,128 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <getopt.h>
+#include <stdio.h>
+
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+#include "tools/tool_support.h"
+#include "util/stream/file_encoder.h"
+
+namespace crashpad {
+namespace {
+
+void Usage(const base::FilePath& me) {
+ fprintf(stderr,
+"Usage: %" PRFilePath " [options] <input-file> <output-file>\n"
+"Encode/Decode the given file\n"
+"\n"
+" -e, --encode compress and encode the input file to a base94 encoded"
+ " file\n"
+" -d, --decode decode and decompress a base94 encoded file\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n",
+ me.value().c_str());
+ ToolSupport::UsageTail(me);
+}
+
+int Base94EncoderMain(int argc, char* argv[]) {
+ const base::FilePath argv0(
+ ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
+ const base::FilePath me(argv0.BaseName());
+
+ enum OptionFlags {
+ // “Short” (single-character) options.
+ kOptionEncode = 'e',
+ kOptionDecode = 'd',
+
+ // Standard options.
+ kOptionHelp = -2,
+ kOptionVersion = -3,
+ };
+
+ struct Options {
+ bool encoding;
+ base::FilePath input_file;
+ base::FilePath output_file;
+ } options = {};
+
+ static constexpr option long_options[] = {
+ {"encode", no_argument, nullptr, kOptionEncode},
+ {"decode", no_argument, nullptr, kOptionDecode},
+ {"help", no_argument, nullptr, kOptionHelp},
+ {"version", no_argument, nullptr, kOptionVersion},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ bool encoding_valid = false;
+ int opt;
+ while ((opt = getopt_long(argc, argv, "de", long_options, nullptr)) != -1) {
+ switch (opt) {
+ case kOptionEncode:
+ options.encoding = true;
+ encoding_valid = true;
+ break;
+ case kOptionDecode:
+ options.encoding = false;
+ encoding_valid = true;
+ break;
+ case kOptionHelp:
+ Usage(me);
+ return EXIT_SUCCESS;
+ case kOptionVersion:
+ ToolSupport::Version(me);
+ return EXIT_SUCCESS;
+ default:
+ ToolSupport::UsageHint(me, nullptr);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!encoding_valid) {
+ ToolSupport::UsageHint(me, "Either -e or -d required");
+ return EXIT_FAILURE;
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc != 2) {
+ ToolSupport::UsageHint(me, "Both input-file and output-file required");
+ return EXIT_FAILURE;
+ }
+
+ options.input_file = base::FilePath(
+ ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
+ options.output_file = base::FilePath(
+ ToolSupport::CommandLineArgumentToFilePathStringType(argv[1]));
+
+ FileEncoder encoder(options.encoding ? crashpad::FileEncoder::Mode::kEncode
+ : crashpad::FileEncoder::Mode::kDecode,
+ options.input_file,
+ options.output_file);
+ return encoder.Process() ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+} // namespace
+} // namespace crashpad
+
+#if defined(OS_POSIX)
+int main(int argc, char* argv[]) {
+ return crashpad::Base94EncoderMain(argc, argv);
+}
+#elif defined(OS_WIN)
+int wmain(int argc, wchar_t* argv[]) {
+ return crashpad::ToolSupport::Wmain(argc, argv, crashpad::Base94EncoderMain);
+}
+#endif // OS_POSIX
diff --git a/tools/base94_encoder.md b/tools/base94_encoder.md
new file mode 100644
index 0000000..292c7a6
--- /dev/null
+++ b/tools/base94_encoder.md
@@ -0,0 +1,100 @@
+<!--
+Copyright 2020 The Crashpad Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+# base94_encoder(1)
+
+## Name
+
+base94_encoder—Encode/Decode the given file
+
+## Synopsis
+
+**base94_encoder** [_OPTION…_] input-file output-file
+
+## Description
+
+Encodes a file for printing safely by compressing and base94 encoding it.
+
+The base94_encoder can decode the input file by base94 decoding and
+uncompressing it.
+
+## Options
+
+ * **-e**, **--encode**
+
+ Compress and encode the input file to a base94 encoded file.
+
+ * **-d**, **--decode**
+
+ Decode and decompress a base94 encoded file.
+
+ * **--help**
+
+ Display help and exit.
+
+ * **--version**
+
+ Output version information and exit.
+
+## Examples
+
+Encode file a to b:
+
+```
+$ base94_encoder --encode a b
+```
+
+Decode file b to a
+
+```
+$ base94_encoder --decode b a
+```
+
+## Exit Status
+
+ * **0**
+
+ Success.
+
+ * **1**
+
+ Failure, with a message printed to the standard error stream.
+
+
+## Resources
+
+Crashpad home page: https://crashpad.chromium.org/.
+
+Report bugs at https://crashpad.chromium.org/bug/new.
+
+## Copyright
+
+Copyright 2020 [The Crashpad
+Authors](https://chromium.googlesource.com/crashpad/crashpad/+/master/AUTHORS).
+
+## License
+
+Licensed under the Apache License, Version 2.0 (the “License”);
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an “AS IS” BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/tools/crashpad_database_util.cc b/tools/crashpad_database_util.cc
index b4c2a15..a003616 100644
--- a/tools/crashpad_database_util.cc
+++ b/tools/crashpad_database_util.cc
@@ -28,8 +28,8 @@
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "client/crash_report_database.h"
@@ -109,14 +109,14 @@
"set",
};
- for (size_t index = 0; index < arraysize(kFalseWords); ++index) {
+ for (size_t index = 0; index < base::size(kFalseWords); ++index) {
if (strcasecmp(string, kFalseWords[index]) == 0) {
*boolean = false;
return true;
}
}
- for (size_t index = 0; index < arraysize(kTrueWords); ++index) {
+ for (size_t index = 0; index < base::size(kTrueWords); ++index) {
if (strcasecmp(string, kTrueWords[index]) == 0) {
*boolean = true;
return true;
@@ -159,7 +159,7 @@
"%+",
};
- for (size_t index = 0; index < arraysize(kFormats); ++index) {
+ for (size_t index = 0; index < base::size(kFormats); ++index) {
tm time_tm;
const char* strptime_result = strptime(string, kFormats[index], &time_tm);
if (strptime_result == end) {
@@ -214,7 +214,7 @@
char string[64];
CHECK_NE(
- strftime(string, arraysize(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm),
+ strftime(string, base::size(string), "%Y-%m-%d %H:%M:%S %Z", &time_tm),
0u);
return std::string(string);
@@ -561,7 +561,7 @@
}
bool used_stdin = false;
- for (const base::FilePath new_report_path : options.new_report_paths) {
+ for (const base::FilePath& new_report_path : options.new_report_paths) {
std::unique_ptr<FileReaderInterface> file_reader;
if (new_report_path.value() == FILE_PATH_LITERAL("-")) {
diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc
index 2cdaed2..2113a1a 100644
--- a/tools/generate_dump.cc
+++ b/tools/generate_dump.cc
@@ -26,11 +26,13 @@
#include "minidump/minidump_file_writer.h"
#include "tools/tool_support.h"
#include "util/file/file_writer.h"
-#include "util/posix/drop_privileges.h"
+#include "util/process/process_id.h"
#include "util/stdlib/string_number_conversion.h"
#if defined(OS_POSIX)
#include <unistd.h>
+
+#include "util/posix/drop_privileges.h"
#endif
#if defined(OS_MACOSX)
@@ -45,11 +47,6 @@
#include "snapshot/win/process_snapshot_win.h"
#include "util/win/scoped_process_suspend.h"
#include "util/win/xp_compat.h"
-#elif defined(OS_FUCHSIA)
-#include "base/fuchsia/scoped_zx_handle.h"
-#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
-#include "util/fuchsia/koid_utilities.h"
-#include "util/fuchsia/scoped_task_suspend.h"
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include "snapshot/linux/process_snapshot_linux.h"
#endif // OS_MACOSX
@@ -90,7 +87,7 @@
struct {
std::string dump_path;
- pid_t pid;
+ ProcessID pid;
bool suspend;
} options = {};
options.suspend = true;
@@ -164,16 +161,11 @@
PLOG(ERROR) << "could not open process " << options.pid;
return EXIT_FAILURE;
}
-#elif defined(OS_FUCHSIA)
- base::ScopedZxHandle task = GetProcessFromKoid(options.pid);
- if (!task.is_valid()) {
- LOG(ERROR) << "could not open process " << options.pid;
- return EXIT_FAILURE;
- }
#endif // OS_MACOSX
if (options.dump_path.empty()) {
- options.dump_path = base::StringPrintf("minidump.%d", options.pid);
+ options.dump_path = base::StringPrintf("minidump.%" PRI_PROCESS_ID,
+ options.pid);
}
{
@@ -187,11 +179,6 @@
if (options.suspend) {
suspend.reset(new ScopedProcessSuspend(process.get()));
}
-#elif defined(OS_FUCHSIA)
- std::unique_ptr<ScopedTaskSuspend> suspend;
- if (options.suspend) {
- suspend.reset(new ScopedTaskSuspend(task.get()));
- }
#endif // OS_MACOSX
#if defined(OS_MACOSX)
@@ -209,11 +196,6 @@
0)) {
return EXIT_FAILURE;
}
-#elif defined(OS_FUCHSIA)
- ProcessSnapshotFuchsia process_snapshot;
- if (!process_snapshot.Initialize(task.get())) {
- return EXIT_FAILURE;
- }
#elif defined(OS_LINUX) || defined(OS_ANDROID)
// TODO(jperaza): https://crashpad.chromium.org/bug/30.
ProcessSnapshotLinux process_snapshot;
diff --git a/tools/mac/catch_exception_tool.cc b/tools/mac/catch_exception_tool.cc
index d4dc3c1..4f40372 100644
--- a/tools/mac/catch_exception_tool.cc
+++ b/tools/mac/catch_exception_tool.cc
@@ -28,6 +28,7 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "tools/tool_support.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"
diff --git a/tools/mac/exception_port_tool.cc b/tools/mac/exception_port_tool.cc
index a047e96..b9d3fdd 100644
--- a/tools/mac/exception_port_tool.cc
+++ b/tools/mac/exception_port_tool.cc
@@ -30,6 +30,7 @@
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "tools/tool_support.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
#include "util/mach/symbolic_constants_mach.h"
diff --git a/util/BUILD.gn b/util/BUILD.gn
index 03220c9..d32863a 100644
--- a/util/BUILD.gn
+++ b/util/BUILD.gn
@@ -13,13 +13,17 @@
# limitations under the License.
import("../build/crashpad_buildconfig.gni")
+import("net/tls.gni")
-declare_args() {
- use_boringssl_for_http_transport_socket = crashpad_is_fuchsia
+if (crashpad_is_in_chromium) {
+ import("//build/config/sanitizers/sanitizers.gni")
+
+ # Prevent Chromium source assignment filters from being inherited.
+ set_sources_assignment_filter([])
}
-if (crashpad_is_mac) {
- if (crashpad_is_in_chromium) {
+if (crashpad_is_mac || crashpad_is_ios) {
+ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
import("//build/config/sysroot.gni")
} else {
import("//third_party/mini_chromium/mini_chromium/build/sysroot.gni")
@@ -27,14 +31,26 @@
action_foreach("mig") {
script = "mach/mig.py"
-
- sources = [
- "$sysroot/usr/include/mach/exc.defs",
- "$sysroot/usr/include/mach/mach_exc.defs",
- "$sysroot/usr/include/mach/notify.defs",
- "mach/child_port.defs",
+ inputs = [
+ "mach/mig_fix.py",
+ "mach/mig_gen.py",
]
+ if (crashpad_is_mac) {
+ sources = [
+ "$sysroot/usr/include/mach/exc.defs",
+ "$sysroot/usr/include/mach/mach_exc.defs",
+ "$sysroot/usr/include/mach/notify.defs",
+ "mach/child_port.defs",
+ ]
+ } else if (crashpad_is_ios) {
+ sources = [
+ # The iOS SDK doesn’t have any .defs files. Get them directly from xnu.
+ "../third_party/xnu/osfmk/mach/exc.defs",
+ "../third_party/xnu/osfmk/mach/mach_exc.defs",
+ ]
+ }
+
outputs = [
"$target_gen_dir/mach/{{source_name_part}}User.c",
"$target_gen_dir/mach/{{source_name_part}}Server.c",
@@ -46,9 +62,20 @@
args += rebase_path(outputs, root_build_dir)
if (crashpad_is_in_chromium) {
if (!use_system_xcode) {
+ import("//build/config/clang/clang.gni")
+ import("//build/config/mac/mac_sdk.gni")
+ clang_path =
+ rebase_path("$clang_base_path/bin/", root_build_dir) + "clang"
+ mig_path = "$mac_bin_path" + "mig"
+ migcom_path = "$mac_bin_path" + "../libexec/migcom"
+
args += [
- "--developer-dir",
- hermetic_xcode_path,
+ "--clang-path",
+ clang_path,
+ "--mig-path",
+ mig_path,
+ "--migcom-path",
+ migcom_path,
]
}
}
@@ -60,8 +87,48 @@
}
args += [
"--include",
+ rebase_path("..", root_build_dir),
+ "--include",
rebase_path("../compat/mac", root_build_dir),
]
+ if (crashpad_is_ios) {
+ args += [
+ "--include",
+ rebase_path("../compat/ios", root_build_dir),
+ ]
+ }
+ if (current_cpu == "x86") {
+ args += [
+ "--arch",
+ "i386",
+ ]
+ } else if (current_cpu == "x64") {
+ args += [
+ "--arch",
+ "x86_64",
+ ]
+ } else if (current_cpu == "arm") {
+ args += [
+ "--arch",
+ "armv7",
+ ]
+ } else if (current_cpu == "arm64") {
+ args += [
+ "--arch",
+ "arm64",
+ ]
+ }
+ }
+
+ static_library("mig_output") {
+ deps = [ ":mig" ]
+ sources = get_target_outputs(":mig")
+ if (crashpad_is_in_chromium) {
+ # mig output contains unreachable code, which irks -Wunreachable-code.
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+ }
+ public_configs = [ "..:crashpad_config" ]
}
}
@@ -79,13 +146,15 @@
"file/file_writer.cc",
"file/file_writer.h",
"file/filesystem.h",
+ "file/output_stream_file_writer.cc",
+ "file/output_stream_file_writer.h",
"file/scoped_remove_file.cc",
"file/scoped_remove_file.h",
"file/string_file.cc",
"file/string_file.h",
"misc/address_sanitizer.h",
"misc/address_types.h",
- "misc/arraysize_unsafe.h",
+ "misc/arraysize.h",
"misc/as_underlying_type.h",
"misc/capture_context.h",
"misc/clock.h",
@@ -97,6 +166,7 @@
"misc/initialization_state_dcheck.h",
"misc/lexing.cc",
"misc/lexing.h",
+ "misc/memory_sanitizer.h",
"misc/metrics.cc",
"misc/metrics.h",
"misc/paths.h",
@@ -136,6 +206,12 @@
"numeric/in_range_cast.h",
"numeric/int128.h",
"numeric/safe_assignment.h",
+ "process/process_id.h",
+ "process/process_memory.cc",
+ "process/process_memory.h",
+ "process/process_memory_native.h",
+ "process/process_memory_range.cc",
+ "process/process_memory_range.h",
"stdlib/aligned_allocator.cc",
"stdlib/aligned_allocator.h",
"stdlib/map_insert.h",
@@ -147,6 +223,17 @@
"stdlib/strnlen.cc",
"stdlib/strnlen.h",
"stdlib/thread_safe_vector.h",
+ "stream/base94_output_stream.cc",
+ "stream/base94_output_stream.h",
+ "stream/file_encoder.cc",
+ "stream/file_encoder.h",
+ "stream/file_output_stream.cc",
+ "stream/file_output_stream.h",
+ "stream/log_output_stream.cc",
+ "stream/log_output_stream.h",
+ "stream/output_stream_interface.h",
+ "stream/zlib_output_stream.cc",
+ "stream/zlib_output_stream.h",
"string/split_string.cc",
"string/split_string.h",
"synchronization/semaphore.h",
@@ -159,6 +246,8 @@
"thread/worker_thread.h",
]
+ defines = [ "ZLIB_CONST" ]
+
if (crashpad_is_posix || crashpad_is_fuchsia) {
sources += [
"file/directory_reader_posix.cc",
@@ -196,22 +285,10 @@
}
}
- if (crashpad_is_mac) {
+ if (crashpad_is_mac || crashpad_is_ios) {
sources += [
- "mac/checked_mach_address_range.h",
- "mac/launchd.h",
- "mac/launchd.mm",
- "mac/mac_util.cc",
- "mac/mac_util.h",
- "mac/service_management.cc",
- "mac/service_management.h",
"mac/xattr.cc",
"mac/xattr.h",
- "mach/child_port_handshake.cc",
- "mach/child_port_handshake.h",
- "mach/child_port_server.cc",
- "mach/child_port_server.h",
- "mach/child_port_types.h",
"mach/composite_mach_message_server.cc",
"mach/composite_mach_message_server.h",
"mach/exc_client_variants.cc",
@@ -222,14 +299,37 @@
"mach/exception_behaviors.h",
"mach/exception_ports.cc",
"mach/exception_ports.h",
- "mach/exception_types.cc",
- "mach/exception_types.h",
"mach/mach_extensions.cc",
"mach/mach_extensions.h",
"mach/mach_message.cc",
"mach/mach_message.h",
"mach/mach_message_server.cc",
"mach/mach_message_server.h",
+ "misc/capture_context_mac.S",
+ "misc/clock_mac.cc",
+ "misc/paths_mac.cc",
+ "synchronization/semaphore_mac.cc",
+ ]
+ }
+
+ if (crashpad_is_mac) {
+ sources += [
+ "mac/checked_mach_address_range.h",
+ "mac/launchd.h",
+ "mac/launchd.mm",
+ "mac/mac_util.cc",
+ "mac/mac_util.h",
+ "mac/service_management.cc",
+ "mac/service_management.h",
+ "mach/bootstrap.cc",
+ "mach/bootstrap.h",
+ "mach/child_port_handshake.cc",
+ "mach/child_port_handshake.h",
+ "mach/child_port_server.cc",
+ "mach/child_port_server.h",
+ "mach/child_port_types.h",
+ "mach/exception_types.cc",
+ "mach/exception_types.h",
"mach/notify_server.cc",
"mach/notify_server.h",
"mach/scoped_task_suspend.cc",
@@ -238,26 +338,30 @@
"mach/symbolic_constants_mach.h",
"mach/task_for_pid.cc",
"mach/task_for_pid.h",
- "mach/task_memory.cc",
- "mach/task_memory.h",
- "misc/capture_context_mac.S",
- "misc/clock_mac.cc",
- "misc/paths_mac.cc",
"net/http_transport_mac.mm",
"posix/process_info_mac.cc",
- "synchronization/semaphore_mac.cc",
+ "process/process_memory_mac.cc",
+ "process/process_memory_mac.h",
]
- sources += get_target_outputs(":mig")
+ }
+
+ if (crashpad_is_ios) {
+ sources += [
+ "ios/exception_processor.h",
+ "ios/exception_processor.mm",
+ "ios/ios_system_data_collector.h",
+ "ios/ios_system_data_collector.mm",
+ ]
}
deps = []
- if (crashpad_is_linux || crashpad_is_fuchsia) {
+ if (crashpad_is_linux || crashpad_is_fuchsia || crashpad_is_android) {
sources += [ "net/http_transport_socket.cc" ]
- if (use_boringssl_for_http_transport_socket) {
- defines = [ "CRASHPAD_USE_BORINGSSL" ]
+ if (crashpad_use_boringssl_for_http_transport_socket) {
+ defines += [ "CRASHPAD_USE_BORINGSSL" ]
- if (crashpad_is_in_fuchsia) {
+ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
deps += [ "//third_party/boringssl" ]
} else {
libs = [
@@ -266,12 +370,16 @@
]
}
}
- } else if (crashpad_is_android) {
- sources += [ "net/http_transport_none.cc" ]
+ }
+
+ if (crashpad_is_android) {
+ sources += [
+ "linux/initial_signal_dispositions.cc",
+ "linux/initial_signal_dispositions.h",
+ ]
}
if (crashpad_is_linux || crashpad_is_android) {
- set_sources_assignment_filter([])
sources += [
"linux/address_types.h",
"linux/auxiliary_vector.cc",
@@ -288,6 +396,8 @@
"linux/memory_map.h",
"linux/proc_stat_reader.cc",
"linux/proc_stat_reader.h",
+ "linux/proc_task_reader.cc",
+ "linux/proc_task_reader.h",
"linux/ptrace_broker.cc",
"linux/ptrace_broker.h",
"linux/ptrace_client.cc",
@@ -295,30 +405,25 @@
"linux/ptrace_connection.h",
"linux/ptracer.cc",
"linux/ptracer.h",
+ "linux/scoped_pr_set_dumpable.cc",
+ "linux/scoped_pr_set_dumpable.h",
"linux/scoped_pr_set_ptracer.cc",
"linux/scoped_pr_set_ptracer.h",
"linux/scoped_ptrace_attach.cc",
"linux/scoped_ptrace_attach.h",
+ "linux/socket.cc",
+ "linux/socket.h",
"linux/thread_info.cc",
"linux/thread_info.h",
"linux/traits.h",
"misc/capture_context_linux.S",
"misc/paths_linux.cc",
+ "misc/time_linux.cc",
"posix/process_info_linux.cc",
"process/process_memory_linux.cc",
"process/process_memory_linux.h",
- ]
- }
-
- if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
- sources += [
- "process/process_memory.cc",
- "process/process_memory.h",
- "process/process_memory_native.h",
-
- # TODO: Port to all platforms.
- "process/process_memory_range.cc",
- "process/process_memory_range.h",
+ "process/process_memory_sanitized.cc",
+ "process/process_memory_sanitized.h",
]
}
@@ -331,12 +436,15 @@
"misc/paths_win.cc",
"misc/time_win.cc",
"net/http_transport_win.cc",
+ "process/process_memory_win.cc",
+ "process/process_memory_win.h",
"synchronization/semaphore_win.cc",
"thread/thread_win.cc",
"win/address_types.h",
"win/checked_win_address_range.h",
"win/command_line.cc",
"win/command_line.h",
+ "win/context_wrappers.h",
"win/critical_section_with_debug_info.cc",
"win/critical_section_with_debug_info.h",
"win/exception_handler_server.cc",
@@ -349,6 +457,8 @@
"win/handle.h",
"win/initial_client_data.cc",
"win/initial_client_data.h",
+ "win/loader_lock.cc",
+ "win/loader_lock.h",
"win/module_version.cc",
"win/module_version.h",
"win/nt_internals.cc",
@@ -386,10 +496,24 @@
# TODO(thakis): Use the .asm file in cross builds somehow,
# https://crbug.com/762167.
if (host_os == "win") {
- sources += [
- "misc/capture_context_win.asm",
- "win/safe_terminate_process.asm",
- ]
+ if (current_cpu != "arm64") {
+ sources += [
+ "misc/capture_context_win.asm",
+ "win/safe_terminate_process.asm",
+ ]
+ } else {
+ # Most Crashpad builds use Microsoft's armasm64.exe macro assembler for
+ # .asm source files. When building in Chromium, clang-cl is used as the
+ # assembler instead. Since the two assemblers recognize different
+ # assembly dialects, the same .asm file can't be used for each. As a
+ # workaround, use a prebuilt .obj file when the Microsoft-dialect
+ # assembler isn't available.
+ if (crashpad_is_in_chromium) {
+ sources += [ "misc/capture_context_win_arm64.obj" ]
+ } else {
+ sources += [ "misc/capture_context_win_arm64.asm" ]
+ }
+ }
} else {
sources += [
"misc/capture_context_broken.cc",
@@ -404,7 +528,6 @@
"fuchsia/koid_utilities.h",
"fuchsia/scoped_task_suspend.cc",
"fuchsia/scoped_task_suspend.h",
- "fuchsia/system_exception_port_key.h",
"misc/capture_context_fuchsia.S",
"misc/paths_fuchsia.cc",
"process/process_memory_fuchsia.cc",
@@ -415,17 +538,24 @@
public_configs = [ "..:crashpad_config" ]
# Include generated files starting with "util".
- include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ]
+ if (crashpad_is_in_fuchsia) {
+ include_dirs = [ "$root_gen_dir/third_party/crashpad" ]
+ } else {
+ include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ]
+ }
public_deps = [
"../compat",
- ]
-
- deps += [
- "../third_party/mini_chromium:base",
"../third_party/zlib",
]
+ deps += [ "../third_party/mini_chromium:base" ]
+
+ if (crashpad_is_mac || crashpad_is_ios) {
+ include_dirs += [ "$root_build_dir/gen" ]
+ deps += [ ":mig_output" ]
+ }
+
if (crashpad_is_mac) {
libs = [
"bsm",
@@ -433,8 +563,7 @@
"Foundation.framework",
"IOKit.framework",
]
- deps += [ ":mig" ]
- include_dirs += [ "$root_build_dir/gen" ]
+ deps += [ "../third_party/apple_cf:apple_cf" ]
}
if (crashpad_is_win) {
@@ -454,25 +583,22 @@
asmflags = [ "/safeseh" ]
}
}
-}
-if (use_boringssl_for_http_transport_socket) {
- action("generate_test_server_key") {
- script = "net/generate_test_server_key.py"
- outputs = [
- "$root_out_dir/crashpad_util_test_cert.pem",
- "$root_out_dir/crashpad_util_test_key.pem",
- ]
- data = outputs
+ if (crashpad_is_fuchsia) {
+ public_deps += [ "../third_party/fuchsia" ]
}
+
+ if (crashpad_is_android || crashpad_is_linux) {
+ deps += [ "../third_party/lss" ]
+ }
+
+ configs += [ "..:disable_ubsan" ]
}
-if (!crashpad_is_android) {
+if (!crashpad_is_android && !crashpad_is_ios) {
crashpad_executable("http_transport_test_server") {
testonly = true
- sources = [
- "net/http_transport_test_server.cc",
- ]
+ sources = [ "net/http_transport_test_server.cc" ]
deps = [
":util",
@@ -490,13 +616,10 @@
libs = [ "ws2_32.lib" ]
}
- if (use_boringssl_for_http_transport_socket) {
- data_deps = [
- ":generate_test_server_key",
- ]
+ if (crashpad_use_boringssl_for_http_transport_socket) {
defines = [ "CRASHPAD_USE_BORINGSSL" ]
- if (crashpad_is_in_fuchsia) {
+ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
deps += [ "//third_party/boringssl" ]
} else {
libs = [
@@ -518,7 +641,7 @@
"file/file_reader_test.cc",
"file/filesystem_test.cc",
"file/string_file_test.cc",
- "misc/arraysize_unsafe_test.cc",
+ "misc/arraysize_test.cc",
"misc/capture_context_test.cc",
"misc/capture_context_test_util.h",
"misc/clock_test.cc",
@@ -542,12 +665,20 @@
"numeric/checked_range_test.cc",
"numeric/in_range_cast_test.cc",
"numeric/int128_test.cc",
+ "process/process_memory_range_test.cc",
+ "process/process_memory_test.cc",
"stdlib/aligned_allocator_test.cc",
"stdlib/map_insert_test.cc",
"stdlib/string_number_conversion_test.cc",
"stdlib/strlcpy_test.cc",
"stdlib/strnlen_test.cc",
"stdlib/thread_safe_vector_test.cc",
+ "stream/base94_output_stream_test.cc",
+ "stream/file_encoder_test.cc",
+ "stream/log_output_stream_test.cc",
+ "stream/test_output_stream.cc",
+ "stream/test_output_stream.h",
+ "stream/zlib_output_stream_test.cc",
"string/split_string_test.cc",
"synchronization/semaphore_test.cc",
"thread/thread_log_messages_test.cc",
@@ -555,13 +686,13 @@
"thread/worker_thread_test.cc",
]
- if (!crashpad_is_android) {
+ if (!crashpad_is_android && !crashpad_is_ios) {
# Android requires an HTTPTransport implementation.
sources += [ "net/http_transport_test.cc" ]
}
if (crashpad_is_posix || crashpad_is_fuchsia) {
- if (!crashpad_is_fuchsia) {
+ if (!crashpad_is_fuchsia && !crashpad_is_ios) {
sources += [
"posix/process_info_test.cc",
"posix/signals_test.cc",
@@ -571,41 +702,59 @@
sources += [ "posix/scoped_mmap_test.cc" ]
}
+ if (crashpad_is_mac || crashpad_is_ios) {
+ sources += [
+ "mac/xattr_test.cc",
+ "mach/composite_mach_message_server_test.cc",
+ "mach/exc_server_variants_test.cc",
+ "mach/exception_behaviors_test.cc",
+ "mach/mach_extensions_test.cc",
+ "mach/mach_message_test.cc",
+ ]
+ }
+
if (crashpad_is_mac) {
sources += [
"mac/launchd_test.mm",
"mac/mac_util_test.mm",
"mac/service_management_test.mm",
- "mac/xattr_test.cc",
+ "mach/bootstrap_test.cc",
"mach/child_port_handshake_test.cc",
"mach/child_port_server_test.cc",
- "mach/composite_mach_message_server_test.cc",
"mach/exc_client_variants_test.cc",
- "mach/exc_server_variants_test.cc",
- "mach/exception_behaviors_test.cc",
"mach/exception_ports_test.cc",
"mach/exception_types_test.cc",
- "mach/mach_extensions_test.cc",
"mach/mach_message_server_test.cc",
- "mach/mach_message_test.cc",
"mach/notify_server_test.cc",
"mach/scoped_task_suspend_test.cc",
"mach/symbolic_constants_mach_test.cc",
- "mach/task_memory_test.cc",
"misc/capture_context_test_util_mac.cc",
+ "process/process_memory_mac_test.cc",
+ ]
+ }
+
+ if (crashpad_is_ios) {
+ sources += [ "ios/exception_processor_test.mm" ]
+
+ sources -= [
+ "misc/capture_context_test.cc",
+ "process/process_memory_range_test.cc",
+ "process/process_memory_test.cc",
]
}
if (crashpad_is_linux || crashpad_is_android) {
- set_sources_assignment_filter([])
sources += [
"linux/auxiliary_vector_test.cc",
"linux/memory_map_test.cc",
"linux/proc_stat_reader_test.cc",
+ "linux/proc_task_reader_test.cc",
"linux/ptrace_broker_test.cc",
"linux/ptracer_test.cc",
"linux/scoped_ptrace_attach_test.cc",
+ "linux/socket_test.cc",
"misc/capture_context_test_util_linux.cc",
+ "process/process_memory_sanitized_test.cc",
]
}
@@ -613,14 +762,6 @@
sources += [ "misc/capture_context_test_util_fuchsia.cc" ]
}
- if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
- sources += [
- # TODO: Port to all platforms.
- "process/process_memory_range_test.cc",
- "process/process_memory_test.cc",
- ]
- }
-
if (crashpad_is_win) {
sources += [
"misc/capture_context_test_util_win.cc",
@@ -630,16 +771,25 @@
"win/get_function_test.cc",
"win/handle_test.cc",
"win/initial_client_data_test.cc",
+ "win/loader_lock_test.cc",
"win/process_info_test.cc",
"win/registration_protocol_win_test.cc",
"win/safe_terminate_process_test.cc",
"win/scoped_process_suspend_test.cc",
"win/session_end_watcher_test.cc",
]
+
+ if (crashpad_is_in_chromium && is_asan && is_component_build) {
+ # TODO(crbug.com/856174): Re-enable these once Windows ASan is fixed.
+ sources -= [ "stdlib/string_number_conversion_test.cc" ]
+ }
}
data = [
- "net/testdata/",
+ "net/testdata/ascii_http_body.txt",
+ "net/testdata/binary_http_body.dat",
+ "net/testdata/crashpad_util_test_cert.pem",
+ "net/testdata/crashpad_util_test_key.pem",
]
deps = [
@@ -653,12 +803,10 @@
"../third_party/zlib",
]
- if (!crashpad_is_android) {
- data_deps = [
- ":http_transport_test_server",
- ]
+ if (!crashpad_is_android && !crashpad_is_ios) {
+ data_deps = [ ":http_transport_test_server" ]
- if (use_boringssl_for_http_transport_socket) {
+ if (crashpad_use_boringssl_for_http_transport_socket) {
defines = [ "CRASHPAD_USE_BORINGSSL" ]
}
}
@@ -667,30 +815,55 @@
libs = [ "Foundation.framework" ]
}
+ if (crashpad_is_ios) {
+ deps += [ ":util_test_bundle_data" ]
+ }
+
+ if (crashpad_is_android || crashpad_is_linux) {
+ deps += [ "../third_party/lss" ]
+ }
+
if (crashpad_is_win) {
libs = [
"rpcrt4.lib",
"dbghelp.lib",
]
data_deps += [
+ ":crashpad_util_test_loader_lock_test",
":crashpad_util_test_process_info_test_child",
":crashpad_util_test_safe_terminate_process_test_child",
]
}
}
+if (crashpad_is_ios) {
+ bundle_data("util_test_bundle_data") {
+ testonly = true
+
+ sources = [
+ "net/testdata/ascii_http_body.txt",
+ "net/testdata/binary_http_body.dat",
+ ]
+
+ outputs = [ "{{bundle_resources_dir}}/crashpad_test_data/" +
+ "{{source_root_relative_dir}}/{{source_file_part}}" ]
+ }
+}
+
if (crashpad_is_win) {
crashpad_executable("crashpad_util_test_process_info_test_child") {
testonly = true
- sources = [
- "win/process_info_test_child.cc",
- ]
+ sources = [ "win/process_info_test_child.cc" ]
}
crashpad_executable("crashpad_util_test_safe_terminate_process_test_child") {
testonly = true
- sources = [
- "win/safe_terminate_process_test_child.cc",
- ]
+ sources = [ "win/safe_terminate_process_test_child.cc" ]
+ }
+
+ crashpad_loadable_module("crashpad_util_test_loader_lock_test") {
+ testonly = true
+ sources = [ "win/loader_lock_test_dll.cc" ]
+ deps = [ ":util" ]
}
}
diff --git a/util/file/delimited_file_reader.cc b/util/file/delimited_file_reader.cc
index a55c2a7..2275ade 100644
--- a/util/file/delimited_file_reader.cc
+++ b/util/file/delimited_file_reader.cc
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
namespace crashpad {
@@ -75,7 +76,7 @@
return Result::kEndOfFile;
}
- DCHECK_LE(static_cast<size_t>(read_result), arraysize(buf_));
+ DCHECK_LE(static_cast<size_t>(read_result), base::size(buf_));
DCHECK(
base::IsValueInRangeForNumericType<decltype(buf_len_)>(read_result));
buf_len_ = static_cast<decltype(buf_len_)>(read_result);
diff --git a/util/file/delimited_file_reader_test.cc b/util/file/delimited_file_reader_test.cc
index 79e331f..a2fd7d5 100644
--- a/util/file/delimited_file_reader_test.cc
+++ b/util/file/delimited_file_reader_test.cc
@@ -17,6 +17,7 @@
#include <vector>
#include "base/format_macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/file/string_file.h"
@@ -259,7 +260,7 @@
TEST(DelimitedFileReader, EmbeddedNUL) {
static constexpr char kString[] = "embedded\0NUL\n";
StringFile string_file;
- string_file.SetString(std::string(kString, arraysize(kString) - 1));
+ string_file.SetString(std::string(kString, base::size(kString) - 1));
DelimitedFileReader delimited_file_reader(&string_file);
std::string line;
@@ -277,7 +278,7 @@
TEST(DelimitedFileReader, NULDelimiter) {
static constexpr char kString[] = "aa\0b\0ccc\0";
StringFile string_file;
- string_file.SetString(std::string(kString, arraysize(kString) - 1));
+ string_file.SetString(std::string(kString, base::size(kString) - 1));
DelimitedFileReader delimited_file_reader(&string_file);
std::string field;
@@ -301,7 +302,7 @@
TEST(DelimitedFileReader, EdgeCases) {
static constexpr size_t kSizes[] =
{4094, 4095, 4096, 4097, 8190, 8191, 8192, 8193};
- for (size_t index = 0; index < arraysize(kSizes); ++index) {
+ for (size_t index = 0; index < base::size(kSizes); ++index) {
size_t size = kSizes[index];
SCOPED_TRACE(
base::StringPrintf("index %" PRIuS ", size %" PRIuS, index, size));
diff --git a/util/file/directory_reader_posix.cc b/util/file/directory_reader_posix.cc
index b33e19b..475b0b1 100644
--- a/util/file/directory_reader_posix.cc
+++ b/util/file/directory_reader_posix.cc
@@ -39,7 +39,7 @@
bool DirectoryReader::Open(const base::FilePath& path) {
dir_.reset(HANDLE_EINTR_IF_EQ(opendir(path.value().c_str()), nullptr));
if (!dir_.is_valid()) {
- PLOG(ERROR) << "opendir";
+ PLOG(ERROR) << "opendir " << path.value();
return false;
}
return true;
@@ -52,7 +52,7 @@
dirent* entry = HANDLE_EINTR_IF_EQ(readdir(dir_.get()), nullptr);
if (!entry) {
if (errno) {
- PLOG(ERROR) << "readdir";
+ PLOG(ERROR) << "readdir " << filename->value();
return Result::kError;
} else {
return Result::kNoMoreFiles;
diff --git a/util/file/directory_reader_test.cc b/util/file/directory_reader_test.cc
index f03669e..812deaf 100644
--- a/util/file/directory_reader_test.cc
+++ b/util/file/directory_reader_test.cc
@@ -22,7 +22,6 @@
#include "base/strings/utf_string_conversions.h"
#include "gtest/gtest.h"
#include "test/filesystem.h"
-#include "test/gtest_disabled.h"
#include "test/scoped_temp_dir.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
@@ -48,7 +47,7 @@
TEST(DirectoryReader, BadPaths_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -144,7 +143,7 @@
TEST(DirectoryReader, FilesAndDirectories_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestFilesAndDirectories(true);
diff --git a/util/file/file_io.h b/util/file/file_io.h
index 797db68..6fa0f96 100644
--- a/util/file/file_io.h
+++ b/util/file/file_io.h
@@ -398,6 +398,30 @@
FileWriteMode mode,
FilePermissions permissions);
+#if defined(OS_LINUX)
+//! \brief Opens an in-memory file for input and output.
+//!
+//! This function first attempts to open the file with `memfd_create()`. If
+//! `memfd_create()` isn't supported by the kernel, this function next attempts
+//! to open a file using `O_TMPFILE`. If `O_TMPFILE` isn't supported, this
+//! function finally falls back to creating a file with a randomized name in
+//! `/tmp` and immediately `unlink()`ing it.
+//!
+//! Unlike other file open operations, this function doesn't set `O_CLOEXEC`.
+//!
+//! \param name A name associated with the file. This name does not indicate any
+//! exact path and may not be used at all, depending on the strategy used to
+//! create the file. The name should not contain any '/' characters.
+//! \return The newly opened FileHandle, or an invalid FileHandle on failure,
+//! with a message logged.
+//!
+//! \sa ScopedFileHandle
+//! \sa LoggingOpenFileForRead
+//! \sa LoggingOpenFileForWrite
+//! \sa LoggingOpenFileForReadAndWrite
+FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name);
+#endif // OS_LINUX
+
//! \brief Wraps OpenFileForReadAndWrite(), logging an error if the operation
//! fails.
//!
diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc
index f311616..91b252a 100644
--- a/util/file/file_io_posix.cc
+++ b/util/file/file_io_posix.cc
@@ -14,8 +14,10 @@
#include "util/file/file_io.h"
+#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
+#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -25,7 +27,9 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/strings/stringprintf.h"
#include "build/build_config.h"
+#include "util/misc/random_string.h"
namespace crashpad {
@@ -97,7 +101,6 @@
flags,
permissions == FilePermissions::kWorldReadable ? 0644 : 0600));
}
-
} // namespace
namespace internal {
@@ -149,6 +152,53 @@
return fd;
}
+#if defined(OS_LINUX)
+FileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name) {
+ DCHECK(name.value().find('/') == std::string::npos);
+
+ int result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
+ if (result >= 0 || errno != ENOSYS) {
+ PLOG_IF(ERROR, result < 0) << "memfd_create";
+ return result;
+ }
+
+ const char* tmp = getenv("TMPDIR");
+ tmp = tmp ? tmp : "/tmp";
+
+ result = HANDLE_EINTR(open(tmp, O_RDWR | O_EXCL | O_TMPFILE, 0600));
+ if (result >= 0 ||
+ // These are the expected possible error codes indicating that O_TMPFILE
+ // doesn't have kernel or filesystem support. O_TMPFILE was added in Linux
+ // 3.11. Experimentation confirms that at least Linux 2.6.29 and Linux
+ // 3.10 set errno to EISDIR. EOPNOTSUPP is returned when the filesystem
+ // doesn't support O_TMPFILE. The man pages also mention ENOENT as an
+ // error code to check, but the language implies it would only occur when
+ // |tmp| is also an invalid directory. EINVAL is mentioned as a possible
+ // error code for any invalid values in flags, but O_TMPFILE isn't
+ // mentioned explicitly in this context and hasn't been observed in
+ // practice.
+ (errno != EISDIR && errno != EOPNOTSUPP)) {
+ PLOG_IF(ERROR, result < 0) << "open";
+ return result;
+ }
+
+ std::string path = base::StringPrintf("%s/%s.%d.%s",
+ tmp,
+ name.value().c_str(),
+ getpid(),
+ RandomString().c_str());
+ result = HANDLE_EINTR(open(path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
+ if (result < 0) {
+ PLOG(ERROR) << "open";
+ return result;
+ }
+ if (unlink(path.c_str()) != 0) {
+ PLOG(WARNING) << "unlink";
+ }
+ return result;
+}
+#endif
+
FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,
FileWriteMode mode,
FilePermissions permissions) {
diff --git a/util/file/file_io_test.cc b/util/file/file_io_test.cc
index 4446c32..0efded8 100644
--- a/util/file/file_io_test.cc
+++ b/util/file/file_io_test.cc
@@ -22,6 +22,7 @@
#include "base/atomicops.h"
#include "base/files/file_path.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/errors.h"
@@ -472,6 +473,23 @@
TestOpenFileForWrite(LoggingOpenFileForReadAndWrite);
}
+#if defined(OS_LINUX)
+TEST(FileIO, LoggingOpenMemoryFileForReadAndWrite) {
+ ScopedFileHandle handle(
+ LoggingOpenMemoryFileForReadAndWrite(base::FilePath("memfile")));
+ ASSERT_TRUE(handle.is_valid());
+
+ static constexpr char kTestData[] = "somedata";
+ ASSERT_TRUE(LoggingWriteFile(handle.get(), kTestData, sizeof(kTestData)));
+
+ ASSERT_EQ(LoggingSeekFile(handle.get(), 0, SEEK_SET), 0);
+
+ char buffer[sizeof(kTestData)];
+ ASSERT_TRUE(LoggingReadFileExactly(handle.get(), buffer, sizeof(buffer)));
+ EXPECT_EQ(memcmp(buffer, kTestData, sizeof(buffer)), 0);
+}
+#endif // OS_LINUX
+
enum class ReadOrWrite : bool {
kRead,
kWrite,
@@ -612,7 +630,7 @@
LockingTestThread threads[20];
int expected_iterations = 0;
- for (size_t index = 0; index < arraysize(threads); ++index) {
+ for (size_t index = 0; index < base::size(threads); ++index) {
int iterations_for_this_thread = static_cast<int>(index * 10);
threads[index].Init(
(other_locks == FileLocking::kShared)
diff --git a/util/file/file_io_win.cc b/util/file/file_io_win.cc
index 7428034..111a8da 100644
--- a/util/file/file_io_win.cc
+++ b/util/file/file_io_win.cc
@@ -28,7 +28,7 @@
// FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous
// pipe. If we are unable to retrieve the pipe information, we know it's a
// socket.
- return !GetNamedPipeInfo(file, NULL, NULL, NULL, NULL);
+ return !GetNamedPipeInfo(file, nullptr, nullptr, nullptr, nullptr);
}
return false;
}
diff --git a/util/file/file_writer.cc b/util/file/file_writer.cc
index 13ac6cf..6dff975 100644
--- a/util/file/file_writer.cc
+++ b/util/file/file_writer.cc
@@ -171,6 +171,23 @@
return true;
}
+#if defined(OS_LINUX)
+bool FileWriter::OpenMemfd(const base::FilePath& path) {
+ CHECK(!file_.is_valid());
+ file_.reset(LoggingOpenMemoryFileForReadAndWrite(path));
+ if (!file_.is_valid()) {
+ return false;
+ }
+
+ weak_file_handle_file_writer_.set_file_handle(file_.get());
+ return true;
+}
+
+int FileWriter::fd() {
+ return file_.get();
+}
+#endif
+
void FileWriter::Close() {
CHECK(file_.is_valid());
diff --git a/util/file/file_writer.h b/util/file/file_writer.h
index ed261ec..4b99b37 100644
--- a/util/file/file_writer.h
+++ b/util/file/file_writer.h
@@ -131,6 +131,22 @@
FileWriteMode write_mode,
FilePermissions permissions);
+#if defined(OS_LINUX)
+ //! \brief Wraps LoggingOpenMemoryFileForWrite().
+ //!
+ //! \return `true` if the operation succeeded, `false` if it failed, with an
+ //! error message logged.
+ //!
+ //! \note After a successful call, this method or Open() cannot be called
+ // again until after Close().
+ bool OpenMemfd(const base::FilePath& path);
+
+ //! \brief Returns the underlying file descriptor.
+ //!
+ //! \note This is used when this writes to a Memfd.
+ int fd();
+#endif
+
//! \brief Wraps CheckedCloseHandle().
//!
//! \note It is only valid to call this method on an object that has had a
diff --git a/util/file/filesystem.h b/util/file/filesystem.h
index 27a4d1d..e12f127 100644
--- a/util/file/filesystem.h
+++ b/util/file/filesystem.h
@@ -73,7 +73,8 @@
//! failure.
//!
//! On POSIX, this function returns `true` if \a path refers to a file that is
-//! not a symbolic link, directory, or other kind of special file.
+//! not a symbolic link, directory, or other kind of special file. If this
+//! function fails because \a path does not exist, then no message is logged.
//!
//! On Windows, this function returns `true` if \a path refers to a file that
//! is not a symbolic link or directory.
@@ -85,6 +86,9 @@
//! \brief Determines if a path refers to a directory, logging a message on
//! failure.
//!
+//! On POSIX, if this function fails because \a path does not exist, then no
+//! message is logged.
+//!
//! \param[in] path The path to check.
//! \param[in] allow_symlinks Whether to allow the final component in the path
//! to be a symbolic link to a directory.
diff --git a/util/file/filesystem_posix.cc b/util/file/filesystem_posix.cc
index c2234da..bb51a9c 100644
--- a/util/file/filesystem_posix.cc
+++ b/util/file/filesystem_posix.cc
@@ -65,14 +65,6 @@
bool MoveFileOrDirectory(const base::FilePath& source,
const base::FilePath& dest) {
-#if defined(OS_FUCHSIA)
- // Fuchsia fails and sets errno to EINVAL if source and dest are the same.
- // Upstream bug is ZX-1729.
- if (!source.empty() && source == dest) {
- return true;
- }
-#endif // OS_FUCHSIA
-
if (rename(source.value().c_str(), dest.value().c_str()) != 0) {
PLOG(ERROR) << "rename " << source.value().c_str() << ", "
<< dest.value().c_str();
@@ -84,7 +76,7 @@
bool IsRegularFile(const base::FilePath& path) {
struct stat st;
if (lstat(path.value().c_str(), &st) != 0) {
- PLOG(ERROR) << "stat " << path.value();
+ PLOG_IF(ERROR, errno != ENOENT) << "stat " << path.value();
return false;
}
return S_ISREG(st.st_mode);
@@ -94,11 +86,11 @@
struct stat st;
if (allow_symlinks) {
if (stat(path.value().c_str(), &st) != 0) {
- PLOG(ERROR) << "stat " << path.value();
+ PLOG_IF(ERROR, errno != ENOENT) << "stat " << path.value();
return false;
}
} else if (lstat(path.value().c_str(), &st) != 0) {
- PLOG(ERROR) << "lstat " << path.value();
+ PLOG_IF(ERROR, errno != ENOENT) << "lstat " << path.value();
return false;
}
return S_ISDIR(st.st_mode);
diff --git a/util/file/filesystem_test.cc b/util/file/filesystem_test.cc
index 3430c3f..37a9290 100644
--- a/util/file/filesystem_test.cc
+++ b/util/file/filesystem_test.cc
@@ -21,7 +21,6 @@
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/filesystem.h"
-#include "test/gtest_disabled.h"
#include "test/scoped_temp_dir.h"
#include "util/misc/time.h"
@@ -93,7 +92,7 @@
TEST(Filesystem, FileModificationTime_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -224,7 +223,7 @@
TEST(Filesystem, MoveFileOrDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -302,7 +301,7 @@
TEST(Filesystem, IsRegularFile_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -344,7 +343,7 @@
TEST(Filesystem, IsDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -393,7 +392,7 @@
TEST(Filesystem, RemoveFile_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
@@ -450,7 +449,7 @@
TEST(Filesystem, RemoveDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
ScopedTempDir temp_dir;
diff --git a/util/file/output_stream_file_writer.cc b/util/file/output_stream_file_writer.cc
new file mode 100644
index 0000000..1172019
--- /dev/null
+++ b/util/file/output_stream_file_writer.cc
@@ -0,0 +1,68 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/file/output_stream_file_writer.h"
+
+#include "base/logging.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+
+OutputStreamFileWriter::OutputStreamFileWriter(
+ std::unique_ptr<OutputStreamInterface> output_stream)
+ : output_stream_(std::move(output_stream)),
+ flush_needed_(false),
+ flushed_(false) {}
+
+OutputStreamFileWriter::~OutputStreamFileWriter() {
+ DCHECK(!flush_needed_);
+}
+
+bool OutputStreamFileWriter::Write(const void* data, size_t size) {
+ DCHECK(!flushed_);
+ flush_needed_ =
+ output_stream_->Write(static_cast<const uint8_t*>(data), size);
+ return flush_needed_;
+}
+
+bool OutputStreamFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {
+ DCHECK(!flushed_);
+ flush_needed_ = true;
+ if (iovecs->empty()) {
+ LOG(ERROR) << "no iovecs";
+ flush_needed_ = false;
+ return false;
+ }
+ for (const WritableIoVec& iov : *iovecs) {
+ if (!output_stream_->Write(static_cast<const uint8_t*>(iov.iov_base),
+ iov.iov_len)) {
+ flush_needed_ = false;
+ return false;
+ }
+ }
+ return true;
+}
+
+FileOffset OutputStreamFileWriter::Seek(FileOffset offset, int whence) {
+ NOTREACHED();
+ return -1;
+}
+
+bool OutputStreamFileWriter::Flush() {
+ flush_needed_ = false;
+ flushed_ = true;
+ return output_stream_->Flush();
+}
+
+} // namespace crashpad
diff --git a/util/file/output_stream_file_writer.h b/util/file/output_stream_file_writer.h
new file mode 100644
index 0000000..6bf1c2c
--- /dev/null
+++ b/util/file/output_stream_file_writer.h
@@ -0,0 +1,62 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_
+#define CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "util/file/file_writer.h"
+
+namespace crashpad {
+
+class OutputStreamInterface;
+
+//! \brief A file writer backed by a OutputSteamInterface.
+//! \note The \a Seek related methods don't work and shouldn't be invoked.
+class OutputStreamFileWriter : public FileWriterInterface {
+ public:
+ //! \param[in] output_stream The output stream that this object writes to.
+ explicit OutputStreamFileWriter(
+ std::unique_ptr<OutputStreamInterface> output_stream);
+ ~OutputStreamFileWriter() override;
+
+ // FileWriterInterface:
+ bool Write(const void* data, size_t size) override;
+ bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;
+
+ // FileSeekerInterface:
+
+ //! \copydoc FileWriterInterface::Seek()
+ //!
+ //! \note This method doesn't work and shouldn't be invoked.
+ FileOffset Seek(FileOffset offset, int whence) override;
+
+ //! \brief Flush data to output_stream.
+ //!
+ //! Either \a Write() or \a WriteIoVec() can't be called afterwards.
+ bool Flush();
+
+ private:
+ std::unique_ptr<OutputStreamInterface> output_stream_;
+ bool flush_needed_;
+ bool flushed_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputStreamFileWriter);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_
diff --git a/util/fuchsia/koid_utilities.cc b/util/fuchsia/koid_utilities.cc
index 0d44f4a..f0377dc 100644
--- a/util/fuchsia/koid_utilities.cc
+++ b/util/fuchsia/koid_utilities.cc
@@ -14,7 +14,10 @@
#include "util/fuchsia/koid_utilities.h"
-#include <zircon/device/sysinfo.h>
+#include <lib/fdio/fdio.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/job.h>
+#include <lib/zx/process.h>
#include <vector>
@@ -26,62 +29,59 @@
namespace {
-base::ScopedZxHandle GetRootJob() {
- ScopedFileHandle sysinfo(
- LoggingOpenFileForRead(base::FilePath("/dev/misc/sysinfo")));
- if (!sysinfo.is_valid())
- return base::ScopedZxHandle();
-
- zx_handle_t root_job;
- size_t n = ioctl_sysinfo_get_root_job(sysinfo.get(), &root_job);
- if (n != sizeof(root_job)) {
- LOG(ERROR) << "unexpected root job size";
- return base::ScopedZxHandle();
+// Casts |handle| into a container of type T, returning a null handle if the
+// actual handle type does not match that of T.
+template <typename T>
+T CastHandle(zx::handle handle) {
+ zx_info_handle_basic_t actual = {};
+ zx_status_t status = handle.get_info(
+ ZX_INFO_HANDLE_BASIC, &actual, sizeof(actual), nullptr, nullptr);
+ if (status != ZX_OK) {
+ ZX_LOG(ERROR, status) << "zx_object_get_info";
+ return T();
}
- return base::ScopedZxHandle(root_job);
+ if (actual.type != T::TYPE) {
+ LOG(ERROR) << "Wrong type: " << actual.type << ", expected " << T::TYPE;
+ return T();
+ }
+ return T(std::move(handle));
}
-bool FindProcess(const base::ScopedZxHandle& job,
- zx_koid_t koid,
- base::ScopedZxHandle* out) {
- for (auto& proc : GetChildHandles(job.get(), ZX_INFO_JOB_PROCESSES)) {
- if (GetKoidForHandle(proc.get()) == koid) {
- *out = std::move(proc);
- return true;
- }
+// Returns null handle if |koid| is not found or an error occurs. If |was_found|
+// is non-null then it will be set, to distinguish not-found from other errors.
+template <typename T, typename U>
+T GetChildHandleByKoid(const U& parent, zx_koid_t child_koid, bool* was_found) {
+ zx::handle handle;
+ zx_status_t status =
+ parent.get_child(child_koid, ZX_RIGHT_SAME_RIGHTS, &handle);
+ if (was_found)
+ *was_found = (status != ZX_ERR_NOT_FOUND);
+ if (status != ZX_OK) {
+ ZX_LOG(ERROR, status) << "zx_object_get_child";
+ return T();
}
- // TODO(scottmg): As this is recursing down the job tree all the handles are
- // kept open, so this could be very expensive in terms of number of open
- // handles. This function should be replaced by a syscall in the
- // not-too-distant future, so hopefully OK for now.
- for (const auto& child_job :
- GetChildHandles(job.get(), ZX_INFO_JOB_CHILDREN)) {
- if (FindProcess(child_job, koid, out))
- return true;
- }
-
- return false;
+ return CastHandle<T>(std::move(handle));
}
} // namespace
-std::vector<zx_koid_t> GetChildKoids(zx_handle_t parent,
+std::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent_object,
zx_object_info_topic_t child_kind) {
size_t actual = 0;
size_t available = 0;
std::vector<zx_koid_t> result(100);
+ zx::unowned_handle parent(parent_object.get());
// This is inherently racy. Better if the process is suspended, but there's
// still no guarantee that a thread isn't externally created. As a result,
// must be in a retry loop.
for (;;) {
- zx_status_t status = zx_object_get_info(parent,
- child_kind,
- result.data(),
- result.size() * sizeof(zx_koid_t),
- &actual,
- &available);
+ zx_status_t status = parent->get_info(child_kind,
+ result.data(),
+ result.size() * sizeof(zx_koid_t),
+ &actual,
+ &available);
// If the buffer is too small (even zero), the result is still ZX_OK, not
// ZX_ERR_BUFFER_TOO_SMALL.
if (status != ZX_OK) {
@@ -102,56 +102,40 @@
return result;
}
-std::vector<base::ScopedZxHandle> GetChildHandles(zx_handle_t parent,
- zx_object_info_topic_t type) {
- auto koids = GetChildKoids(parent, type);
- return GetHandlesForChildKoids(parent, koids);
+std::vector<zx::thread> GetThreadHandles(const zx::process& parent) {
+ auto koids = GetChildKoids(parent, ZX_INFO_PROCESS_THREADS);
+ return GetHandlesForThreadKoids(parent, koids);
}
-std::vector<base::ScopedZxHandle> GetHandlesForChildKoids(
- zx_handle_t parent,
+std::vector<zx::thread> GetHandlesForThreadKoids(
+ const zx::process& parent,
const std::vector<zx_koid_t>& koids) {
- std::vector<base::ScopedZxHandle> result;
+ std::vector<zx::thread> result;
result.reserve(koids.size());
for (zx_koid_t koid : koids) {
- result.emplace_back(GetChildHandleByKoid(parent, koid));
+ result.emplace_back(GetThreadHandleByKoid(parent, koid));
}
return result;
}
-base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent,
- zx_koid_t child_koid) {
- zx_handle_t handle;
- zx_status_t status =
- zx_object_get_child(parent, child_koid, ZX_RIGHT_SAME_RIGHTS, &handle);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_object_get_child";
- return base::ScopedZxHandle();
- }
- return base::ScopedZxHandle(handle);
+zx::thread GetThreadHandleByKoid(const zx::process& parent,
+ zx_koid_t child_koid) {
+ return GetChildHandleByKoid<zx::thread>(parent, child_koid, nullptr);
}
-zx_koid_t GetKoidForHandle(zx_handle_t object) {
+zx_koid_t GetKoidForHandle(const zx::object_base& object) {
zx_info_handle_basic_t info;
- zx_status_t status = zx_object_get_info(
- object, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
+ zx_status_t status = zx_object_get_info(object.get(),
+ ZX_INFO_HANDLE_BASIC,
+ &info,
+ sizeof(info),
+ nullptr,
+ nullptr);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_object_get_info";
- return ZX_HANDLE_INVALID;
+ return ZX_KOID_INVALID;
}
return info.koid;
}
-// TODO(scottmg): This implementation uses some debug/temporary/hacky APIs and
-// ioctls that are currently the only way to go from pid to handle. This should
-// hopefully eventually be replaced by more or less a single
-// zx_debug_something() syscall.
-base::ScopedZxHandle GetProcessFromKoid(zx_koid_t koid) {
- base::ScopedZxHandle result;
- if (!FindProcess(GetRootJob(), koid, &result)) {
- LOG(ERROR) << "process " << koid << " not found";
- }
- return result;
-}
-
} // namespace crashpad
diff --git a/util/fuchsia/koid_utilities.h b/util/fuchsia/koid_utilities.h
index 5bcea10..8859bc5 100644
--- a/util/fuchsia/koid_utilities.h
+++ b/util/fuchsia/koid_utilities.h
@@ -15,13 +15,14 @@
#ifndef CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_
#define CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_
+#include <lib/zx/object.h>
+#include <lib/zx/process.h>
+#include <lib/zx/thread.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <vector>
-#include "base/fuchsia/scoped_zx_handle.h"
-
namespace crashpad {
//! \brief Get a list of child koids for a parent handle.
@@ -38,57 +39,43 @@
//! \return A vector of the koids representing the child objects.
//!
//! \sa GetChildHandles
-std::vector<zx_koid_t> GetChildKoids(zx_handle_t parent,
+std::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent,
zx_object_info_topic_t child_kind);
//! \brief Get handles representing a list of child objects of a given parent.
//!
//! \param[in] parent The handle to the parent object.
-//! \param[in] child_kind The type of children to retrieve from \a parent. Valid
-//! values depend on the type of \a parent, but include
-//! `ZX_INFO_JOB_CHILDREN` (child jobs of a job), `ZX_INFO_JOB_PROCESSES`
-//! (child processes of a job), and `ZX_INFO_PROCESS_THREADS` (child threads
-//! of a process).
//! \return The resulting list of handles corresponding to the child objects.
//!
//! \sa GetChildKoids
-std::vector<base::ScopedZxHandle> GetChildHandles(
- zx_handle_t parent,
- zx_object_info_topic_t child_kind);
+std::vector<zx::thread> GetThreadHandles(const zx::process& parent);
-//! \brief Convert a list of koids that are all children of a particular object
-//! into handles.
+//! \brief Convert a list of koids that are all children of a particular process
+//! into thread handles.
//!
//! \param[in] parent The parent object to which the koids belong.
//! \param[in] koids The list of koids.
//! \return The resulting list of handles corresponding to the koids. If an
//! element of \a koids is invalid or can't be retrieved, there will be a
//! corresponding `ZX_HANDLE_INVALID` entry in the return.
-std::vector<base::ScopedZxHandle> GetHandlesForChildKoids(
- zx_handle_t parent,
+std::vector<zx::thread> GetHandlesForThreadKoids(
+ const zx::process& parent,
const std::vector<zx_koid_t>& koids);
-//! \brief Retrieve the child of a parent handle, based on koid.
+//! \brief Retrieve the handle of a process' thread, based on koid.
//!
//! \param[in] parent The parent object to which the child belongs.
//! \param[in] child_koid The koid of the child to retrieve.
//! \return A handle representing \a child_koid, or `ZX_HANDLE_INVALID` if the
//! handle could not be retrieved, in which case an error will be logged.
-base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent,
- zx_koid_t child_koid);
-
-//! \brief Gets a process handle given the process' koid.
-//!
-//! \param[in] koid The process id.
-//! \return A zx_handle_t (owned by a base::ScopedZxHandle) for the process. If
-//! the handle is invalid, an error will have been logged.
-base::ScopedZxHandle GetProcessFromKoid(zx_koid_t koid);
+zx::thread GetThreadHandleByKoid(const zx::process& parent,
+ zx_koid_t child_koid);
//! \brief Retrieves the koid for a given object handle.
//!
//! \param[in] object The handle for which the koid is to be retrieved.
//! \return The koid of \a handle, or `ZX_HANDLE_INVALID` with an error logged.
-zx_koid_t GetKoidForHandle(zx_handle_t object);
+zx_koid_t GetKoidForHandle(const zx::object_base& object);
} // namespace crashpad
diff --git a/util/fuchsia/scoped_task_suspend.cc b/util/fuchsia/scoped_task_suspend.cc
index ca0fd56..8591d55 100644
--- a/util/fuchsia/scoped_task_suspend.cc
+++ b/util/fuchsia/scoped_task_suspend.cc
@@ -14,68 +14,55 @@
#include "util/fuchsia/scoped_task_suspend.h"
-#include <zircon/process.h>
-#include <zircon/syscalls.h>
-#include <zircon/syscalls/debug.h>
+#include <lib/zx/time.h>
+#include <zircon/errors.h>
+#include <zircon/status.h>
+#include <zircon/syscalls/object.h>
#include <vector>
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/scoped_zx_handle.h"
#include "base/logging.h"
#include "util/fuchsia/koid_utilities.h"
namespace crashpad {
-namespace {
+ScopedTaskSuspend::ScopedTaskSuspend(const zx::process& process) {
+ DCHECK_NE(process.get(), zx::process::self()->get());
-zx_obj_type_t GetHandleType(zx_handle_t handle) {
- zx_info_handle_basic_t basic;
- zx_status_t status = zx_object_get_info(
- handle, ZX_INFO_HANDLE_BASIC, &basic, sizeof(basic), nullptr, nullptr);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_object_get_info";
- return ZX_OBJ_TYPE_NONE;
- }
- return basic.type;
-}
-
-// Returns the suspend token of the suspended thread. This function attempts
-// to wait a short time for the thread to actually suspend before returning
-// but this is not guaranteed.
-base::ScopedZxHandle SuspendThread(zx_handle_t thread) {
- zx_handle_t token = ZX_HANDLE_INVALID;
- zx_status_t status = zx_task_suspend_token(thread, &token);
- if (status != ZX_OK) {
- ZX_LOG(ERROR, status) << "zx_task_suspend";
- base::ScopedZxHandle();
+ const zx_status_t suspend_status = process.suspend(&suspend_token_);
+ if (suspend_status != ZX_OK) {
+ ZX_LOG(ERROR, suspend_status) << "zx_task_suspend";
+ return;
}
- zx_signals_t observed = 0u;
- if (zx_object_wait_one(thread, ZX_THREAD_SUSPENDED,
- zx_deadline_after(ZX_MSEC(50)), &observed) != ZX_OK) {
- LOG(ERROR) << "thread failed to suspend";
- }
- return base::ScopedZxHandle(token);
-}
+ // suspend() is asynchronous so we now check that each thread is indeed
+ // suspended, up to some deadline.
+ for (const auto& thread : GetThreadHandles(process)) {
+ // We omit the crashed thread (blocked in an exception) as it is technically
+ // not suspended, cf. ZX-3772.
+ zx_info_thread_t info;
+ if (thread.get_info(
+ ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr) == ZX_OK) {
+ if (info.state == ZX_THREAD_STATE_BLOCKED_EXCEPTION) {
+ continue;
+ }
+ }
-} // namespace
-
-ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) {
- DCHECK_NE(task, zx_process_self());
- DCHECK_NE(task, zx_thread_self());
-
- zx_obj_type_t type = GetHandleType(task);
- if (type == ZX_OBJ_TYPE_THREAD) {
- suspend_tokens_.push_back(SuspendThread(task));
- } else if (type == ZX_OBJ_TYPE_PROCESS) {
- for (const auto& thread : GetChildHandles(task, ZX_INFO_PROCESS_THREADS))
- suspend_tokens_.push_back(SuspendThread(thread.get()));
- } else {
- LOG(ERROR) << "unexpected handle type";
+ zx_signals_t observed = 0u;
+ const zx_status_t wait_status = thread.wait_one(
+ ZX_THREAD_SUSPENDED, zx::deadline_after(zx::msec(50)), &observed);
+ if (wait_status != ZX_OK) {
+ zx_info_thread_t info = {};
+ zx_status_t info_status = thread.get_info(
+ ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr);
+ ZX_LOG(ERROR, wait_status) << "thread failed to suspend";
+ LOG(ERROR) << "Thread info status " << info_status;
+ if (info_status == ZX_OK) {
+ LOG(ERROR) << "Thread state " << info.state;
+ }
+ }
}
}
-ScopedTaskSuspend::~ScopedTaskSuspend() = default;
-
} // namespace crashpad
diff --git a/util/fuchsia/scoped_task_suspend.h b/util/fuchsia/scoped_task_suspend.h
index f817364..afe497b 100644
--- a/util/fuchsia/scoped_task_suspend.h
+++ b/util/fuchsia/scoped_task_suspend.h
@@ -15,37 +15,31 @@
#ifndef CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_
#define CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_
-#include <zircon/types.h>
+#include <lib/zx/process.h>
+#include <lib/zx/suspend_token.h>
+#include <lib/zx/thread.h>
#include <vector>
-#include "base/fuchsia/scoped_zx_handle.h"
#include "base/macros.h"
namespace crashpad {
//! \brief Manages the suspension of another task.
//!
-//! The underlying API only supports suspending threads (despite its name) not
-//! entire tasks. As a result, it's possible some threads may not be correctly
-//! suspended/resumed as their creation might race enumeration.
-//!
-//! Additionally, suspending a thread is asynchronous and may take an
-//! arbitrary amount of time.
-//!
-//! Because of these limitations, this class is limited to being a best-effort,
-//! and correct suspension/resumption cannot be relied upon.
+//! Suspending a process is asynchronous, and may take an arbitrary amount of
+//! time. As a result, this class is limited to being a best-effort, and
+//! correct suspension/resumption cannot be relied upon.
//!
//! Callers should not attempt to suspend the current task as obtained via
//! `zx_process_self()`.
class ScopedTaskSuspend {
public:
- explicit ScopedTaskSuspend(zx_handle_t task);
- ~ScopedTaskSuspend();
+ explicit ScopedTaskSuspend(const zx::process& process);
+ ~ScopedTaskSuspend() = default;
private:
- // Could be one (for a thread) or many (for every process in a thread).
- std::vector<base::ScopedZxHandle> suspend_tokens_;
+ zx::suspend_token suspend_token_;
DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend);
};
diff --git a/util/fuchsia/system_exception_port_key.h b/util/fuchsia/system_exception_port_key.h
deleted file mode 100644
index 0bbae69..0000000
--- a/util/fuchsia/system_exception_port_key.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2018 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_
-#define CRASHPAD_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_
-
-namespace crashpad {
-
-//! \brief The key used in `zx_task_bind_exception_port()` and packet
-//! processing. This matches the value that Zircon's `devmgr` and
-//! `crashlogger` use for interoperability, for now.
-constexpr uint64_t kSystemExceptionPortKey = 1166444u;
-
-} // namespace crashpad
-
-#endif // CRASHPAD_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_
diff --git a/util/ios/exception_processor.h b/util/ios/exception_processor.h
new file mode 100644
index 0000000..76a8c8b
--- /dev/null
+++ b/util/ios/exception_processor.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_
+#define CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_
+
+namespace crashpad {
+
+//! \brief Installs the Objective-C exception preprocessor.
+//!
+//! When code raises an Objective-C exception, unwind the stack looking for
+//! any exception handlers. If an exception handler is encountered, test to
+//! see if it is a function known to be a catch-and-rethrow 'sinkhole' exception
+//! handler. Various routines in UIKit do this, and they obscure the
+//! crashing stack, since the original throw location is no longer present
+//! on the stack (just the re-throw) when Crashpad captures the crash
+//! report. In the case of sinkholes, trigger an immediate exception to
+//! capture the original stack.
+//!
+//! This should be installed at the same time the CrashpadClient installs the
+//! signal handler. It should only be installed once.
+void InstallObjcExceptionPreprocessor();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_
diff --git a/util/ios/exception_processor.mm b/util/ios/exception_processor.mm
new file mode 100644
index 0000000..139a851
--- /dev/null
+++ b/util/ios/exception_processor.mm
@@ -0,0 +1,337 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/ios/exception_processor.h"
+
+#import <Foundation/Foundation.h>
+#include <TargetConditionals.h>
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <libunwind.h>
+#include <mach-o/loader.h>
+#include <objc/message.h>
+#include <objc/objc-exception.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unwind.h>
+
+#include <exception>
+#include <type_traits>
+#include <typeinfo>
+
+#include "base/bit_cast.h"
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+#include "build/build_config.h"
+
+namespace {
+
+// From 10.15.0 objc4-779.1/runtime/objc-exception.mm.
+struct objc_typeinfo {
+ const void* const* vtable;
+ const char* name;
+ Class cls_unremapped;
+};
+struct objc_exception {
+ id obj;
+ objc_typeinfo tinfo;
+};
+
+// From 10.15.0 objc4-779.1/runtime/objc-abi.h.
+extern "C" const void* const objc_ehtype_vtable[];
+
+// https://github.com/llvm/llvm-project/blob/09dc884eb2e4/libcxxabi/src/cxa_exception.h
+static const uint64_t kOurExceptionClass = 0x434c4e47432b2b00;
+struct __cxa_exception {
+#if defined(ARCH_CPU_64_BITS)
+ void* reserve;
+ size_t referenceCount;
+#endif
+ std::type_info* exceptionType;
+ void (*exceptionDestructor)(void*);
+ std::unexpected_handler unexpectedHandler;
+ std::terminate_handler terminateHandler;
+ __cxa_exception* nextException;
+ int handlerCount;
+ int handlerSwitchValue;
+ const unsigned char* actionRecord;
+ const unsigned char* languageSpecificData;
+ void* catchTemp;
+ void* adjustedPtr;
+#if !defined(ARCH_CPU_64_BITS)
+ size_t referenceCount;
+#endif
+ _Unwind_Exception unwindHeader;
+};
+
+objc_exception_preprocessor g_next_preprocessor;
+bool g_exception_preprocessor_installed;
+
+void TerminatingFromUncaughtNSException(id exception, const char* sinkhole) {
+ // TODO(justincohen): This is incomplete, as the signal handler will not have
+ // access to the exception name and reason. Pass that along somehow here.
+ NSString* exception_message_ns = [NSString
+ stringWithFormat:@"%@: %@", [exception name], [exception reason]];
+ std::string exception_message = base::SysNSStringToUTF8(exception_message_ns);
+ LOG(INFO) << "Terminating from Objective-C exception: " << exception_message
+ << " with sinkhole: " << sinkhole;
+ // TODO(justincohen): This is temporary, as crashpad can capture this
+ // exception directly instead.
+ std::terminate();
+}
+
+// Returns true if |path| equals |sinkhole| on device. Simulator paths prepend
+// much of Xcode's internal structure, so check that |path| ends with |sinkhole|
+// for simulator.
+bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {
+#if TARGET_OS_SIMULATOR
+ size_t path_length = strlen(path);
+ size_t sinkhole_length = strlen(sinkhole);
+ if (sinkhole_length > path_length)
+ return false;
+ return strncmp(path + path_length - sinkhole_length,
+ sinkhole,
+ sinkhole_length) == 0;
+#else
+ return strcmp(path, sinkhole) == 0;
+#endif
+}
+
+int LoggingUnwStep(unw_cursor_t* cursor) {
+ int rv = unw_step(cursor);
+ if (rv < 0) {
+ LOG(ERROR) << "unw_step: " << rv;
+ }
+ return rv;
+}
+
+id ObjcExceptionPreprocessor(id exception) {
+ // Unwind the stack looking for any exception handlers. If an exception
+ // handler is encountered, test to see if it is a function known to catch-
+ // and-rethrow as a "top-level" exception handler. Various routines in
+ // Cocoa/UIKit do this, and it obscures the crashing stack, since the original
+ // throw location is no longer present on the stack (just the re-throw) when
+ // Crashpad captures the crash report.
+ unw_context_t context;
+ unw_getcontext(&context);
+
+ unw_cursor_t cursor;
+ unw_init_local(&cursor, &context);
+
+ static const void* this_base_address = []() -> const void* {
+ Dl_info dl_info;
+ if (!dladdr(reinterpret_cast<const void*>(&ObjcExceptionPreprocessor),
+ &dl_info)) {
+ LOG(ERROR) << "dladdr: " << dlerror();
+ return nullptr;
+ }
+ return dl_info.dli_fbase;
+ }();
+
+ // Generate an exception_header for the __personality_routine.
+ // From 10.15.0 objc4-779.1/runtime/objc-exception.mm objc_exception_throw.
+ objc_exception* exception_objc = reinterpret_cast<objc_exception*>(
+ __cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception)));
+ exception_objc->obj = exception;
+ exception_objc->tinfo.vtable = objc_ehtype_vtable + 2;
+ exception_objc->tinfo.name = object_getClassName(exception);
+ exception_objc->tinfo.cls_unremapped = object_getClass(exception);
+
+ // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp
+ // __cxa_throw
+ __cxa_exception* exception_header =
+ reinterpret_cast<__cxa_exception*>(exception_objc) - 1;
+ exception_header->unexpectedHandler = std::get_unexpected();
+ exception_header->terminateHandler = std::get_terminate();
+ exception_header->exceptionType =
+ reinterpret_cast<std::type_info*>(&exception_objc->tinfo);
+ exception_header->unwindHeader.exception_class = kOurExceptionClass;
+
+ bool handler_found = false;
+ while (LoggingUnwStep(&cursor) > 0) {
+ unw_proc_info_t frame_info;
+ if (unw_get_proc_info(&cursor, &frame_info) != UNW_ESUCCESS) {
+ continue;
+ }
+
+ if (frame_info.handler == 0) {
+ continue;
+ }
+
+ // Check to see if the handler is really an exception handler.
+ __personality_routine p =
+ reinterpret_cast<__personality_routine>(frame_info.handler);
+
+ // From 10.15.0 libunwind-35.4/src/UnwindLevel1.c.
+ _Unwind_Reason_Code personalityResult = (*p)(
+ 1,
+ _UA_SEARCH_PHASE,
+ exception_header->unwindHeader.exception_class,
+ reinterpret_cast<_Unwind_Exception*>(&exception_header->unwindHeader),
+ reinterpret_cast<_Unwind_Context*>(&cursor));
+ switch (personalityResult) {
+ case _URC_HANDLER_FOUND:
+ break;
+ case _URC_CONTINUE_UNWIND:
+ continue;
+ default:
+ break;
+ }
+
+ char proc_name[512];
+ unw_word_t offset;
+ if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name), &offset) !=
+ UNW_ESUCCESS) {
+ // The symbol has no name, so see if it belongs to the same image as
+ // this function.
+ Dl_info dl_info;
+ if (dladdr(reinterpret_cast<const void*>(frame_info.start_ip),
+ &dl_info)) {
+ if (dl_info.dli_fbase == this_base_address) {
+ // This is a handler in our image, so allow it to run.
+ handler_found = true;
+ break;
+ }
+ }
+
+ // This handler does not belong to us, so continue the search.
+ continue;
+ }
+
+ // Check if the function is one that is known to obscure (by way of
+ // catch-and-rethrow) exception stack traces. If it is, sinkhole it
+ // by crashing here at the point of throw.
+ constexpr const char* kExceptionSymbolNameSinkholes[] = {
+ // The two CF symbol names will also be captured by the CoreFoundation
+ // library path check below, but for completeness they are listed here,
+ // since they appear unredacted.
+ "CFRunLoopRunSpecific",
+ "_CFXNotificationPost",
+ "__NSFireDelayedPerform",
+ };
+ for (const char* sinkhole : kExceptionSymbolNameSinkholes) {
+ if (strcmp(sinkhole, proc_name) == 0) {
+ TerminatingFromUncaughtNSException(exception, sinkhole);
+ }
+ }
+
+ // On iOS, function names are often reported as "<redacted>", although they
+ // do appear when attached to the debugger. When this happens, use the path
+ // of the image to determine if the handler is an exception sinkhole.
+ constexpr const char* kExceptionLibraryPathSinkholes[] = {
+ // Everything in this library is a sinkhole, specifically
+ // _dispatch_client_callout. Both are needed here depending on whether
+ // the debugger is attached (introspection only appears when a simulator
+ // is attached to a debugger).
+ "/usr/lib/system/introspection/libdispatch.dylib",
+ "/usr/lib/system/libdispatch.dylib",
+
+ // __CFRunLoopDoTimers and __CFRunLoopRun are sinkholes. Consider also
+ // checking that a few frames up is CFRunLoopRunSpecific().
+ "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
+ };
+
+ Dl_info dl_info;
+ if (dladdr(reinterpret_cast<const void*>(frame_info.start_ip), &dl_info) !=
+ 0) {
+ for (const char* sinkhole : kExceptionLibraryPathSinkholes) {
+ if (ModulePathMatchesSinkhole(dl_info.dli_fname, sinkhole)) {
+ TerminatingFromUncaughtNSException(exception, sinkhole);
+ }
+ }
+ }
+
+ // Some <redacted> sinkholes are harder to find. _UIGestureEnvironmentUpdate
+ // in UIKitCore is an example. UIKitCore can't be added to
+ // kExceptionLibraryPathSinkholes because it uses Objective-C exceptions
+ // internally and also has has non-sinkhole handlers. Since
+ // _UIGestureEnvironmentUpdate is always called from
+ // -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:],
+ // inspect the caller frame info to match the sinkhole.
+ constexpr const char* kUIKitCorePath =
+ "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore";
+ if (ModulePathMatchesSinkhole(dl_info.dli_fname, kUIKitCorePath)) {
+ unw_proc_info_t caller_frame_info;
+ if (LoggingUnwStep(&cursor) > 0 &&
+ unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS) {
+ static IMP uigesture_deliver_event_imp = [] {
+ IMP imp = class_getMethodImplementation(
+ NSClassFromString(@"UIGestureEnvironment"),
+ NSSelectorFromString(
+ @"_deliverEvent:toGestureRecognizers:usingBlock:"));
+
+ // From 10.15.0 objc4-779.1/runtime/objc-class.mm
+ // class_getMethodImplementation returns nil or _objc_msgForward on
+ // failure.
+ if (!imp || imp == _objc_msgForward) {
+ LOG(WARNING) << "Unable to find -[UIGestureEnvironment "
+ "_deliverEvent:toGestureRecognizers:usingBlock:]";
+ return bit_cast<IMP>(nullptr); // IMP is a function pointer type.
+ }
+ return imp;
+ }();
+
+ if (uigesture_deliver_event_imp ==
+ reinterpret_cast<IMP>(caller_frame_info.start_ip)) {
+ TerminatingFromUncaughtNSException(exception,
+ "_UIGestureEnvironmentUpdate");
+ }
+ }
+ }
+
+ handler_found = true;
+
+ break;
+ }
+
+ // If no handler is found, __cxa_throw would call failed_throw and terminate.
+ // See:
+ // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp
+ // __cxa_throw. Instead, terminate via TerminatingFromUncaughtNSException so
+ // the exception name and reason are properly recorded.
+ if (!handler_found) {
+ TerminatingFromUncaughtNSException(exception, "__cxa_throw");
+ }
+
+ // Forward to the next preprocessor.
+ if (g_next_preprocessor)
+ return g_next_preprocessor(exception);
+
+ return exception;
+}
+
+} // namespace
+
+namespace crashpad {
+
+void InstallObjcExceptionPreprocessor() {
+ DCHECK(!g_exception_preprocessor_installed);
+
+ g_next_preprocessor =
+ objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);
+ g_exception_preprocessor_installed = true;
+}
+
+void UninstallObjcExceptionPreprocessor() {
+ DCHECK(g_exception_preprocessor_installed);
+
+ objc_setExceptionPreprocessor(g_next_preprocessor);
+ g_next_preprocessor = nullptr;
+ g_exception_preprocessor_installed = false;
+}
+
+} // namespace crashpad
diff --git a/util/ios/exception_processor_test.mm b/util/ios/exception_processor_test.mm
new file mode 100644
index 0000000..c9aa716
--- /dev/null
+++ b/util/ios/exception_processor_test.mm
@@ -0,0 +1,41 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+#include <objc/message.h>
+#include <objc/runtime.h>
+
+#include "gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+using IOSExceptionProcessor = PlatformTest;
+
+TEST_F(IOSExceptionProcessor, SelectorExists) {
+ IMP uigesture_deliver_event_imp = class_getMethodImplementation(
+ NSClassFromString(@"UIGestureEnvironment"),
+ NSSelectorFromString(@"_deliverEvent:toGestureRecognizers:usingBlock:"));
+
+ // From 10.15.0 objc4-779.1/runtime/objc-class.mm
+ // class_getMethodImplementation returns nil or _objc_msgForward on failure.
+ ASSERT_TRUE(uigesture_deliver_event_imp);
+ ASSERT_NE(uigesture_deliver_event_imp, _objc_msgForward);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/ios/ios_system_data_collector.h b/util/ios/ios_system_data_collector.h
new file mode 100644
index 0000000..45837c6
--- /dev/null
+++ b/util/ios/ios_system_data_collector.h
@@ -0,0 +1,81 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_
+#define CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#include <string>
+
+namespace crashpad {
+
+//! \brief Used to collect system level data before a crash occurs.
+class IOSSystemDataCollector {
+ public:
+ IOSSystemDataCollector();
+ ~IOSSystemDataCollector();
+
+ void OSVersion(int* major, int* minor, int* bugfix, std::string* build) const;
+ std::string MachineDescription() const { return machine_description_; }
+ int ProcessorCount() const { return processor_count_; }
+ std::string CPUVendor() const { return cpu_vendor_; }
+ bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; }
+ bool IsDaylightSavingTime() const { return is_daylight_saving_time_; }
+ int StandardOffsetSeconds() const { return standard_offset_seconds_; }
+ int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }
+ std::string StandardName() const { return standard_name_; }
+ std::string DaylightName() const { return daylight_name_; }
+
+ // Currently unused by minidump.
+ int Orientation() const { return orientation_; }
+
+ private:
+ // Notification handlers.
+ void InstallHandlers();
+ static void SystemTimeZoneDidChangeNotificationHandler(
+ CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo);
+ void SystemTimeZoneDidChangeNotification();
+
+ static void OrientationDidChangeNotificationHandler(
+ CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo);
+ void OrientationDidChangeNotification();
+
+ int major_version_;
+ int minor_version_;
+ int patch_version_;
+ std::string build_;
+ std::string machine_description_;
+ int orientation_;
+ int processor_count_;
+ std::string cpu_vendor_;
+ bool has_next_daylight_saving_time_;
+ bool is_daylight_saving_time_;
+ int standard_offset_seconds_;
+ int daylight_offset_seconds_;
+ std::string standard_name_;
+ std::string daylight_name_;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_
diff --git a/util/ios/ios_system_data_collector.mm b/util/ios/ios_system_data_collector.mm
new file mode 100644
index 0000000..c81fe3f
--- /dev/null
+++ b/util/ios/ios_system_data_collector.mm
@@ -0,0 +1,209 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/ios/ios_system_data_collector.h"
+
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+#import <Foundation/Foundation.h>
+#include <TargetConditionals.h>
+#import <UIKit/UIKit.h>
+
+#include "base/mac/mach_logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "build/build_config.h"
+
+namespace {
+
+std::string ReadStringSysctlByName(const char* name) {
+ size_t buf_len;
+ if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
+ PLOG(WARNING) << "sysctlbyname (size) " << name;
+ return std::string();
+ }
+
+ if (buf_len == 0) {
+ return std::string();
+ }
+
+ std::string value(buf_len - 1, '\0');
+ if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
+ PLOG(WARNING) << "sysctlbyname " << name;
+ return std::string();
+ }
+
+ return value;
+}
+
+} // namespace
+
+namespace crashpad {
+
+IOSSystemDataCollector::IOSSystemDataCollector()
+ : major_version_(0),
+ minor_version_(0),
+ patch_version_(0),
+ build_(),
+ machine_description_(),
+ orientation_(0),
+ processor_count_(0),
+ cpu_vendor_(),
+ has_next_daylight_saving_time_(false),
+ is_daylight_saving_time_(false),
+ standard_offset_seconds_(0),
+ daylight_offset_seconds_(0),
+ standard_name_(),
+ daylight_name_() {
+ NSOperatingSystemVersion version =
+ [[NSProcessInfo processInfo] operatingSystemVersion];
+ major_version_ = base::saturated_cast<int>(version.majorVersion);
+ minor_version_ = base::saturated_cast<int>(version.minorVersion);
+ patch_version_ = base::saturated_cast<int>(version.patchVersion);
+ processor_count_ =
+ base::saturated_cast<int>([[NSProcessInfo processInfo] processorCount]);
+ build_ = ReadStringSysctlByName("kern.osversion");
+
+#if defined(ARCH_CPU_X86_64)
+ cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor");
+#endif
+
+#if TARGET_OS_SIMULATOR
+ // TODO(justincohen): Consider adding board and model information to
+ // |machine_description| as well (similar to MacModelAndBoard in
+ // util/mac/mac_util.cc).
+ switch (UI_USER_INTERFACE_IDIOM()) {
+ case UIUserInterfaceIdiomPhone:
+ machine_description_ = "iOS Simulator (iPhone)";
+ break;
+ case UIUserInterfaceIdiomPad:
+ machine_description_ = "iOS Simulator (iPad)";
+ break;
+ default:
+ machine_description_ = "iOS Simulator (Unknown)";
+ break;
+ }
+#elif TARGET_OS_IPHONE
+ utsname uts;
+ if (uname(&uts) == 0) {
+ machine_description_ = uts.machine;
+ }
+#else
+#error "Unexpected target type OS."
+#endif
+
+ InstallHandlers();
+}
+
+IOSSystemDataCollector::~IOSSystemDataCollector() {
+ CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetLocalCenter(),
+ this);
+}
+
+void IOSSystemDataCollector::OSVersion(int* major,
+ int* minor,
+ int* bugfix,
+ std::string* build) const {
+ *major = major_version_;
+ *minor = minor_version_;
+ *bugfix = patch_version_;
+ build->assign(build_);
+}
+
+void IOSSystemDataCollector::InstallHandlers() {
+ // Timezone.
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetLocalCenter(),
+ this,
+ IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler,
+ reinterpret_cast<CFStringRef>(NSSystemTimeZoneDidChangeNotification),
+ nullptr,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ SystemTimeZoneDidChangeNotification();
+
+ // Orientation.
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetLocalCenter(),
+ this,
+ IOSSystemDataCollector::OrientationDidChangeNotificationHandler,
+ reinterpret_cast<CFStringRef>(UIDeviceOrientationDidChangeNotification),
+ nullptr,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+ OrientationDidChangeNotification();
+}
+
+// static
+void IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler(
+ CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo) {
+ static_cast<IOSSystemDataCollector*>(observer)
+ ->SystemTimeZoneDidChangeNotification();
+}
+
+void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() {
+ NSTimeZone* time_zone = NSTimeZone.localTimeZone;
+ NSDate* transition =
+ [time_zone nextDaylightSavingTimeTransitionAfterDate:[NSDate date]];
+ if (transition == nil) {
+ has_next_daylight_saving_time_ = false;
+ is_daylight_saving_time_ = false;
+ standard_offset_seconds_ =
+ base::saturated_cast<int>([time_zone secondsFromGMTForDate:transition]);
+ standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
+ daylight_offset_seconds_ = standard_offset_seconds_;
+ daylight_name_ = standard_name_;
+ } else {
+ has_next_daylight_saving_time_ = true;
+ is_daylight_saving_time_ = time_zone.isDaylightSavingTime;
+ if (time_zone.isDaylightSavingTime) {
+ standard_offset_seconds_ = base::saturated_cast<int>(
+ [time_zone secondsFromGMTForDate:transition]);
+ standard_name_ =
+ base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);
+ daylight_offset_seconds_ =
+ base::saturated_cast<int>([time_zone secondsFromGMT]);
+ daylight_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
+ } else {
+ standard_offset_seconds_ =
+ base::saturated_cast<int>([time_zone secondsFromGMT]);
+ standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);
+ daylight_offset_seconds_ = base::saturated_cast<int>(
+ [time_zone secondsFromGMTForDate:transition]);
+ daylight_name_ =
+ base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);
+ }
+ }
+}
+
+// static
+void IOSSystemDataCollector::OrientationDidChangeNotificationHandler(
+ CFNotificationCenterRef center,
+ void* observer,
+ CFStringRef name,
+ const void* object,
+ CFDictionaryRef userInfo) {
+ static_cast<IOSSystemDataCollector*>(observer)
+ ->OrientationDidChangeNotification();
+}
+
+void IOSSystemDataCollector::OrientationDidChangeNotification() {
+ orientation_ =
+ base::saturated_cast<int>([[UIDevice currentDevice] orientation]);
+}
+
+} // namespace crashpad
diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc
index d4cfcb7..00af5ab 100644
--- a/util/linux/auxiliary_vector_test.cc
+++ b/util/linux/auxiliary_vector_test.cc
@@ -120,8 +120,9 @@
#if defined(AT_SYSINFO_EHDR)
LinuxVMAddress vdso_addr;
- ASSERT_TRUE(aux.GetValue(AT_SYSINFO_EHDR, &vdso_addr));
- EXPECT_TRUE(mappings.FindMapping(vdso_addr));
+ if (aux.GetValue(AT_SYSINFO_EHDR, &vdso_addr)) {
+ EXPECT_TRUE(mappings.FindMapping(vdso_addr));
+ }
#endif // AT_SYSINFO_EHDR
#if defined(AT_EXECFN)
diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc
index a3df425..8b55205 100644
--- a/util/linux/direct_ptrace_connection.cc
+++ b/util/linux/direct_ptrace_connection.cc
@@ -17,6 +17,7 @@
#include <utility>
#include "util/file/file_io.h"
+#include "util/linux/proc_task_reader.h"
namespace crashpad {
@@ -81,4 +82,9 @@
return &memory_;
}
+bool DirectPtraceConnection::Threads(std::vector<pid_t>* threads) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return ReadThreadIDs(pid_, threads);
+}
+
} // namespace crashpad
diff --git a/util/linux/direct_ptrace_connection.h b/util/linux/direct_ptrace_connection.h
index 53594ef..10f8370 100644
--- a/util/linux/direct_ptrace_connection.h
+++ b/util/linux/direct_ptrace_connection.h
@@ -56,6 +56,7 @@
bool ReadFileContents(const base::FilePath& path,
std::string* contents) override;
ProcessMemory* Memory() override;
+ bool Threads(std::vector<pid_t>* threads) override;
private:
std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;
diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc
index 6333351..98edb30 100644
--- a/util/linux/exception_handler_client.cc
+++ b/util/linux/exception_handler_client.cc
@@ -15,27 +15,84 @@
#include "util/linux/exception_handler_client.h"
#include <errno.h>
+#include <signal.h>
#include <sys/prctl.h>
-#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
+#include "third_party/lss/lss.h"
#include "util/file/file_io.h"
#include "util/linux/ptrace_broker.h"
+#include "util/linux/socket.h"
+#include "util/misc/from_pointer_cast.h"
#include "util/posix/signals.h"
+#if defined(OS_ANDROID)
+#include <android/api-level.h>
+#endif
+
namespace crashpad {
-ExceptionHandlerClient::ExceptionHandlerClient(int sock)
- : server_sock_(sock), ptracer_(-1), can_set_ptracer_(true) {}
+namespace {
+
+class ScopedSigprocmaskRestore {
+ public:
+ explicit ScopedSigprocmaskRestore(const kernel_sigset_t& set_to_block)
+ : orig_mask_(), mask_is_set_(false) {
+ mask_is_set_ = sys_sigprocmask(SIG_BLOCK, &set_to_block, &orig_mask_) == 0;
+ DPLOG_IF(ERROR, !mask_is_set_) << "sigprocmask";
+ }
+
+ ~ScopedSigprocmaskRestore() {
+ if (mask_is_set_ &&
+ sys_sigprocmask(SIG_SETMASK, &orig_mask_, nullptr) != 0) {
+ DPLOG(ERROR) << "sigprocmask";
+ }
+ }
+
+ private:
+ kernel_sigset_t orig_mask_;
+ bool mask_is_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSigprocmaskRestore);
+};
+
+} // namespace
+
+ExceptionHandlerClient::ExceptionHandlerClient(int sock, bool multiple_clients)
+ : server_sock_(sock),
+ ptracer_(-1),
+ can_set_ptracer_(true),
+ multiple_clients_(multiple_clients) {}
ExceptionHandlerClient::~ExceptionHandlerClient() = default;
-int ExceptionHandlerClient::RequestCrashDump(const ClientInformation& info) {
- int status = SendCrashDumpRequest(info);
+bool ExceptionHandlerClient::GetHandlerCredentials(ucred* creds) {
+ ExceptionHandlerProtocol::ClientToServerMessage message = {};
+ message.type =
+ ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials;
+ if (UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message)) !=
+ 0) {
+ return false;
+ }
+
+ ExceptionHandlerProtocol::ServerToClientMessage response;
+ return UnixCredentialSocket::RecvMsg(
+ server_sock_, &response, sizeof(response), creds);
+}
+
+int ExceptionHandlerClient::RequestCrashDump(
+ const ExceptionHandlerProtocol::ClientInformation& info) {
+ VMAddress sp = FromPointerCast<VMAddress>(&sp);
+
+ if (multiple_clients_) {
+ return SignalCrashDump(info, sp);
+ }
+
+ int status = SendCrashDumpRequest(info, sp);
if (status != 0) {
return status;
}
@@ -61,59 +118,56 @@
can_set_ptracer_ = can_set_ptracer;
}
-int ExceptionHandlerClient::SendCrashDumpRequest(
- const ClientInformation& info) {
- ClientToServerMessage message;
- message.type = ClientToServerMessage::kCrashDumpRequest;
- message.client_info = info;
+int ExceptionHandlerClient::SignalCrashDump(
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress stack_pointer) {
+ kernel_sigset_t dump_done_sigset;
+ sys_sigemptyset(&dump_done_sigset);
+ sys_sigaddset(&dump_done_sigset, ExceptionHandlerProtocol::kDumpDoneSignal);
+ ScopedSigprocmaskRestore scoped_block(dump_done_sigset);
- iovec iov;
- iov.iov_base = &message;
- iov.iov_len = sizeof(message);
+ int status = SendCrashDumpRequest(info, stack_pointer);
+ if (status != 0) {
+ return status;
+ }
- msghdr msg;
- msg.msg_name = nullptr;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- ucred creds;
- creds.pid = getpid();
- creds.uid = geteuid();
- creds.gid = getegid();
-
- char cmsg_buf[CMSG_SPACE(sizeof(creds))];
- msg.msg_control = cmsg_buf;
- msg.msg_controllen = sizeof(cmsg_buf);
-
- cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDENTIALS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(creds));
- *reinterpret_cast<ucred*>(CMSG_DATA(cmsg)) = creds;
-
- if (HANDLE_EINTR(sendmsg(server_sock_, &msg, MSG_NOSIGNAL)) < 0) {
- PLOG(ERROR) << "sendmsg";
+ siginfo_t siginfo = {};
+ timespec timeout;
+ timeout.tv_sec = 5;
+ timeout.tv_nsec = 0;
+ if (HANDLE_EINTR(sys_sigtimedwait(&dump_done_sigset, &siginfo, &timeout)) <
+ 0) {
return errno;
}
return 0;
}
+int ExceptionHandlerClient::SendCrashDumpRequest(
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress stack_pointer) {
+ ExceptionHandlerProtocol::ClientToServerMessage message;
+ message.type =
+ ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest;
+ message.requesting_thread_stack_address = stack_pointer;
+ message.client_info = info;
+ return UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message));
+}
+
int ExceptionHandlerClient::WaitForCrashDumpComplete() {
- ServerToClientMessage message;
+ ExceptionHandlerProtocol::ServerToClientMessage message;
// If the server hangs up, ReadFileExactly will return false without setting
// errno.
errno = 0;
while (ReadFileExactly(server_sock_, &message, sizeof(message))) {
switch (message.type) {
- case ServerToClientMessage::kTypeForkBroker: {
+ case ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker: {
Signals::InstallDefaultHandler(SIGCHLD);
pid_t pid = fork();
if (pid <= 0) {
- Errno error = pid < 0 ? errno : 0;
+ ExceptionHandlerProtocol::Errno error = pid < 0 ? errno : 0;
if (!WriteFile(server_sock_, &error, sizeof(error))) {
return errno;
}
@@ -144,16 +198,22 @@
continue;
}
- case ServerToClientMessage::kTypeSetPtracer: {
- Errno result = SetPtracer(message.pid);
+ case ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer: {
+ ExceptionHandlerProtocol::Errno result = SetPtracer(message.pid);
if (!WriteFile(server_sock_, &result, sizeof(result))) {
return errno;
}
continue;
}
- case ServerToClientMessage::kTypeCrashDumpComplete:
- case ServerToClientMessage::kTypeCrashDumpFailed:
+ case ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials:
+ DCHECK(false);
+ continue;
+
+ case ExceptionHandlerProtocol::ServerToClientMessage::
+ kTypeCrashDumpComplete:
+ case ExceptionHandlerProtocol::ServerToClientMessage::
+ kTypeCrashDumpFailed:
return 0;
}
diff --git a/util/linux/exception_handler_client.h b/util/linux/exception_handler_client.h
index a60b065..4e10fa6 100644
--- a/util/linux/exception_handler_client.h
+++ b/util/linux/exception_handler_client.h
@@ -15,6 +15,7 @@
#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_
#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_
+#include <sys/socket.h>
#include <sys/types.h>
#include "base/macros.h"
@@ -28,17 +29,30 @@
//! \brief Constructs this object.
//!
//! \param[in] sock A socket connected to an ExceptionHandlerServer.
- explicit ExceptionHandlerClient(int sock);
+ //! \param[in] multiple_clients `true` if this socket may be used by multiple
+ //! clients.
+ ExceptionHandlerClient(int sock, bool multiple_clients);
~ExceptionHandlerClient();
+ //! \brief Communicates with the handler to determine its credentials.
+ //!
+ //! If using a multi-client socket, this method should be called before
+ //! sharing the client socket end, or the handler's response may not be
+ //! received.
+ //!
+ //! \param[out] creds The handler process' credentials, valid if this method
+ //! returns `true`.
+ //! \return `true` on success. Otherwise, `false` with a message logged.
+ bool GetHandlerCredentials(ucred* creds);
+
//! \brief Request a crash dump from the ExceptionHandlerServer.
//!
//! This method blocks until the crash dump is complete.
//!
//! \param[in] info Information about this client.
//! \return 0 on success or an error code on failure.
- int RequestCrashDump(const ClientInformation& info);
+ int RequestCrashDump(const ExceptionHandlerProtocol::ClientInformation& info);
//! \brief Uses `prctl(PR_SET_PTRACER, ...)` to set the process with
//! process ID \a pid as the ptracer for this process.
@@ -53,12 +67,17 @@
void SetCanSetPtracer(bool can_set_ptracer);
private:
- int SendCrashDumpRequest(const ClientInformation& info);
+ int SendCrashDumpRequest(
+ const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress stack_pointer);
+ int SignalCrashDump(const ExceptionHandlerProtocol::ClientInformation& info,
+ VMAddress stack_pointer);
int WaitForCrashDumpComplete();
int server_sock_;
pid_t ptracer_;
bool can_set_ptracer_;
+ bool multiple_clients_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerClient);
};
diff --git a/util/linux/exception_handler_protocol.cc b/util/linux/exception_handler_protocol.cc
index 3feca69..45590c8 100644
--- a/util/linux/exception_handler_protocol.cc
+++ b/util/linux/exception_handler_protocol.cc
@@ -16,10 +16,18 @@
namespace crashpad {
-ClientInformation::ClientInformation()
- : exception_information_address(0), sanitization_information_address(0) {}
+ExceptionHandlerProtocol::ClientInformation::ClientInformation()
+ : exception_information_address(0),
+ sanitization_information_address(0)
+#if defined(OS_LINUX)
+ , crash_loop_before_time(0)
+#endif // OS_LINUX
+{}
-ClientToServerMessage::ClientToServerMessage()
- : version(kVersion), type(kCrashDumpRequest), client_info() {}
+ExceptionHandlerProtocol::ClientToServerMessage::ClientToServerMessage()
+ : version(kVersion),
+ type(kTypeCrashDumpRequest),
+ requesting_thread_stack_address(0),
+ client_info() {}
} // namespace crashpad
diff --git a/util/linux/exception_handler_protocol.h b/util/linux/exception_handler_protocol.h
index 4ba1b5d..7312b9d 100644
--- a/util/linux/exception_handler_protocol.h
+++ b/util/linux/exception_handler_protocol.h
@@ -16,81 +16,119 @@
#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_
#include <errno.h>
+#include <signal.h>
#include <stdint.h>
#include <sys/types.h>
+#include "base/macros.h"
+#include "build/build_config.h"
#include "util/file/file_io.h"
#include "util/misc/address_types.h"
namespace crashpad {
+class ExceptionHandlerProtocol {
+ public:
#pragma pack(push, 1)
-//! \brief The type used for error reporting.
-using Errno = int32_t;
-static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small");
+ //! \brief The type used for error reporting.
+ using Errno = int32_t;
+ static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small");
-//! \brief A boolean status suitable for communication between processes.
-enum Bool : char { kBoolFalse, kBoolTrue };
+ //! \brief A boolean status suitable for communication between processes.
+ enum Bool : char { kBoolFalse, kBoolTrue };
-//! \brief Information about a client registered with an ExceptionHandlerServer.
-struct ClientInformation {
- //! \brief Constructs this object.
- ClientInformation();
+ //! \brief Information about a client registered with an
+ //! ExceptionHandlerServer.
+ struct ClientInformation {
+ //! \brief Constructs this object.
+ ClientInformation();
- //! \brief The address in the client's address space of an
- //! ExceptionInformation struct.
- VMAddress exception_information_address;
+ //! \brief The address in the client's address space of an
+ //! ExceptionInformation struct.
+ VMAddress exception_information_address;
- //! \brief The address in the client's address space of a
- //! SanitizationInformation struct, or 0 if there is no such struct.
- VMAddress sanitization_information_address;
-};
+ //! \brief The address in the client's address space of a
+ //! SanitizationInformation struct, or 0 if there is no such struct.
+ VMAddress sanitization_information_address;
-//! \brief The message passed from client to server.
-struct ClientToServerMessage {
- static constexpr int32_t kVersion = 1;
-
- //! \brief Constructs this object.
- ClientToServerMessage();
-
- //! \brief Indicates what message version is being used.
- int32_t version;
-
- enum Type : uint32_t {
- //! \brief Used to request a crash dump for the sending client.
- kCrashDumpRequest
- } type;
-
- union {
- //! \brief Valid for type == kCrashDumpRequest
- ClientInformation client_info;
+#if defined(OS_LINUX)
+ //! \brief Indicates that the client is likely in a crash loop if a crash
+ //! occurs before this timestamp. This value is only used by ChromeOS's
+ //! `/sbin/crash_reporter`.
+ uint64_t crash_loop_before_time;
+#endif
};
-};
-//! \brief The message passed from server to client.
-struct ServerToClientMessage {
- enum Type : uint32_t {
- //! \brief Indicates that the client should fork a PtraceBroker process.
- kTypeForkBroker,
+ //! \brief The signal used to indicate a crash dump is complete.
+ //!
+ //! When multiple clients share a single socket connection with the handler,
+ //! the handler sends this signal to the dump requestor to indicate when the
+ //! the dump is either done or has failed and the client may continue.
+ static constexpr int kDumpDoneSignal = SIGCONT;
- //! \brief Inidicates that the client should set allow the handler to trace
- //! it using PR_SET_PTRACER.
- kTypeSetPtracer,
+ //! \brief The message passed from client to server.
+ struct ClientToServerMessage {
+ static constexpr int32_t kVersion = 1;
- //! \brief Indicates that the handler has completed a requested crash dump.
- kTypeCrashDumpComplete,
+ //! \brief Constructs this object.
+ ClientToServerMessage();
- //! \brief Indicicates that the handler was unable to produce a crash dump.
- kTypeCrashDumpFailed
- } type;
+ //! \brief Indicates what message version is being used.
+ int32_t version;
- //! \brief The handler's process ID. Valid for kTypeSetPtracer.
- pid_t pid;
-};
+ enum Type : uint32_t {
+ //! \brief Request that the server respond with its credentials.
+ kTypeCheckCredentials,
+
+ //! \brief Used to request a crash dump for the sending client.
+ kTypeCrashDumpRequest
+ };
+
+ Type type;
+
+ //! \brief A stack address of the thread sending the message.
+ VMAddress requesting_thread_stack_address;
+
+ union {
+ //! \brief Valid for type == kCrashDumpRequest
+ ClientInformation client_info;
+ };
+ };
+
+ //! \brief The message passed from server to client.
+ struct ServerToClientMessage {
+ enum Type : uint32_t {
+ //! \brief Used to pass credentials with `SCM_CREDENTIALS`.
+ kTypeCredentials,
+
+ //! \brief Indicates that the client should fork a PtraceBroker process.
+ kTypeForkBroker,
+
+ //! \brief Inidicates that the client should set allow the handler to
+ //! trace it using PR_SET_PTRACER.
+ kTypeSetPtracer,
+
+ //! \brief Indicates that the handler has completed a requested crash
+ //! dump.
+ kTypeCrashDumpComplete,
+
+ //! \brief Indicicates that the handler was unable to produce a crash
+ //! dump.
+ kTypeCrashDumpFailed
+ };
+
+ Type type;
+
+ //! \brief The handler's process ID. Valid for kTypeSetPtracer.
+ pid_t pid;
+ };
#pragma pack(pop)
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ExceptionHandlerProtocol);
+};
+
} // namespace crashpad
#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_
diff --git a/util/linux/initial_signal_dispositions.cc b/util/linux/initial_signal_dispositions.cc
new file mode 100644
index 0000000..b72b247
--- /dev/null
+++ b/util/linux/initial_signal_dispositions.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/initial_signal_dispositions.h"
+
+#include <android/api-level.h>
+#include <signal.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if __ANDROID_API__ <= 23
+
+namespace crashpad {
+namespace {
+bool LoggingSignal(int signum, sighandler_t handler) {
+ sighandler_t previous = signal(signum, handler);
+ PLOG_IF(ERROR, previous == SIG_ERR) << "signal " << signum;
+ return previous != SIG_ERR;
+}
+} // namespace
+
+#endif // __ANDROID_API__ <= 23
+
+bool InitializeSignalDispositions() {
+#if __ANDROID_API__ <= 23
+ const int api_level = android_get_device_api_level();
+ if (api_level < 0) {
+ LOG(WARNING) << "bad api level";
+ return false;
+ }
+
+ // Bionic installs signal handlers which request crash dumps from Android's
+ // debuggerd, but there are errors in how signals which aren't automatically
+ // re-raised are handled on Marshmallow (API 23).
+ //
+ // Before requesting a dump, Bionic acquires a lock to communicate with
+ // debuggerd and expecting imminent death, never releases it.
+ //
+ // While handling the dump request, debuggerd allows the dying process to
+ // continue before ptrace-detaching it. So, when Bionic manually re-raises a
+ // signal, it is intercepted by debuggerd and the dying process is allowed to
+ // live.
+ //
+ // Bionic restores SIG_DFL for the signal it's just handled, but if a
+ // different crash signal is later recieved, Bionic attempts to reacquire the
+ // lock to communicate with debuggerd and blocks forever.
+ //
+ // Disable Bionic's signal handlers for these signals on Marshmallow.
+ bool success = true;
+ if (api_level == 23) {
+ success = LoggingSignal(SIGABRT, SIG_DFL);
+ success = LoggingSignal(SIGFPE, SIG_DFL) && success;
+ success = LoggingSignal(SIGPIPE, SIG_DFL) && success;
+#if defined(SIGSTKFLT)
+ success = LoggingSignal(SIGSTKFLT, SIG_DFL) && success;
+#endif
+ success = LoggingSignal(SIGTRAP, SIG_DFL) && success;
+ }
+
+ return success;
+#else
+ return true;
+#endif // __ANDROID_API__ <= 23
+}
+
+} // namespace crashpad
diff --git a/util/linux/initial_signal_dispositions.h b/util/linux/initial_signal_dispositions.h
new file mode 100644
index 0000000..b67083c
--- /dev/null
+++ b/util/linux/initial_signal_dispositions.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H
+#define CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H
+
+namespace crashpad {
+
+//! \brief Establishes signal dispositions for a process based on the platform.
+//!
+//! Default signal dispositions are normally configured by the kernel, but
+//! additional signal handlers might be installed by dependent or preloaded
+//! libraries, e.g. Bionic normally installs signal handlers which log stack
+//! traces to Android's logcat.
+//!
+//! This function initializes signal dispositions when the default dispositions
+//! provided by the platform are broken. This function must be called before any
+//! application level signal handlers have been installed and should be called
+//! early in the process lifetime to reduce the chance of any broken signal
+//! handlers being triggered.
+//!
+//! When running on Android M (API 23), this function installs `SIG_DFL` for
+//! signals: `SIGABRT`, `SIGFPE`, `SIGPIPE`, `SIGSTKFLT`, and `SIGTRAP`.
+//!
+//! \return `true` on success. Otherwise `false` with a message logged.
+bool InitializeSignalDispositions();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H
diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc
index 345f63c..0890cd6 100644
--- a/util/linux/memory_map.cc
+++ b/util/linux/memory_map.cc
@@ -18,6 +18,7 @@
#include <string.h>
#include <sys/sysmacros.h>
+#include "base/bit_cast.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "build/build_config.h"
@@ -30,31 +31,9 @@
namespace {
-// This function is used in this file specfically for signed or unsigned longs.
-// longs are typically either int or int64 sized, but pointers to longs are not
-// automatically coerced to pointers to ints when they are the same size.
-// Simply adding a StringToNumber for longs doesn't work since sometimes long
-// and int64_t are actually the same type, resulting in a redefinition error.
-template <typename Type>
-bool LocalStringToNumber(const std::string& string, Type* number) {
- static_assert(sizeof(Type) == sizeof(int) || sizeof(Type) == sizeof(int64_t),
- "Unexpected Type size");
-
- if (sizeof(Type) == sizeof(int)) {
- return std::numeric_limits<Type>::is_signed
- ? StringToNumber(string, reinterpret_cast<int*>(number))
- : StringToNumber(string,
- reinterpret_cast<unsigned int*>(number));
- } else {
- return std::numeric_limits<Type>::is_signed
- ? StringToNumber(string, reinterpret_cast<int64_t*>(number))
- : StringToNumber(string, reinterpret_cast<uint64_t*>(number));
- }
-}
-
template <typename Type>
bool HexStringToNumber(const std::string& string, Type* number) {
- return LocalStringToNumber("0x" + string, number);
+ return StringToNumber("0x" + string, number);
}
// The result from parsing a line from the maps file.
@@ -179,7 +158,7 @@
if (maps_file_reader->GetDelim(' ', &field) !=
DelimitedFileReader::Result::kSuccess ||
- (field.pop_back(), !LocalStringToNumber(field, &mapping.inode))) {
+ (field.pop_back(), !StringToNumber(field, &mapping.inode))) {
LOG(ERROR) << "format error";
return ParseResult::kError;
}
@@ -204,6 +183,48 @@
return ParseResult::kSuccess;
}
+class SparseReverseIterator : public MemoryMap::Iterator {
+ public:
+ SparseReverseIterator(const std::vector<const MemoryMap::Mapping*>& mappings)
+ : mappings_(mappings), riter_(mappings_.rbegin()) {}
+
+ SparseReverseIterator() : mappings_(), riter_(mappings_.rend()) {}
+
+ // Iterator:
+ const MemoryMap::Mapping* Next() override {
+ return riter_ == mappings_.rend() ? nullptr : *(riter_++);
+ }
+
+ unsigned int Count() override { return mappings_.rend() - riter_; }
+
+ private:
+ std::vector<const MemoryMap::Mapping*> mappings_;
+ std::vector<const MemoryMap::Mapping*>::reverse_iterator riter_;
+
+ DISALLOW_COPY_AND_ASSIGN(SparseReverseIterator);
+};
+
+class FullReverseIterator : public MemoryMap::Iterator {
+ public:
+ FullReverseIterator(
+ std::vector<MemoryMap::Mapping>::const_reverse_iterator rbegin,
+ std::vector<MemoryMap::Mapping>::const_reverse_iterator rend)
+ : riter_(rbegin), rend_(rend) {}
+
+ // Iterator:
+ const MemoryMap::Mapping* Next() override {
+ return riter_ == rend_ ? nullptr : &*riter_++;
+ }
+
+ unsigned int Count() override { return rend_ - riter_; }
+
+ private:
+ std::vector<MemoryMap::Mapping>::const_reverse_iterator riter_;
+ std::vector<MemoryMap::Mapping>::const_reverse_iterator rend_;
+
+ DISALLOW_COPY_AND_ASSIGN(FullReverseIterator);
+};
+
} // namespace
MemoryMap::Mapping::Mapping()
@@ -296,7 +317,7 @@
return nullptr;
}
-std::vector<const MemoryMap::Mapping*> MemoryMap::FindFilePossibleMmapStarts(
+std::unique_ptr<MemoryMap::Iterator> MemoryMap::FindFilePossibleMmapStarts(
const Mapping& mapping) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
@@ -308,27 +329,74 @@
for (const auto& candidate : mappings_) {
if (mapping.Equals(candidate)) {
possible_starts.push_back(&candidate);
- return possible_starts;
+ return std::make_unique<SparseReverseIterator>(possible_starts);
}
}
LOG(ERROR) << "mapping not found";
- return std::vector<const Mapping*>();
+ return std::make_unique<SparseReverseIterator>();
}
+#if defined(OS_ANDROID)
+ // The Android Chromium linker uses ashmem to share RELRO segments between
+ // processes. The original RELRO segment has been unmapped and replaced with a
+ // mapping named "/dev/ashmem/RELRO:<libname>" where <libname> is the base
+ // library name (e.g. libchrome.so) sans any preceding path that may be
+ // present in other mappings for the library.
+ // https://crashpad.chromium.org/bug/253
+ static constexpr char kRelro[] = "/dev/ashmem/RELRO:";
+ if (mapping.name.compare(0, strlen(kRelro), kRelro, 0, strlen(kRelro)) == 0) {
+ // The kernel appends "(deleted)" to ashmem mappings because there isn't
+ // any corresponding file on the filesystem.
+ static constexpr char kDeleted[] = " (deleted)";
+ size_t libname_end = mapping.name.rfind(kDeleted);
+ DCHECK_NE(libname_end, std::string::npos);
+ if (libname_end == std::string::npos) {
+ libname_end = mapping.name.size();
+ }
+
+ std::string libname =
+ mapping.name.substr(strlen(kRelro), libname_end - strlen(kRelro));
+ for (const auto& candidate : mappings_) {
+ if (candidate.name.rfind(libname) != std::string::npos) {
+ possible_starts.push_back(&candidate);
+ }
+ if (mapping.Equals(candidate)) {
+ return std::make_unique<SparseReverseIterator>(possible_starts);
+ }
+ }
+ }
+#endif // OS_ANDROID
+
for (const auto& candidate : mappings_) {
if (candidate.device == mapping.device &&
- candidate.inode == mapping.inode &&
- candidate.offset == 0) {
+ candidate.inode == mapping.inode
+#if !defined(OS_ANDROID)
+ // Libraries on Android may be mapped from zipfiles (APKs), in which
+ // case the offset is not 0.
+ && candidate.offset == 0
+#endif // !defined(OS_ANDROID)
+ ) {
possible_starts.push_back(&candidate);
}
if (mapping.Equals(candidate)) {
- return possible_starts;
+ return std::make_unique<SparseReverseIterator>(possible_starts);
}
}
LOG(ERROR) << "mapping not found";
- return std::vector<const Mapping*>();
+ return std::make_unique<SparseReverseIterator>();
+}
+
+std::unique_ptr<MemoryMap::Iterator> MemoryMap::ReverseIteratorFrom(
+ const Mapping& target) const {
+ for (auto riter = mappings_.crbegin(); riter != mappings_.rend(); ++riter) {
+ if (riter->Equals(target)) {
+ return std::make_unique<FullReverseIterator>(riter, mappings_.rend());
+ }
+ }
+ return std::make_unique<FullReverseIterator>(mappings_.rend(),
+ mappings_.rend());
}
} // namespace crashpad
diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h
index af194e9..d43b7af 100644
--- a/util/linux/memory_map.h
+++ b/util/linux/memory_map.h
@@ -17,6 +17,7 @@
#include <sys/types.h>
+#include <memory>
#include <string>
#include <vector>
@@ -42,7 +43,7 @@
std::string name;
CheckedLinuxAddressRange range;
- off_t offset;
+ off64_t offset;
dev_t device;
ino_t inode;
bool readable;
@@ -76,14 +77,37 @@
//! it was obtained from.
const Mapping* FindMappingWithName(const std::string& name) const;
- //! \brief Find Mappings that share a Mapping's file, mapped from offset 0.
+ //! \brief An abstract base class for iterating over ordered sets of mappings
+ //! in a MemoryMap.
+ class Iterator {
+ public:
+ virtual ~Iterator() = default;
+
+ //! \return the mapping pointed to by the iterator and advance the iterator
+ //! to the next mapping. If there are no more mappings, this method
+ //! returns `nullptr` on all subsequent invocations.
+ virtual const Mapping* Next() = 0;
+
+ //! \return the number of mappings remaining.
+ virtual unsigned int Count() = 0;
+
+ protected:
+ Iterator() = default;
+ };
+
+ //! \brief Find possible initial mappings of files mapped over several
+ //! segments.
//!
//! Executables and libaries are typically loaded into several mappings with
//! varying permissions for different segments. Portions of an ELF file may
//! be mapped multiple times as part of loading the file, for example, when
- //! initializing GNU_RELRO segments. This method searches for mappings at or
- //! below \a mapping in memory that are mapped from the same file as \a
- //! mapping from offset 0.
+ //! initializing GNU_RELRO segments.
+ //!
+ //! This method searches for mappings at or below \a mapping in memory that
+ //! are mapped from the same file as \a mapping from offset 0.
+ //!
+ //! On Android, ELF modules may be loaded from within a zipfile, so this
+ //! method may return mappings whose offset is not 0.
//!
//! This method is intended to help identify the possible base address for
//! loaded modules, but it is the caller's responsibility to determine which
@@ -94,10 +118,15 @@
//! map a file, \a mapping is returned in \a possible_starts.
//!
//! \param[in] mapping A Mapping whose series to find the start of.
- //! \return a vector of the possible mapping starts.
- std::vector<const Mapping*> FindFilePossibleMmapStarts(
+ //! \return a reverse iterator over the possible mapping starts, starting from
+ //! the mapping with highest base address.
+ std::unique_ptr<Iterator> FindFilePossibleMmapStarts(
const Mapping& mapping) const;
+ //! \return A reverse iterator over all mappings in the MemoryMap from \a
+ //! mapping to the start of the MemoryMap.
+ std::unique_ptr<Iterator> ReverseIteratorFrom(const Mapping& mapping) const;
+
private:
std::vector<Mapping> mappings_;
InitializationStateDcheck initialized_;
diff --git a/util/linux/memory_map_test.cc b/util/linux/memory_map_test.cc
index 645a0b4..0ee9080 100644
--- a/util/linux/memory_map_test.cc
+++ b/util/linux/memory_map_test.cc
@@ -29,7 +29,9 @@
#include "test/linux/fake_ptrace_connection.h"
#include "test/multiprocess.h"
#include "test/scoped_temp_dir.h"
+#include "third_party/lss/lss.h"
#include "util/file/file_io.h"
+#include "util/file/scoped_remove_file.h"
#include "util/linux/direct_ptrace_connection.h"
#include "util/misc/clock.h"
#include "util/misc/from_pointer_cast.h"
@@ -39,6 +41,38 @@
namespace test {
namespace {
+TEST(MemoryMap, SelfLargeFiles) {
+ // This test is meant to test the handler's ability to understand files
+ // mapped from large offsets, even if the handler wasn't built with
+ // _FILE_OFFSET_BITS=64. ScopedTempDir needs to stat files to determine
+ // whether to recurse into directories, which may will fail without large file
+ // support. ScopedRemoveFile doesn't have that restriction.
+ ScopedTempDir dir;
+ ScopedRemoveFile large_file_path(dir.path().Append("crashpad_test_file"));
+ ScopedFileHandle handle(
+ LoggingOpenFileForReadAndWrite(large_file_path.get(),
+ FileWriteMode::kCreateOrFail,
+ FilePermissions::kWorldReadable));
+ ASSERT_TRUE(handle.is_valid());
+
+ // sys_fallocate supports large files as long as the kernel supports them,
+ // regardless of _FILE_OFFSET_BITS.
+ off64_t off = 1llu + UINT32_MAX;
+ ASSERT_EQ(sys_fallocate(handle.get(), 0, off, getpagesize()), 0)
+ << ErrnoMessage("fallocate");
+
+ ScopedMmap mapping;
+ void* addr = sys_mmap(
+ nullptr, getpagesize(), PROT_READ, MAP_SHARED, handle.get(), off);
+ ASSERT_TRUE(addr);
+ ASSERT_TRUE(mapping.ResetAddrLen(addr, getpagesize()));
+
+ FakePtraceConnection connection;
+ ASSERT_TRUE(connection.Initialize(getpid()));
+ MemoryMap map;
+ ASSERT_TRUE(map.Initialize(&connection));
+}
+
TEST(MemoryMap, SelfBasic) {
ScopedMmap mmapping;
ASSERT_TRUE(mmapping.ResetMmap(nullptr,
@@ -67,7 +101,10 @@
ASSERT_TRUE(mapping);
EXPECT_GE(code_address, mapping->range.Base());
EXPECT_LT(code_address, mapping->range.End());
+#if !defined(OS_ANDROID)
+ // Android Q+ supports execute only memory.
EXPECT_TRUE(mapping->readable);
+#endif
EXPECT_FALSE(mapping->writable);
EXPECT_TRUE(mapping->executable);
@@ -133,7 +170,10 @@
ASSERT_TRUE(mapping);
EXPECT_GE(code_address, mapping->range.Base());
EXPECT_LT(code_address, mapping->range.End());
+#if !defined(OS_ANDROID)
+ // Android Q+ supports execute only memory.
EXPECT_TRUE(mapping->readable);
+#endif
EXPECT_TRUE(mapping->executable);
EXPECT_FALSE(mapping->writable);
@@ -373,19 +413,21 @@
ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3);
- std::vector<const MemoryMap::Mapping*> mappings;
-
- mappings = map.FindFilePossibleMmapStarts(*mapping1);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping1);
+ auto mappings = map.FindFilePossibleMmapStarts(*mapping1);
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping2);
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping2);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping1);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(mappings->Count(), 2u);
+#else
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
+#endif
}
TEST(MemoryMap, FindFilePossibleMmapStarts) {
@@ -428,19 +470,31 @@
ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3);
- std::vector<const MemoryMap::Mapping*> mappings;
-
- mappings = map.FindFilePossibleMmapStarts(*mapping1);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping1);
+#if defined(OS_ANDROID)
+ auto mappings = map.FindFilePossibleMmapStarts(*mapping1);
+ EXPECT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
+ EXPECT_EQ(mappings->Next(), nullptr);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping1);
+ EXPECT_EQ(mappings->Count(), 2u);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
- ASSERT_EQ(mappings.size(), 1u);
- EXPECT_EQ(mappings[0], mapping1);
+ EXPECT_EQ(mappings->Count(), 3u);
+#else
+ auto mappings = map.FindFilePossibleMmapStarts(*mapping1);
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
+ EXPECT_EQ(mappings->Next(), nullptr);
+
+ mappings = map.FindFilePossibleMmapStarts(*mapping2);
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
+
+ mappings = map.FindFilePossibleMmapStarts(*mapping3);
+ ASSERT_EQ(mappings->Count(), 1u);
+ EXPECT_EQ(mappings->Next(), mapping1);
+#endif
#if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true;
@@ -449,7 +503,9 @@
#endif
MemoryMap::Mapping bad_mapping;
bad_mapping.range.SetRange(is_64_bit, 0, 1);
- EXPECT_EQ(map.FindFilePossibleMmapStarts(bad_mapping).size(), 0u);
+ mappings = map.FindFilePossibleMmapStarts(bad_mapping);
+ EXPECT_EQ(mappings->Count(), 0u);
+ EXPECT_EQ(mappings->Next(), nullptr);
}
// Make the second page an anonymous mapping
@@ -562,27 +618,47 @@
auto mapping = map.FindMapping(file_mapping0.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
auto possible_starts = map.FindFilePossibleMmapStarts(*mapping);
- EXPECT_EQ(possible_starts.size(), 0u);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(possible_starts->Count(), 1u);
+#else
+ EXPECT_EQ(possible_starts->Count(), 0u);
+#endif
mapping = map.FindMapping(file_mapping1.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
- EXPECT_EQ(possible_starts.size(), 1u);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(possible_starts->Count(), 2u);
+#else
+ EXPECT_EQ(possible_starts->Count(), 1u);
+#endif
mapping = map.FindMapping(file_mapping2.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
- EXPECT_EQ(possible_starts.size(), 2u);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(possible_starts->Count(), 3u);
+#else
+ EXPECT_EQ(possible_starts->Count(), 2u);
+#endif
mapping = map.FindMapping(file_mapping3.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
- EXPECT_EQ(possible_starts.size(), 3u);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(possible_starts->Count(), 4u);
+#else
+ EXPECT_EQ(possible_starts->Count(), 3u);
+#endif
mapping = map.FindMapping(file_mapping4.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
- EXPECT_EQ(possible_starts.size(), 4u);
+#if defined(OS_ANDROID)
+ EXPECT_EQ(possible_starts->Count(), 5u);
+#else
+ EXPECT_EQ(possible_starts->Count(), 4u);
+#endif
}
} // namespace
diff --git a/util/linux/proc_stat_reader.cc b/util/linux/proc_stat_reader.cc
index b1dfe94..dd663d6 100644
--- a/util/linux/proc_stat_reader.cc
+++ b/util/linux/proc_stat_reader.cc
@@ -20,6 +20,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "util/file/file_io.h"
#include "util/misc/lexing.h"
#include "util/misc/time.h"
@@ -43,9 +44,12 @@
ProcStatReader::~ProcStatReader() {}
-bool ProcStatReader::Initialize(pid_t tid) {
+bool ProcStatReader::Initialize(PtraceConnection* connection, pid_t tid) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- if (!ReadFile(tid)) {
+
+ char path[32];
+ snprintf(path, base::size(path), "/proc/%d/stat", tid);
+ if (!connection->ReadFileContents(base::FilePath(path), &contents_)) {
return false;
}
@@ -81,7 +85,8 @@
return ReadTimeAtIndex(14, system_time);
}
-bool ProcStatReader::StartTime(timeval* start_time) const {
+bool ProcStatReader::StartTime(const timeval& boot_time,
+ timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
timeval time_after_boot;
@@ -89,33 +94,7 @@
return false;
}
- timespec uptime;
- if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
- PLOG(ERROR) << "clock_gettime";
- return false;
- }
-
- timespec current_time;
- if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) {
- PLOG(ERROR) << "clock_gettime";
- return false;
- }
-
- timespec boot_time_ts;
- SubtractTimespec(current_time, uptime, &boot_time_ts);
- timeval boot_time_tv;
- TimespecToTimeval(boot_time_ts, &boot_time_tv);
- timeradd(&boot_time_tv, &time_after_boot, start_time);
-
- return true;
-}
-
-bool ProcStatReader::ReadFile(pid_t tid) {
- char path[32];
- snprintf(path, arraysize(path), "/proc/%d/stat", tid);
- if (!LoggingReadEntireFile(base::FilePath(path), &contents_)) {
- return false;
- }
+ timeradd(&boot_time, &time_after_boot, start_time);
return true;
}
diff --git a/util/linux/proc_stat_reader.h b/util/linux/proc_stat_reader.h
index 4cad97c..6eae8fb 100644
--- a/util/linux/proc_stat_reader.h
+++ b/util/linux/proc_stat_reader.h
@@ -22,6 +22,7 @@
#include <string>
#include "base/macros.h"
+#include "util/linux/ptrace_connection.h"
#include "util/misc/initialization_state_dcheck.h"
namespace crashpad {
@@ -36,8 +37,10 @@
//!
//! This method must be successfully called before calling any other.
//!
+ //! \param[in] connection A connection to the process to which the target
+ //! thread belongs.
//! \param[in] tid The thread ID to read the stat file for.
- bool Initialize(pid_t tid);
+ bool Initialize(PtraceConnection* connection, pid_t tid);
//! \brief Determines the time the thread has spent executing in user mode.
//!
@@ -57,14 +60,14 @@
//! \brief Determines the target thread’s start time.
//!
+ //! \param[in] boot_time The kernel boot time.
//! \param[out] start_time The time that the thread started.
//!
//! \return `true` on success, with \a start_time set. Otherwise, `false` with
//! a message logged.
- bool StartTime(timeval* start_time) const;
+ bool StartTime(const timeval& boot_time, timeval* start_time) const;
private:
- bool ReadFile(pid_t tid);
bool FindColumn(int index, const char** column) const;
bool ReadTimeAtIndex(int index, timeval* time_val) const;
diff --git a/util/linux/proc_stat_reader_test.cc b/util/linux/proc_stat_reader_test.cc
index 8189c2e..01b9afd 100644
--- a/util/linux/proc_stat_reader_test.cc
+++ b/util/linux/proc_stat_reader_test.cc
@@ -20,6 +20,8 @@
#include "base/logging.h"
#include "gtest/gtest.h"
+#include "test/linux/fake_ptrace_connection.h"
+#include "util/misc/time.h"
#include "util/thread/thread.h"
namespace crashpad {
@@ -27,11 +29,19 @@
namespace {
TEST(ProcStatReader, Basic) {
+ FakePtraceConnection connection;
+ ASSERT_TRUE(connection.Initialize(getpid()));
+
ProcStatReader stat;
- ASSERT_TRUE(stat.Initialize(getpid()));
+ ASSERT_TRUE(stat.Initialize(&connection, getpid()));
+
+ timespec boot_time_ts;
+ ASSERT_TRUE(GetBootTime(&boot_time_ts));
+ timeval boot_time;
+ TimespecToTimeval(boot_time_ts, &boot_time);
timeval start_time;
- ASSERT_TRUE(stat.StartTime(&start_time));
+ ASSERT_TRUE(stat.StartTime(boot_time, &start_time));
time_t now;
time(&now);
@@ -52,27 +62,38 @@
return syscall(SYS_gettid);
}
-void GetStartTime(timeval* start_time) {
+void GetStartTime(const timeval& boot_time, timeval* start_time) {
+ FakePtraceConnection connection;
+ ASSERT_TRUE(connection.Initialize(getpid()));
+
ProcStatReader stat;
- ASSERT_TRUE(stat.Initialize(gettid()));
- ASSERT_TRUE(stat.StartTime(start_time));
+ ASSERT_TRUE(stat.Initialize(&connection, gettid()));
+ ASSERT_TRUE(stat.StartTime(boot_time, start_time));
}
class StatTimeThread : public Thread {
public:
- StatTimeThread(timeval* start_time) : start_time_(start_time) {}
+ StatTimeThread(const timeval& boot_time, timeval* start_time)
+ : boot_time_(boot_time), start_time_(start_time) {}
private:
- void ThreadMain() override { GetStartTime(start_time_); }
+ void ThreadMain() override { GetStartTime(boot_time_, start_time_); }
+
+ const timeval& boot_time_;
timeval* start_time_;
};
TEST(ProcStatReader, Threads) {
+ timespec boot_time_ts;
+ ASSERT_TRUE(GetBootTime(&boot_time_ts));
+ timeval boot_time;
+ TimespecToTimeval(boot_time_ts, &boot_time);
+
timeval main_time;
- ASSERT_NO_FATAL_FAILURE(GetStartTime(&main_time));
+ ASSERT_NO_FATAL_FAILURE(GetStartTime(boot_time, &main_time));
timeval thread_time;
- StatTimeThread thread(&thread_time);
+ StatTimeThread thread(boot_time, &thread_time);
thread.Start();
ASSERT_NO_FATAL_FAILURE(thread.Join());
@@ -82,7 +103,7 @@
time_t thread_sec,
suseconds_t thread_usec) {
return (thread_sec > main_sec) ||
- (thread_sec == main_sec && thread_usec > main_usec);
+ (thread_sec == main_sec && thread_usec >= main_usec);
},
main_time.tv_sec,
main_time.tv_usec,
diff --git a/util/linux/proc_task_reader.cc b/util/linux/proc_task_reader.cc
new file mode 100644
index 0000000..360f83a
--- /dev/null
+++ b/util/linux/proc_task_reader.cc
@@ -0,0 +1,59 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/proc_task_reader.h"
+
+#include <stdio.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "util/file/directory_reader.h"
+#include "util/misc/as_underlying_type.h"
+
+namespace crashpad {
+
+bool ReadThreadIDs(pid_t pid, std::vector<pid_t>* tids) {
+ DCHECK(tids->empty());
+
+ char path[32];
+ snprintf(path, base::size(path), "/proc/%d/task", pid);
+ DirectoryReader reader;
+ if (!reader.Open(base::FilePath(path))) {
+ return false;
+ }
+
+ std::vector<pid_t> local_tids;
+ base::FilePath tid_str;
+ DirectoryReader::Result result;
+ while ((result = reader.NextFile(&tid_str)) ==
+ DirectoryReader::Result::kSuccess) {
+ pid_t tid;
+ if (!base::StringToInt(tid_str.value(), &tid)) {
+ LOG(ERROR) << "format error";
+ continue;
+ }
+
+ local_tids.push_back(tid);
+ }
+ DCHECK_EQ(AsUnderlyingType(result),
+ AsUnderlyingType(DirectoryReader::Result::kNoMoreFiles));
+ DCHECK(!local_tids.empty());
+
+ tids->swap(local_tids);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/linux/proc_task_reader.h b/util/linux/proc_task_reader.h
new file mode 100644
index 0000000..9b95f12
--- /dev/null
+++ b/util/linux/proc_task_reader.h
@@ -0,0 +1,35 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_
+#define CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_
+
+#include <sys/types.h>
+
+#include <vector>
+
+namespace crashpad {
+
+//! \brief Enumerates the thread IDs of a process by reading
+//! <code>/proc/<i>pid</i>/task</code>.
+//!
+//! \param[in] pid The process ID for which to read thread IDs.
+//! \param[out] tids The read thread IDs.
+//! \return `true` if the task directory was successfully read. Format errors
+//! are logged, but won't cause this function to return `false`.
+bool ReadThreadIDs(pid_t pid, std::vector<pid_t>* tids);
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_
diff --git a/util/linux/proc_task_reader_test.cc b/util/linux/proc_task_reader_test.cc
new file mode 100644
index 0000000..911f6d3
--- /dev/null
+++ b/util/linux/proc_task_reader_test.cc
@@ -0,0 +1,162 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/proc_task_reader.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess_exec.h"
+#include "third_party/lss/lss.h"
+#include "util/synchronization/semaphore.h"
+#include "util/thread/thread.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+bool FindThreadID(pid_t tid, const std::vector<pid_t>& threads) {
+ for (const auto& thread : threads) {
+ if (thread == tid) {
+ return true;
+ }
+ }
+ return false;
+}
+
+class ScopedBlockingThread : public Thread {
+ public:
+ ScopedBlockingThread() : tid_sem_(0), join_sem_(0), tid_(-1) {}
+
+ ~ScopedBlockingThread() {
+ join_sem_.Signal();
+ Join();
+ }
+
+ pid_t ThreadID() {
+ tid_sem_.Wait();
+ return tid_;
+ }
+
+ private:
+ void ThreadMain() override {
+ tid_ = sys_gettid();
+ tid_sem_.Signal();
+ join_sem_.Wait();
+ }
+
+ Semaphore tid_sem_;
+ Semaphore join_sem_;
+ pid_t tid_;
+};
+
+TEST(ProcTaskReader, Self) {
+ std::vector<pid_t> tids;
+ ASSERT_TRUE(ReadThreadIDs(getpid(), &tids));
+ EXPECT_TRUE(FindThreadID(getpid(), tids));
+ EXPECT_TRUE(FindThreadID(sys_gettid(), tids));
+
+ ScopedBlockingThread thread1;
+ thread1.Start();
+
+ ScopedBlockingThread thread2;
+ thread2.Start();
+
+ pid_t thread1_tid = thread1.ThreadID();
+ pid_t thread2_tid = thread2.ThreadID();
+
+ tids.clear();
+ ASSERT_TRUE(ReadThreadIDs(getpid(), &tids));
+ EXPECT_TRUE(FindThreadID(getpid(), tids));
+ EXPECT_TRUE(FindThreadID(thread1_tid, tids));
+ EXPECT_TRUE(FindThreadID(thread2_tid, tids));
+}
+
+TEST(ProcTaskReader, BadPID) {
+ std::vector<pid_t> tids;
+ EXPECT_FALSE(ReadThreadIDs(-1, &tids));
+
+ tids.clear();
+ EXPECT_FALSE(ReadThreadIDs(0, &tids));
+}
+
+CRASHPAD_CHILD_TEST_MAIN(ProcTaskTestChild) {
+ FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
+ FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+
+ pid_t tid = getpid();
+ CheckedWriteFile(out, &tid, sizeof(tid));
+
+ tid = sys_gettid();
+ CheckedWriteFile(out, &tid, sizeof(tid));
+
+ ScopedBlockingThread thread1;
+ thread1.Start();
+
+ ScopedBlockingThread thread2;
+ thread2.Start();
+
+ tid = thread1.ThreadID();
+ CheckedWriteFile(out, &tid, sizeof(tid));
+
+ tid = thread2.ThreadID();
+ CheckedWriteFile(out, &tid, sizeof(tid));
+
+ CheckedReadFileAtEOF(in);
+ return 0;
+}
+
+class ProcTaskTest : public MultiprocessExec {
+ public:
+ ProcTaskTest() : MultiprocessExec() {
+ SetChildTestMainFunction("ProcTaskTestChild");
+ }
+
+ private:
+ bool ReadIDFromChild(std::vector<pid_t>* threads) {
+ pid_t tid;
+ if (!LoggingReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid))) {
+ return false;
+ }
+ threads->push_back(tid);
+ return true;
+ }
+
+ void MultiprocessParent() override {
+ std::vector<pid_t> ids_to_find;
+ for (size_t id_count = 0; id_count < 4; ++id_count) {
+ ASSERT_TRUE(ReadIDFromChild(&ids_to_find));
+ }
+
+ std::vector<pid_t> threads;
+ ASSERT_TRUE(ReadThreadIDs(ChildPID(), &threads));
+ for (size_t index = 0; index < ids_to_find.size(); ++index) {
+ SCOPED_TRACE(
+ base::StringPrintf("index %zd, tid %d", index, ids_to_find[index]));
+ EXPECT_TRUE(FindThreadID(ids_to_find[index], threads));
+ }
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ProcTaskTest);
+};
+
+TEST(ProcTaskReader, ReadChild) {
+ ProcTaskTest test;
+ test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/linux/ptrace_broker.cc b/util/linux/ptrace_broker.cc
index e9d3068..155a1e0 100644
--- a/util/linux/ptrace_broker.cc
+++ b/util/linux/ptrace_broker.cc
@@ -17,12 +17,14 @@
#include <fcntl.h>
#include <limits.h>
#include <string.h>
+#include <syscall.h>
#include <unistd.h>
#include <algorithm>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "util/misc/memory_sanitizer.h"
namespace crashpad {
@@ -138,9 +140,10 @@
attach_on_stack = true;
}
- Bool status = kBoolFalse;
+ ExceptionHandlerProtocol::Bool status =
+ ExceptionHandlerProtocol::kBoolFalse;
if (attach->ResetAttach(request.tid)) {
- status = kBoolTrue;
+ status = ExceptionHandlerProtocol::kBoolTrue;
if (!attach_on_stack) {
++attach_count_;
}
@@ -150,21 +153,23 @@
return errno;
}
- if (status == kBoolFalse) {
- Errno error = errno;
+ if (status == ExceptionHandlerProtocol::kBoolFalse) {
+ ExceptionHandlerProtocol::Errno error = errno;
if (!WriteFile(sock_, &error, sizeof(error))) {
return errno;
}
}
- if (attach_on_stack && status == kBoolTrue) {
+ if (attach_on_stack && status == ExceptionHandlerProtocol::kBoolTrue) {
return RunImpl();
}
continue;
}
case Request::kTypeIs64Bit: {
- Bool is_64_bit = ptracer_.Is64Bit() ? kBoolTrue : kBoolFalse;
+ ExceptionHandlerProtocol::Bool is_64_bit =
+ ptracer_.Is64Bit() ? ExceptionHandlerProtocol::kBoolTrue
+ : ExceptionHandlerProtocol::kBoolFalse;
if (!WriteFile(sock_, &is_64_bit, sizeof(is_64_bit))) {
return errno;
}
@@ -174,15 +179,15 @@
case Request::kTypeGetThreadInfo: {
GetThreadInfoResponse response;
response.success = ptracer_.GetThreadInfo(request.tid, &response.info)
- ? kBoolTrue
- : kBoolFalse;
+ ? ExceptionHandlerProtocol::kBoolTrue
+ : ExceptionHandlerProtocol::kBoolFalse;
if (!WriteFile(sock_, &response, sizeof(response))) {
return errno;
}
- if (response.success == kBoolFalse) {
- Errno error = errno;
+ if (response.success == ExceptionHandlerProtocol::kBoolFalse) {
+ ExceptionHandlerProtocol::Errno error = errno;
if (!WriteFile(sock_, &error, sizeof(error))) {
return errno;
}
@@ -192,7 +197,9 @@
case Request::kTypeReadFile: {
ScopedFileHandle handle;
- int result = ReceiveAndOpenFilePath(request.path.path_length, &handle);
+ int result = ReceiveAndOpenFilePath(request.path.path_length,
+ /* is_directory= */ false,
+ &handle);
if (result != 0) {
return result;
}
@@ -217,6 +224,26 @@
continue;
}
+ case Request::kTypeListDirectory: {
+ ScopedFileHandle handle;
+ int result = ReceiveAndOpenFilePath(request.path.path_length,
+ /* is_directory= */ true,
+ &handle);
+ if (result != 0) {
+ return result;
+ }
+
+ if (!handle.is_valid()) {
+ continue;
+ }
+
+ result = SendDirectory(handle.get());
+ if (result != 0) {
+ return result;
+ }
+ continue;
+ }
+
case Request::kTypeExit:
return 0;
}
@@ -226,7 +253,7 @@
}
}
-int PtraceBroker::SendError(Errno err) {
+int PtraceBroker::SendError(ExceptionHandlerProtocol::Errno err) {
return WriteFile(sock_, &err, sizeof(err)) ? 0 : errno;
}
@@ -329,7 +356,36 @@
return 0;
}
+#if defined(MEMORY_SANITIZER)
+// MSan doesn't intercept syscall() and doesn't see that buffer is initialized.
+__attribute__((no_sanitize("memory")))
+#endif // defined(MEMORY_SANITIZER)
+int PtraceBroker::SendDirectory(FileHandle handle) {
+ char buffer[4096];
+ int rv;
+ do {
+ rv = syscall(SYS_getdents64, handle, buffer, sizeof(buffer));
+
+ if (rv < 0) {
+ return SendReadError(static_cast<ReadError>(errno));
+ }
+
+ if (!WriteFile(sock_, &rv, sizeof(rv))) {
+ return errno;
+ }
+
+ if (rv > 0) {
+ if (!WriteFile(sock_, buffer, static_cast<size_t>(rv))) {
+ return errno;
+ }
+ }
+ } while (rv > 0);
+
+ return 0;
+}
+
int PtraceBroker::ReceiveAndOpenFilePath(VMSize path_length,
+ bool is_directory,
ScopedFileHandle* handle) {
char path[std::max(4096, PATH_MAX)];
@@ -346,8 +402,11 @@
return SendOpenResult(kOpenResultAccessDenied);
}
- ScopedFileHandle local_handle(
- HANDLE_EINTR(open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY)));
+ int flags = O_RDONLY | O_CLOEXEC | O_NOCTTY;
+ if (is_directory) {
+ flags |= O_DIRECTORY;
+ }
+ ScopedFileHandle local_handle(HANDLE_EINTR(open(path, flags)));
if (!local_handle.is_valid()) {
return SendOpenResult(static_cast<OpenResult>(errno));
}
diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h
index 2e5decd..6a7bfb7 100644
--- a/util/linux/ptrace_broker.h
+++ b/util/linux/ptrace_broker.h
@@ -75,6 +75,16 @@
//! errors, followed by an Errno. On success, the bytes read follow.
kTypeReadFile,
+ //! \brief Reads the contents of a directory. The data is returned in a
+ //! series of messages. The first message is an OpenResult, indicating
+ //! the validity of the received file path. If the OpenResult is
+ //! kOpenResultSuccess, the subsequent messages return the contents of
+ //! the directory as a dirent stream, as read by `getdents64()`. Each
+ //! subsequent message begins with an int32_t indicating the number of
+ //! bytes read, 0 for end-of-file, or -1 for errors, followed by an
+ //! Errno. On success, the bytes read follow.
+ kTypeListDirectory,
+
//! \brief Causes the broker to return from Run(), detaching all attached
//! threads. Does not respond.
kTypeExit
@@ -136,7 +146,7 @@
ThreadInfo info;
//! \brief Specifies the success or failure of this call.
- Bool success;
+ ExceptionHandlerProtocol::Bool success;
};
#pragma pack(pop)
@@ -186,13 +196,16 @@
bool AllocateAttachments();
void ReleaseAttachments();
int RunImpl();
- int SendError(Errno err);
+ int SendError(ExceptionHandlerProtocol::Errno err);
int SendReadError(ReadError err);
int SendOpenResult(OpenResult result);
int SendFileContents(FileHandle handle);
+ int SendDirectory(FileHandle handle);
void TryOpeningMemFile();
int SendMemory(pid_t pid, VMAddress address, VMSize size);
- int ReceiveAndOpenFilePath(VMSize path_length, ScopedFileHandle* handle);
+ int ReceiveAndOpenFilePath(VMSize path_length,
+ bool is_directory,
+ ScopedFileHandle* handle);
char file_root_buffer_[32];
Ptracer ptracer_;
diff --git a/util/linux/ptrace_broker_test.cc b/util/linux/ptrace_broker_test.cc
index 9ed1973..341f2c8 100644
--- a/util/linux/ptrace_broker_test.cc
+++ b/util/linux/ptrace_broker_test.cc
@@ -155,6 +155,17 @@
client_sock.get(), ChildPID(), /* try_direct_memory= */ false));
EXPECT_EQ(client.GetProcessID(), ChildPID());
+
+ std::vector<pid_t> threads;
+ ASSERT_TRUE(client.Threads(&threads));
+ EXPECT_EQ(threads.size(), 2u);
+ if (threads[0] == ChildPID()) {
+ EXPECT_EQ(threads[1], child2_tid);
+ } else {
+ EXPECT_EQ(threads[0], child2_tid);
+ EXPECT_EQ(threads[1], ChildPID());
+ }
+
EXPECT_TRUE(client.Attach(child2_tid));
EXPECT_EQ(client.Is64Bit(), am_64_bit);
diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc
index b5cceba..f097ad9 100644
--- a/util/linux/ptrace_client.cc
+++ b/util/linux/ptrace_client.cc
@@ -20,6 +20,8 @@
#include <string>
#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "util/file/file_io.h"
#include "util/linux/ptrace_broker.h"
#include "util/process/process_memory_linux.h"
@@ -29,7 +31,7 @@
namespace {
bool ReceiveAndLogError(int sock, const std::string& operation) {
- Errno error;
+ ExceptionHandlerProtocol::Errno error;
if (!LoggingReadFileExactly(sock, &error, sizeof(error))) {
return false;
}
@@ -60,19 +62,19 @@
}
bool AttachImpl(int sock, pid_t tid) {
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeAttach;
request.tid = tid;
if (!LoggingWriteFile(sock, &request, sizeof(request))) {
return false;
}
- Bool success;
+ ExceptionHandlerProtocol::Bool success;
if (!LoggingReadFileExactly(sock, &success, sizeof(success))) {
return false;
}
- if (success != kBoolTrue) {
+ if (success != ExceptionHandlerProtocol::kBoolTrue) {
ReceiveAndLogError(sock, "PtraceBroker Attach");
return false;
}
@@ -80,6 +82,48 @@
return true;
}
+struct Dirent64 {
+ ino64_t d_ino;
+ off64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[];
+};
+
+void ReadDentsAsThreadIDs(char* buffer,
+ size_t size,
+ std::vector<pid_t>* threads) {
+ while (size > offsetof(Dirent64, d_name)) {
+ auto dirent = reinterpret_cast<Dirent64*>(buffer);
+ if (size < dirent->d_reclen) {
+ LOG(ERROR) << "short dirent";
+ break;
+ }
+ buffer += dirent->d_reclen;
+ size -= dirent->d_reclen;
+
+ const size_t max_name_length =
+ dirent->d_reclen - offsetof(Dirent64, d_name);
+ size_t name_len = strnlen(dirent->d_name, max_name_length);
+ if (name_len >= max_name_length) {
+ LOG(ERROR) << "format error";
+ break;
+ }
+
+ if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+
+ pid_t tid;
+ if (!base::StringToInt(dirent->d_name, &tid)) {
+ LOG(ERROR) << "format error";
+ continue;
+ }
+ threads->push_back(tid);
+ }
+ DCHECK_EQ(size, 0u);
+}
+
} // namespace
PtraceClient::PtraceClient()
@@ -92,7 +136,7 @@
PtraceClient::~PtraceClient() {
if (sock_ != kInvalidFileHandle) {
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeExit;
LoggingWriteFile(sock_, &request, sizeof(request));
}
@@ -107,7 +151,7 @@
return false;
}
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeIs64Bit;
request.tid = pid_;
@@ -115,11 +159,11 @@
return false;
}
- Bool is_64_bit;
+ ExceptionHandlerProtocol::Bool is_64_bit;
if (!LoggingReadFileExactly(sock_, &is_64_bit, sizeof(is_64_bit))) {
return false;
}
- is_64_bit_ = is_64_bit == kBoolTrue;
+ is_64_bit_ = is_64_bit == ExceptionHandlerProtocol::kBoolTrue;
if (try_direct_memory) {
auto direct_mem = std::make_unique<ProcessMemoryLinux>();
@@ -153,7 +197,7 @@
bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeGetThreadInfo;
request.tid = tid;
if (!LoggingWriteFile(sock_, &request, sizeof(request))) {
@@ -165,7 +209,7 @@
return false;
}
- if (response.success == kBoolTrue) {
+ if (response.success == ExceptionHandlerProtocol::kBoolTrue) {
*info = response.info;
return true;
}
@@ -178,7 +222,7 @@
std::string* contents) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeReadFile;
request.path.path_length = path.value().size();
@@ -218,6 +262,51 @@
return memory_.get();
}
+bool PtraceClient::Threads(std::vector<pid_t>* threads) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK(threads->empty());
+
+ // If the broker is unable to read thread IDs, fall-back to just the main
+ // thread's ID.
+ threads->push_back(pid_);
+
+ char path[32];
+ snprintf(path, base::size(path), "/proc/%d/task", pid_);
+
+ PtraceBroker::Request request = {};
+ request.type = PtraceBroker::Request::kTypeListDirectory;
+ request.path.path_length = strlen(path);
+
+ if (!LoggingWriteFile(sock_, &request, sizeof(request)) ||
+ !SendFilePath(path, request.path.path_length)) {
+ return false;
+ }
+
+ std::vector<pid_t> local_threads;
+ int32_t read_result;
+ do {
+ if (!LoggingReadFileExactly(sock_, &read_result, sizeof(read_result))) {
+ return false;
+ }
+
+ if (read_result < 0) {
+ return ReceiveAndLogReadError(sock_, "Threads");
+ }
+
+ if (read_result > 0) {
+ auto buffer = std::make_unique<char[]>(read_result);
+ if (!LoggingReadFileExactly(sock_, buffer.get(), read_result)) {
+ return false;
+ }
+
+ ReadDentsAsThreadIDs(buffer.get(), read_result, &local_threads);
+ }
+ } while (read_result > 0);
+
+ threads->swap(local_threads);
+ return true;
+}
+
PtraceClient::BrokeredMemory::BrokeredMemory(PtraceClient* client)
: ProcessMemory(), client_(client) {}
@@ -235,7 +324,7 @@
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
char* buffer_c = reinterpret_cast<char*>(buffer);
- PtraceBroker::Request request;
+ PtraceBroker::Request request = {};
request.type = PtraceBroker::Request::kTypeReadMemory;
request.tid = pid_;
request.iov.base = address;
diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h
index a7abc0c..b4f27ae 100644
--- a/util/linux/ptrace_client.h
+++ b/util/linux/ptrace_client.h
@@ -61,6 +61,7 @@
bool ReadFileContents(const base::FilePath& path,
std::string* contents) override;
ProcessMemory* Memory() override;
+ bool Threads(std::vector<pid_t>* threads) override;
private:
class BrokeredMemory : public ProcessMemory {
diff --git a/util/linux/ptrace_connection.h b/util/linux/ptrace_connection.h
index 86dc2dd..2111747 100644
--- a/util/linux/ptrace_connection.h
+++ b/util/linux/ptrace_connection.h
@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <string>
+#include <vector>
#include "base/files/file_path.h"
#include "util/linux/thread_info.h"
@@ -64,6 +65,14 @@
//! The caller does not take ownership of the reader. The reader is valid for
//! the lifetime of the PtraceConnection that created it.
virtual ProcessMemory* Memory() = 0;
+
+ //! \brief Determines the thread IDs of the threads in the connected process.
+ //!
+ //! \param[out] threads The list of thread IDs.
+ //! \return `true` on success, `false` on failure with a message logged. If
+ //! this method returns `false`, \a threads may contain a partial list of
+ //! thread IDs.
+ virtual bool Threads(std::vector<pid_t>* threads) = 0;
};
} // namespace crashpad
diff --git a/util/linux/ptracer.cc b/util/linux/ptracer.cc
index c6c9229..557e0d3 100644
--- a/util/linux/ptracer.cc
+++ b/util/linux/ptracer.cc
@@ -44,7 +44,8 @@
return false;
}
if (iov.iov_len != sizeof(*dest)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
+ << " != " << sizeof(*dest);
return false;
}
return true;
@@ -176,7 +177,8 @@
}
} else {
if (iov.iov_len != sizeof(context->f32.fpregs)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
+ << " != " << sizeof(context->f32.fpregs);
return false;
}
context->f32.have_fpregs = true;
@@ -197,7 +199,8 @@
}
} else {
if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
+ << " != " << sizeof(context->f32.vfp);
return false;
}
context->f32.have_vfp = true;
@@ -223,7 +226,8 @@
return false;
}
if (iov.iov_len != sizeof(context->f64)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
+ << " != " << sizeof(context->f64);
return false;
}
return true;
@@ -425,9 +429,10 @@
bool GetGeneralPurposeRegisters32(pid_t tid,
ThreadContext* context,
bool can_log) {
- if (GetGeneralPurposeRegistersAndLength(tid, context, can_log) !=
- sizeof(context->t32)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
+ if (length != sizeof(context->t32)) {
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
+ << " != " << sizeof(context->t32);
return false;
}
return true;
@@ -436,9 +441,10 @@
bool GetGeneralPurposeRegisters64(pid_t tid,
ThreadContext* context,
bool can_log) {
- if (GetGeneralPurposeRegistersAndLength(tid, context, can_log) !=
- sizeof(context->t64)) {
- LOG_IF(ERROR, can_log) << "Unexpected registers size";
+ size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);
+ if (length != sizeof(context->t64)) {
+ LOG_IF(ERROR, can_log) << "Unexpected registers size " << length
+ << " != " << sizeof(context->t64);
return false;
}
return true;
@@ -467,7 +473,9 @@
} else if (length == sizeof(context.t32)) {
is_64_bit_ = false;
} else {
- LOG_IF(ERROR, can_log_) << "Unexpected registers size";
+ LOG_IF(ERROR, can_log_)
+ << "Unexpected registers size " << length
+ << " != " << sizeof(context.t64) << ", " << sizeof(context.t32);
return false;
}
diff --git a/util/linux/scoped_pr_set_dumpable.cc b/util/linux/scoped_pr_set_dumpable.cc
new file mode 100644
index 0000000..cbec009
--- /dev/null
+++ b/util/linux/scoped_pr_set_dumpable.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/scoped_pr_set_dumpable.h"
+
+#include <sys/prctl.h>
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+ScopedPrSetDumpable::ScopedPrSetDumpable(bool may_log) : may_log_(may_log) {
+ int result = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ PLOG_IF(ERROR, result < 0 && may_log_) << "prctl";
+ was_dumpable_ = result > 0;
+
+ if (!was_dumpable_) {
+ result = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ PLOG_IF(ERROR, result != 0 && may_log_) << "prctl";
+ }
+}
+
+ScopedPrSetDumpable::~ScopedPrSetDumpable() {
+ if (!was_dumpable_) {
+ int result = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+ PLOG_IF(ERROR, result != 0 && may_log_) << "prctl";
+ }
+}
+
+} // namespace crashpad
diff --git a/util/linux/scoped_pr_set_dumpable.h b/util/linux/scoped_pr_set_dumpable.h
new file mode 100644
index 0000000..1681930
--- /dev/null
+++ b/util/linux/scoped_pr_set_dumpable.h
@@ -0,0 +1,44 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_
+#define CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_
+
+#include "base/macros.h"
+
+namespace crashpad {
+
+class ScopedPrSetDumpable {
+ public:
+ //! \brief Uses `PR_SET_DUMPABLE` to make the current process dumpable.
+ //!
+ //! Restores the dumpable flag to its original value on destruction. If the
+ //! original value couldn't be determined, the destructor attempts to restore
+ //! the flag to 0 (non-dumpable).
+ //!
+ //! \param[in] may_log `true` if this object may log error messages.
+ explicit ScopedPrSetDumpable(bool may_log);
+
+ ~ScopedPrSetDumpable();
+
+ private:
+ bool was_dumpable_;
+ bool may_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPrSetDumpable);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_
diff --git a/util/linux/socket.cc b/util/linux/socket.cc
new file mode 100644
index 0000000..f56eacf
--- /dev/null
+++ b/util/linux/socket.cc
@@ -0,0 +1,192 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/socket.h"
+
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "third_party/lss/lss.h"
+
+namespace crashpad {
+
+// static
+bool UnixCredentialSocket::CreateCredentialSocketpair(ScopedFileHandle* sock1,
+ ScopedFileHandle* sock2) {
+ int socks[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socks) != 0) {
+ PLOG(ERROR) << "socketpair";
+ return false;
+ }
+ ScopedFileHandle local_sock1(socks[0]);
+ ScopedFileHandle local_sock2(socks[1]);
+
+ int optval = 1;
+ socklen_t optlen = sizeof(optval);
+ if (setsockopt(local_sock1.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=
+ 0 ||
+ setsockopt(local_sock2.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=
+ 0) {
+ PLOG(ERROR) << "setsockopt";
+ return false;
+ }
+
+ sock1->swap(local_sock1);
+ sock2->swap(local_sock2);
+ return true;
+}
+
+constexpr size_t UnixCredentialSocket::kMaxSendRecvMsgFDs = 4;
+
+// static
+int UnixCredentialSocket::SendMsg(int fd,
+ const void* buf,
+ size_t buf_size,
+ const int* fds,
+ size_t fd_count) {
+ // This function is intended to be used after a crash. fds is an integer
+ // array instead of a vector to avoid forcing callers to provide a vector,
+ // which they would have to create prior to the crash.
+ if (fds && fd_count > kMaxSendRecvMsgFDs) {
+ DLOG(ERROR) << "too many fds " << fd_count;
+ return EINVAL;
+ }
+
+ iovec iov;
+ iov.iov_base = const_cast<void*>(buf);
+ iov.iov_len = buf_size;
+
+ msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(int) * kMaxSendRecvMsgFDs)];
+ if (fds) {
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(int) * fd_count);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ DCHECK(cmsg);
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fd_count);
+ memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * fd_count);
+ }
+
+ // TODO(jperaza): Use sys_sendmsg when lss has macros for maniuplating control
+ // messages. https://crbug.com/crashpad/265
+ if (HANDLE_EINTR(sendmsg(fd, &msg, MSG_NOSIGNAL)) < 0) {
+ DPLOG(ERROR) << "sendmsg";
+ return errno;
+ }
+ return 0;
+}
+
+// static
+bool UnixCredentialSocket::RecvMsg(int fd,
+ void* buf,
+ size_t buf_size,
+ ucred* creds,
+ std::vector<ScopedFileHandle>* fds) {
+ iovec iov;
+ iov.iov_base = buf;
+ iov.iov_len = buf_size;
+
+ msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(ucred)) +
+ CMSG_SPACE(sizeof(int) * kMaxSendRecvMsgFDs)];
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ int res = HANDLE_EINTR(recvmsg(fd, &msg, 0));
+ if (res < 0) {
+ PLOG(ERROR) << "recvmsg";
+ return false;
+ }
+
+ ucred* local_creds = nullptr;
+ std::vector<ScopedFileHandle> local_fds;
+ bool unhandled_cmsgs = false;
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int* fdp = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ size_t fd_count = (reinterpret_cast<char*>(cmsg) + cmsg->cmsg_len -
+ reinterpret_cast<char*>(fdp)) /
+ sizeof(int);
+ DCHECK_LE(fd_count, kMaxSendRecvMsgFDs);
+ for (size_t index = 0; index < fd_count; ++index) {
+ if (fds) {
+ local_fds.emplace_back(fdp[index]);
+ } else if (IGNORE_EINTR(close(fdp[index])) != 0) {
+ PLOG(ERROR) << "close";
+ }
+ }
+ continue;
+ }
+
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
+ DCHECK(!local_creds);
+ local_creds = reinterpret_cast<ucred*>(CMSG_DATA(cmsg));
+ continue;
+ }
+
+ LOG(ERROR) << "unhandled cmsg " << cmsg->cmsg_level << ", "
+ << cmsg->cmsg_type;
+ unhandled_cmsgs = true;
+ }
+
+ if (unhandled_cmsgs) {
+ return false;
+ }
+
+ if (msg.msg_name != nullptr || msg.msg_namelen != 0) {
+ LOG(ERROR) << "unexpected msg name";
+ return false;
+ }
+
+ if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
+ LOG(ERROR) << "truncated msg";
+ return false;
+ }
+
+ // Credentials are missing from the message either when the recv socket wasn't
+ // configured with SO_PASSCRED or when all sending sockets have been closed.
+ // In the latter case, res == 0. This case is also indistinguishable from an
+ // empty message sent to a recv socket which hasn't set SO_PASSCRED.
+ if (!local_creds) {
+ LOG_IF(ERROR, res != 0) << "missing credentials";
+ return false;
+ }
+
+ if (static_cast<size_t>(res) != buf_size) {
+ LOG(ERROR) << "incorrect payload size " << res;
+ return false;
+ }
+
+ *creds = *local_creds;
+ if (fds) {
+ fds->swap(local_fds);
+ }
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/linux/socket.h b/util/linux/socket.h
new file mode 100644
index 0000000..85860f7
--- /dev/null
+++ b/util/linux/socket.h
@@ -0,0 +1,96 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_LINUX_SOCKET_H_
+#define CRASHPAD_UTIL_LINUX_SOCKET_H_
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "util/file/file_io.h"
+
+namespace crashpad {
+
+//! \brief Utilities for communicating over `SO_PASSCRED` enabled `AF_UNIX`
+//! sockets.
+class UnixCredentialSocket {
+ public:
+ //! \brief Creates an `AF_UNIX` family socket pair with `SO_PASSCRED` set on
+ //! each socket.
+ //!
+ //! \param[out] s1 One end of the connected pair.
+ //! \param[out] s2 The other end of the connected pair.
+ //! \return `true` on success. Otherwise, `false` with a message logged.
+ static bool CreateCredentialSocketpair(ScopedFileHandle* s1,
+ ScopedFileHandle* s2);
+
+ //! \brief The maximum number of file descriptors that may be sent/received
+ //! with `SendMsg()` or `RecvMsg()`.
+ static const size_t kMaxSendRecvMsgFDs;
+
+ //! \brief Wraps `sendmsg()` to send a message with file descriptors.
+ //!
+ //! This function is intended for use with `AF_UNIX` family sockets and
+ //! passes file descriptors with `SCM_RIGHTS`.
+ //!
+ //! This function may be used in a compromised context.
+ //!
+ //! \param[in] fd The file descriptor to write the message to.
+ //! \param[in] buf The buffer containing the message.
+ //! \param[in] buf_size The size of the message.
+ //! \param[in] fds An array of at most `kMaxSendRecvMsgFDs` file descriptors.
+ //! Optional.
+ //! \param[in] fd_count The number of file descriptors in \a fds. Required
+ //! only if \a fds was set.
+ //! \return 0 on success or an error code on failure.
+ static int SendMsg(int fd,
+ const void* buf,
+ size_t buf_size,
+ const int* fds = nullptr,
+ size_t fd_count = 0);
+
+ //! \brief Wraps `recvmsg()` to receive a message with file descriptors and
+ //! credentials.
+ //!
+ //! This function is intended to be used with `AF_UNIX` family sockets. Up to
+ //! `kMaxSendRecvMsgFDs` file descriptors may be received (via `SCM_RIGHTS`).
+ //! The socket must have `SO_PASSCRED` set.
+ //!
+ //! \param[in] fd The file descriptor to receive the message on.
+ //! \param[out] buf The buffer to fill with the message.
+ //! \param[in] buf_size The size of the message.
+ //! \param[out] creds The credentials of the sender.
+ //! \param[out] fds The recieved file descriptors. Optional. If `nullptr`, all
+ //! received file descriptors will be closed.
+ //! \return `true` on success. Otherwise, `false`, with a message logged. No
+ //! message will be logged if the message was detected to be an EOF
+ //! condition triggered by all clients disconnecting. This case is
+ //! indistinguishable from misuses of this interface that haven't set
+ //! `SO_PASSCRED` on \a fd.
+ static bool RecvMsg(int fd,
+ void* buf,
+ size_t buf_size,
+ ucred* creds,
+ std::vector<ScopedFileHandle>* fds = nullptr);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UnixCredentialSocket);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_LINUX_SOCKET_H_
diff --git a/util/linux/socket_test.cc b/util/linux/socket_test.cc
new file mode 100644
index 0000000..4e583ae
--- /dev/null
+++ b/util/linux/socket_test.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/linux/socket.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "gtest/gtest.h"
+#include "util/linux/socket.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Socket, Credentials) {
+ ScopedFileHandle send_sock, recv_sock;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));
+
+ char msg = 42;
+ ASSERT_EQ(UnixCredentialSocket::SendMsg(send_sock.get(), &msg, sizeof(msg)),
+ 0);
+
+ char recv_msg = 0;
+ ucred creds;
+ ASSERT_TRUE(UnixCredentialSocket::RecvMsg(
+ recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));
+ EXPECT_EQ(recv_msg, msg);
+ EXPECT_EQ(creds.pid, getpid());
+ EXPECT_EQ(creds.uid, geteuid());
+ EXPECT_EQ(creds.gid, getegid());
+}
+
+TEST(Socket, EmptyMessages) {
+ ScopedFileHandle send_sock, recv_sock;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));
+
+ ASSERT_EQ(UnixCredentialSocket::SendMsg(send_sock.get(), nullptr, 0), 0);
+
+ ucred creds;
+ ASSERT_TRUE(
+ UnixCredentialSocket::RecvMsg(recv_sock.get(), nullptr, 0, &creds));
+ EXPECT_EQ(creds.pid, getpid());
+ EXPECT_EQ(creds.uid, geteuid());
+ EXPECT_EQ(creds.gid, getegid());
+}
+
+TEST(Socket, Hangup) {
+ ScopedFileHandle send_sock, recv_sock;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));
+
+ send_sock.reset();
+
+ char recv_msg = 0;
+ ucred creds;
+ EXPECT_FALSE(UnixCredentialSocket::RecvMsg(
+ recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));
+}
+
+TEST(Socket, FileDescriptors) {
+ ScopedFileHandle send_sock, recv_sock;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));
+
+ ScopedFileHandle test_fd1, test_fd2;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&test_fd1, &test_fd2));
+
+ char msg = 42;
+ ASSERT_EQ(UnixCredentialSocket::SendMsg(
+ send_sock.get(), &msg, sizeof(msg), &test_fd1.get(), 1),
+ 0);
+
+ char recv_msg = 0;
+ ucred creds;
+ std::vector<ScopedFileHandle> fds;
+ ASSERT_TRUE(UnixCredentialSocket::RecvMsg(
+ recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds, &fds));
+ ASSERT_EQ(fds.size(), 1u);
+}
+
+TEST(Socket, RecvClosesFileDescriptors) {
+ ScopedFileHandle send_sock, recv_sock;
+ ASSERT_TRUE(
+ UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));
+
+ ScopedFileHandle send_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];
+ ScopedFileHandle recv_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];
+ int raw_recv_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];
+ for (size_t index = 0; index < UnixCredentialSocket::kMaxSendRecvMsgFDs;
+ ++index) {
+ ASSERT_TRUE(UnixCredentialSocket::CreateCredentialSocketpair(
+ &send_fds[index], &recv_fds[index]));
+ raw_recv_fds[index] = recv_fds[index].get();
+ }
+
+ char msg = 42;
+ ASSERT_EQ(
+ UnixCredentialSocket::SendMsg(send_sock.get(),
+ &msg,
+ sizeof(msg),
+ raw_recv_fds,
+ UnixCredentialSocket::kMaxSendRecvMsgFDs),
+ 0);
+
+ char recv_msg = 0;
+ ucred creds;
+ ASSERT_TRUE(UnixCredentialSocket::RecvMsg(
+ recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));
+ EXPECT_EQ(creds.pid, getpid());
+
+ for (size_t index = 0; index < UnixCredentialSocket::kMaxSendRecvMsgFDs;
+ ++index) {
+ recv_fds[index].reset();
+ char c;
+ EXPECT_EQ(
+ HANDLE_EINTR(send(send_fds[index].get(), &c, sizeof(c), MSG_NOSIGNAL)),
+ -1);
+ EXPECT_EQ(errno, EPIPE);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/mac/checked_mach_address_range_test.cc b/util/mac/checked_mach_address_range_test.cc
index 089f681..8531379 100644
--- a/util/mac/checked_mach_address_range_test.cc
+++ b/util/mac/checked_mach_address_range_test.cc
@@ -19,7 +19,7 @@
#include <limits>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -116,7 +116,7 @@
{0xffffffffffffffff, 1, kInvalid},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
index,
@@ -166,7 +166,7 @@
CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(
base::StringPrintf("index %zu, value 0x%llx", index, testcase.value));
@@ -223,7 +223,7 @@
CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
index,
diff --git a/util/mac/launchd_test.mm b/util/mac/launchd_test.mm
index 12ed832..b793a9b 100644
--- a/util/mac/launchd_test.mm
+++ b/util/mac/launchd_test.mm
@@ -23,7 +23,7 @@
#include <limits>
#include "base/mac/scoped_launch_data.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "util/stdlib/objc.h"
@@ -58,7 +58,7 @@
@0xfedcba9876543210,
};
- for (size_t index = 0; index < arraysize(integer_nses); ++index) {
+ for (size_t index = 0; index < base::size(integer_nses); ++index) {
NSNumber* integer_ns = integer_nses[index];
launch_data.reset(CFPropertyToLaunchData(integer_ns));
ASSERT_TRUE(launch_data.get());
@@ -88,7 +88,7 @@
[NSNumber numberWithDouble:std::numeric_limits<double>::signaling_NaN()],
};
- for (size_t index = 0; index < arraysize(double_nses); ++index) {
+ for (size_t index = 0; index < base::size(double_nses); ++index) {
NSNumber* double_ns = double_nses[index];
launch_data.reset(CFPropertyToLaunchData(double_ns));
ASSERT_TRUE(launch_data.get());
@@ -114,7 +114,7 @@
@YES,
};
- for (size_t index = 0; index < arraysize(bool_nses); ++index) {
+ for (size_t index = 0; index < base::size(bool_nses); ++index) {
NSNumber* bool_ns = bool_nses[index];
launch_data.reset(CFPropertyToLaunchData(bool_ns));
ASSERT_TRUE(launch_data.get());
@@ -138,7 +138,7 @@
@"Üñîçø∂é",
};
- for (size_t index = 0; index < arraysize(string_nses); ++index) {
+ for (size_t index = 0; index < base::size(string_nses); ++index) {
NSString* string_ns = string_nses[index];
launch_data.reset(CFPropertyToLaunchData(string_ns));
ASSERT_TRUE(launch_data.get());
diff --git a/util/mac/xattr.cc b/util/mac/xattr.cc
index 75d7938..c63fc87 100644
--- a/util/mac/xattr.cc
+++ b/util/mac/xattr.cc
@@ -20,7 +20,6 @@
#include <sys/xattr.h>
#include "base/logging.h"
-#include "base/macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
diff --git a/util/mach/bootstrap.cc b/util/mach/bootstrap.cc
new file mode 100644
index 0000000..f553437
--- /dev/null
+++ b/util/mach/bootstrap.cc
@@ -0,0 +1,109 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/bootstrap.h"
+
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+
+#include "base/mac/mach_logging.h"
+
+namespace {
+
+// This forms the internal implementation for BootstrapCheckIn() and
+// BootstrapLookUp(), which follow the same logic aside from the routine called
+// and the right type returned.
+
+struct BootstrapCheckInTraits {
+ using Type = base::mac::ScopedMachReceiveRight;
+ static kern_return_t Call(mach_port_t bootstrap_port,
+ const char* service_name,
+ mach_port_t* service_port) {
+ return bootstrap_check_in(bootstrap_port, service_name, service_port);
+ }
+ static constexpr char kName[] = "bootstrap_check_in";
+};
+constexpr char BootstrapCheckInTraits::kName[];
+
+struct BootstrapLookUpTraits {
+ using Type = base::mac::ScopedMachSendRight;
+ static kern_return_t Call(mach_port_t bootstrap_port,
+ const char* service_name,
+ mach_port_t* service_port) {
+ return bootstrap_look_up(bootstrap_port, service_name, service_port);
+ }
+ static constexpr char kName[] = "bootstrap_look_up";
+};
+constexpr char BootstrapLookUpTraits::kName[];
+
+template <typename Traits>
+typename Traits::Type BootstrapCheckInOrLookUp(
+ const std::string& service_name) {
+ // bootstrap_check_in() and bootstrap_look_up() silently truncate service
+ // names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name
+ // will not be truncated.
+ if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) {
+ LOG(ERROR) << Traits::kName << " " << service_name << ": name too long";
+ return typename Traits::Type(MACH_PORT_NULL);
+ }
+
+ mach_port_t service_port;
+ kern_return_t kr =
+ Traits::Call(bootstrap_port, service_name.c_str(), &service_port);
+ if (kr != BOOTSTRAP_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << " " << service_name;
+ service_port = MACH_PORT_NULL;
+ }
+
+ return typename Traits::Type(service_port);
+}
+
+} // namespace
+
+namespace crashpad {
+
+base::mac::ScopedMachReceiveRight BootstrapCheckIn(
+ const std::string& service_name) {
+ return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name);
+}
+
+base::mac::ScopedMachSendRight BootstrapLookUp(
+ const std::string& service_name) {
+ base::mac::ScopedMachSendRight send(
+ BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name));
+
+ // It’s possible to race the bootstrap server when the receive right
+ // corresponding to the looked-up send right is destroyed immediately before
+ // the bootstrap_look_up() call. If the bootstrap server believes that
+ // |service_name| is still registered before processing the port-destroyed
+ // notification sent to it by the kernel, it will respond to a
+ // bootstrap_look_up() request with a send right that has become a dead name,
+ // which will be returned to the bootstrap_look_up() caller, translated into
+ // the caller’s IPC port name space, as the special MACH_PORT_DEAD port name.
+ // Check for that and return MACH_PORT_NULL in its place, as though the
+ // bootstrap server had fully processed the port-destroyed notification before
+ // responding to bootstrap_look_up().
+ if (send.get() == MACH_PORT_DEAD) {
+ LOG(ERROR) << "bootstrap_look_up " << service_name << ": service is dead";
+ send.reset();
+ }
+
+ return send;
+}
+
+base::mac::ScopedMachSendRight SystemCrashReporterHandler() {
+ return BootstrapLookUp("com.apple.ReportCrash");
+}
+
+} // namespace crashpad
diff --git a/util/mach/bootstrap.h b/util/mach/bootstrap.h
new file mode 100644
index 0000000..680f5b9
--- /dev/null
+++ b/util/mach/bootstrap.h
@@ -0,0 +1,63 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MACH_BOOTSTRAP_H_
+#define CRASHPAD_UTIL_MACH_BOOTSTRAP_H_
+
+#include <string>
+
+#include "base/mac/scoped_mach_port.h"
+
+namespace crashpad {
+
+//! \brief Makes a `boostrap_check_in()` call to the process’ bootstrap server.
+//!
+//! This function is provided to make it easier to call `bootstrap_check_in()`
+//! while avoiding accidental leaks of the returned receive right.
+//!
+//! \param[in] service_name The service name to check in.
+//!
+//! \return On success, a receive right to the service port. On failure,
+//! `MACH_PORT_NULL` with a message logged.
+base::mac::ScopedMachReceiveRight BootstrapCheckIn(
+ const std::string& service_name);
+
+//! \brief Makes a `boostrap_look_up()` call to the process’ bootstrap server.
+//!
+//! This function is provided to make it easier to call `bootstrap_look_up()`
+//! while avoiding accidental leaks of the returned send right.
+//!
+//! \param[in] service_name The service name to look up.
+//!
+//! \return On success, a send right to the service port. On failure,
+//! `MACH_PORT_NULL` with a message logged.
+base::mac::ScopedMachSendRight BootstrapLookUp(const std::string& service_name);
+
+//! \brief Obtains the system’s default Mach exception handler for crash-type
+//! exceptions.
+//!
+//! This is obtained by looking up `"com.apple.ReportCrash"` with the bootstrap
+//! server. The service name comes from the first launch agent loaded by
+//! `launchd` with a `MachServices` entry having `ExceptionServer` set. This
+//! launch agent is normally loaded from
+//! `/System/Library/LaunchAgents/com.apple.ReportCrash.plist`.
+//!
+//! \return On success, a send right to an `exception_handler_t` corresponding
+//! to the system’s default crash reporter. On failure, `MACH_PORT_NULL`,
+//! with a message logged.
+base::mac::ScopedMachSendRight SystemCrashReporterHandler();
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_MACH_BOOTSTRAP_H_
diff --git a/util/mach/bootstrap_test.cc b/util/mach/bootstrap_test.cc
new file mode 100644
index 0000000..44587d0
--- /dev/null
+++ b/util/mach/bootstrap_test.cc
@@ -0,0 +1,70 @@
+// Copyright 2020 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/bootstrap.h"
+
+#include "base/mac/scoped_mach_port.h"
+#include "gtest/gtest.h"
+#include "util/mach/mach_extensions.h"
+#include "util/misc/random_string.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(Bootstrap, BootstrapCheckInAndLookUp) {
+ // This should always exist.
+ base::mac::ScopedMachSendRight report_crash(
+ BootstrapLookUp("com.apple.ReportCrash"));
+ EXPECT_NE(report_crash, kMachPortNull);
+
+ std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in.";
+ service_name.append(RandomString());
+
+ {
+ // The new service hasn’t checked in yet, so this should fail.
+ base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
+ EXPECT_EQ(send, kMachPortNull);
+
+ // Check it in.
+ base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
+ EXPECT_NE(receive, kMachPortNull);
+
+ // Now it should be possible to look up the new service.
+ send = BootstrapLookUp(service_name);
+ EXPECT_NE(send, kMachPortNull);
+
+ // It shouldn’t be possible to check the service in while it’s active.
+ base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name));
+ EXPECT_EQ(receive_2, kMachPortNull);
+ }
+
+ // The new service should be gone now.
+ base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
+ EXPECT_EQ(send, kMachPortNull);
+
+ // It should be possible to check it in again.
+ base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
+ EXPECT_NE(receive, kMachPortNull);
+}
+
+TEST(Bootstrap, SystemCrashReporterHandler) {
+ base::mac::ScopedMachSendRight system_crash_reporter_handler(
+ SystemCrashReporterHandler());
+ EXPECT_TRUE(system_crash_reporter_handler.is_valid());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/mach/child_port_handshake.cc b/util/mach/child_port_handshake.cc
index 750d3cb..3ff9828 100644
--- a/util/mach/child_port_handshake.cc
+++ b/util/mach/child_port_handshake.cc
@@ -31,8 +31,10 @@
#include "base/mac/scoped_mach_port.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "util/file/file_io.h"
+#include "util/mach/bootstrap.h"
#include "util/mach/child_port.h"
#include "util/mach/child_port_server.h"
#include "util/mach/mach_extensions.h"
@@ -125,8 +127,12 @@
return MACH_PORT_NULL;
}
- // A kqueue cannot monitor a raw Mach receive right with EVFILT_MACHPORT. It
- // requires a port set. Create a new port set and add the receive right to it.
+ // Prior to macOS 10.12, a kqueue cannot monitor a raw Mach receive right with
+ // EVFILT_MACHPORT. It requires a port set. Compare 10.11.6
+ // xnu-3248.60.10/osfmk/ipc/ipc_pset.c filt_machportattach(), which requires
+ // MACH_PORT_RIGHT_PORT_SET, to 10.12.0 xnu-3789.1.32/osfmk/ipc/ipc_pset.c
+ // filt_machportattach(), which also handles MACH_PORT_TYPE_RECEIVE. Create a
+ // new port set and add the receive right to it.
base::mac::ScopedMachPortSet server_port_set(
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
CHECK(server_port_set.is_valid());
@@ -157,8 +163,8 @@
0,
0,
nullptr);
- int rv = HANDLE_EINTR(
- kevent(kq.get(), changelist, arraysize(changelist), nullptr, 0, nullptr));
+ int rv = HANDLE_EINTR(kevent(
+ kq.get(), changelist, base::size(changelist), nullptr, 0, nullptr));
PCHECK(rv != -1) << "kevent";
ChildPortServer child_port_server(this);
diff --git a/util/mach/child_port_server.cc b/util/mach/child_port_server.cc
index a322302..aa7a1b8 100644
--- a/util/mach/child_port_server.cc
+++ b/util/mach/child_port_server.cc
@@ -15,6 +15,7 @@
#include "util/mach/child_port_server.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "util/mach/child_portServer.h"
#include "util/mach/mach_message.h"
@@ -90,7 +91,7 @@
static constexpr mach_msg_id_t request_ids[] =
{kMachMessageIDChildPortCheckIn};
return std::set<mach_msg_id_t>(&request_ids[0],
- &request_ids[arraysize(request_ids)]);
+ &request_ids[base::size(request_ids)]);
}
mach_msg_size_t ChildPortServer::MachMessageServerRequestSize() {
diff --git a/util/mach/composite_mach_message_server_test.cc b/util/mach/composite_mach_message_server_test.cc
index d45eca0..87242be 100644
--- a/util/mach/composite_mach_message_server_test.cc
+++ b/util/mach/composite_mach_message_server_test.cc
@@ -16,6 +16,7 @@
#include <sys/types.h>
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/gtest_death.h"
@@ -196,7 +197,7 @@
TestMachMessageHandler handlers[3];
std::set<mach_msg_id_t> expect_request_ids;
- for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs0); ++index) {
const mach_msg_id_t request_id = kRequestIDs0[index];
handlers[0].AddRequestID(request_id);
expect_request_ids.insert(request_id);
@@ -205,7 +206,7 @@
handlers[0].SetReplySize(sizeof(mig_reply_error_t));
handlers[0].SetReturnCodes(true, kReturnCode0, false);
- for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs1); ++index) {
const mach_msg_id_t request_id = kRequestIDs1[index];
handlers[1].AddRequestID(request_id);
expect_request_ids.insert(request_id);
@@ -214,7 +215,7 @@
handlers[1].SetReplySize(200);
handlers[1].SetReturnCodes(false, kReturnCode1, true);
- for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs2); ++index) {
const mach_msg_id_t request_id = kRequestIDs2[index];
handlers[2].AddRequestID(request_id);
expect_request_ids.insert(request_id);
@@ -252,7 +253,7 @@
// Send messages with known request IDs.
- for (size_t index = 0; index < arraysize(kRequestIDs0); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs0); ++index) {
request.header.msgh_id = kRequestIDs0[index];
SCOPED_TRACE(base::StringPrintf(
"handler 0, index %zu, id %d", index, request.header.msgh_id));
@@ -263,7 +264,7 @@
EXPECT_FALSE(destroy_complex_request);
}
- for (size_t index = 0; index < arraysize(kRequestIDs1); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs1); ++index) {
request.header.msgh_id = kRequestIDs1[index];
SCOPED_TRACE(base::StringPrintf(
"handler 1, index %zu, id %d", index, request.header.msgh_id));
@@ -274,7 +275,7 @@
EXPECT_TRUE(destroy_complex_request);
}
- for (size_t index = 0; index < arraysize(kRequestIDs2); ++index) {
+ for (size_t index = 0; index < base::size(kRequestIDs2); ++index) {
request.header.msgh_id = kRequestIDs2[index];
SCOPED_TRACE(base::StringPrintf(
"handler 2, index %zu, id %d", index, request.header.msgh_id));
diff --git a/util/mach/exc_client_variants_test.cc b/util/mach/exc_client_variants_test.cc
index b728856..007442a 100644
--- a/util/mach/exc_client_variants_test.cc
+++ b/util/mach/exc_client_variants_test.cc
@@ -20,6 +20,7 @@
#include <sys/types.h>
#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
@@ -181,11 +182,11 @@
// These aren’t real flavors, it’s just for testing.
flavor = exception_ + 10;
flavor_p = &flavor;
- for (size_t index = 0; index < arraysize(old_state); ++index) {
+ for (size_t index = 0; index < base::size(old_state); ++index) {
old_state[index] = index;
}
old_state_p = reinterpret_cast<thread_state_t>(&old_state);
- old_state_count = arraysize(old_state);
+ old_state_count = base::size(old_state);
// new_state and new_state_count are out parameters that the server should
// never see or use, so set them to bogus values. The call to the server
@@ -202,7 +203,7 @@
task,
exception,
code,
- arraysize(code),
+ base::size(code),
flavor_p,
old_state_p,
old_state_count,
@@ -273,7 +274,7 @@
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
};
- for (size_t index = 0; index < arraysize(kBehaviors); ++index) {
+ for (size_t index = 0; index < base::size(kBehaviors); ++index) {
exception_behavior_t behavior = kBehaviors[index];
SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior));
diff --git a/util/mach/exc_server_variants.cc b/util/mach/exc_server_variants.cc
index e7174ac..c272ae7 100644
--- a/util/mach/exc_server_variants.cc
+++ b/util/mach/exc_server_variants.cc
@@ -21,6 +21,8 @@
#include <vector>
#include "base/logging.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
#include "util/mac/mac_util.h"
#include "util/mach/composite_mach_message_server.h"
#include "util/mach/exc.h"
@@ -242,7 +244,7 @@
Traits::kMachMessageIDExceptionRaiseStateIdentity,
};
return std::set<mach_msg_id_t>(&request_ids[0],
- &request_ids[arraysize(request_ids)]);
+ &request_ids[base::size(request_ids)]);
}
mach_msg_size_t MachMessageServerRequestSize() override {
@@ -319,7 +321,7 @@
using Reply = typename Traits::ExceptionRaiseStateReply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->flavor = in_request_1->flavor;
- out_reply->new_stateCnt = arraysize(out_reply->new_state);
+ out_reply->new_stateCnt = base::size(out_reply->new_state);
out_reply->RetCode =
interface_->CatchExceptionRaiseState(in_header->msgh_local_port,
in_request->exception,
@@ -362,7 +364,7 @@
using Reply = typename Traits::ExceptionRaiseStateIdentityReply;
Reply* out_reply = reinterpret_cast<Reply*>(out_header);
out_reply->flavor = in_request_1->flavor;
- out_reply->new_stateCnt = arraysize(out_reply->new_state);
+ out_reply->new_stateCnt = base::size(out_reply->new_state);
out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(
in_header->msgh_local_port,
in_request->thread.name,
@@ -681,7 +683,7 @@
exception_behavior_t behavior,
bool set_thread_state) {
if (exception == EXC_CRASH
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
+#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
&& MacOSXMinorVersion() >= 11
#endif
) {
diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc
index 5e67bfe..bea3d76 100644
--- a/util/mach/exc_server_variants_test.cc
+++ b/util/mach/exc_server_variants_test.cc
@@ -19,18 +19,22 @@
#include <string.h>
#include <sys/types.h>
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
-#include "test/mac/mach_multiprocess.h"
#include "util/mac/mac_util.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"
#include "util/mach/mach_message.h"
#include "util/misc/implicit_cast.h"
+#if !defined(OS_IOS)
+#include "test/mac/mach_multiprocess.h"
+#endif // !OS_IOS
+
namespace crashpad {
namespace test {
namespace {
@@ -228,7 +232,7 @@
EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);
EXPECT_EQ(RetCode, KERN_SUCCESS);
EXPECT_EQ(flavor, kThreadStateFlavor);
- EXPECT_EQ(new_stateCnt, arraysize(new_state));
+ EXPECT_EQ(new_stateCnt, base::size(new_state));
}
mach_msg_header_t Head;
@@ -659,7 +663,7 @@
AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateAndCount(kThreadStateFlavorCount),
- IsThreadStateAndCount(arraysize(reply.new_state)),
+ IsThreadStateAndCount(base::size(reply.new_state)),
Eq(request.Trailer())))
.WillOnce(Return(KERN_SUCCESS))
.RetiresOnSaturation();
@@ -708,7 +712,7 @@
AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateAndCount(kThreadStateFlavorCount),
- IsThreadStateAndCount(arraysize(reply.new_state)),
+ IsThreadStateAndCount(base::size(reply.new_state)),
Eq(request.Trailer())))
.WillOnce(Return(KERN_SUCCESS))
.RetiresOnSaturation();
@@ -802,7 +806,7 @@
kTestMachExceptionCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateAndCount(kThreadStateFlavorCount),
- IsThreadStateAndCount(arraysize(reply.new_state)),
+ IsThreadStateAndCount(base::size(reply.new_state)),
Eq(request.Trailer())))
.WillOnce(Return(KERN_SUCCESS))
.RetiresOnSaturation();
@@ -852,7 +856,7 @@
kTestMachExceptionCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateAndCount(kThreadStateFlavorCount),
- IsThreadStateAndCount(arraysize(reply.new_state)),
+ IsThreadStateAndCount(base::size(reply.new_state)),
Eq(request.Trailer())))
.WillOnce(Return(KERN_SUCCESS))
.RetiresOnSaturation();
@@ -906,7 +910,7 @@
2508,
};
- for (size_t index = 0; index < arraysize(unknown_ids); ++index) {
+ for (size_t index = 0; index < base::size(unknown_ids); ++index) {
mach_msg_id_t id = unknown_ids[index];
SCOPED_TRACE(base::StringPrintf("unknown id %d", id));
@@ -957,6 +961,8 @@
expect_request_ids);
}
+#if !defined(OS_IOS)
+
class TestExcServerVariants : public MachMultiprocess,
public UniversalMachExcServer::Interface {
public:
@@ -1179,7 +1185,7 @@
#endif
};
- for (size_t index = 0; index < arraysize(test_data); ++index) {
+ for (size_t index = 0; index < base::size(test_data); ++index) {
const auto& test = test_data[index];
SCOPED_TRACE(
base::StringPrintf("index %zu, flavor %d", index, test.flavor));
@@ -1192,9 +1198,16 @@
}
}
+#endif // !OS_IOS
+
TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {
+#if defined(OS_IOS)
+ // iOS 9 ≅ OS X 10.11.
+ const kern_return_t prefer_not_set_thread_state = KERN_SUCCESS;
+#else
const kern_return_t prefer_not_set_thread_state =
MacOSXMinorVersion() < 11 ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
+#endif
const struct {
exception_type_t exception;
@@ -1252,7 +1265,7 @@
KERN_SUCCESS},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& test_data = kTestData[index];
SCOPED_TRACE(
base::StringPrintf("index %zu, behavior %d, set_thread_state %s",
@@ -1271,8 +1284,8 @@
static constexpr natural_t old_state[] = {1, 2, 3, 4, 5};
natural_t new_state[10] = {};
- constexpr mach_msg_type_number_t old_state_count = arraysize(old_state);
- mach_msg_type_number_t new_state_count = arraysize(new_state);
+ constexpr mach_msg_type_number_t old_state_count = base::size(old_state);
+ mach_msg_type_number_t new_state_count = base::size(new_state);
// EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not
// state-carrying. new_state and new_state_count should be untouched.
@@ -1281,8 +1294,8 @@
old_state_count,
new_state,
&new_state_count);
- EXPECT_EQ(new_state_count, arraysize(new_state));
- for (size_t i = 0; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(new_state_count, base::size(new_state));
+ for (size_t i = 0; i < base::size(new_state); ++i) {
EXPECT_EQ(new_state[i], 0u) << "i " << i;
}
@@ -1291,8 +1304,8 @@
old_state_count,
new_state,
&new_state_count);
- EXPECT_EQ(new_state_count, arraysize(new_state));
- for (size_t i = 0; i < arraysize(new_state); ++i) {
+ EXPECT_EQ(new_state_count, base::size(new_state));
+ for (size_t i = 0; i < base::size(new_state); ++i) {
EXPECT_EQ(new_state[i], 0u) << "i " << i;
}
@@ -1304,7 +1317,7 @@
for (size_t i = 0; i < copy_limit; ++i) {
EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
}
- for (size_t i = copy_limit; i < arraysize(new_state); ++i) {
+ for (size_t i = copy_limit; i < base::size(new_state); ++i) {
EXPECT_EQ(new_state[i], 0u) << "i " << i;
}
@@ -1320,23 +1333,23 @@
for (size_t i = 0; i < copy_limit; ++i) {
EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
}
- for (size_t i = copy_limit; i < arraysize(new_state); ++i) {
+ for (size_t i = copy_limit; i < base::size(new_state); ++i) {
EXPECT_EQ(new_state[i], 0u) << "i " << i;
}
// This is a state-carrying exception where all of old_state is copied to
// new_state, which is large enough to receive it and then some.
- new_state_count = arraysize(new_state);
+ new_state_count = base::size(new_state);
ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
old_state,
old_state_count,
new_state,
&new_state_count);
EXPECT_EQ(new_state_count, old_state_count);
- for (size_t i = 0; i < arraysize(old_state); ++i) {
+ for (size_t i = 0; i < base::size(old_state); ++i) {
EXPECT_EQ(new_state[i], old_state[i]) << "i " << i;
}
- for (size_t i = arraysize(old_state); i < arraysize(new_state); ++i) {
+ for (size_t i = base::size(old_state); i < base::size(new_state); ++i) {
EXPECT_EQ(new_state[i], 0u) << "i " << i;
}
}
diff --git a/util/mach/exception_behaviors_test.cc b/util/mach/exception_behaviors_test.cc
index d45110d..bdbf673 100644
--- a/util/mach/exception_behaviors_test.cc
+++ b/util/mach/exception_behaviors_test.cc
@@ -16,7 +16,7 @@
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/mach/mach_extensions.h"
@@ -53,7 +53,7 @@
EXCEPTION_STATE_IDENTITY},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& test_data = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %zu, behavior %d", index, test_data.behavior));
diff --git a/util/mach/exception_types_test.cc b/util/mach/exception_types_test.cc
index c24428a..da2c822 100644
--- a/util/mach/exception_types_test.cc
+++ b/util/mach/exception_types_test.cc
@@ -20,7 +20,7 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/mac/mac_util.h"
@@ -67,7 +67,7 @@
{0, 0, 0, 0},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& test_data = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %zu, code_0 0x%llx", index, test_data.code_0));
@@ -84,7 +84,7 @@
// Now make sure that ExcCrashRecoverOriginalException() properly ignores
// optional arguments.
- static_assert(arraysize(kTestData) >= 1, "must have something to test");
+ static_assert(base::size(kTestData) >= 1, "must have something to test");
const auto& test_data = kTestData[0];
EXPECT_EQ(
ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr),
@@ -238,7 +238,7 @@
{0x00010000, 0x00010000, static_cast<int32_t>(0xffffffff)},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& test_data = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %zu, exception 0x%x, code_0 0x%llx",
index,
diff --git a/util/mach/mach_extensions.cc b/util/mach/mach_extensions.cc
index 5450ab2..cefb9bf 100644
--- a/util/mach/mach_extensions.cc
+++ b/util/mach/mach_extensions.cc
@@ -14,66 +14,14 @@
#include "util/mach/mach_extensions.h"
+#include <Availability.h>
#include <AvailabilityMacros.h>
#include <pthread.h>
-#include <servers/bootstrap.h>
#include "base/mac/mach_logging.h"
+#include "build/build_config.h"
#include "util/mac/mac_util.h"
-namespace {
-
-// This forms the internal implementation for BootstrapCheckIn() and
-// BootstrapLookUp(), which follow the same logic aside from the routine called
-// and the right type returned.
-
-struct BootstrapCheckInTraits {
- using Type = base::mac::ScopedMachReceiveRight;
- static kern_return_t Call(mach_port_t bootstrap_port,
- const char* service_name,
- mach_port_t* service_port) {
- return bootstrap_check_in(bootstrap_port, service_name, service_port);
- }
- static constexpr char kName[] = "bootstrap_check_in";
-};
-constexpr char BootstrapCheckInTraits::kName[];
-
-struct BootstrapLookUpTraits {
- using Type = base::mac::ScopedMachSendRight;
- static kern_return_t Call(mach_port_t bootstrap_port,
- const char* service_name,
- mach_port_t* service_port) {
- return bootstrap_look_up(bootstrap_port, service_name, service_port);
- }
- static constexpr char kName[] = "bootstrap_look_up";
-};
-constexpr char BootstrapLookUpTraits::kName[];
-
-template <typename Traits>
-typename Traits::Type BootstrapCheckInOrLookUp(
- const std::string& service_name) {
- // bootstrap_check_in() and bootstrap_look_up() silently truncate service
- // names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name
- // will not be truncated.
- if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) {
- LOG(ERROR) << Traits::kName << " " << service_name << ": name too long";
- return typename Traits::Type(MACH_PORT_NULL);
- }
-
- mach_port_t service_port;
- kern_return_t kr = Traits::Call(bootstrap_port,
- service_name.c_str(),
- &service_port);
- if (kr != BOOTSTRAP_SUCCESS) {
- BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << " " << service_name;
- service_port = MACH_PORT_NULL;
- }
-
- return typename Traits::Type(service_port);
-}
-
-} // namespace
-
namespace crashpad {
thread_t MachThreadSelf() {
@@ -97,7 +45,12 @@
// 10.9.4 xnu-2422.110.17/osfmk/mach/ipc_host.c and
// xnu-2422.110.17/osfmk/mach/ipc_tt.c.
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+#if defined(OS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
+// iOS 7 ≅ OS X 10.9.
+#error This code was not ported to iOS versions older than 7
+#endif
+
+#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
const int mac_os_x_minor_version = MacOSXMinorVersion();
#endif
@@ -114,7 +67,7 @@
EXC_MASK_MACH_SYSCALL |
EXC_MASK_RPC_ALERT |
EXC_MASK_MACHINE;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
+#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
if (mac_os_x_minor_version < 8) {
return kExcMaskAll_10_6;
}
@@ -124,7 +77,7 @@
// xnu-2050.48.11/osfmk/mach/exception_types.h.
constexpr exception_mask_t kExcMaskAll_10_8 =
kExcMaskAll_10_6 | EXC_MASK_RESOURCE;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
+#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
if (mac_os_x_minor_version < 9) {
return kExcMaskAll_10_8;
}
@@ -139,7 +92,12 @@
exception_mask_t ExcMaskValid() {
const exception_mask_t kExcMaskValid_10_6 = ExcMaskAll() | EXC_MASK_CRASH;
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
+#if defined(OS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
+// iOS 9 ≅ OS X 10.11.
+#error This code was not ported to iOS versions older than 9
+#endif
+
+#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
if (MacOSXMinorVersion() < 11) {
return kExcMaskValid_10_6;
}
@@ -151,37 +109,4 @@
return kExcMaskValid_10_11;
}
-base::mac::ScopedMachReceiveRight BootstrapCheckIn(
- const std::string& service_name) {
- return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name);
-}
-
-base::mac::ScopedMachSendRight BootstrapLookUp(
- const std::string& service_name) {
- base::mac::ScopedMachSendRight send(
- BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name));
-
- // It’s possible to race the bootstrap server when the receive right
- // corresponding to the looked-up send right is destroyed immediately before
- // the bootstrap_look_up() call. If the bootstrap server believes that
- // |service_name| is still registered before processing the port-destroyed
- // notification sent to it by the kernel, it will respond to a
- // bootstrap_look_up() request with a send right that has become a dead name,
- // which will be returned to the bootstrap_look_up() caller, translated into
- // the caller’s IPC port name space, as the special MACH_PORT_DEAD port name.
- // Check for that and return MACH_PORT_NULL in its place, as though the
- // bootstrap server had fully processed the port-destroyed notification before
- // responding to bootstrap_look_up().
- if (send.get() == MACH_PORT_DEAD) {
- LOG(ERROR) << "bootstrap_look_up " << service_name << ": service is dead";
- send.reset();
- }
-
- return send;
-}
-
-base::mac::ScopedMachSendRight SystemCrashReporterHandler() {
- return BootstrapLookUp("com.apple.ReportCrash");
-}
-
} // namespace crashpad
diff --git a/util/mach/mach_extensions.h b/util/mach/mach_extensions.h
index 25e401c..9247ce9 100644
--- a/util/mach/mach_extensions.h
+++ b/util/mach/mach_extensions.h
@@ -17,10 +17,6 @@
#include <mach/mach.h>
-#include <string>
-
-#include "base/mac/scoped_mach_port.h"
-
namespace crashpad {
//! \brief `MACH_PORT_NULL` with the correct type for a Mach port,
@@ -119,43 +115,6 @@
//! support is present.
exception_mask_t ExcMaskValid();
-//! \brief Makes a `boostrap_check_in()` call to the process’ bootstrap server.
-//!
-//! This function is provided to make it easier to call `bootstrap_check_in()`
-//! while avoiding accidental leaks of the returned receive right.
-//!
-//! \param[in] service_name The service name to check in.
-//!
-//! \return On success, a receive right to the service port. On failure,
-//! `MACH_PORT_NULL` with a message logged.
-base::mac::ScopedMachReceiveRight BootstrapCheckIn(
- const std::string& service_name);
-
-//! \brief Makes a `boostrap_look_up()` call to the process’ bootstrap server.
-//!
-//! This function is provided to make it easier to call `bootstrap_look_up()`
-//! while avoiding accidental leaks of the returned send right.
-//!
-//! \param[in] service_name The service name to look up.
-//!
-//! \return On success, a send right to the service port. On failure,
-//! `MACH_PORT_NULL` with a message logged.
-base::mac::ScopedMachSendRight BootstrapLookUp(const std::string& service_name);
-
-//! \brief Obtains the system’s default Mach exception handler for crash-type
-//! exceptions.
-//!
-//! This is obtained by looking up `"com.apple.ReportCrash"` with the bootstrap
-//! server. The service name comes from the first launch agent loaded by
-//! `launchd` with a `MachServices` entry having `ExceptionServer` set. This
-//! launch agent is normally loaded from
-//! `/System/Library/LaunchAgents/com.apple.ReportCrash.plist`.
-//!
-//! \return On success, a send right to an `exception_handler_t` corresponding
-//! to the system’s default crash reporter. On failure, `MACH_PORT_NULL`,
-//! with a message logged.
-base::mac::ScopedMachSendRight SystemCrashReporterHandler();
-
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_
diff --git a/util/mach/mach_extensions_test.cc b/util/mach/mach_extensions_test.cc
index 1c5b368..9fd4940 100644
--- a/util/mach/mach_extensions_test.cc
+++ b/util/mach/mach_extensions_test.cc
@@ -15,10 +15,10 @@
#include "util/mach/mach_extensions.h"
#include "base/mac/scoped_mach_port.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "util/mac/mac_util.h"
-#include "util/misc/random_string.h"
namespace crashpad {
namespace test {
@@ -80,6 +80,11 @@
EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH);
EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY);
+#if defined(OS_IOS)
+ // Assume at least iOS 7 (≅ OS X 10.9).
+ EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
+ EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);
+#else // OS_IOS
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
@@ -92,6 +97,7 @@
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);
}
+#endif // OS_IOS
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskAll() & 1);
@@ -106,6 +112,12 @@
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH);
+#if defined(OS_IOS)
+ // Assume at least iOS 9 (≅ OS X 10.11).
+ EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
+ EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);
+ EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
+#else // OS_IOS
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
@@ -124,6 +136,7 @@
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
}
+#endif // OS_IOS
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskValid() & 1);
@@ -132,48 +145,6 @@
EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());
}
-TEST(MachExtensions, BootstrapCheckInAndLookUp) {
- // This should always exist.
- base::mac::ScopedMachSendRight
- report_crash(BootstrapLookUp("com.apple.ReportCrash"));
- EXPECT_NE(report_crash, kMachPortNull);
-
- std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in.";
- service_name.append(RandomString());
-
- {
- // The new service hasn’t checked in yet, so this should fail.
- base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
- EXPECT_EQ(send, kMachPortNull);
-
- // Check it in.
- base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
- EXPECT_NE(receive, kMachPortNull);
-
- // Now it should be possible to look up the new service.
- send = BootstrapLookUp(service_name);
- EXPECT_NE(send, kMachPortNull);
-
- // It shouldn’t be possible to check the service in while it’s active.
- base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name));
- EXPECT_EQ(receive_2, kMachPortNull);
- }
-
- // The new service should be gone now.
- base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
- EXPECT_EQ(send, kMachPortNull);
-
- // It should be possible to check it in again.
- base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
- EXPECT_NE(receive, kMachPortNull);
-}
-
-TEST(MachExtensions, SystemCrashReporterHandler) {
- base::mac::ScopedMachSendRight
- system_crash_reporter_handler(SystemCrashReporterHandler());
- EXPECT_TRUE(system_crash_reporter_handler.is_valid());
-}
-
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/util/mach/mach_message.cc b/util/mach/mach_message.cc
index 5294c03..72d71e3 100644
--- a/util/mach/mach_message.cc
+++ b/util/mach/mach_message.cc
@@ -15,7 +15,6 @@
#include "util/mach/mach_message.h"
#include <AvailabilityMacros.h>
-#include <bsm/libbsm.h>
#include <limits>
@@ -24,6 +23,10 @@
#include "util/misc/clock.h"
#include "util/misc/implicit_cast.h"
+#if !defined(OS_IOS)
+#include <bsm/libbsm.h>
+#endif // !OS_IOS
+
namespace crashpad {
namespace {
@@ -217,38 +220,6 @@
return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address);
}
-pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer) {
- if (trailer->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) {
- LOG(ERROR) << "unexpected msgh_trailer_type " << trailer->msgh_trailer_type;
- return -1;
- }
- if (trailer->msgh_trailer_size <
- REQUESTED_TRAILER_SIZE(kMachMessageReceiveAuditTrailer)) {
- LOG(ERROR) << "small msgh_trailer_size " << trailer->msgh_trailer_size;
- return -1;
- }
-
- const mach_msg_audit_trailer_t* audit_trailer =
- reinterpret_cast<const mach_msg_audit_trailer_t*>(trailer);
-
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
- pid_t audit_pid;
- audit_token_to_au32(audit_trailer->msgh_audit,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- &audit_pid,
- nullptr,
- nullptr);
-#else
- pid_t audit_pid = audit_token_to_pid(audit_trailer->msgh_audit);
-#endif
-
- return audit_pid;
-}
-
bool MachMessageDestroyReceivedPort(mach_port_t port,
mach_msg_type_name_t port_right_type) {
// This implements a subset of 10.10.5
@@ -282,4 +253,40 @@
}
}
+#if !defined(OS_IOS)
+
+pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer) {
+ if (trailer->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) {
+ LOG(ERROR) << "unexpected msgh_trailer_type " << trailer->msgh_trailer_type;
+ return -1;
+ }
+ if (trailer->msgh_trailer_size <
+ REQUESTED_TRAILER_SIZE(kMachMessageReceiveAuditTrailer)) {
+ LOG(ERROR) << "small msgh_trailer_size " << trailer->msgh_trailer_size;
+ return -1;
+ }
+
+ const mach_msg_audit_trailer_t* audit_trailer =
+ reinterpret_cast<const mach_msg_audit_trailer_t*>(trailer);
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
+ pid_t audit_pid;
+ audit_token_to_au32(audit_trailer->msgh_audit,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &audit_pid,
+ nullptr,
+ nullptr);
+#else
+ pid_t audit_pid = audit_token_to_pid(audit_trailer->msgh_audit);
+#endif
+
+ return audit_pid;
+}
+
+#endif // !OS_IOS
+
} // namespace crashpad
diff --git a/util/mach/mach_message.h b/util/mach/mach_message.h
index e0fc6f6..fd8d3d5 100644
--- a/util/mach/mach_message.h
+++ b/util/mach/mach_message.h
@@ -19,6 +19,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include "build/build_config.h"
+
namespace crashpad {
//! \brief A Mach message option specifying that an audit trailer should be
@@ -166,6 +168,23 @@
const mach_msg_trailer_t* MachMessageTrailerFromHeader(
const mach_msg_header_t* header);
+//! \brief Destroys or deallocates a Mach port received in a Mach message.
+//!
+//! This function disposes of port rights received in a Mach message. Receive
+//! rights will be destroyed with `mach_port_mod_refs()`. Send and send-once
+//! rights will be deallocated with `mach_port_deallocate()`.
+//!
+//! \param[in] port The port to destroy or deallocate.
+//! \param[in] port_right_type The right type held for \a port:
+//! `MACH_MSG_TYPE_PORT_RECEIVE`, `MACH_MSG_TYPE_PORT_SEND`, or
+//! `MACH_MSG_TYPE_PORT_SEND_ONCE`.
+//!
+//! \return `true` on success, or `false` on failure with a message logged.
+bool MachMessageDestroyReceivedPort(mach_port_t port,
+ mach_msg_type_name_t port_right_type);
+
+#if !defined(OS_IOS) || DOXYGEN
+
//! \brief Returns the process ID of a Mach message’s sender from its audit
//! trailer.
//!
@@ -182,20 +201,7 @@
//! audit information.
pid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer);
-//! \brief Destroys or deallocates a Mach port received in a Mach message.
-//!
-//! This function disposes of port rights received in a Mach message. Receive
-//! rights will be destroyed with `mach_port_mod_refs()`. Send and send-once
-//! rights will be deallocated with `mach_port_deallocate()`.
-//!
-//! \param[in] port The port to destroy or deallocate.
-//! \param[in] port_right_type The right type held for \a port:
-//! `MACH_MSG_TYPE_PORT_RECEIVE`, `MACH_MSG_TYPE_PORT_SEND`, or
-//! `MACH_MSG_TYPE_PORT_SEND_ONCE`.
-//!
-//! \return `true` on success, or `false` on failure with a message logged.
-bool MachMessageDestroyReceivedPort(mach_port_t port,
- mach_msg_type_name_t port_right_type);
+#endif // !OS_IOS
} // namespace crashpad
diff --git a/util/mach/mach_message_server_test.cc b/util/mach/mach_message_server_test.cc
index 16ba8f2..e3a3f94 100644
--- a/util/mach/mach_message_server_test.cc
+++ b/util/mach/mach_message_server_test.cc
@@ -23,6 +23,7 @@
#include "base/mac/scoped_mach_port.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "test/mac/mach_multiprocess.h"
@@ -281,7 +282,7 @@
std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
static constexpr mach_msg_id_t request_ids[] = {kRequestMessageID};
return std::set<mach_msg_id_t>(&request_ids[0],
- &request_ids[arraysize(request_ids)]);
+ &request_ids[base::size(request_ids)]);
}
mach_msg_size_t MachMessageServerRequestSize() override {
diff --git a/util/mach/mach_message_test.cc b/util/mach/mach_message_test.cc
index d88ca85..729e3a3 100644
--- a/util/mach/mach_message_test.cc
+++ b/util/mach/mach_message_test.cc
@@ -17,6 +17,7 @@
#include <unistd.h>
#include "base/mac/scoped_mach_port.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "util/mach/mach_extensions.h"
@@ -108,46 +109,6 @@
EXPECT_EQ(MachMessageTrailerFromHeader(&test.receive), &test.receive.trailer);
}
-TEST(MachMessage, AuditPIDFromMachMessageTrailer) {
- base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
- ASSERT_NE(port, kMachPortNull);
-
- mach_msg_empty_send_t send = {};
- send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);
- send.header.msgh_size = sizeof(send);
- send.header.msgh_remote_port = port.get();
- mach_msg_return_t mr =
- MachMessageWithDeadline(&send.header,
- MACH_SEND_MSG,
- 0,
- MACH_PORT_NULL,
- kMachMessageDeadlineNonblocking,
- MACH_PORT_NULL,
- false);
- ASSERT_EQ(mr, MACH_MSG_SUCCESS)
- << MachErrorMessage(mr, "MachMessageWithDeadline send");
-
- struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t {
- union {
- mach_msg_trailer_t trailer;
- mach_msg_audit_trailer_t audit_trailer;
- };
- };
-
- EmptyReceiveMessageWithAuditTrailer receive;
- mr = MachMessageWithDeadline(&receive.header,
- MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
- sizeof(receive),
- port.get(),
- kMachMessageDeadlineNonblocking,
- MACH_PORT_NULL,
- false);
- ASSERT_EQ(mr, MACH_MSG_SUCCESS)
- << MachErrorMessage(mr, "MachMessageWithDeadline receive");
-
- EXPECT_EQ(AuditPIDFromMachMessageTrailer(&receive.trailer), getpid());
-}
-
TEST(MachMessage, MachMessageDestroyReceivedPort) {
mach_port_t port = NewMachPort(MACH_PORT_RIGHT_RECEIVE);
ASSERT_NE(port, kMachPortNull);
@@ -197,6 +158,50 @@
EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND));
}
+#if !defined(OS_IOS)
+
+TEST(MachMessage, AuditPIDFromMachMessageTrailer) {
+ base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
+ ASSERT_NE(port, kMachPortNull);
+
+ mach_msg_empty_send_t send = {};
+ send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);
+ send.header.msgh_size = sizeof(send);
+ send.header.msgh_remote_port = port.get();
+ mach_msg_return_t mr =
+ MachMessageWithDeadline(&send.header,
+ MACH_SEND_MSG,
+ 0,
+ MACH_PORT_NULL,
+ kMachMessageDeadlineNonblocking,
+ MACH_PORT_NULL,
+ false);
+ ASSERT_EQ(mr, MACH_MSG_SUCCESS)
+ << MachErrorMessage(mr, "MachMessageWithDeadline send");
+
+ struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t {
+ union {
+ mach_msg_trailer_t trailer;
+ mach_msg_audit_trailer_t audit_trailer;
+ };
+ };
+
+ EmptyReceiveMessageWithAuditTrailer receive;
+ mr = MachMessageWithDeadline(&receive.header,
+ MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
+ sizeof(receive),
+ port.get(),
+ kMachMessageDeadlineNonblocking,
+ MACH_PORT_NULL,
+ false);
+ ASSERT_EQ(mr, MACH_MSG_SUCCESS)
+ << MachErrorMessage(mr, "MachMessageWithDeadline receive");
+
+ EXPECT_EQ(AuditPIDFromMachMessageTrailer(&receive.trailer), getpid());
+}
+
+#endif // !OS_IOS
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/util/mach/mig.py b/util/mach/mig.py
index 9833c8c..fa35e00 100755
--- a/util/mach/mig.py
+++ b/util/mach/mig.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
-# coding: utf-8
-# Copyright 2014 The Crashpad Authors. All rights reserved.
+# Copyright 2019 The Crashpad Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,143 +14,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import argparse
-import os
-import re
-import subprocess
import sys
-def FixUserImplementation(implementation):
- """Rewrites a MIG-generated user implementation (.c) file.
+import mig_fix
+import mig_gen
- Rewrites the file at |implementation| by adding “__attribute__((unused))” to
- the definition of any structure typedefed as “__Reply” by searching for the
- pattern unique to those structure definitions. These structures are in fact
- unused in the user implementation file, and this will trigger a
- -Wunused-local-typedefs warning in gcc unless removed or marked with the
- “unused” attribute.
- """
-
- file = open(implementation, 'r+')
- contents = file.read()
-
- pattern = re.compile('^(\t} __Reply);$', re.MULTILINE)
- contents = pattern.sub(r'\1 __attribute__((unused));', contents)
-
- file.seek(0)
- file.truncate()
- file.write(contents)
- file.close()
-
-def FixServerImplementation(implementation):
- """Rewrites a MIG-generated server implementation (.c) file.
-
- Rewrites the file at |implementation| by replacing “mig_internal” with
- “mig_external” on functions that begin with “__MIG_check__”. This makes these
- functions available to other callers outside this file from a linkage
- perspective. It then returns, as a list of lines, declarations that can be
- added to a header file, so that other files that include that header file will
- have access to these declarations from a compilation perspective.
- """
-
- file = open(implementation, 'r+')
- contents = file.read()
-
- # Find interesting declarations.
- declaration_pattern = \
- re.compile('^mig_internal (kern_return_t __MIG_check__.*)$',
- re.MULTILINE)
- declarations = declaration_pattern.findall(contents)
-
- # Remove “__attribute__((__unused__))” from the declarations, and call them
- # “mig_external” or “extern” depending on whether “mig_external” is defined.
- attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ')
- declarations = ['#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n' +
- attribute_pattern.sub('', x) +
- ';\n' for x in declarations]
-
- # Rewrite the declarations in this file as “mig_external”.
- contents = declaration_pattern.sub(r'mig_external \1', contents);
-
- # Crashpad never implements the mach_msg_server() MIG callouts. To avoid
- # needing to provide stub implementations, set KERN_FAILURE as the RetCode
- # and abort().
- routine_callout_pattern = re.compile(
- r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));')
- routine_callouts = routine_callout_pattern.findall(contents)
- for routine in routine_callouts:
- contents = contents.replace(routine[0], 'KERN_FAILURE; abort()')
-
- # Include the header for abort().
- contents = '#include <stdlib.h>\n' + contents
-
- file.seek(0)
- file.truncate()
- file.write(contents)
- file.close()
- return declarations
-
-def FixHeader(header, declarations=[]):
- """Rewrites a MIG-generated header (.h) file.
-
- Rewrites the file at |header| by placing it inside an “extern "C"” block, so
- that it declares things properly when included by a C++ compilation unit.
- |declarations| can be a list of additional declarations to place inside the
- “extern "C"” block after the original contents of |header|.
- """
-
- file = open(header, 'r+')
- contents = file.read()
- declarations_text = ''.join(declarations)
- contents = '''\
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-%s
-%s
-#ifdef __cplusplus
-}
-#endif
-''' % (contents, declarations_text)
- file.seek(0)
- file.truncate()
- file.write(contents)
- file.close()
def main(args):
- parser = argparse.ArgumentParser()
- parser.add_argument('--developer-dir', help='Path to Xcode')
- parser.add_argument('--sdk', help='Path to SDK')
- parser.add_argument('--include',
- default=[],
- action='append',
- help='Additional include directory')
- parser.add_argument('defs')
- parser.add_argument('user_c')
- parser.add_argument('server_c')
- parser.add_argument('user_h')
- parser.add_argument('server_h')
- parsed = parser.parse_args(args)
+ parsed = mig_gen.parse_args(args)
- command = ['mig',
- '-user', parsed.user_c,
- '-server', parsed.server_c,
- '-header', parsed.user_h,
- '-sheader', parsed.server_h,
- ]
- if parsed.developer_dir is not None:
- os.environ['DEVELOPER_DIR'] = parsed.developer_dir
- if parsed.sdk is not None:
- command.extend(['-isysroot', parsed.sdk])
- for include in parsed.include:
- command.extend(['-I' + include])
- command.append(parsed.defs)
- subprocess.check_call(command)
- FixUserImplementation(parsed.user_c)
- server_declarations = FixServerImplementation(parsed.server_c)
- FixHeader(parsed.user_h)
- FixHeader(parsed.server_h, server_declarations)
+ interface = mig_gen.MigInterface(parsed.user_c, parsed.server_c,
+ parsed.user_h, parsed.server_h)
+ mig_gen.generate_interface(parsed.defs, interface, parsed.include,
+ parsed.sdk, parsed.clang_path, parsed.mig_path,
+ parsed.migcom_path, parsed.arch)
+ mig_fix.fix_interface(interface)
+
if __name__ == '__main__':
- sys.exit(main(sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/util/mach/mig_fix.py b/util/mach/mig_fix.py
new file mode 100755
index 0000000..037746f
--- /dev/null
+++ b/util/mach/mig_fix.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import os
+import re
+import sys
+
+from mig_gen import MigInterface
+
+
+def _fix_user_implementation(implementation, fixed_implementation, header,
+ fixed_header):
+ """Rewrites a MIG-generated user implementation (.c) file.
+
+ Rewrites the file at |implementation| by adding “__attribute__((unused))” to
+ the definition of any structure typedefed as “__Reply” by searching for the
+ pattern unique to those structure definitions. These structures are in fact
+ unused in the user implementation file, and this will trigger a
+ -Wunused-local-typedefs warning in gcc unless removed or marked with the
+ “unused” attribute. Also changes header references to point to the new
+ header filename, if changed.
+
+ If |fixed_implementation| is None, overwrites the original; otherwise, puts
+ the result in the file at |fixed_implementation|.
+ """
+
+ file = open(implementation, 'r+' if fixed_implementation is None else 'r')
+ contents = file.read()
+
+ pattern = re.compile('^(\t} __Reply);$', re.MULTILINE)
+ contents = pattern.sub(r'\1 __attribute__((unused));', contents)
+
+ if fixed_header is not None:
+ contents = contents.replace(
+ '#include "%s"' % os.path.basename(header),
+ '#include "%s"' % os.path.basename(fixed_header))
+
+ if fixed_implementation is None:
+ file.seek(0)
+ file.truncate()
+ else:
+ file.close()
+ file = open(fixed_implementation, 'w')
+ file.write(contents)
+ file.close()
+
+
+def _fix_server_implementation(implementation, fixed_implementation, header,
+ fixed_header):
+ """Rewrites a MIG-generated server implementation (.c) file.
+
+ Rewrites the file at |implementation| by replacing “mig_internal” with
+ “mig_external” on functions that begin with “__MIG_check__”. This makes
+ these functions available to other callers outside this file from a linkage
+ perspective. It then returns, as a list of lines, declarations that can be
+ added to a header file, so that other files that include that header file
+ will have access to these declarations from a compilation perspective. Also
+ changes header references to point to the new header filename, if changed.
+
+ If |fixed_implementation| is None or not provided, overwrites the original;
+ otherwise, puts the result in the file at |fixed_implementation|.
+ """
+
+ file = open(implementation, 'r+' if fixed_implementation is None else 'r')
+ contents = file.read()
+
+ # Find interesting declarations.
+ declaration_pattern = re.compile(
+ '^mig_internal (kern_return_t __MIG_check__.*)$', re.MULTILINE)
+ declarations = declaration_pattern.findall(contents)
+
+ # Remove “__attribute__((__unused__))” from the declarations, and call them
+ # “mig_external” or “extern” depending on whether “mig_external” is defined.
+ attribute_pattern = re.compile(r'__attribute__\(\(__unused__\)\) ')
+ declarations = [
+ '''\
+#ifdef mig_external
+mig_external
+#else
+extern
+#endif
+''' + attribute_pattern.sub('', x) + ';\n' for x in declarations
+ ]
+
+ # Rewrite the declarations in this file as “mig_external”.
+ contents = declaration_pattern.sub(r'mig_external \1', contents)
+
+ # Crashpad never implements the mach_msg_server() MIG callouts. To avoid
+ # needing to provide stub implementations, set KERN_FAILURE as the RetCode
+ # and abort().
+ routine_callout_pattern = re.compile(
+ r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));')
+ routine_callouts = routine_callout_pattern.findall(contents)
+ for routine in routine_callouts:
+ contents = contents.replace(routine[0], 'KERN_FAILURE; abort()')
+
+ # Include the header for abort().
+ contents = '#include <stdlib.h>\n' + contents
+
+ if fixed_header is not None:
+ contents = contents.replace(
+ '#include "%s"' % os.path.basename(header),
+ '#include "%s"' % os.path.basename(fixed_header))
+
+ if fixed_implementation is None:
+ file.seek(0)
+ file.truncate()
+ else:
+ file.close()
+ file = open(fixed_implementation, 'w')
+ file.write(contents)
+ file.close()
+ return declarations
+
+
+def _fix_header(header, fixed_header, declarations=[]):
+ """Rewrites a MIG-generated header (.h) file.
+
+ Rewrites the file at |header| by placing it inside an “extern "C"” block, so
+ that it declares things properly when included by a C++ compilation unit.
+ |declarations| can be a list of additional declarations to place inside the
+ “extern "C"” block after the original contents of |header|.
+
+ If |fixed_header| is None or not provided, overwrites the original;
+ otherwise, puts the result in the file at |fixed_header|.
+ """
+
+ file = open(header, 'r+' if fixed_header is None else 'r')
+ contents = file.read()
+ declarations_text = ''.join(declarations)
+ contents = '''\
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+%s
+%s
+#ifdef __cplusplus
+}
+#endif
+''' % (contents, declarations_text)
+
+ if fixed_header is None:
+ file.seek(0)
+ file.truncate()
+ else:
+ file.close()
+ file = open(fixed_header, 'w')
+ file.write(contents)
+ file.close()
+
+
+def fix_interface(interface, fixed_interface=None):
+ if fixed_interface is None:
+ fixed_interface = MigInterface(None, None, None, None)
+
+ _fix_user_implementation(interface.user_c, fixed_interface.user_c,
+ interface.user_h, fixed_interface.user_h)
+ server_declarations = _fix_server_implementation(interface.server_c,
+ fixed_interface.server_c,
+ interface.server_h,
+ fixed_interface.server_h)
+ _fix_header(interface.user_h, fixed_interface.user_h)
+ _fix_header(interface.server_h, fixed_interface.server_h,
+ server_declarations)
+
+
+def main(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('user_c')
+ parser.add_argument('--fixed_user_c', default=None)
+ parser.add_argument('server_c')
+ parser.add_argument('--fixed_server_c', default=None)
+ parser.add_argument('user_h')
+ parser.add_argument('--fixed_user_h', default=None)
+ parser.add_argument('server_h')
+ parser.add_argument('--fixed_server_h', default=None)
+ parsed = parser.parse_args(args)
+
+ interface = MigInterface(parsed.user_c, parsed.server_c, parsed.user_h,
+ parsed.server_h)
+ fixed_interface = MigInterface(parsed.fixed_user_c, parsed.fixed_server_c,
+ parsed.fixed_user_h, parsed.fixed_server_h)
+ fix_interface(interface, fixed_interface)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/util/mach/mig_gen.py b/util/mach/mig_gen.py
new file mode 100755
index 0000000..dcbf829
--- /dev/null
+++ b/util/mach/mig_gen.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+
+# Copyright 2019 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import collections
+import os
+import subprocess
+import sys
+
+MigInterface = collections.namedtuple(
+ 'MigInterface', ['user_c', 'server_c', 'user_h', 'server_h'])
+
+
+def generate_interface(defs,
+ interface,
+ includes=[],
+ sdk=None,
+ clang_path=None,
+ mig_path=None,
+ migcom_path=None,
+ arch=None):
+ if mig_path is None:
+ mig_path = 'mig'
+
+ # yapf: disable
+ command = [
+ mig_path,
+ '-user', interface.user_c,
+ '-server', interface.server_c,
+ '-header', interface.user_h,
+ '-sheader', interface.server_h,
+ ]
+ # yapf: enable
+
+ if clang_path is not None:
+ os.environ['MIGCC'] = clang_path
+ if migcom_path is not None:
+ os.environ['MIGCOM'] = migcom_path
+ if arch is not None:
+ command.extend(['-arch', arch])
+ if sdk is not None:
+ command.extend(['-isysroot', sdk])
+ for include in includes:
+ command.extend(['-I' + include])
+ command.append(defs)
+ subprocess.check_call(command)
+
+
+def parse_args(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--clang-path', help='Path to Clang')
+ parser.add_argument('--mig-path', help='Path to mig')
+ parser.add_argument('--migcom-path', help='Path to migcom')
+ parser.add_argument('--arch', help='Target architecture')
+ parser.add_argument('--sdk', help='Path to SDK')
+ parser.add_argument('--include',
+ default=[],
+ action='append',
+ help='Additional include directory')
+ parser.add_argument('defs')
+ parser.add_argument('user_c')
+ parser.add_argument('server_c')
+ parser.add_argument('user_h')
+ parser.add_argument('server_h')
+ return parser.parse_args(args)
+
+
+def main(args):
+ parsed = parse_args(args)
+ interface = MigInterface(parsed.user_c, parsed.server_c, parsed.user_h,
+ parsed.server_h)
+ generate_interface(parsed.defs, interface, parsed.include, parsed.sdk,
+ parsed.clang_path, parsed.mig_path, parsed.migcom_path,
+ parsed.arch)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/util/mach/notify_server.cc b/util/mach/notify_server.cc
index 711529b..b9da795 100644
--- a/util/mach/notify_server.cc
+++ b/util/mach/notify_server.cc
@@ -15,6 +15,7 @@
#include "util/mach/notify_server.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "util/mach/mach_message.h"
#include "util/mach/notifyServer.h"
@@ -227,7 +228,7 @@
MACH_NOTIFY_DEAD_NAME,
};
return std::set<mach_msg_id_t>(&request_ids[0],
- &request_ids[arraysize(request_ids)]);
+ &request_ids[base::size(request_ids)]);
}
mach_msg_size_t NotifyServer::MachMessageServerRequestSize() {
diff --git a/util/mach/notify_server_test.cc b/util/mach/notify_server_test.cc
index b5e152d..d334e57 100644
--- a/util/mach/notify_server_test.cc
+++ b/util/mach/notify_server_test.cc
@@ -31,6 +31,7 @@
namespace {
using testing::AllOf;
+using testing::DoAll;
using testing::Eq;
using testing::Invoke;
using testing::Pointee;
diff --git a/util/mach/symbolic_constants_mach.cc b/util/mach/symbolic_constants_mach.cc
index 98a2d20..71fe115 100644
--- a/util/mach/symbolic_constants_mach.cc
+++ b/util/mach/symbolic_constants_mach.cc
@@ -17,7 +17,7 @@
#include <string.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/mach_extensions.h"
@@ -45,7 +45,7 @@
"GUARD",
"CORPSE_NOTIFY",
};
-static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT,
+static_assert(base::size(kExceptionNames) == EXC_TYPES_COUNT,
"kExceptionNames length");
constexpr char kExcPrefix[] = "EXC_";
@@ -170,8 +170,7 @@
{"_STATE32", "32"},
{"_STATE64", "64"},
};
- for (size_t suffix_index = 0;
- suffix_index < arraysize(kStateSuffixes);
+ for (size_t suffix_index = 0; suffix_index < base::size(kStateSuffixes);
++suffix_index) {
const char* suffix = kStateSuffixes[suffix_index].orig;
size_t suffix_len = strlen(suffix);
@@ -195,7 +194,7 @@
std::string ExceptionToString(exception_type_t exception,
SymbolicConstantToStringOptions options) {
const char* exception_name =
- implicit_cast<size_t>(exception) < arraysize(kExceptionNames)
+ implicit_cast<size_t>(exception) < base::size(kExceptionNames)
? kExceptionNames[exception]
: nullptr;
if (!exception_name) {
@@ -221,7 +220,7 @@
base::StringPiece short_string =
can_match_full ? string.substr(strlen(kExcPrefix)) : string;
for (exception_type_t index = 0;
- index < implicit_cast<exception_type_t>(arraysize(kExceptionNames));
+ index < implicit_cast<exception_type_t>(base::size(kExceptionNames));
++index) {
const char* exception_name = kExceptionNames[index];
if (!exception_name) {
@@ -251,8 +250,7 @@
exception_mask_t local_exception_mask = exception_mask;
std::string mask_string;
bool has_forbidden_or = false;
- for (size_t exception = 0;
- exception < arraysize(kExceptionNames);
+ for (size_t exception = 0; exception < base::size(kExceptionNames);
++exception) {
const char* exception_name = kExceptionNames[exception];
exception_mask_t exception_mask_value = 1 << exception;
@@ -326,7 +324,7 @@
base::StringPiece short_string =
can_match_full ? string.substr(strlen(kExcMaskPrefix)) : string;
for (exception_type_t index = 0;
- index < implicit_cast<exception_type_t>(arraysize(kExceptionNames));
+ index < implicit_cast<exception_type_t>(base::size(kExceptionNames));
++index) {
const char* exception_name = kExceptionNames[index];
if (!exception_name) {
@@ -365,7 +363,7 @@
const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);
const char* behavior_name =
- implicit_cast<size_t>(basic_behavior) < arraysize(kBehaviorNames)
+ implicit_cast<size_t>(basic_behavior) < base::size(kBehaviorNames)
? kBehaviorNames[basic_behavior]
: nullptr;
if (!behavior_name) {
@@ -432,7 +430,8 @@
base::StringPiece short_string =
can_match_full ? sp.substr(strlen(kBehaviorPrefix)) : sp;
for (exception_behavior_t index = 0;
- index < implicit_cast<exception_behavior_t>(arraysize(kBehaviorNames));
+ index <
+ implicit_cast<exception_behavior_t>(base::size(kBehaviorNames));
++index) {
const char* behavior_name = kBehaviorNames[index];
if (!behavior_name) {
@@ -468,13 +467,13 @@
std::string ThreadStateFlavorToString(thread_state_flavor_t flavor,
SymbolicConstantToStringOptions options) {
const char* flavor_name =
- implicit_cast<size_t>(flavor) < arraysize(kFlavorNames)
+ implicit_cast<size_t>(flavor) < base::size(kFlavorNames)
? kFlavorNames[flavor]
: nullptr;
if (!flavor_name) {
for (size_t generic_flavor_index = 0;
- generic_flavor_index < arraysize(kGenericFlavorNames);
+ generic_flavor_index < base::size(kGenericFlavorNames);
++generic_flavor_index) {
if (flavor == kGenericFlavorNames[generic_flavor_index].flavor) {
flavor_name = kGenericFlavorNames[generic_flavor_index].name;
@@ -501,7 +500,7 @@
thread_state_flavor_t* flavor) {
if ((options & kAllowFullName) || (options & kAllowShortName)) {
for (thread_state_flavor_t index = 0;
- index < implicit_cast<thread_state_flavor_t>(arraysize(kFlavorNames));
+ index < implicit_cast<thread_state_flavor_t>(base::size(kFlavorNames));
++index) {
const char* flavor_name = kFlavorNames[index];
if (!flavor_name) {
@@ -521,7 +520,7 @@
}
for (size_t generic_flavor_index = 0;
- generic_flavor_index < arraysize(kGenericFlavorNames);
+ generic_flavor_index < base::size(kGenericFlavorNames);
++generic_flavor_index) {
const char* flavor_name = kGenericFlavorNames[generic_flavor_index].name;
thread_state_flavor_t flavor_number =
diff --git a/util/mach/symbolic_constants_mach_test.cc b/util/mach/symbolic_constants_mach_test.cc
index 3bf5abb..e58a9c5 100644
--- a/util/mach/symbolic_constants_mach_test.cc
+++ b/util/mach/symbolic_constants_mach_test.cc
@@ -18,14 +18,15 @@
#include <string.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/implicit_cast.h"
-#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 }
+#define NUL_TEST_DATA(string) \
+ { string, base::size(string) - 1 }
namespace crashpad {
namespace test {
@@ -159,7 +160,7 @@
}
TEST(SymbolicConstantsMach, ExceptionToString) {
- for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) {
+ for (size_t index = 0; index < base::size(kExceptionTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestExceptionToString(kExceptionTestData[index].exception,
kExceptionTestData[index].full_name,
@@ -187,12 +188,11 @@
}
TEST(SymbolicConstantsMach, StringToException) {
- for (size_t option_index = 0;
- option_index < arraysize(kNormalOptions);
+ for (size_t option_index = 0; option_index < base::size(kNormalOptions);
++option_index) {
SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
StringToSymbolicConstantOptions options = kNormalOptions[option_index];
- for (size_t index = 0; index < arraysize(kExceptionTestData); ++index) {
+ for (size_t index = 0; index < base::size(kExceptionTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
exception_type_t exception = kExceptionTestData[index].exception;
{
@@ -230,7 +230,7 @@
"",
};
- for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToException(kNegativeTestData[index], options, false, 0);
}
@@ -251,7 +251,7 @@
NUL_TEST_DATA("1\0002"),
};
- for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNULTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
base::StringPiece string(kNULTestData[index].string,
kNULTestData[index].length);
@@ -334,7 +334,7 @@
}
TEST(SymbolicConstantsMach, ExceptionMaskToString) {
- for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) {
+ for (size_t index = 0; index < base::size(kExceptionMaskTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestExceptionMaskToString(kExceptionMaskTestData[index].exception_mask,
kExceptionMaskTestData[index].full_name,
@@ -389,12 +389,12 @@
kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,
};
- for (size_t option_index = 0;
- option_index < arraysize(kOptions);
+ for (size_t option_index = 0; option_index < base::size(kOptions);
++option_index) {
SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
StringToSymbolicConstantOptions options = kOptions[option_index];
- for (size_t index = 0; index < arraysize(kExceptionMaskTestData); ++index) {
+ for (size_t index = 0; index < base::size(kExceptionMaskTestData);
+ ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
exception_mask_t exception_mask =
kExceptionMaskTestData[index].exception_mask;
@@ -445,7 +445,7 @@
"",
};
- for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToExceptionMask(kNegativeTestData[index], options, false, 0);
}
@@ -471,7 +471,7 @@
NUL_TEST_DATA("ARITHMETIC|\0EMULATION"),
};
- for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNULTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
base::StringPiece string(kNULTestData[index].string,
kNULTestData[index].length);
@@ -506,7 +506,7 @@
EXC_MASK_SYSCALL | 0x100},
};
- for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNonCanonicalTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToExceptionMask(kNonCanonicalTestData[index].string,
kNonCanonicalTestData[index].options,
@@ -577,8 +577,7 @@
}
TEST(SymbolicConstantsMach, ExceptionBehaviorToString) {
- for (size_t index = 0;
- index < arraysize(kExceptionBehaviorTestData);
+ for (size_t index = 0; index < base::size(kExceptionBehaviorTestData);
++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestExceptionBehaviorToString(kExceptionBehaviorTestData[index].behavior,
@@ -608,13 +607,11 @@
}
TEST(SymbolicConstantsMach, StringToExceptionBehavior) {
- for (size_t option_index = 0;
- option_index < arraysize(kNormalOptions);
+ for (size_t option_index = 0; option_index < base::size(kNormalOptions);
++option_index) {
SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
StringToSymbolicConstantOptions options = kNormalOptions[option_index];
- for (size_t index = 0;
- index < arraysize(kExceptionBehaviorTestData);
+ for (size_t index = 0; index < base::size(kExceptionBehaviorTestData);
++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
exception_behavior_t behavior =
@@ -660,7 +657,7 @@
"",
};
- for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToExceptionBehavior(
kNegativeTestData[index], options, false, 0);
@@ -686,7 +683,7 @@
NUL_TEST_DATA("STATE_IDENTITY|\0MACH"),
};
- for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNULTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
base::StringPiece string(kNULTestData[index].string,
kNULTestData[index].length);
@@ -723,7 +720,7 @@
implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 0x2)},
};
- for (size_t index = 0; index < arraysize(kNonCanonicalTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNonCanonicalTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToExceptionBehavior(kNonCanonicalTestData[index].string,
kNonCanonicalTestData[index].options,
@@ -840,8 +837,7 @@
}
TEST(SymbolicConstantsMach, ThreadStateFlavorToString) {
- for (size_t index = 0;
- index < arraysize(kThreadStateFlavorTestData);
+ for (size_t index = 0; index < base::size(kThreadStateFlavorTestData);
++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestThreadStateFlavorToString(kThreadStateFlavorTestData[index].flavor,
@@ -883,13 +879,11 @@
}
TEST(SymbolicConstantsMach, StringToThreadStateFlavor) {
- for (size_t option_index = 0;
- option_index < arraysize(kNormalOptions);
+ for (size_t option_index = 0; option_index < base::size(kNormalOptions);
++option_index) {
SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
StringToSymbolicConstantOptions options = kNormalOptions[option_index];
- for (size_t index = 0;
- index < arraysize(kThreadStateFlavorTestData);
+ for (size_t index = 0; index < base::size(kThreadStateFlavorTestData);
++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
thread_state_flavor_t flavor = kThreadStateFlavorTestData[index].flavor;
@@ -959,7 +953,7 @@
#endif
};
- for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToThreadStateFlavor(
kNegativeTestData[index], options, false, 0);
@@ -1025,7 +1019,7 @@
#endif
};
- for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNULTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
base::StringPiece string(kNULTestData[index].string,
kNULTestData[index].length);
diff --git a/util/mach/task_memory.cc b/util/mach/task_memory.cc
deleted file mode 100644
index 6168434..0000000
--- a/util/mach/task_memory.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2014 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "util/mach/task_memory.h"
-
-#include <mach/mach_vm.h>
-#include <string.h>
-
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/mac/mach_logging.h"
-#include "base/strings/stringprintf.h"
-#include "util/stdlib/strnlen.h"
-
-namespace crashpad {
-
-TaskMemory::MappedMemory::~MappedMemory() {
-}
-
-bool TaskMemory::MappedMemory::ReadCString(
- size_t offset, std::string* string) const {
- if (offset >= user_size_) {
- LOG(WARNING) << "offset out of range";
- return false;
- }
-
- const char* string_base = reinterpret_cast<const char*>(data_) + offset;
- size_t max_length = user_size_ - offset;
- size_t string_length = strnlen(string_base, max_length);
- if (string_length == max_length) {
- LOG(WARNING) << "unterminated string";
- return false;
- }
-
- string->assign(string_base, string_length);
- return true;
-}
-
-TaskMemory::MappedMemory::MappedMemory(vm_address_t vm_address,
- size_t vm_size,
- size_t user_offset,
- size_t user_size)
- : vm_(vm_address, vm_size),
- data_(reinterpret_cast<const void*>(vm_address + user_offset)),
- user_size_(user_size) {
- vm_address_t vm_end = vm_address + vm_size;
- vm_address_t user_address = reinterpret_cast<vm_address_t>(data_);
- vm_address_t user_end = user_address + user_size;
- DCHECK_GE(user_address, vm_address);
- DCHECK_LE(user_address, vm_end);
- DCHECK_GE(user_end, vm_address);
- DCHECK_LE(user_end, vm_end);
-}
-
-TaskMemory::TaskMemory(task_t task) : task_(task) {
-}
-
-bool TaskMemory::Read(mach_vm_address_t address, size_t size, void* buffer) {
- std::unique_ptr<MappedMemory> memory = ReadMapped(address, size);
- if (!memory) {
- return false;
- }
-
- memcpy(buffer, memory->data(), size);
- return true;
-}
-
-std::unique_ptr<TaskMemory::MappedMemory> TaskMemory::ReadMapped(
- mach_vm_address_t address,
- size_t size) {
- if (size == 0) {
- return std::unique_ptr<MappedMemory>(new MappedMemory(0, 0, 0, 0));
- }
-
- mach_vm_address_t region_address = mach_vm_trunc_page(address);
- mach_vm_size_t region_size =
- mach_vm_round_page(address - region_address + size);
-
- vm_offset_t region;
- mach_msg_type_number_t region_count;
- kern_return_t kr =
- mach_vm_read(task_, region_address, region_size, ®ion, ®ion_count);
- if (kr != KERN_SUCCESS) {
- MACH_LOG(WARNING, kr) << base::StringPrintf(
- "mach_vm_read(0x%llx, 0x%llx)", region_address, region_size);
- return std::unique_ptr<MappedMemory>();
- }
-
- DCHECK_EQ(region_count, region_size);
- return std::unique_ptr<MappedMemory>(
- new MappedMemory(region, region_size, address - region_address, size));
-}
-
-bool TaskMemory::ReadCString(mach_vm_address_t address, std::string* string) {
- return ReadCStringInternal(address, false, 0, string);
-}
-
-bool TaskMemory::ReadCStringSizeLimited(mach_vm_address_t address,
- mach_vm_size_t size,
- std::string* string) {
- return ReadCStringInternal(address, true, size, string);
-}
-
-bool TaskMemory::ReadCStringInternal(mach_vm_address_t address,
- bool has_size,
- mach_vm_size_t size,
- std::string* string) {
- if (has_size) {
- if (size == 0) {
- string->clear();
- return true;
- }
- } else {
- size = PAGE_SIZE;
- }
-
- std::string local_string;
- mach_vm_address_t read_address = address;
- do {
- mach_vm_size_t read_length =
- std::min(size, PAGE_SIZE - (read_address % PAGE_SIZE));
- std::unique_ptr<MappedMemory> read_region =
- ReadMapped(read_address, read_length);
- if (!read_region) {
- return false;
- }
-
- const char* read_region_data =
- reinterpret_cast<const char*>(read_region->data());
- size_t read_region_data_length = strnlen(read_region_data, read_length);
- local_string.append(read_region_data, read_region_data_length);
- if (read_region_data_length < read_length) {
- string->swap(local_string);
- return true;
- }
-
- if (has_size) {
- size -= read_length;
- }
- read_address = mach_vm_trunc_page(read_address + read_length);
- } while ((!has_size || size > 0) && read_address > address);
-
- LOG(WARNING) << base::StringPrintf("unterminated string at 0x%llx", address);
- return false;
-}
-
-} // namespace crashpad
diff --git a/util/mach/task_memory.h b/util/mach/task_memory.h
deleted file mode 100644
index c8caf85..0000000
--- a/util/mach/task_memory.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2014 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
-#define CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
-
-#include <mach/mach.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-
-#include "base/mac/scoped_mach_vm.h"
-#include "base/macros.h"
-
-namespace crashpad {
-
-//! \brief Accesses the memory of another Mach task.
-class TaskMemory {
- public:
- //! \brief A memory region mapped from another Mach task.
- //!
- //! The mapping is maintained until this object is destroyed.
- class MappedMemory {
- public:
- ~MappedMemory();
-
- //! \brief Returns a pointer to the data requested by the user.
- //!
- //! This is the value of the \a vm_address + \a user_offset parameters
- //! passed to the constructor, casted to `const void*`.
- const void* data() const { return data_; }
-
- //! \brief Reads a `NUL`-terminated C string from the mapped region.
- //!
- //! This method will read contiguous memory until a `NUL` terminator is
- //! found.
- //!
- //! \param[in] offset The offset into data() of the string to be read.
- //! \param[out] string The string, whose contents begin at data() and
- //! continue up to a `NUL` terminator.
- //!
- //! \return `true` on success, with \a string set appropriately. If \a
- //! offset is greater than or equal to the \a user_size constructor
- //! parameter, or if no `NUL` terminator was found in data() after \a
- //! offset, returns `false` with an appropriate warning logged.
- bool ReadCString(size_t offset, std::string* string) const;
-
- private:
- //! \brief Creates an object that owns a memory region mapped from another
- //! Mach task.
- //!
- //! \param[in] vm_address The address in this process’ address space where
- //! the mapping begins. This must be page-aligned.
- //! \param[in] vm_size The total size of the mapping that begins at \a
- //! vm_address. This must be page-aligned.
- //! \param[in] user_offset The offset into the mapped region where the data
- //! requested by the user begins. This accounts for the fact that a
- //! mapping must be page-aligned but the user data may not be. This
- //! parameter must be equal to or less than \a vm_size.
- //! \param[in] user_size The size of the data requested by the user. This
- //! parameter can be used to compute the end address of user data, which
- //! must be within the mapped region.
- MappedMemory(vm_address_t vm_address,
- size_t vm_size,
- size_t user_offset,
- size_t user_size);
-
- base::mac::ScopedMachVM vm_;
- const void* data_;
- size_t user_size_;
-
- // The outer class needs to be able to call this class’ private constructor.
- friend class TaskMemory;
-
- DISALLOW_COPY_AND_ASSIGN(MappedMemory);
- };
-
- //! \param[in] task A send right to the target task’s task port. This object
- //! does not take ownership of the send right.
- explicit TaskMemory(task_t task);
-
- ~TaskMemory() {}
-
- //! \brief Copies memory from the target task into a caller-provided buffer in
- //! the current task.
- //!
- //! \param[in] address The address, in the target task’s address space, of the
- //! memory region to copy.
- //! \param[in] size The size, in bytes, of the memory region to copy. \a
- //! buffer must be at least this size.
- //! \param[out] buffer The buffer into which the contents of the other task’s
- //! memory will be copied.
- //!
- //! \return `true` on success, with \a buffer filled appropriately. `false` on
- //! failure, with a warning logged. Failures can occur, for example, when
- //! encountering unmapped or unreadable pages.
- //!
- //! \sa ReadMapped()
- bool Read(mach_vm_address_t address, size_t size, void* buffer);
-
- //! \brief Maps memory from the target task into the current task.
- //!
- //! This interface is an alternative to Read() that does not require the
- //! caller to provide a buffer to fill. This avoids copying memory, which can
- //! offer a performance improvement.
- //!
- //! \param[in] address The address, in the target task’s address space, of the
- //! memory region to map.
- //! \param[in] size The size, in bytes, of the memory region to map.
- //!
- //! \return On success, a MappedMemory object that provides access to the data
- //! requested. On faliure, `nullptr`, with a warning logged. Failures can
- //! occur, for example, when encountering unmapped or unreadable pages.
- std::unique_ptr<MappedMemory> ReadMapped(mach_vm_address_t address,
- size_t size);
-
- //! \brief Reads a `NUL`-terminated C string from the target task into a
- //! string in the current task.
- //!
- //! The length of the string need not be known ahead of time. This method will
- //! read contiguous memory until a `NUL` terminator is found.
- //!
- //! \param[in] address The address, in the target task’s address space, of the
- //! string to copy.
- //! \param[out] string The string read from the other task.
- //!
- //! \return `true` on success, with \a string set appropriately. `false` on
- //! failure, with a warning logged. Failures can occur, for example, when
- //! encountering unmapped or unreadable pages.
- //!
- //! \sa MappedMemory::ReadCString()
- bool ReadCString(mach_vm_address_t address, std::string* string);
-
- //! \brief Reads a `NUL`-terminated C string from the target task into a
- //! string in the current task.
- //!
- //! \param[in] address The address, in the target task’s address space, of the
- //! string to copy.
- //! \param[in] size The maximum number of bytes to read. The string is
- //! required to be `NUL`-terminated within this many bytes.
- //! \param[out] string The string read from the other task.
- //!
- //! \return `true` on success, with \a string set appropriately. `false` on
- //! failure, with a warning logged. Failures can occur, for example, when
- //! a `NUL` terminator is not found within \a size bytes, or when
- //! encountering unmapped or unreadable pages.
- //!
- //! \sa MappedMemory::ReadCString()
- bool ReadCStringSizeLimited(mach_vm_address_t address,
- mach_vm_size_t size,
- std::string* string);
-
- private:
- // The common internal implementation shared by the ReadCString*() methods.
- bool ReadCStringInternal(mach_vm_address_t address,
- bool has_size,
- mach_vm_size_t size,
- std::string* string);
-
- task_t task_; // weak
-
- DISALLOW_COPY_AND_ASSIGN(TaskMemory);
-};
-
-} // namespace crashpad
-
-#endif // CRASHPAD_UTIL_MACH_TASK_MEMORY_H_
diff --git a/util/mach/task_memory_test.cc b/util/mach/task_memory_test.cc
deleted file mode 100644
index d9f0cb7..0000000
--- a/util/mach/task_memory_test.cc
+++ /dev/null
@@ -1,578 +0,0 @@
-// Copyright 2014 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "util/mach/task_memory.h"
-
-#include <mach/mach.h>
-#include <string.h>
-
-#include <algorithm>
-#include <memory>
-#include <string>
-
-#include "base/mac/scoped_mach_port.h"
-#include "base/mac/scoped_mach_vm.h"
-#include "gtest/gtest.h"
-#include "test/mac/mach_errors.h"
-#include "util/misc/from_pointer_cast.h"
-
-namespace crashpad {
-namespace test {
-namespace {
-
-TEST(TaskMemory, ReadSelf) {
- vm_address_t address = 0;
- constexpr vm_size_t kSize = 4 * PAGE_SIZE;
- kern_return_t kr =
- vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
- base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
-
- char* region = reinterpret_cast<char*>(address);
- for (size_t index = 0; index < kSize; ++index) {
- region[index] = (index % 256) ^ ((index >> 8) % 256);
- }
-
- TaskMemory memory(mach_task_self());
-
- // This tests using both the Read() and ReadMapped() interfaces.
- std::string result(kSize, '\0');
- std::unique_ptr<TaskMemory::MappedMemory> mapped;
-
- // Ensure that the entire region can be read.
- ASSERT_TRUE(memory.Read(address, kSize, &result[0]));
- EXPECT_EQ(memcmp(region, &result[0], kSize), 0);
- ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize)));
- EXPECT_EQ(memcmp(region, mapped->data(), kSize), 0);
-
- // Ensure that a read of length 0 succeeds and doesn’t touch the result.
- result.assign(kSize, '\0');
- std::string zeroes = result;
- ASSERT_TRUE(memory.Read(address, 0, &result[0]));
- EXPECT_EQ(result, zeroes);
- ASSERT_TRUE((mapped = memory.ReadMapped(address, 0)));
-
- // Ensure that a read starting at an unaligned address works.
- ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0]));
- EXPECT_EQ(memcmp(region + 1, &result[0], kSize - 1), 0);
- ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
- EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 1), 0);
-
- // Ensure that a read ending at an unaligned address works.
- ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0]));
- EXPECT_EQ(memcmp(region, &result[0], kSize - 1), 0);
- ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1)));
- EXPECT_EQ(memcmp(region, mapped->data(), kSize - 1), 0);
-
- // Ensure that a read starting and ending at unaligned addresses works.
- ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0]));
- EXPECT_EQ(memcmp(region + 1, &result[0], kSize - 2), 0);
- ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2)));
- EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 2), 0);
-
- // Ensure that a read of exactly one page works.
- ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0]));
- EXPECT_EQ(memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE), 0);
- ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE)));
- EXPECT_EQ(memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE), 0);
-
- // Ensure that a read of a single byte works.
- ASSERT_TRUE(memory.Read(address + 2, 1, &result[0]));
- EXPECT_EQ(result[0], region[2]);
- ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1)));
- EXPECT_EQ(reinterpret_cast<const char*>(mapped->data())[0], region[2]);
-
- // Ensure that a read of length zero works and doesn’t touch the data.
- result[0] = 'M';
- ASSERT_TRUE(memory.Read(address + 3, 0, &result[0]));
- EXPECT_EQ(result[0], 'M');
- ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0)));
-}
-
-TEST(TaskMemory, ReadSelfUnmapped) {
- vm_address_t address = 0;
- constexpr vm_size_t kSize = 2 * PAGE_SIZE;
- kern_return_t kr =
- vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
- base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
-
- char* region = reinterpret_cast<char*>(address);
- for (size_t index = 0; index < kSize; ++index) {
- // Don’t include any NUL bytes, because ReadCString stops when it encounters
- // a NUL.
- region[index] = (index % 255) + 1;
- }
-
- kr = vm_protect(
- mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect");
-
- TaskMemory memory(mach_task_self());
- std::string result(kSize, '\0');
-
- EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
- EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
- EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
- EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
- EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
- EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
-
- // Do the same thing with the ReadMapped() interface.
- std::unique_ptr<TaskMemory::MappedMemory> mapped;
- EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
- EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
- EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
-
- // Repeat the test with an unmapped page instead of an unreadable one. This
- // portion of the test may be flaky in the presence of other threads, if
- // another thread maps something in the region that is deallocated here.
- kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
- vm_owner.reset(address, PAGE_SIZE);
-
- EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
- EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
- EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
- EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
- EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
- EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
-
- // Do the same thing with the ReadMapped() interface.
- EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
- EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
- EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
- EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
-}
-
-// This function consolidates the cast from a char* to mach_vm_address_t in one
-// location when reading from the current task.
-bool ReadCStringSelf(TaskMemory* memory,
- const char* pointer,
- std::string* result) {
- return memory->ReadCString(FromPointerCast<mach_vm_address_t>(pointer),
- result);
-}
-
-TEST(TaskMemory, ReadCStringSelf) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- const char kConstCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kConstCharEmpty);
-
- const char kConstCharShort[] = "A short const char[]";
- ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kConstCharShort);
-
- static const char kStaticConstCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kStaticConstCharEmpty);
-
- static const char kStaticConstCharShort[] = "A short static const char[]";
- ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kStaticConstCharShort);
-
- constexpr char kConstexprCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharEmpty, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kConstexprCharEmpty);
-
- constexpr char kConstexprCharShort[] = "A short constexpr char[]";
- ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharShort, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kConstexprCharShort);
-
- static constexpr char kStaticConstexprCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharEmpty, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kStaticConstexprCharEmpty);
-
- static constexpr char kStaticConstexprCharShort[] =
- "A short static constexpr char[]";
- ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharShort, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kStaticConstexprCharShort);
-
- std::string string_short("A short std::string in a function");
- ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, string_short);
-
- std::string string_long;
- constexpr size_t kStringLongSize = 4 * PAGE_SIZE;
- for (size_t index = 0; index < kStringLongSize; ++index) {
- // Don’t include any NUL bytes, because ReadCString stops when it encounters
- // a NUL.
- string_long.append(1, (index % 255) + 1);
- }
- ASSERT_EQ(string_long.size(), kStringLongSize);
- ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result.size(), kStringLongSize);
- EXPECT_EQ(result, string_long);
-}
-
-TEST(TaskMemory, ReadCStringSelfUnmapped) {
- vm_address_t address = 0;
- constexpr vm_size_t kSize = 2 * PAGE_SIZE;
- kern_return_t kr =
- vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
- base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
-
- char* region = reinterpret_cast<char*>(address);
- for (size_t index = 0; index < kSize; ++index) {
- // Don’t include any NUL bytes, because ReadCString stops when it encounters
- // a NUL.
- region[index] = (index % 255) + 1;
- }
-
- kr = vm_protect(
- mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect");
-
- TaskMemory memory(mach_task_self());
- std::string result;
- EXPECT_FALSE(memory.ReadCString(address, &result));
-
- // Make sure that if the string is NUL-terminated within the mapped memory
- // region, it can be read properly.
- char terminator_or_not = '\0';
- std::swap(region[PAGE_SIZE - 1], terminator_or_not);
- ASSERT_TRUE(memory.ReadCString(address, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result.size(), PAGE_SIZE - 1u);
- EXPECT_EQ(result, region);
-
- // Repeat the test with an unmapped page instead of an unreadable one. This
- // portion of the test may be flaky in the presence of other threads, if
- // another thread maps something in the region that is deallocated here.
- std::swap(region[PAGE_SIZE - 1], terminator_or_not);
- kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
- ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
- vm_owner.reset(address, PAGE_SIZE);
-
- EXPECT_FALSE(memory.ReadCString(address, &result));
-
- // Clear the result before testing that the string can be read. This makes
- // sure that the result is actually filled in, because it already contains the
- // expected value from the tests above.
- result.clear();
- std::swap(region[PAGE_SIZE - 1], terminator_or_not);
- ASSERT_TRUE(memory.ReadCString(address, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result.size(), PAGE_SIZE - 1u);
- EXPECT_EQ(result, region);
-}
-
-// This function consolidates the cast from a char* to mach_vm_address_t in one
-// location when reading from the current task.
-bool ReadCStringSizeLimitedSelf(TaskMemory* memory,
- const char* pointer,
- size_t size,
- std::string* result) {
- return memory->ReadCStringSizeLimited(
- FromPointerCast<mach_vm_address_t>(pointer), size, result);
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- static constexpr char kConstCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kConstCharEmpty);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kConstCharEmpty);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kConstCharEmpty);
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- static constexpr char kConstCharShort[] = "A short const char[]";
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, kConstCharShort, arraysize(kConstCharShort), &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kConstCharShort);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kConstCharShort);
-
- ASSERT_FALSE(ReadCStringSizeLimitedSelf(
- &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result));
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- static constexpr char kStaticConstCharEmpty[] = "";
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
- kStaticConstCharEmpty,
- arraysize(kStaticConstCharEmpty),
- &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kStaticConstCharEmpty);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
- kStaticConstCharEmpty,
- arraysize(kStaticConstCharEmpty) + 1,
- &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kStaticConstCharEmpty);
-
- result.clear();
- ASSERT_TRUE(
- ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result));
- EXPECT_TRUE(result.empty());
- EXPECT_EQ(result, kStaticConstCharEmpty);
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- static constexpr char kStaticConstCharShort[] =
- "A short static constexpr char[]";
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
- kStaticConstCharShort,
- arraysize(kStaticConstCharShort),
- &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kStaticConstCharShort);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory,
- kStaticConstCharShort,
- arraysize(kStaticConstCharShort) + 1,
- &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, kStaticConstCharShort);
-
- ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory,
- kStaticConstCharShort,
- arraysize(kStaticConstCharShort) - 1,
- &result));
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_StringShort) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- std::string string_short("A short std::string in a function");
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, &string_short[0], string_short.size() + 1, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, string_short);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, &string_short[0], string_short.size() + 2, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result, string_short);
-
- ASSERT_FALSE(ReadCStringSizeLimitedSelf(
- &memory, &string_short[0], string_short.size(), &result));
-}
-
-TEST(TaskMemory, ReadCStringSizeLimited_StringLong) {
- TaskMemory memory(mach_task_self());
- std::string result;
-
- std::string string_long;
- constexpr size_t kStringLongSize = 4 * PAGE_SIZE;
- for (size_t index = 0; index < kStringLongSize; ++index) {
- // Don’t include any NUL bytes, because ReadCString stops when it encounters
- // a NUL.
- string_long.append(1, (index % 255) + 1);
- }
- ASSERT_EQ(string_long.size(), kStringLongSize);
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, &string_long[0], string_long.size() + 1, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result.size(), kStringLongSize);
- EXPECT_EQ(result, string_long);
-
- result.clear();
- ASSERT_TRUE(ReadCStringSizeLimitedSelf(
- &memory, &string_long[0], string_long.size() + 2, &result));
- EXPECT_FALSE(result.empty());
- EXPECT_EQ(result.size(), kStringLongSize);
- EXPECT_EQ(result, string_long);
-
- ASSERT_FALSE(ReadCStringSizeLimitedSelf(
- &memory, &string_long[0], string_long.size(), &result));
-}
-
-bool IsAddressMapped(vm_address_t address) {
- vm_address_t region_address = address;
- vm_size_t region_size;
- mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
- vm_region_basic_info_64 info;
- mach_port_t object;
- kern_return_t kr = vm_region_64(mach_task_self(),
- ®ion_address,
- ®ion_size,
- VM_REGION_BASIC_INFO_64,
- reinterpret_cast<vm_region_info_t>(&info),
- &count,
- &object);
- if (kr == KERN_SUCCESS) {
- // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c
- // vm_map_region()), but the interface acts as if it might carry a send
- // right, so treat it as documented.
- base::mac::ScopedMachSendRight object_owner(object);
-
- return address >= region_address && address <= region_address + region_size;
- }
-
- if (kr == KERN_INVALID_ADDRESS) {
- return false;
- }
-
- ADD_FAILURE() << MachErrorMessage(kr, "vm_region_64");
- return false;
-}
-
-TEST(TaskMemory, MappedMemoryDeallocates) {
- // This tests that once a TaskMemory::MappedMemory object is destroyed, it
- // releases the mapped memory that it owned. Technically, this test is not
- // valid because after the mapping is released, something else (on another
- // thread) might wind up mapped in the same address. In the test environment,
- // hopefully there are either no other threads or they’re all quiescent, so
- // nothing else should wind up mapped in the address.
-
- TaskMemory memory(mach_task_self());
- std::unique_ptr<TaskMemory::MappedMemory> mapped;
-
- static constexpr char kTestBuffer[] = "hello!";
- mach_vm_address_t test_address =
- FromPointerCast<mach_vm_address_t>(&kTestBuffer);
- ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));
- EXPECT_EQ(memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)), 0);
-
- vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
- EXPECT_TRUE(IsAddressMapped(mapped_address));
-
- mapped.reset();
- EXPECT_FALSE(IsAddressMapped(mapped_address));
-
- // This is the same but with a big buffer that’s definitely larger than a
- // single page. This makes sure that the whole mapped region winds up being
- // deallocated.
- constexpr size_t kBigSize = 4 * PAGE_SIZE;
- std::unique_ptr<char[]> big_buffer(new char[kBigSize]);
- test_address = FromPointerCast<mach_vm_address_t>(&big_buffer[0]);
- ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize)));
-
- mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
- vm_address_t mapped_last_address = mapped_address + kBigSize - 1;
- EXPECT_TRUE(IsAddressMapped(mapped_address));
- EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE));
- EXPECT_TRUE(IsAddressMapped(mapped_last_address));
-
- mapped.reset();
- EXPECT_FALSE(IsAddressMapped(mapped_address));
- EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE));
- EXPECT_FALSE(IsAddressMapped(mapped_last_address));
-}
-
-TEST(TaskMemory, MappedMemoryReadCString) {
- // This tests the behavior of TaskMemory::MappedMemory::ReadCString().
- TaskMemory memory(mach_task_self());
- std::unique_ptr<TaskMemory::MappedMemory> mapped;
-
- static constexpr char kTestBuffer[] = "0\0" "2\0" "45\0" "789";
- const mach_vm_address_t kTestAddress =
- FromPointerCast<mach_vm_address_t>(&kTestBuffer);
- ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));
-
- std::string string;
- ASSERT_TRUE(mapped->ReadCString(0, &string));
- EXPECT_EQ(string, "0");
- ASSERT_TRUE(mapped->ReadCString(1, &string));
- EXPECT_EQ(string, "");
- ASSERT_TRUE(mapped->ReadCString(2, &string));
- EXPECT_EQ(string, "2");
- ASSERT_TRUE(mapped->ReadCString(3, &string));
- EXPECT_EQ(string, "");
- ASSERT_TRUE(mapped->ReadCString(4, &string));
- EXPECT_EQ(string, "45");
- ASSERT_TRUE(mapped->ReadCString(5, &string));
- EXPECT_EQ(string, "5");
- ASSERT_TRUE(mapped->ReadCString(6, &string));
- EXPECT_EQ(string, "");
-
- // kTestBuffer’s NUL terminator was not read, so these will see an
- // unterminated string and fail.
- EXPECT_FALSE(mapped->ReadCString(7, &string));
- EXPECT_FALSE(mapped->ReadCString(8, &string));
- EXPECT_FALSE(mapped->ReadCString(9, &string));
-
- // This is out of the range of what was read, so it will fail.
- EXPECT_FALSE(mapped->ReadCString(10, &string));
- EXPECT_FALSE(mapped->ReadCString(11, &string));
-
- // Read it again, this time with a length long enough to include the NUL
- // terminator.
- ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11)));
-
- ASSERT_TRUE(mapped->ReadCString(6, &string));
- EXPECT_EQ(string, "");
-
- // These should now succeed.
- ASSERT_TRUE(mapped->ReadCString(7, &string));
- EXPECT_EQ(string, "789");
- ASSERT_TRUE(mapped->ReadCString(8, &string));
- EXPECT_EQ(string, "89");
- ASSERT_TRUE(mapped->ReadCString(9, &string));
- EXPECT_EQ(string, "9");
- EXPECT_TRUE(mapped->ReadCString(10, &string));
- EXPECT_EQ(string, "");
-
- // These are still out of range.
- EXPECT_FALSE(mapped->ReadCString(11, &string));
- EXPECT_FALSE(mapped->ReadCString(12, &string));
-}
-
-} // namespace
-} // namespace test
-} // namespace crashpad
diff --git a/util/misc/arraysize.h b/util/misc/arraysize.h
new file mode 100644
index 0000000..93a6388
--- /dev/null
+++ b/util/misc/arraysize.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_ARRAYSIZE_H_
+#define CRASHPAD_UTIL_MISC_ARRAYSIZE_H_
+
+#include <sys/types.h> // For size_t.
+
+#include <type_traits>
+
+//! \file
+
+namespace crashpad {
+namespace internal {
+
+//! \brief A helper to implement ArraySize.
+template <typename ArrayType>
+constexpr size_t ArraySizeHelper() noexcept {
+ return std::extent<typename std::remove_reference<ArrayType>::type>::value;
+}
+
+} // namespace internal
+} // namespace crashpad
+
+//! \brief A way of computing an array’s size.
+//!
+//! Use this only where `base::size()` or `std::size()` won’t work, such as in
+//! constant expressions (including `static_assert` expressions) that consider
+//! the sizes of non-static data members.
+#define ArraySize(array) crashpad::internal::ArraySizeHelper<decltype(array)>()
+
+#endif // CRASHPAD_UTIL_MISC_ARRAYSIZE_H_
diff --git a/util/misc/arraysize_unsafe_test.cc b/util/misc/arraysize_test.cc
similarity index 68%
rename from util/misc/arraysize_unsafe_test.cc
rename to util/misc/arraysize_test.cc
index a6660ae..ad8da09 100644
--- a/util/misc/arraysize_unsafe_test.cc
+++ b/util/misc/arraysize_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/misc/arraysize_unsafe.h"
+#include "util/misc/arraysize.h"
#include "base/compiler_specific.h"
#include "gtest/gtest.h"
@@ -21,37 +21,37 @@
namespace test {
namespace {
-TEST(ArraySizeUnsafe, ArraySizeUnsafe) {
+TEST(ArraySize, ArraySize) {
char c1[1];
- static_assert(ARRAYSIZE_UNSAFE(c1) == 1, "c1");
+ static_assert(ArraySize(c1) == 1, "c1");
ALLOW_UNUSED_LOCAL(c1);
char c2[2];
- static_assert(ARRAYSIZE_UNSAFE(c2) == 2, "c2");
+ static_assert(ArraySize(c2) == 2, "c2");
ALLOW_UNUSED_LOCAL(c2);
char c4[4];
- static_assert(ARRAYSIZE_UNSAFE(c4) == 4, "c4");
+ static_assert(ArraySize(c4) == 4, "c4");
ALLOW_UNUSED_LOCAL(c4);
int i1[1];
- static_assert(ARRAYSIZE_UNSAFE(i1) == 1, "i1");
+ static_assert(ArraySize(i1) == 1, "i1");
ALLOW_UNUSED_LOCAL(i1);
int i2[2];
- static_assert(ARRAYSIZE_UNSAFE(i2) == 2, "i2");
+ static_assert(ArraySize(i2) == 2, "i2");
ALLOW_UNUSED_LOCAL(i2);
int i4[4];
- static_assert(ARRAYSIZE_UNSAFE(i4) == 4, "i4");
+ static_assert(ArraySize(i4) == 4, "i4");
ALLOW_UNUSED_LOCAL(i4);
long l8[8];
- static_assert(ARRAYSIZE_UNSAFE(l8) == 8, "l8");
+ static_assert(ArraySize(l8) == 8, "l8");
ALLOW_UNUSED_LOCAL(l8);
int l9[9];
- static_assert(ARRAYSIZE_UNSAFE(l9) == 9, "l9");
+ static_assert(ArraySize(l9) == 9, "l9");
ALLOW_UNUSED_LOCAL(l9);
struct S {
@@ -62,11 +62,11 @@
};
S s1[1];
- static_assert(ARRAYSIZE_UNSAFE(s1) == 1, "s1");
+ static_assert(ArraySize(s1) == 1, "s1");
ALLOW_UNUSED_LOCAL(s1);
S s10[10];
- static_assert(ARRAYSIZE_UNSAFE(s10) == 10, "s10");
+ static_assert(ArraySize(s10) == 10, "s10");
ALLOW_UNUSED_LOCAL(s10);
}
diff --git a/util/misc/arraysize_unsafe.h b/util/misc/arraysize_unsafe.h
deleted file mode 100644
index e53c70b..0000000
--- a/util/misc/arraysize_unsafe.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
-#define CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
-
-//! \file
-
-//! \brief Not the safest way of computing an array’s size…
-//!
-//! `#%include "base/macros.h"` and use its `arraysize()` instead. This macro
-//! should only be used in rare situations where `arraysize()` does not
-//! function.
-#define ARRAYSIZE_UNSAFE(array) (sizeof(array) / sizeof(array[0]))
-
-#endif // CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h
index 541589d..3ff7118 100644
--- a/util/misc/capture_context.h
+++ b/util/misc/capture_context.h
@@ -32,6 +32,8 @@
#if defined(OS_MACOSX)
#if defined(ARCH_CPU_X86_FAMILY)
using NativeCPUContext = x86_thread_state;
+#elif defined(ARCH_CPU_ARM64)
+using NativeCPUContext = arm_unified_thread_state;
#endif
#elif defined(OS_WIN)
using NativeCPUContext = CONTEXT;
diff --git a/util/misc/capture_context_fuchsia.S b/util/misc/capture_context_fuchsia.S
index 8e5c0cb..0ebc7f7 100644
--- a/util/misc/capture_context_fuchsia.S
+++ b/util/misc/capture_context_fuchsia.S
@@ -116,7 +116,7 @@
movq 0x90(%rdi), %rax
movq 0x28(%rdi), %r8
- // TODO(scottmg): save floating-point registers.
+ // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
popfq
@@ -159,14 +159,14 @@
// The link register holds the return address for this function.
str LR, [x0, #0x1b8] // context->uc_mcontext.pc
- // NZCV, pstate, and CPSR are synonyms.
+ // pstate should hold SPSR but NZCV are the only bits we know about.
mrs x1, NZCV
str x1, [x0, #0x1c0] // context->uc_mcontext.pstate
// Restore x1 from the saved context.
ldr x1, [x0, #0xc0]
- // TODO(scottmg): save floating-point registers.
+ // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
ret
diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S
index 42999a9..52215ee 100644
--- a/util/misc/capture_context_linux.S
+++ b/util/misc/capture_context_linux.S
@@ -28,7 +28,11 @@
.globl CAPTURECONTEXT_SYMBOL2
#if defined(__i386__) || defined(__x86_64__)
.balign 16, 0x90
-#elif defined(__arm__) || defined(__aarch64__) || defined(__mips__)
+#elif defined(__arm__) || defined(__aarch64__)
+ .balign 4, 0x0
+ .type CAPTURECONTEXT_SYMBOL, %function
+ .type CAPTURECONTEXT_SYMBOL2, %function
+#elif defined(__mips__)
.balign 4, 0x0
#endif
@@ -257,9 +261,7 @@
// Restore r0.
sub r0, r0, #0x24
- // Save named general purpose registers.
- str FP, [r0, #0x4c] // context->uc_mcontext.fp
- str IP, [r0, #0x50] // context->uc_mcontext.ip
+ // Save SP/r13.
str SP, [r0, #0x54] // context->uc_mcontext.sp
// The original LR can't be recovered.
@@ -284,7 +286,7 @@
// Restore r1.
ldr r1, [r0, #0x24]
- // TODO(jperaza): save floating-point registers.
+ // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
mov PC, LR
@@ -312,23 +314,23 @@
stp x28, x29, [x0, #0x198]
// The original LR can't be recovered.
- str LR, [x0, #0x1a8]
+ str x30, [x0, #0x1a8]
// Use x1 as a scratch register.
mov x1, SP
str x1, [x0, #0x1b0] // context->uc_mcontext.sp
// The link register holds the return address for this function.
- str LR, [x0, #0x1b8] // context->uc_mcontext.pc
+ str x30, [x0, #0x1b8] // context->uc_mcontext.pc
- // NZCV, pstate, and CPSR are synonyms.
+ // pstate should hold SPSR but NZCV are the only bits we know about.
mrs x1, NZCV
str x1, [x0, #0x1c0] // context->uc_mcontext.pstate
// Restore x1 from the saved context.
ldr x1, [x0, #0xc0]
- // TODO(jperaza): save floating-point registers.
+ // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.
ret
#elif defined(__mips__)
diff --git a/util/misc/capture_context_test.cc b/util/misc/capture_context_test.cc
index 7f3890f..cf23c2d 100644
--- a/util/misc/capture_context_test.cc
+++ b/util/misc/capture_context_test.cc
@@ -18,9 +18,11 @@
#include <algorithm>
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/capture_context_test_util.h"
+#include "util/misc/memory_sanitizer.h"
namespace crashpad {
namespace test {
@@ -33,7 +35,12 @@
// find an approximately valid stack pointer by comparing locals to the
// captured one, disable safe-stack for this function.
__attribute__((no_sanitize("safe-stack")))
-#endif
+#endif // defined(OS_FUCHSIA)
+
+#if defined(MEMORY_SANITIZER)
+// CaptureContext() calls inline assembly and is incompatible with MSan.
+__attribute__((no_sanitize("memory")))
+#endif // defined(MEMORY_SANITIZER)
void TestCaptureContext() {
NativeCPUContext context_1;
@@ -49,16 +56,17 @@
// reference program counter.
uintptr_t pc = ProgramCounterFromContext(context_1);
-#if !defined(ADDRESS_SANITIZER) && !defined(ARCH_CPU_MIPS_FAMILY)
- // AddressSanitizer can cause enough code bloat that the “nearby” check would
+#if !defined(ADDRESS_SANITIZER) && !defined(ARCH_CPU_MIPS_FAMILY) && \
+ !defined(MEMORY_SANITIZER)
+ // Sanitizers can cause enough code bloat that the “nearby” check would
// likely fail.
const uintptr_t kReferencePC =
reinterpret_cast<uintptr_t>(TestCaptureContext);
EXPECT_PRED2([](uintptr_t actual,
- uintptr_t reference) { return actual - reference < 64u; },
+ uintptr_t reference) { return actual - reference < 128u; },
pc,
kReferencePC);
-#endif // !defined(ADDRESS_SANITIZER)
+#endif
const uintptr_t sp = StackPointerFromContext(context_1);
@@ -67,9 +75,9 @@
// value can be the lowest value possible.
NativeCPUContext context_2;
-// AddressSanitizer on Linux causes stack variables to be stored separately from
-// the call stack.
-#if !defined(ADDRESS_SANITIZER) || (!defined(OS_LINUX) && !defined(OS_ANDROID))
+ // AddressSanitizer with use-after-return detection causes stack variables to
+ // be allocated on the heap.
+#if !defined(ADDRESS_SANITIZER)
// The stack pointer reference value is the lowest address of a local variable
// in this function. The captured program counter will be slightly less than
// or equal to the reference stack pointer.
@@ -82,7 +90,7 @@
uintptr_t reference) { return reference - actual < 768u; },
sp,
kReferenceSP);
-#endif // !ADDRESS_SANITIZER || (!OS_LINUX && !OS_ANDROID)
+#endif // !defined(ADDRESS_SANITIZER)
// Capture the context again, expecting that the stack pointer stays the same
// and the program counter increases. Strictly speaking, there’s no guarantee
diff --git a/util/misc/capture_context_test_util_win.cc b/util/misc/capture_context_test_util_win.cc
index 239beac..16d81b7 100644
--- a/util/misc/capture_context_test_util_win.cc
+++ b/util/misc/capture_context_test_util_win.cc
@@ -13,8 +13,9 @@
// limitations under the License.
#include "util/misc/capture_context_test_util.h"
+#include "util/win/context_wrappers.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
namespace crashpad {
@@ -58,7 +59,7 @@
#if defined(ARCH_CPU_X86)
// fxsave doesn’t write these bytes.
- for (size_t i = 464; i < arraysize(context.ExtendedRegisters); ++i) {
+ for (size_t i = 464; i < base::size(context.ExtendedRegisters); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(context.ExtendedRegisters[i], 0);
}
@@ -68,7 +69,7 @@
EXPECT_EQ(context.FltSave.MxCsr, context.MxCsr);
// fxsave doesn’t write these bytes.
- for (size_t i = 0; i < arraysize(context.FltSave.Reserved4); ++i) {
+ for (size_t i = 0; i < base::size(context.FltSave.Reserved4); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(context.FltSave.Reserved4[i], 0);
}
@@ -80,7 +81,7 @@
EXPECT_EQ(context.P4Home, 0u);
EXPECT_EQ(context.P5Home, 0u);
EXPECT_EQ(context.P6Home, 0u);
- for (size_t i = 0; i < arraysize(context.VectorRegister); ++i) {
+ for (size_t i = 0; i < base::size(context.VectorRegister); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(context.VectorRegister[i].Low, 0u);
EXPECT_EQ(context.VectorRegister[i].High, 0u);
@@ -95,11 +96,7 @@
}
uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {
-#if defined(ARCH_CPU_X86)
- return context.Eip;
-#elif defined(ARCH_CPU_X86_64)
- return context.Rip;
-#endif
+ return reinterpret_cast<uintptr_t>(ProgramCounterFromCONTEXT(&context));
}
uintptr_t StackPointerFromContext(const NativeCPUContext& context) {
@@ -107,6 +104,8 @@
return context.Esp;
#elif defined(ARCH_CPU_X86_64)
return context.Rsp;
+#elif defined(ARCH_CPU_ARM64)
+ return context.Sp;
#endif
}
diff --git a/util/misc/capture_context_win_arm64.asm b/util/misc/capture_context_win_arm64.asm
new file mode 100644
index 0000000..5630698
--- /dev/null
+++ b/util/misc/capture_context_win_arm64.asm
@@ -0,0 +1,64 @@
+; Copyright 2019 The Crashpad Authors. All rights reserved.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+ EXPORT |?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z|
+ AREA |.text|, CODE
+|?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z| PROC
+ ; Save general purpose registers in context.regs[i].
+ ; The original x0 can't be recovered.
+ stp x0, x1, [x0, #0x008]
+ stp x2, x3, [x0, #0x018]
+ stp x4, x5, [x0, #0x028]
+ stp x6, x7, [x0, #0x038]
+ stp x8, x9, [x0, #0x048]
+ stp x10, x11, [x0, #0x058]
+ stp x12, x13, [x0, #0x068]
+ stp x14, x15, [x0, #0x078]
+ stp x16, x17, [x0, #0x088]
+ stp x18, x19, [x0, #0x098]
+ stp x20, x21, [x0, #0x0a8]
+ stp x22, x23, [x0, #0x0b8]
+ stp x24, x25, [x0, #0x0c8]
+ stp x26, x27, [x0, #0x0d8]
+ stp x28, x29, [x0, #0x0e8]
+
+ ; The original LR can't be recovered.
+ str LR, [x0, #0x0f8]
+
+ ; Use x1 as a scratch register.
+ mov x1, SP
+ str x1, [x0, #0x100] ; context.sp
+
+ ; The link register holds the return address for this function.
+ str LR, [x0, #0x108] ; context.pc
+
+ ; pstate should hold SPSR but NZCV are the only bits we know about.
+ mrs x1, NZCV
+
+ ; Enable Control flags, such as CONTEXT_ARM64, CONTEXT_CONTROL,
+ ; CONTEXT_INTEGER
+ ldr w1, =0x00400003
+
+ ; Set ControlFlags /0x000/ and pstate /0x004/ at the same time.
+ str x1, [x0, #0x000]
+
+ ; Restore x1 from the saved context.
+ ldr x1, [x0, #0x010]
+
+ ; TODO(https://crashpad.chromium.org/bug/300): save floating-point registers
+
+ ret
+ ENDP
+
+ END
diff --git a/util/misc/capture_context_win_arm64.obj b/util/misc/capture_context_win_arm64.obj
new file mode 100644
index 0000000..11c76a1
--- /dev/null
+++ b/util/misc/capture_context_win_arm64.obj
Binary files differ
diff --git a/util/misc/clock_test.cc b/util/misc/clock_test.cc
index 95330eb..6bfb87b 100644
--- a/util/misc/clock_test.cc
+++ b/util/misc/clock_test.cc
@@ -20,7 +20,7 @@
#include "base/format_macros.h"
#include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
@@ -83,7 +83,7 @@
static_cast<uint64_t>(5E7), // 50 milliseconds
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const uint64_t nanoseconds = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %zu, nanoseconds %" PRIu64, index, nanoseconds));
diff --git a/util/misc/from_pointer_cast_test.cc b/util/misc/from_pointer_cast_test.cc
index 1a6850f..ce53de5 100644
--- a/util/misc/from_pointer_cast_test.cc
+++ b/util/misc/from_pointer_cast_test.cc
@@ -41,7 +41,7 @@
volatile SomeType*,
const volatile SomeType*>;
-TYPED_TEST_CASE(FromPointerCastTest, FromPointerCastTestTypes);
+TYPED_TEST_SUITE(FromPointerCastTest, FromPointerCastTestTypes);
TYPED_TEST(FromPointerCastTest, ToSigned) {
EXPECT_EQ(FromPointerCast<int64_t>(nullptr), 0);
diff --git a/util/misc/lexing.cc b/util/misc/lexing.cc
index 6c31865..5e8fa30 100644
--- a/util/misc/lexing.cc
+++ b/util/misc/lexing.cc
@@ -32,10 +32,10 @@
bool ConvertStringToNumber(const base::StringPiece& input, type* value) { \
return function(input, value); \
}
-MAKE_ADAPTER(int, base::StringToInt);
-MAKE_ADAPTER(unsigned int, base::StringToUint);
-MAKE_ADAPTER(int64_t, base::StringToInt64);
-MAKE_ADAPTER(uint64_t, base::StringToUint64);
+MAKE_ADAPTER(int, base::StringToInt)
+MAKE_ADAPTER(unsigned int, base::StringToUint)
+MAKE_ADAPTER(int64_t, base::StringToInt64)
+MAKE_ADAPTER(uint64_t, base::StringToUint64)
#undef MAKE_ADAPTER
} // namespace
diff --git a/util/misc/memory_sanitizer.h b/util/misc/memory_sanitizer.h
new file mode 100644
index 0000000..a3e0d8e
--- /dev/null
+++ b/util/misc/memory_sanitizer.h
@@ -0,0 +1,27 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_
+#define CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_
+
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+#if !defined(MEMORY_SANITIZER)
+#if HAS_FEATURE(memory_sanitizer)
+#define MEMORY_SANITIZER 1
+#endif // HAS_FEATURE(memory_sanitizer)
+#endif // !defined(MEMORY_SANITIZER)
+
+#endif // CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_
diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc
index 7d191f7..bda9031 100644
--- a/util/misc/metrics.cc
+++ b/util/misc/metrics.cc
@@ -16,6 +16,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
@@ -55,43 +56,40 @@
// static
void Metrics::CrashReportPending(PendingReportReason reason) {
UMA_HISTOGRAM_ENUMERATION(
- "Crashpad.CrashReportPending",
- static_cast<int32_t>(reason),
- static_cast<int32_t>(PendingReportReason::kMaxValue));
+ "Crashpad.CrashReportPending", reason, PendingReportReason::kMaxValue);
}
// static
void Metrics::CrashReportSize(FileOffset size) {
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Crashpad.CrashReportSize", size, 0, 20 * 1024 * 1024, 50);
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Crashpad.CrashReportSize",
+ base::saturated_cast<uint32_t>(size),
+ 0,
+ 20 * 1024 * 1024,
+ 50);
}
// static
void Metrics::CrashUploadAttempted(bool successful) {
- UMA_HISTOGRAM_COUNTS("Crashpad.CrashUpload.AttemptSuccessful",
- static_cast<int32_t>(successful));
+ UMA_HISTOGRAM_COUNTS("Crashpad.CrashUpload.AttemptSuccessful", successful);
}
// static
void Metrics::CrashUploadSkipped(CrashSkippedReason reason) {
UMA_HISTOGRAM_ENUMERATION(
- "Crashpad.CrashUpload.Skipped",
- static_cast<int32_t>(reason),
- static_cast<int32_t>(CrashSkippedReason::kMaxValue));
+ "Crashpad.CrashUpload.Skipped", reason, CrashSkippedReason::kMaxValue);
}
// static
void Metrics::ExceptionCaptureResult(CaptureResult result) {
ExceptionProcessing(ExceptionProcessingState::kFinished);
- UMA_HISTOGRAM_ENUMERATION("Crashpad.ExceptionCaptureResult",
- static_cast<int32_t>(result),
- static_cast<int32_t>(CaptureResult::kMaxValue));
+ UMA_HISTOGRAM_ENUMERATION(
+ "Crashpad.ExceptionCaptureResult", result, CaptureResult::kMaxValue);
}
// static
void Metrics::ExceptionCode(uint32_t exception_code) {
base::UmaHistogramSparse("Crashpad.ExceptionCode." METRICS_OS_NAME,
- static_cast<int32_t>(exception_code));
+ exception_code);
}
// static
@@ -102,15 +100,14 @@
// static
void Metrics::HandlerLifetimeMilestone(LifetimeMilestone milestone) {
UMA_HISTOGRAM_ENUMERATION("Crashpad.HandlerLifetimeMilestone",
- static_cast<int32_t>(milestone),
- static_cast<int32_t>(LifetimeMilestone::kMaxValue));
+ milestone,
+ LifetimeMilestone::kMaxValue);
}
// static
void Metrics::HandlerCrashed(uint32_t exception_code) {
base::UmaHistogramSparse(
- "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME,
- static_cast<int32_t>(exception_code));
+ "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME, exception_code);
}
} // namespace crashpad
diff --git a/util/misc/metrics.h b/util/misc/metrics.h
index 8046497..d976381 100644
--- a/util/misc/metrics.h
+++ b/util/misc/metrics.h
@@ -139,6 +139,9 @@
//! \brief Sanitization caused this crash dump to be skipped.
kSkippedDueToSanitization = 11,
+ //! \brief Failure to open a memfd caused this crash dump to be skipped.
+ kOpenMemfdFailed = 12,
+
//! \brief The number of values in this enumeration; not a valid value.
kMaxValue
};
diff --git a/util/misc/paths_fuchsia.cc b/util/misc/paths_fuchsia.cc
index 0908448..6dea633 100644
--- a/util/misc/paths_fuchsia.cc
+++ b/util/misc/paths_fuchsia.cc
@@ -25,7 +25,7 @@
// static
bool Paths::Executable(base::FilePath* path) {
// Assume the environment has been set up following
- // https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure
+ // https://fuchsia.googlesource.com/docs/+/master/the-book/namespaces.md#typical-directory-structure
// .
*path = base::FilePath("/pkg/bin/app");
return true;
diff --git a/util/misc/paths_win.cc b/util/misc/paths_win.cc
index 4c402fe..f05bdcf 100644
--- a/util/misc/paths_win.cc
+++ b/util/misc/paths_win.cc
@@ -17,6 +17,7 @@
#include <windows.h>
#include "base/logging.h"
+#include "base/stl_util.h"
namespace crashpad {
@@ -24,11 +25,13 @@
bool Paths::Executable(base::FilePath* path) {
wchar_t executable_path[_MAX_PATH];
unsigned int len =
- GetModuleFileName(nullptr, executable_path, arraysize(executable_path));
+ GetModuleFileName(nullptr,
+ executable_path,
+ static_cast<DWORD>(base::size(executable_path)));
if (len == 0) {
PLOG(ERROR) << "GetModuleFileName";
return false;
- } else if (len >= arraysize(executable_path)) {
+ } else if (len >= base::size(executable_path)) {
LOG(ERROR) << "GetModuleFileName";
return false;
}
diff --git a/util/misc/pdb_structures.cc b/util/misc/pdb_structures.cc
index c62f11c..e904dc6 100644
--- a/util/misc/pdb_structures.cc
+++ b/util/misc/pdb_structures.cc
@@ -18,5 +18,6 @@
const uint32_t CodeViewRecordPDB20::kSignature;
const uint32_t CodeViewRecordPDB70::kSignature;
+const uint32_t CodeViewRecordBuildID::kSignature;
} // namespace crashpad
diff --git a/util/misc/pdb_structures.h b/util/misc/pdb_structures.h
index d0cc9a3..834cfdc 100644
--- a/util/misc/pdb_structures.h
+++ b/util/misc/pdb_structures.h
@@ -90,12 +90,7 @@
// UUID has a constructor, which makes it non-POD, which makes this structure
// non-POD. In order for the default constructor to zero-initialize other
// members, an explicit constructor must be provided.
- CodeViewRecordPDB70()
- : signature(),
- uuid(),
- age(),
- pdb_name() {
- }
+ CodeViewRecordPDB70() : signature(), uuid(), age(), pdb_name() {}
//! \brief The magic number identifying this structure version, stored in
//! #signature.
@@ -127,6 +122,27 @@
uint8_t pdb_name[1];
};
+//! \brief A CodeView record containing an ELF build-id.
+//!
+//! This identifier comes from the ELF section `NT_GNU_BUILD_ID`.
+struct CodeViewRecordBuildID {
+ //! \brief The magic number identifying this structure version, stored in
+ //! #signature.
+ //!
+ //! In a hex dump, this will appear as “LEpB” when produced by a little-endian
+ //! machine.
+ static const uint32_t kSignature = 'BpEL';
+
+ //! \brief The magic number identifying this structure version, the value of
+ //! #kSignature.
+ uint32_t signature;
+
+ //! \brief The build ID for this object.
+ //!
+ //! This usually comes from `NT_GNU_BUILD_ID` on ELF objects.
+ uint8_t build_id[1];
+};
+
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_
diff --git a/util/misc/random_string_test.cc b/util/misc/random_string_test.cc
index b0866c0..f5f0f32 100644
--- a/util/misc/random_string_test.cc
+++ b/util/misc/random_string_test.cc
@@ -18,7 +18,7 @@
#include <set>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
namespace crashpad {
@@ -33,7 +33,7 @@
const std::string allowed_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
size_t character_counts[26] = {};
- ASSERT_EQ(allowed_characters.size(), arraysize(character_counts));
+ ASSERT_EQ(allowed_characters.size(), base::size(character_counts));
std::set<std::string> strings;
@@ -61,7 +61,7 @@
// Make sure every character appears at least once. It is possible, but
// extremely unlikely, for a character to not appear at all.
for (size_t character_index = 0;
- character_index < arraysize(character_counts);
+ character_index < base::size(character_counts);
++character_index) {
EXPECT_GT(character_counts[character_index], 0u)
<< allowed_characters[character_index];
diff --git a/util/misc/time.h b/util/misc/time.h
index aabe8e6..dffe1a8 100644
--- a/util/misc/time.h
+++ b/util/misc/time.h
@@ -69,6 +69,14 @@
#endif // OS_WIN
+#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
+//! \brief Get the kernel boot time. Subsequent calls to this function may
+//! return different results due to the system clock being changed or
+//! imprecision in measuring the boot time.
+//! \return `true` on success. Otherwise, `false` with a message logged.
+bool GetBootTime(timespec* ts);
+#endif // OS_LINUX || OS_ANDROID || DOXYGEN
+
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_TIME_H_
diff --git a/util/misc/time_linux.cc b/util/misc/time_linux.cc
new file mode 100644
index 0000000..bc73533
--- /dev/null
+++ b/util/misc/time_linux.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/misc/time.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+bool GetBootTime(timespec* boot_time) {
+ timespec uptime;
+ if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {
+ PLOG(ERROR) << "clock_gettime";
+ return false;
+ }
+
+ timespec current_time;
+ if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) {
+ PLOG(ERROR) << "clock_gettime";
+ return false;
+ }
+
+ SubtractTimespec(current_time, uptime, boot_time);
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/misc/uuid_test.cc b/util/misc/uuid_test.cc
index c05c5c1..35f9ecd 100644
--- a/util/misc/uuid_test.cc
+++ b/util/misc/uuid_test.cc
@@ -20,8 +20,8 @@
#include <string>
#include "base/format_macros.h"
-#include "base/macros.h"
#include "base/scoped_generic.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
@@ -95,12 +95,12 @@
++uuid.data_3;
EXPECT_NE(uuid, uuid_2);
--uuid.data_3;
- for (size_t index = 0; index < arraysize(uuid.data_4); ++index) {
+ for (size_t index = 0; index < base::size(uuid.data_4); ++index) {
++uuid.data_4[index];
EXPECT_NE(uuid, uuid_2);
--uuid.data_4[index];
}
- for (size_t index = 0; index < arraysize(uuid.data_5); ++index) {
+ for (size_t index = 0; index < base::size(uuid.data_5); ++index) {
++uuid.data_5[index];
EXPECT_NE(uuid, uuid_2);
--uuid.data_5[index];
@@ -190,7 +190,7 @@
uuid_zero.InitializeToZero();
const std::string empty_uuid = uuid_zero.ToString();
- for (size_t index = 0; index < arraysize(kCases); ++index) {
+ for (size_t index = 0; index < base::size(kCases); ++index) {
const TestCase& test_case = kCases[index];
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ": %s", index, test_case.uuid_string));
@@ -226,7 +226,7 @@
};
// clang-format on
EXPECT_TRUE(uuid.InitializeFromString(
- base::StringPiece16(kChar16UUID, arraysize(kChar16UUID))));
+ base::StringPiece16(kChar16UUID, base::size(kChar16UUID))));
EXPECT_EQ(uuid.ToString(), "f32e5bdc-2681-4c73-a4e6-333ffd33b333");
#if defined(OS_WIN)
diff --git a/util/net/generate_test_server_key.py b/util/net/generate_test_server_key.py
index 6e6340e..3db08ad 100755
--- a/util/net/generate_test_server_key.py
+++ b/util/net/generate_test_server_key.py
@@ -16,13 +16,35 @@
import os
import subprocess
+import sys
-# GN requires a Python script for actions, so this just wraps the openssl
-# command needed to generate a test private key and a certificate. These names
-# must correspond to what TestPaths::BuildArtifact() constructs.
-key = 'crashpad_util_test_key.pem'
-cert = 'crashpad_util_test_cert.pem'
-subprocess.check_call(
- ['openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost',
- '-days', '365', '-newkey', 'rsa:2048', '-keyout', key, '-out', cert],
- stderr=open(os.devnull, 'w'))
+testdata = os.path.join(os.path.dirname(__file__), 'testdata')
+key = os.path.join(testdata, 'crashpad_util_test_key.pem')
+cert = os.path.join(testdata, 'crashpad_util_test_cert.pem')
+
+with open(cert, 'w') as cert_file, open(key, 'w') as key_file:
+ MESSAGE = ('DO NOT EDIT: This file was auto-generated by ' + __file__ +
+ '\n\n')
+ cert_file.write(MESSAGE)
+ key_file.write(MESSAGE)
+
+ proc = subprocess.Popen([
+ 'openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost', '-days',
+ '3650', '-newkey', 'rsa:2048', '-keyout', '-'
+ ],
+ stderr=open(os.devnull, 'w'),
+ stdout=subprocess.PIPE)
+
+ contents = proc.communicate()[0]
+ dest = sys.stderr
+ for line in contents.splitlines(True):
+ if line.startswith("-----BEGIN PRIVATE KEY-----"):
+ dest = key_file
+ elif line.startswith("-----BEGIN CERTIFICATE-----"):
+ dest = cert_file
+ elif line.startswith("-----END"):
+ dest.write(line)
+ dest = sys.stderr
+ continue
+
+ dest.write(line)
diff --git a/util/net/http_body_test.cc b/util/net/http_body_test.cc
index e48cbf9..1e5a479 100644
--- a/util/net/http_body_test.cc
+++ b/util/net/http_body_test.cc
@@ -207,9 +207,9 @@
EXPECT_EQ(actual_string, expected_string);
}
-INSTANTIATE_TEST_CASE_P(VariableBufferSize,
- CompositeHTTPBodyStreamBufferSize,
- testing::Values(1, 2, 9, 16, 31, 128, 1024));
+INSTANTIATE_TEST_SUITE_P(VariableBufferSize,
+ CompositeHTTPBodyStreamBufferSize,
+ testing::Values(1, 2, 9, 16, 31, 128, 1024));
} // namespace
} // namespace test
diff --git a/util/net/http_transport.h b/util/net/http_transport.h
index f91a556..acd4e44 100644
--- a/util/net/http_transport.h
+++ b/util/net/http_transport.h
@@ -90,7 +90,7 @@
//! if the response body is not required.
//!
//! \return Whether or not the request was successful, defined as returning
- //! a HTTP status 200 (OK) code.
+ //! a HTTP status code in the range 200-203 (inclusive).
virtual bool ExecuteSynchronously(std::string* response_body) = 0;
protected:
diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm
index 8d5f78c..a433bb3 100644
--- a/util/net/http_transport_mac.mm
+++ b/util/net/http_transport_mac.mm
@@ -293,7 +293,7 @@
return false;
}
NSInteger http_status = [http_response statusCode];
- if (http_status != 200) {
+ if (http_status < 200 || http_status > 203) {
LOG(ERROR) << base::StringPrintf("HTTP status %ld",
implicit_cast<long>(http_status));
return false;
diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc
index f0e2dc1..4dd01b6 100644
--- a/util/net/http_transport_socket.cc
+++ b/util/net/http_transport_socket.cc
@@ -24,6 +24,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/scoped_generic.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "util/file/file_io.h"
@@ -57,8 +58,7 @@
static addrinfo* InvalidValue() { return nullptr; }
static void Free(addrinfo* ai) { freeaddrinfo(ai); }
};
-using ScopedAddrinfo =
- base::ScopedGeneric<addrinfo*, ScopedAddrinfoTraits>;
+using ScopedAddrinfo = base::ScopedGeneric<addrinfo*, ScopedAddrinfoTraits>;
class Stream {
public:
@@ -81,7 +81,7 @@
return LoggingReadFileExactly(fd_, data, size);
}
- bool LoggingReadToEOF(std::string* result) override{
+ bool LoggingReadToEOF(std::string* result) override {
return crashpad::LoggingReadToEOF(fd_, result);
}
@@ -366,7 +366,7 @@
FileOperationResult data_bytes;
do {
- constexpr size_t kCRLFSize = arraysize(kCRLFTerminator) - 1;
+ constexpr size_t kCRLFSize = base::size(kCRLFTerminator) - 1;
struct __attribute__((packed)) {
char size[8];
char crlf[2];
@@ -394,11 +394,13 @@
// placed immediately following the used portion of buf.data, even if
// buf.data is not full.
- // Not snprintf because non-null terminated is desired.
- int rv = sprintf(
- buf.size, "%08x", base::checked_cast<unsigned int>(data_bytes));
- DCHECK_GE(rv, 0);
+ char tmp[9];
+ int rv = snprintf(tmp,
+ sizeof(tmp),
+ "%08x",
+ base::checked_cast<unsigned int>(data_bytes));
DCHECK_EQ(static_cast<size_t>(rv), sizeof(buf.size));
+ strncpy(buf.size, tmp, sizeof(buf.size));
DCHECK_NE(buf.size[sizeof(buf.size) - 1], '\0');
memcpy(&buf.crlf[0], kCRLFTerminator, kCRLFSize);
@@ -457,10 +459,18 @@
LOG(ERROR) << "ReadLine";
return false;
}
- static constexpr const char kHttp10[] = "HTTP/1.0 200 ";
- static constexpr const char kHttp11[] = "HTTP/1.1 200 ";
- return StartsWith(response_line, kHttp10, strlen(kHttp10)) ||
- StartsWith(response_line, kHttp11, strlen(kHttp11));
+ static constexpr const char kHttp10[] = "HTTP/1.0 ";
+ static constexpr const char kHttp11[] = "HTTP/1.1 ";
+ if (!(StartsWith(response_line, kHttp10, strlen(kHttp10)) ||
+ StartsWith(response_line, kHttp11, strlen(kHttp11))) ||
+ response_line.size() < strlen(kHttp10) + 3 ||
+ response_line.at(strlen(kHttp10) + 3) != ' ') {
+ return false;
+ }
+ unsigned int http_status = 0;
+ return base::StringToUint(response_line.substr(strlen(kHttp10), 3),
+ &http_status) &&
+ http_status >= 200 && http_status <= 203;
}
bool ReadResponseHeaders(Stream* stream, HTTPHeaders* headers) {
@@ -537,7 +547,8 @@
}
#if !defined(CRASHPAD_USE_BORINGSSL)
- CHECK(scheme == "http");
+ CHECK(scheme == "http") << "Got " << scheme << " for scheme in '" << url()
+ << "'";
#endif
base::ScopedFD sock(CreateSocket(hostname, port));
diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc
index 7b5f41d..d6a7675 100644
--- a/util/net/http_transport_test.cc
+++ b/util/net/http_transport_test.cc
@@ -54,10 +54,10 @@
class HTTPTransportTestFixture : public MultiprocessExec {
public:
- using RequestValidator =
- void(*)(HTTPTransportTestFixture*, const std::string&);
+ using RequestValidator = void (*)(HTTPTransportTestFixture*,
+ const std::string&);
- HTTPTransportTestFixture(const base::FilePath::StringType& scheme,
+ HTTPTransportTestFixture(const std::string& scheme,
const HTTPHeaders& headers,
std::unique_ptr<HTTPBodyStream> body_stream,
uint16_t http_response_code,
@@ -74,22 +74,21 @@
#if defined(OS_WIN)
FILE_PATH_LITERAL(".exe")
#endif
- );
+ );
- if (ToUTF8IfWin(scheme) == "http") {
+ if (scheme == "http") {
scheme_and_host_ = "http://localhost";
SetChildCommand(server_path, nullptr);
} else {
std::vector<std::string> args;
- cert_ = TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"),
- FILE_PATH_LITERAL("cert"),
- TestPaths::FileType::kCertificate);
+ cert_ = TestPaths::TestDataRoot().Append(
+ FILE_PATH_LITERAL("util/net/testdata/crashpad_util_test_cert.pem"));
args.push_back(ToUTF8IfWin(cert_.value()));
- args.emplace_back(ToUTF8IfWin(
- TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"),
- FILE_PATH_LITERAL("key"),
- TestPaths::FileType::kCertificate)
- .value()));
+ args.emplace_back(
+ ToUTF8IfWin(TestPaths::TestDataRoot()
+ .Append(FILE_PATH_LITERAL(
+ "util/net/testdata/crashpad_util_test_key.pem"))
+ .value()));
SetChildCommand(server_path, &args);
scheme_and_host_ = "https://localhost";
}
@@ -117,9 +116,8 @@
// 200.
const std::string random_string = RandomString();
- ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(),
- random_string.c_str(),
- random_string.size()));
+ ASSERT_TRUE(LoggingWriteFile(
+ WritePipeHandle(), random_string.c_str(), random_string.size()));
// Now execute the HTTP request.
std::unique_ptr<HTTPTransport> transport(HTTPTransport::Create());
@@ -137,7 +135,7 @@
std::string response_body;
bool success = transport->ExecuteSynchronously(&response_body);
- if (response_code_ == 200) {
+ if (response_code_ >= 200 && response_code_ <= 203) {
EXPECT_TRUE(success);
std::string expect_response_body = random_string + "\r\n";
EXPECT_EQ(response_body, expect_response_body);
@@ -246,8 +244,7 @@
EXPECT_EQ(request.substr(body_start), expected);
}
-class HTTPTransport
- : public testing::TestWithParam<base::FilePath::StringType> {};
+class HTTPTransport : public testing::TestWithParam<std::string> {};
TEST_P(HTTPTransport, ValidFormData) {
HTTPMultipartBuilder builder;
@@ -257,8 +254,8 @@
HTTPHeaders headers;
builder.PopulateContentHeaders(&headers);
- HTTPTransportTestFixture test(GetParam(),
- headers, builder.GetBodyStream(), 200, &ValidFormData);
+ HTTPTransportTestFixture test(
+ GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData);
test.Run();
}
@@ -289,8 +286,8 @@
HTTPMultipartBuilder builder;
HTTPHeaders headers;
headers[kContentType] = kTextPlain;
- HTTPTransportTestFixture test(GetParam(), headers, builder.GetBodyStream(),
- 404, &ErrorResponse);
+ HTTPTransportTestFixture test(
+ GetParam(), headers, builder.GetBodyStream(), 404, &ErrorResponse);
test.Run();
}
@@ -321,13 +318,12 @@
headers[kContentType] = kTextPlain;
headers[kContentLength] = base::StringPrintf("%" PRIuS, strlen(kTextBody));
- HTTPTransportTestFixture test(GetParam(),
- headers, std::move(body_stream), 200, &UnchunkedPlainText);
+ HTTPTransportTestFixture test(
+ GetParam(), headers, std::move(body_stream), 200, &UnchunkedPlainText);
test.Run();
}
-void RunUpload33k(const base::FilePath::StringType& scheme,
- bool has_content_length) {
+void RunUpload33k(const std::string& scheme, bool has_content_length) {
// On macOS, NSMutableURLRequest winds up calling into a CFReadStream’s Read()
// callback with a 32kB buffer. Make sure that it’s able to get everything
// when enough is available to fill this buffer, requiring more than one
@@ -364,20 +360,29 @@
RunUpload33k(GetParam(), false);
}
-#if defined(CRASHPAD_USE_BORINGSSL)
+// This should be on for Fuchsia, but DX-382. Debug and re-enabled.
+#if defined(CRASHPAD_USE_BORINGSSL) && !defined(OS_FUCHSIA)
// The test server requires BoringSSL or OpenSSL, so https in tests can only be
// enabled where that's readily available. Additionally on Linux, the bots fail
// lacking libcrypto.so.1.1, so disabled there for now. On Mac, they could also
// likely be enabled relatively easily, if HTTPTransportMac learned to respect
// the user-supplied cert.
-INSTANTIATE_TEST_CASE_P(HTTPTransport,
- HTTPTransport,
- testing::Values(FILE_PATH_LITERAL("http"),
- FILE_PATH_LITERAL("https")));
+//
+// If tests with boringssl are failing because of expired certificates, try
+// re-running generate_test_server_key.py.
+INSTANTIATE_TEST_SUITE_P(HTTPTransport,
+ HTTPTransport,
+ testing::Values("http", "https"),
+ [](const testing::TestParamInfo<std::string>& info) {
+ return info.param;
+ });
#else
-INSTANTIATE_TEST_CASE_P(HTTPTransport,
- HTTPTransport,
- testing::Values(FILE_PATH_LITERAL("http")));
+INSTANTIATE_TEST_SUITE_P(HTTPTransport,
+ HTTPTransport,
+ testing::Values("http"),
+ [](const testing::TestParamInfo<std::string>& info) {
+ return info.param;
+ });
#endif
} // namespace
diff --git a/util/net/http_transport_win.cc b/util/net/http_transport_win.cc
index 18d343c..06ecc4f 100644
--- a/util/net/http_transport_win.cc
+++ b/util/net/http_transport_win.cc
@@ -25,14 +25,15 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/scoped_generic.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "package.h"
#include "util/file/file_io.h"
-#include "util/numeric/safe_assignment.h"
#include "util/net/http_body.h"
+#include "util/numeric/safe_assignment.h"
#include "util/win/module_version.h"
namespace crashpad {
@@ -65,6 +66,8 @@
user_agent.append("x86");
#elif defined(ARCH_CPU_X86_64)
user_agent.append("x64");
+#elif defined(ARCH_CPU_ARM64)
+ user_agent.append("arm64");
#else
#error Port
#endif
@@ -93,8 +96,8 @@
error_code,
0,
msgbuf,
- arraysize(msgbuf),
- NULL);
+ static_cast<DWORD>(base::size(msgbuf)),
+ nullptr);
if (!len) {
return base::StringPrintf("%s: error 0x%lx while retrieving error 0x%lx",
extra,
@@ -375,7 +378,7 @@
return false;
}
- if (status_code != 200) {
+ if (status_code < 200 || status_code > 203) {
LOG(ERROR) << base::StringPrintf("HTTP status %lu", status_code);
return false;
}
diff --git a/util/net/testdata/crashpad_util_test_cert.pem b/util/net/testdata/crashpad_util_test_cert.pem
new file mode 100644
index 0000000..5a2261e
--- /dev/null
+++ b/util/net/testdata/crashpad_util_test_cert.pem
@@ -0,0 +1,21 @@
+DO NOT EDIT: This file was auto-generated by ./util/net/generate_test_server_key.py
+
+-----BEGIN CERTIFICATE-----
+MIIDCDCCAfCgAwIBAgITF9h5BaPfzje590HrE1UF+f9PVDANBgkqhkiG9w0BAQsF
+ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkxMDMwMjIyNDE4WhcNMjkxMDI3
+MjIyNDE4WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCrT0iXfixTPH6nBxgvCgiKkSTeVTSvfhcoUYA7yPZj/1XH
+VYfECoGyvFN4/5g/GL4c721XZmI92Twg2Oa3HksRY8VlNXGNwqbt+wftSLOz+dOE
+bsJ3+NsBtXuaIphFPA9u0h+yrplBtM/dxLTW5v8e3OuEMBXa+94dp4y78u21UkzM
+uWB66tduT7/f0S9P68XhksYwwwvCn5lu258wHKNM5mb4CTwOcUgyyprlS2FS3Fsx
+pIsg9wDfQy5rHINxcZGZZ8Ht4KOJ7egwhfiP4XEiLK6+5DZoOQtrkEUFfLL4i5sx
++eOeD12GP+Dxs6KHYoejGftNx/7U0E2GVMWwWzstAgMBAAGjUzBRMB0GA1UdDgQW
+BBT8y2bnyty/YWcvzlE9JIAPG7/q4DAfBgNVHSMEGDAWgBT8y2bnyty/YWcvzlE9
+JIAPG7/q4DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCF+R21
+Cd1fgITfhYJE7JAV3jLfact5h/2MP5UwgtODfZt2Idwxs8qqwPB0g7WQqEjrPfTv
+6y90AEwr2L8e+wXGnTKtWelCqzztFGov4Hj+rd158eOmEo0IPGrx8dwqLQbw91uU
+cq4onf5iuHkOt99TmIqVYn2zaOHbOF2YYyU052X+9XE/Z5fhibX5THfEG3w0+XM6
+aKEGIM/MxfVaLA8yDXFxhDXHBrH+QAAXTQaC8exnp+po/psyJARD0sM509MeTdAv
+po9JyIzesNAsLW4I7kL8I5i8e+WN79lX2jgwaWMxPmHadYN3mtoltpmFM40oFN6q
+8wjuU07eN/G79c3B
+-----END CERTIFICATE-----
diff --git a/util/net/testdata/crashpad_util_test_key.pem b/util/net/testdata/crashpad_util_test_key.pem
new file mode 100644
index 0000000..b0d6973
--- /dev/null
+++ b/util/net/testdata/crashpad_util_test_key.pem
@@ -0,0 +1,30 @@
+DO NOT EDIT: This file was auto-generated by ./util/net/generate_test_server_key.py
+
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrT0iXfixTPH6n
+BxgvCgiKkSTeVTSvfhcoUYA7yPZj/1XHVYfECoGyvFN4/5g/GL4c721XZmI92Twg
+2Oa3HksRY8VlNXGNwqbt+wftSLOz+dOEbsJ3+NsBtXuaIphFPA9u0h+yrplBtM/d
+xLTW5v8e3OuEMBXa+94dp4y78u21UkzMuWB66tduT7/f0S9P68XhksYwwwvCn5lu
+258wHKNM5mb4CTwOcUgyyprlS2FS3FsxpIsg9wDfQy5rHINxcZGZZ8Ht4KOJ7egw
+hfiP4XEiLK6+5DZoOQtrkEUFfLL4i5sx+eOeD12GP+Dxs6KHYoejGftNx/7U0E2G
+VMWwWzstAgMBAAECggEANEgJDnrqSijfOlh27/wu6SMszlHQt3JS3PIqoZROZexK
+IICg45qVRJgnHXlb3H3Pn4MOqqrLdraynA+2MdKj9FWvq5io5CuwyFZhb/BNL7Mh
+83veC8E+DYJ2i27da9vNlfO4ys5wZVYqTjM3QZLT73Zaxkfqk59khUZaNA4Kr9hD
+EBs9gaDMATyIAN5eAbYTVxgobZ+7OjCNmQ46x2KOg6Wwg/ZQEXtqFoDB3aCNVfTz
+OxlLb5XWF9PvqW0p1wFpF0XEDipkVtjorjp3mHXRuq0gS9Gev1ChQej1yVqMee9J
+jnVK0k7qZw4WLStxBjHqWlBnYLcXLb4f2c6cssA6YQKBgQDfqtYFjU3HCeoRaAoP
+MWTSW2PX8fitnp0O4e1yHymDgtirepvPL8pa7rTBkHuR6lfXNjY0qD0NPsbr81nG
+kNv/btLOK1YPw6f468S6DMsOzGPIWWE+jkhF/1iUTMUQLvOM3lCLEi0ayZ26yx1T
+F2wO2u1kGc1eJ8wn6TccsYiguQKBgQDEEt7eGQMkxLFV4N5e5iunHBvQg+EgfLZZ
+bnuftwruqafmCht/BZgmKQD+e6B6h3nC/iaKET101zpWL95x7Ayd1+mg6Rmdzl87
+ctftScrNLYUTl36BuhP82Y9HvLgf20xrEwBGivc0QtqhMAz/DJoknEM/29LmWEsZ
+VwvWQxhsFQKBgQCrOcJMT8d6Fznsh2QkC2EutK3ztBb2+xUrPoQjOH30YqfyZpN/
+Agv8nv8bq7sdknQamjLXDvBmAmgQW6SfoWf53OJe2Mgym0stAXkCIScWNhwxVVNf
+q1bi1z79kOPPptHmRo8MWCbVegFY7YOOh8C+gpT3a9VPPlJJP31kZvi8aQKBgDcD
+ZGzEb9FdLrR9x2axBgZ5KIS0u/G1jCRDj4Qcg4C7MVSl+VkGZM4wKws7/KbkZBGF
+5aJPfALQcJnGDI/CPzf6YJ65SGqygJ3ZdyQo1DIFV5VLqD8Vyo3jLQRfuvmVOjfA
+uQ8R5pJPP7CCHuNg0c772RKNxvrCQy/08GlJogyRAoGAK6A/8r/iDRJipJSOZdCl
+MreiJvjFOQrORhy+TeDGn02EHK0lijzj1I7SlgfUDLWVnQpBlk12wV6aHGUj2NlG
+Ctk15MFs6gggPV1Fmt6PeviE1e9E4+SDWX35Jhe8JDIqxdgZ0HW/S9Nl4Xt3owhw
+/DSneMQd7X0Tdq7lesJjd3o=
+-----END PRIVATE KEY-----
diff --git a/util/net/tls.gni b/util/net/tls.gni
new file mode 100644
index 0000000..eb96845
--- /dev/null
+++ b/util/net/tls.gni
@@ -0,0 +1,23 @@
+# Copyright 2018 The Crashpad Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../build/crashpad_buildconfig.gni")
+
+declare_args() {
+ # TODO(scottmg): https://crbug.com/crashpad/266 fuchsia:DX-690: BoringSSL
+ # was removed from the Fuchsia SDK. Re-enable it when we have a way to acquire
+ # a BoringSSL lib again.
+ crashpad_use_boringssl_for_http_transport_socket =
+ crashpad_is_in_fuchsia || (crashpad_is_linux && crashpad_is_in_chromium)
+}
diff --git a/util/numeric/checked_address_range_test.cc b/util/numeric/checked_address_range_test.cc
index e6bd9ec..08bc551 100644
--- a/util/numeric/checked_address_range_test.cc
+++ b/util/numeric/checked_address_range_test.cc
@@ -19,7 +19,7 @@
#include <limits>
#include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -119,7 +119,7 @@
{0xffffffffffffffff, 1, kInvalid},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
", base 0x%" PRIx64 ", size 0x%" PRIx64,
@@ -170,7 +170,7 @@
CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", value 0x%" PRIx64, index, testcase.value));
@@ -227,7 +227,7 @@
CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);
ASSERT_TRUE(parent_range_32.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS
", base 0x%" PRIx64 ", size 0x%" PRIx64,
diff --git a/util/numeric/checked_range_test.cc b/util/numeric/checked_range_test.cc
index 04f6bb1..9d611e8 100644
--- a/util/numeric/checked_range_test.cc
+++ b/util/numeric/checked_range_test.cc
@@ -20,7 +20,7 @@
#include <limits>
#include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
@@ -78,7 +78,7 @@
{0xffffffff, 0xffffffff, false},
};
- for (size_t index = 0; index < arraysize(kUnsignedTestData); ++index) {
+ for (size_t index = 0; index < base::size(kUnsignedTestData); ++index) {
const auto& testcase = kUnsignedTestData[index];
SCOPED_TRACE(base::StringPrintf("unsigned index %" PRIuS
", base 0x%x, size 0x%x",
@@ -140,7 +140,7 @@
{-1, 0xffffffff, false},
};
- for (size_t index = 0; index < arraysize(kSignedTestData); ++index) {
+ for (size_t index = 0; index < base::size(kSignedTestData); ++index) {
const auto& testcase = kSignedTestData[index];
SCOPED_TRACE(base::StringPrintf("signed index %" PRIuS
", base 0x%x, size 0x%x",
@@ -186,7 +186,7 @@
CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
ASSERT_TRUE(parent_range.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %" PRIuS ", value 0x%x", index, testcase.value));
@@ -234,7 +234,7 @@
CheckedRange<uint32_t> parent_range(0x2000, 0x1000);
ASSERT_TRUE(parent_range.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
index,
@@ -287,7 +287,7 @@
CheckedRange<uint32_t> first_range(0x2000, 0x1000);
ASSERT_TRUE(first_range.IsValid());
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto& testcase = kTestData[index];
SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x",
index,
diff --git a/util/posix/close_multiple.cc b/util/posix/close_multiple.cc
index 02c8a76..238f158 100644
--- a/util/posix/close_multiple.cc
+++ b/util/posix/close_multiple.cc
@@ -25,6 +25,7 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "util/file/directory_reader.h"
@@ -152,7 +153,7 @@
int maxfilesperproc;
size_t maxfilesperproc_size = sizeof(maxfilesperproc);
if (sysctl(oid,
- arraysize(oid),
+ base::size(oid),
&maxfilesperproc,
&maxfilesperproc_size,
nullptr,
diff --git a/util/posix/close_stdio.cc b/util/posix/close_stdio.cc
index cc9cdac..02bd4a9 100644
--- a/util/posix/close_stdio.cc
+++ b/util/posix/close_stdio.cc
@@ -20,6 +20,7 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
namespace crashpad {
diff --git a/util/posix/double_fork_and_exec.cc b/util/posix/double_fork_and_exec.cc
index df74f70..e4ad998 100644
--- a/util/posix/double_fork_and_exec.cc
+++ b/util/posix/double_fork_and_exec.cc
@@ -27,9 +27,12 @@
namespace crashpad {
bool DoubleForkAndExec(const std::vector<std::string>& argv,
+ const std::vector<std::string>* envp,
int preserve_fd,
bool use_path,
void (*child_function)()) {
+ DCHECK(!envp || !use_path);
+
// argv_c contains const char* pointers and is terminated by nullptr. This is
// suitable for passing to execv(). Although argv_c is not used in the parent
// process, it must be built in the parent process because it’s unsafe to do
@@ -41,6 +44,15 @@
}
argv_c.push_back(nullptr);
+ std::vector<const char*> envp_c;
+ if (envp) {
+ envp_c.reserve(envp->size() + 1);
+ for (const std::string& variable : *envp) {
+ envp_c.push_back(variable.c_str());
+ }
+ envp_c.push_back(nullptr);
+ }
+
// Double-fork(). The three processes involved are parent, child, and
// grandchild. The grandchild will call execv(). The child exits immediately
// after spawning the grandchild, so the grandchild becomes an orphan and its
@@ -102,6 +114,13 @@
// const_cast is safe.
char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]);
+ if (envp) {
+ // This cast is safe for the same reason that the argv_for_execv cast is.
+ char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]);
+ execve(argv_for_execv[0], argv_for_execv, envp_for_execv);
+ PLOG(FATAL) << "execve " << argv_for_execv[0];
+ }
+
if (use_path) {
execvp(argv_for_execv[0], argv_for_execv);
PLOG(FATAL) << "execvp " << argv_for_execv[0];
diff --git a/util/posix/double_fork_and_exec.h b/util/posix/double_fork_and_exec.h
index df340d0..02fc0f2 100644
--- a/util/posix/double_fork_and_exec.h
+++ b/util/posix/double_fork_and_exec.h
@@ -36,6 +36,9 @@
//!
//! \param[in] argv The argument vector to start the grandchild process with.
//! `argv[0]` is used as the path to the executable.
+//! \param[in] envp A vector of environment variables of the form `var=value` to
+//! be passed to `execve()`. If this value is `nullptr`, the current
+//! environment is used.
//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
//! process. This file descriptor is inherited in addition to the three file
//! descriptors associated with the standard input/output streams. Use `-1`
@@ -49,6 +52,9 @@
//! that this function will run in the context of a forked process, and must
//! be safe for that purpose.
//!
+//! Setting both \a envp to a value other than `nullptr` and \a use_path to
+//! `true` is not currently supported.
+//!
//! \return `true` on success, and `false` on failure with a message logged.
//! Only failures that occur in the parent process that indicate a definite
//! failure to start the the grandchild are reported in the return value.
@@ -58,6 +64,7 @@
//! failures, for example, by observing a failure to perform a successful
//! handshake with the grandchild process.
bool DoubleForkAndExec(const std::vector<std::string>& argv,
+ const std::vector<std::string>* envp,
int preserve_fd,
bool use_path,
void (*child_function)());
diff --git a/util/posix/process_info.h b/util/posix/process_info.h
index 18a9cd9..7017cc6 100644
--- a/util/posix/process_info.h
+++ b/util/posix/process_info.h
@@ -174,6 +174,7 @@
// multiple successive calls will always produce the same return value and out
// parameters. This is necessary for intergration with the Snapshot interface.
// See https://crashpad.chromium.org/bug/9.
+ PtraceConnection* connection_;
std::set<gid_t> supplementary_groups_;
mutable timeval start_time_;
pid_t pid_;
diff --git a/util/posix/process_info_linux.cc b/util/posix/process_info_linux.cc
index 10c94b3..3ffd96a 100644
--- a/util/posix/process_info_linux.cc
+++ b/util/posix/process_info_linux.cc
@@ -20,13 +20,16 @@
#include "base/logging.h"
#include "util/file/delimited_file_reader.h"
#include "util/file/file_reader.h"
+#include "util/file/string_file.h"
#include "util/linux/proc_stat_reader.h"
#include "util/misc/lexing.h"
+#include "util/misc/time.h"
namespace crashpad {
ProcessInfo::ProcessInfo()
- : supplementary_groups_(),
+ : connection_(),
+ supplementary_groups_(),
start_time_(),
pid_(-1),
ppid_(-1),
@@ -45,16 +48,19 @@
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
DCHECK(connection);
+ connection_ = connection;
pid_ = connection->GetProcessID();
is_64_bit_ = connection->Is64Bit();
{
char path[32];
snprintf(path, sizeof(path), "/proc/%d/status", pid_);
- FileReader status_file;
- if (!status_file.Open(base::FilePath(path))) {
+ std::string contents;
+ if (!connection->ReadFileContents(base::FilePath(path), &contents)) {
return false;
}
+ StringFile status_file;
+ status_file.SetString(contents);
DelimitedFileReader status_file_line_reader(&status_file);
@@ -230,10 +236,16 @@
if (start_time_initialized_.is_uninitialized()) {
start_time_initialized_.set_invalid();
ProcStatReader reader;
- if (!reader.Initialize(pid_)) {
+ if (!reader.Initialize(connection_, pid_)) {
return false;
}
- if (!reader.StartTime(&start_time_)) {
+ timespec boot_time_ts;
+ if (!GetBootTime(&boot_time_ts)) {
+ return false;
+ }
+ timeval boot_time;
+ TimespecToTimeval(boot_time_ts, &boot_time);
+ if (!reader.StartTime(boot_time, &start_time_)) {
return false;
}
start_time_initialized_.set_valid();
@@ -252,10 +264,12 @@
char path[32];
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid_);
- FileReader cmdline_file;
- if (!cmdline_file.Open(base::FilePath(path))) {
+ std::string contents;
+ if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {
return false;
}
+ StringFile cmdline_file;
+ cmdline_file.SetString(contents);
DelimitedFileReader cmdline_file_field_reader(&cmdline_file);
diff --git a/util/posix/process_info_mac.cc b/util/posix/process_info_mac.cc
index fe9fb65..9e86e08 100644
--- a/util/posix/process_info_mac.cc
+++ b/util/posix/process_info_mac.cc
@@ -18,6 +18,7 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
+#include "base/stl_util.h"
namespace crashpad {
@@ -32,7 +33,7 @@
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
size_t len = sizeof(kern_proc_info_);
- if (sysctl(mib, arraysize(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
+ if (sysctl(mib, base::size(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {
PLOG(ERROR) << "sysctl for pid " << pid;
return false;
}
@@ -111,7 +112,7 @@
const short ngroups = kern_proc_info_.kp_eproc.e_ucred.cr_ngroups;
DCHECK_GE(ngroups, 0);
DCHECK_LE(static_cast<size_t>(ngroups),
- arraysize(kern_proc_info_.kp_eproc.e_ucred.cr_groups));
+ base::size(kern_proc_info_.kp_eproc.e_ucred.cr_groups));
const gid_t* groups = kern_proc_info_.kp_eproc.e_ucred.cr_groups;
return std::set<gid_t>(&groups[0], &groups[ngroups]);
@@ -168,7 +169,7 @@
do {
int mib[] = {CTL_KERN, KERN_PROCARGS2, pid};
int rv =
- sysctl(mib, arraysize(mib), nullptr, &args_size_estimate, nullptr, 0);
+ sysctl(mib, base::size(mib), nullptr, &args_size_estimate, nullptr, 0);
if (rv != 0) {
PLOG(ERROR) << "sysctl (size) for pid " << pid;
return false;
@@ -176,7 +177,7 @@
args_size = args_size_estimate + 1;
args.resize(args_size);
- rv = sysctl(mib, arraysize(mib), &args[0], &args_size, nullptr, 0);
+ rv = sysctl(mib, base::size(mib), &args[0], &args_size, nullptr, 0);
if (rv != 0) {
PLOG(ERROR) << "sysctl (data) for pid " << pid;
return false;
diff --git a/util/posix/process_info_test.cc b/util/posix/process_info_test.cc
index 6154ef7..69390fa 100644
--- a/util/posix/process_info_test.cc
+++ b/util/posix/process_info_test.cc
@@ -14,6 +14,7 @@
#include "util/posix/process_info.h"
+#include <sys/utsname.h>
#include <time.h>
#include <algorithm>
@@ -21,6 +22,7 @@
#include <string>
#include <vector>
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -29,6 +31,7 @@
#include "test/multiprocess.h"
#include "util/file/file_io.h"
#include "util/misc/implicit_cast.h"
+#include "util/string/split_string.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
#include "util/linux/direct_ptrace_connection.h"
@@ -93,11 +96,39 @@
time(&now);
EXPECT_LE(start_time.tv_sec, now);
+ const std::vector<std::string>& expect_argv = GetMainArguments();
+
+#if defined(OS_ANDROID) || defined(OS_LINUX)
+ // Prior to Linux 4.2, the kernel only allowed reading a single page from
+ // /proc/<pid>/cmdline, causing any further arguments to be truncated. Disable
+ // testing arguments in this case.
+ // TODO(jperaza): The main arguments are stored on the main thread's stack
+ // (and so should be included in dumps automatically), and
+ // ProcessInfo::Arguments() might be updated to read the arguments directly,
+ // rather than via procfs on older kernels.
+ utsname uts;
+ ASSERT_EQ(uname(&uts), 0) << ErrnoMessage("uname");
+ std::vector<std::string> parts = SplitString(uts.release, '.');
+ ASSERT_GE(parts.size(), 2u);
+
+ int major, minor;
+ ASSERT_TRUE(base::StringToInt(parts[0], &major));
+ ASSERT_TRUE(base::StringToInt(parts[1], &minor));
+
+ size_t argv_size = 0;
+ for (const auto& arg : expect_argv) {
+ argv_size += arg.size() + 1;
+ }
+
+ if ((major < 4 || (major == 4 && minor < 2)) &&
+ argv_size > static_cast<size_t>(getpagesize())) {
+ return;
+ }
+#endif // OS_ANDROID || OS_LINUX
+
std::vector<std::string> argv;
ASSERT_TRUE(process_info.Arguments(&argv));
- const std::vector<std::string>& expect_argv = GetMainArguments();
-
// expect_argv always contains the initial view of the arguments at the time
// the program was invoked. argv may contain this view, or it may contain the
// current view of arguments after gtest argv processing. argv may be a subset
diff --git a/util/posix/scoped_mmap.cc b/util/posix/scoped_mmap.cc
index 4860cec..fbf47fa 100644
--- a/util/posix/scoped_mmap.cc
+++ b/util/posix/scoped_mmap.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
namespace {
@@ -32,6 +33,11 @@
return true;
}
+size_t RoundPage(size_t size) {
+ const size_t kPageMask = base::checked_cast<size_t>(getpagesize()) - 1;
+ return (size + kPageMask) & ~kPageMask;
+}
+
} // namespace
namespace crashpad {
@@ -40,7 +46,7 @@
ScopedMmap::~ScopedMmap() {
if (is_valid()) {
- Munmap(reinterpret_cast<uintptr_t>(addr_), len_);
+ Munmap(reinterpret_cast<uintptr_t>(addr_), RoundPage(len_));
}
}
@@ -50,26 +56,28 @@
bool ScopedMmap::ResetAddrLen(void* addr, size_t len) {
const uintptr_t new_addr = reinterpret_cast<uintptr_t>(addr);
+ const size_t new_len_round = RoundPage(len);
if (addr == MAP_FAILED) {
DCHECK_EQ(len, 0u);
} else {
DCHECK_NE(len, 0u);
DCHECK_EQ(new_addr % getpagesize(), 0u);
- DCHECK_EQ(len % getpagesize(), 0u);
- DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (len - 1)).IsValid());
+ DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (new_len_round - 1))
+ .IsValid());
}
bool result = true;
if (is_valid()) {
const uintptr_t old_addr = reinterpret_cast<uintptr_t>(addr_);
+ const size_t old_len_round = RoundPage(len_);
if (old_addr < new_addr) {
- result &= Munmap(old_addr, std::min(len_, new_addr - old_addr));
+ result &= Munmap(old_addr, std::min(old_len_round, new_addr - old_addr));
}
- if (old_addr + len_ > new_addr + len) {
- uintptr_t unmap_start = std::max(old_addr, new_addr + len);
- result &= Munmap(unmap_start, old_addr + len_ - unmap_start);
+ if (old_addr + old_len_round > new_addr + new_len_round) {
+ uintptr_t unmap_start = std::max(old_addr, new_addr + new_len_round);
+ result &= Munmap(unmap_start, old_addr + old_len_round - unmap_start);
}
}
@@ -104,7 +112,7 @@
}
bool ScopedMmap::Mprotect(int prot) {
- if (mprotect(addr_, len_, prot) < 0) {
+ if (mprotect(addr_, RoundPage(len_), prot) < 0) {
PLOG(ERROR) << "mprotect";
return false;
}
diff --git a/util/posix/scoped_mmap.h b/util/posix/scoped_mmap.h
index b0ff3dc..9f22372 100644
--- a/util/posix/scoped_mmap.h
+++ b/util/posix/scoped_mmap.h
@@ -91,6 +91,11 @@
}
//! \brief Returns the size of the memory-mapped region.
+ //!
+ //! This is the value originally passed to ResetAddrLen() or ResetMmap(), or
+ //! after Reset(), `0`. It may not be a round number of pages. Providing the
+ //! passed-in value is intended to ease tracking the intended lengths of
+ //! memory-mapped regions backed by files whose sizes are not whole pages.
size_t len() const { return len_; }
private:
diff --git a/util/posix/scoped_mmap_test.cc b/util/posix/scoped_mmap_test.cc
index 7312b84..5279fdb 100644
--- a/util/posix/scoped_mmap_test.cc
+++ b/util/posix/scoped_mmap_test.cc
@@ -20,6 +20,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "test/gtest_death.h"
@@ -150,7 +151,7 @@
EXPECT_EQ(mapping.len(), 3 * kPageSize);
TestCookie cookies[3];
- for (size_t index = 0; index < arraysize(cookies); ++index) {
+ for (size_t index = 0; index < base::size(cookies); ++index) {
cookies[index].SetUp(reinterpret_cast<uint64_t*>(
mapping.addr_as<uintptr_t>() + index * kPageSize));
}
@@ -185,7 +186,7 @@
EXPECT_EQ(mapping.len(), kPageSize);
TestCookie cookies[3];
- for (size_t index = 0; index < arraysize(cookies); ++index) {
+ for (size_t index = 0; index < base::size(cookies); ++index) {
cookies[index].SetUp(reinterpret_cast<uint64_t*>(
reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
}
@@ -196,7 +197,7 @@
EXPECT_EQ(mapping.addr(), pages);
EXPECT_EQ(mapping.len(), 3 * kPageSize);
- for (size_t index = 0; index < arraysize(cookies); ++index) {
+ for (size_t index = 0; index < base::size(cookies); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
EXPECT_EQ(cookies[index].Observed(), cookies[index].Expected());
}
@@ -217,7 +218,7 @@
EXPECT_EQ(mapping.len(), kPageSize);
TestCookie cookies[3];
- for (size_t index = 0; index < arraysize(cookies); ++index) {
+ for (size_t index = 0; index < base::size(cookies); ++index) {
cookies[index].SetUp(reinterpret_cast<uint64_t*>(
reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
}
@@ -248,7 +249,7 @@
EXPECT_EQ(mapping.len(), 2 * kPageSize);
TestCookie cookies[3];
- for (size_t index = 0; index < arraysize(cookies); ++index) {
+ for (size_t index = 0; index < base::size(cookies); ++index) {
cookies[index].SetUp(reinterpret_cast<uint64_t*>(
reinterpret_cast<uintptr_t>(pages) + index * kPageSize));
}
@@ -293,6 +294,94 @@
EXPECT_DEATH_CRASH(cookie.Check(), "");
}
+TEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {
+ ScopedMmap mapping;
+ EXPECT_FALSE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), MAP_FAILED);
+ EXPECT_EQ(mapping.len(), 0u);
+
+ ASSERT_TRUE(mapping.Reset());
+ EXPECT_FALSE(mapping.is_valid());
+
+ // Establishing a half-page mapping actually establishes a single page.
+ const size_t kPageSize = base::checked_cast<size_t>(getpagesize());
+ const size_t kHalfPageSize = kPageSize / 2;
+ ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kHalfPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_NE(mapping.addr(), MAP_FAILED);
+ EXPECT_EQ(mapping.len(), kHalfPageSize);
+
+ TestCookie cookie;
+ cookie.SetUp(mapping.addr_as<uint64_t*>());
+
+ // Shrinking a one-page mapping to a half page is a no-op.
+ void* orig_addr = mapping.addr();
+ ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), orig_addr);
+ EXPECT_EQ(mapping.len(), kHalfPageSize);
+
+ EXPECT_EQ(cookie.Observed(), cookie.Expected());
+
+ // Same thing shrinking it to a single byte, or one byte less than a whole
+ // page.
+ ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, 1));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), orig_addr);
+ EXPECT_EQ(mapping.len(), 1u);
+
+ EXPECT_EQ(cookie.Observed(), cookie.Expected());
+
+ ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize - 1));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), orig_addr);
+ EXPECT_EQ(mapping.len(), kPageSize - 1);
+
+ EXPECT_EQ(cookie.Observed(), cookie.Expected());
+
+ // Shrinking a two-page mapping to a half page frees the second page but
+ // leaves the first alone.
+ ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_NE(mapping.addr(), MAP_FAILED);
+ EXPECT_EQ(mapping.len(), 2 * kPageSize);
+
+ TestCookie two_cookies[2];
+ for (size_t index = 0; index < base::size(two_cookies); ++index) {
+ two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(
+ mapping.addr_as<uintptr_t>() + index * kPageSize));
+ }
+
+ orig_addr = mapping.addr();
+ ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), orig_addr);
+ EXPECT_EQ(mapping.len(), kHalfPageSize);
+
+ EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());
+ EXPECT_DEATH_CRASH(two_cookies[1].Check(), "");
+
+ // Shrinking a two-page mapping to a page and a half is a no-op.
+ ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_NE(mapping.addr(), MAP_FAILED);
+ EXPECT_EQ(mapping.len(), 2 * kPageSize);
+
+ for (size_t index = 0; index < base::size(two_cookies); ++index) {
+ two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(
+ mapping.addr_as<uintptr_t>() + index * kPageSize));
+ }
+
+ orig_addr = mapping.addr();
+ ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize + kHalfPageSize));
+ EXPECT_TRUE(mapping.is_valid());
+ EXPECT_EQ(mapping.addr(), orig_addr);
+ EXPECT_EQ(mapping.len(), kPageSize + kHalfPageSize);
+
+ EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());
+ EXPECT_EQ(two_cookies[1].Observed(), two_cookies[1].Expected());
+}
+
TEST(ScopedMmapDeathTest, Mprotect) {
ScopedMmap mapping;
diff --git a/util/posix/signals.cc b/util/posix/signals.cc
index 63764ab..53c1038 100644
--- a/util/posix/signals.cc
+++ b/util/posix/signals.cc
@@ -19,6 +19,7 @@
#include <vector>
#include "base/logging.h"
+#include "base/stl_util.h"
namespace crashpad {
@@ -92,9 +93,14 @@
bool InstallHandlers(const std::vector<int>& signals,
Signals::Handler handler,
int flags,
- Signals::OldActions* old_actions) {
+ Signals::OldActions* old_actions,
+ const std::set<int>* unhandled_signals) {
bool success = true;
for (int sig : signals) {
+ if (unhandled_signals &&
+ unhandled_signals->find(sig) != unhandled_signals->end()) {
+ continue;
+ }
success &= Signals::InstallHandler(
sig,
handler,
@@ -118,7 +124,7 @@
struct sigaction* Signals::OldActions::ActionForSignal(int sig) {
DCHECK_GT(sig, 0);
const size_t slot = sig - 1;
- DCHECK_LT(slot, arraysize(actions_));
+ DCHECK_LT(slot, base::size(actions_));
return &actions_[slot];
}
@@ -150,12 +156,15 @@
// static
bool Signals::InstallCrashHandlers(Handler handler,
int flags,
- OldActions* old_actions) {
+ OldActions* old_actions,
+ const std::set<int>* unhandled_signals) {
return InstallHandlers(
- std::vector<int>(kCrashSignals, kCrashSignals + arraysize(kCrashSignals)),
+ std::vector<int>(kCrashSignals,
+ kCrashSignals + base::size(kCrashSignals)),
handler,
flags,
- old_actions);
+ old_actions,
+ unhandled_signals);
}
// static
@@ -164,10 +173,11 @@
OldActions* old_actions) {
return InstallHandlers(
std::vector<int>(kTerminateSignals,
- kTerminateSignals + arraysize(kTerminateSignals)),
+ kTerminateSignals + base::size(kTerminateSignals)),
handler,
flags,
- old_actions);
+ old_actions,
+ nullptr);
}
// static
@@ -279,12 +289,12 @@
// static
bool Signals::IsCrashSignal(int sig) {
- return IsSignalInSet(sig, kCrashSignals, arraysize(kCrashSignals));
+ return IsSignalInSet(sig, kCrashSignals, base::size(kCrashSignals));
}
// static
bool Signals::IsTerminateSignal(int sig) {
- return IsSignalInSet(sig, kTerminateSignals, arraysize(kTerminateSignals));
+ return IsSignalInSet(sig, kTerminateSignals, base::size(kTerminateSignals));
}
} // namespace crashpad
diff --git a/util/posix/signals.h b/util/posix/signals.h
index dc55059..368161b 100644
--- a/util/posix/signals.h
+++ b/util/posix/signals.h
@@ -17,6 +17,8 @@
#include <signal.h>
+#include <set>
+
#include "base/macros.h"
namespace crashpad {
@@ -114,15 +116,19 @@
//! the new action. May be `nullptr` if not needed. The same \a
//! old_actions object may be used for calls to both this function and
//! InstallTerminateHandlers().
+ //! \param[in] unhandled_signals Signal handlers will not be installed for
+ //! signal numbers in this set. Optional.
//!
//! \return `true` on success. `false` on failure with a message logged.
//!
//! \warning This function may not be called from a signal handler because of
//! its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()
//! instead.
- static bool InstallCrashHandlers(Handler handler,
- int flags,
- OldActions* old_actions);
+ static bool InstallCrashHandlers(
+ Handler handler,
+ int flags,
+ OldActions* old_actions,
+ const std::set<int>* unhandled_signals = nullptr);
//! \brief Installs a new signal handler for all signals associated with
//! termination.
diff --git a/util/posix/signals_test.cc b/util/posix/signals_test.cc
index 75ff558..58bfa8f 100644
--- a/util/posix/signals_test.cc
+++ b/util/posix/signals_test.cc
@@ -26,6 +26,7 @@
#include "base/compiler_specific.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
@@ -257,7 +258,12 @@
void MultiprocessChild() override {
bool (*install_handlers)(Signals::Handler, int, Signals::OldActions*);
if (Signals::IsCrashSignal(sig_)) {
- install_handlers = Signals::InstallCrashHandlers;
+ install_handlers = [](Signals::Handler handler,
+ int flags,
+ Signals::OldActions* old_actions) {
+ return Signals::InstallCrashHandlers(
+ handler, flags, old_actions, nullptr);
+ };
} else if (Signals::IsTerminateSignal(sig_)) {
install_handlers = Signals::InstallTerminateHandlers;
} else {
@@ -340,7 +346,7 @@
{SIGHUP, SEGV_MAPERR, false},
{SIGINT, SI_USER, false},
};
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+ for (size_t index = 0; index < base::size(kTestData); ++index) {
const auto test_data = kTestData[index];
SCOPED_TRACE(base::StringPrintf(
"index %zu, sig %d, code %d", index, test_data.sig, test_data.code));
diff --git a/util/posix/symbolic_constants_posix.cc b/util/posix/symbolic_constants_posix.cc
index 8008ffb..5937c57 100644
--- a/util/posix/symbolic_constants_posix.cc
+++ b/util/posix/symbolic_constants_posix.cc
@@ -18,7 +18,7 @@
#include <string.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "util/misc/implicit_cast.h"
@@ -137,9 +137,9 @@
};
#if defined(OS_LINUX) || defined(OS_ANDROID)
// NSIG is 64 to account for real-time signals.
-static_assert(arraysize(kSignalNames) == 32, "kSignalNames length");
+static_assert(base::size(kSignalNames) == 32, "kSignalNames length");
#else
-static_assert(arraysize(kSignalNames) == NSIG, "kSignalNames length");
+static_assert(base::size(kSignalNames) == NSIG, "kSignalNames length");
#endif
constexpr char kSigPrefix[] = "SIG";
@@ -151,7 +151,7 @@
std::string SignalToString(int signal,
SymbolicConstantToStringOptions options) {
const char* signal_name =
- implicit_cast<size_t>(signal) < arraysize(kSignalNames)
+ implicit_cast<size_t>(signal) < base::size(kSignalNames)
? kSignalNames[signal]
: nullptr;
if (!signal_name) {
@@ -176,8 +176,7 @@
string.substr(0, strlen(kSigPrefix)).compare(kSigPrefix) == 0;
base::StringPiece short_string =
can_match_full ? string.substr(strlen(kSigPrefix)) : string;
- for (int index = 0;
- index < implicit_cast<int>(arraysize(kSignalNames));
+ for (int index = 0; index < implicit_cast<int>(base::size(kSignalNames));
++index) {
const char* signal_name = kSignalNames[index];
if (!signal_name) {
diff --git a/util/posix/symbolic_constants_posix_test.cc b/util/posix/symbolic_constants_posix_test.cc
index 32c1d43..c7d62ee 100644
--- a/util/posix/symbolic_constants_posix_test.cc
+++ b/util/posix/symbolic_constants_posix_test.cc
@@ -17,13 +17,14 @@
#include <signal.h>
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
-#define NUL_TEST_DATA(string) { string, arraysize(string) - 1 }
+#define NUL_TEST_DATA(string) \
+ { string, base::size(string) - 1 }
namespace crashpad {
namespace test {
@@ -115,7 +116,7 @@
}
TEST(SymbolicConstantsPOSIX, SignalToString) {
- for (size_t index = 0; index < arraysize(kSignalTestData); ++index) {
+ for (size_t index = 0; index < base::size(kSignalTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestSignalToString(kSignalTestData[index].signal,
kSignalTestData[index].full_name,
@@ -170,12 +171,11 @@
kAllowFullName | kAllowShortName | kAllowNumber,
};
- for (size_t option_index = 0;
- option_index < arraysize(kOptions);
+ for (size_t option_index = 0; option_index < base::size(kOptions);
++option_index) {
SCOPED_TRACE(base::StringPrintf("option_index %zu", option_index));
StringToSymbolicConstantOptions options = kOptions[option_index];
- for (size_t index = 0; index < arraysize(kSignalTestData); ++index) {
+ for (size_t index = 0; index < base::size(kSignalTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
int signal = kSignalTestData[index].signal;
{
@@ -213,7 +213,7 @@
"",
};
- for (size_t index = 0; index < arraysize(kNegativeTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNegativeTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
TestStringToSignal(kNegativeTestData[index], options, false, 0);
}
@@ -234,7 +234,7 @@
NUL_TEST_DATA("1\0002"),
};
- for (size_t index = 0; index < arraysize(kNULTestData); ++index) {
+ for (size_t index = 0; index < base::size(kNULTestData); ++index) {
SCOPED_TRACE(base::StringPrintf("index %zu", index));
base::StringPiece string(kNULTestData[index].string,
kNULTestData[index].length);
diff --git a/util/process/process_id.h b/util/process/process_id.h
new file mode 100644
index 0000000..113f6fc
--- /dev/null
+++ b/util/process/process_id.h
@@ -0,0 +1,54 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_
+#define CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_
+
+#include <type_traits>
+
+#include "base/format_macros.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <sys/types.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_FUCHSIA)
+#include <zircon/types.h>
+#endif
+
+namespace crashpad {
+
+#if defined(OS_POSIX) || DOXYGEN
+//! \brief Alias for platform-specific type to represent a process.
+using ProcessID = pid_t;
+constexpr ProcessID kInvalidProcessID = -1;
+static_assert(std::is_same<ProcessID, int>::value, "Port.");
+#define PRI_PROCESS_ID "d"
+#elif defined(OS_WIN)
+using ProcessID = DWORD;
+constexpr ProcessID kInvalidProcessID = 0;
+#define PRI_PROCESS_ID "lu"
+#elif defined(OS_FUCHSIA)
+using ProcessID = zx_koid_t;
+constexpr ProcessID kInvalidProcessID = ZX_KOID_INVALID;
+static_assert(std::is_same<ProcessID, int64_t>::value, "Port.");
+#define PRI_PROCESS_ID PRId64
+#else
+#error Port.
+#endif
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_
diff --git a/util/process/process_memory.cc b/util/process/process_memory.cc
index 6bc0010..ab87b94 100644
--- a/util/process/process_memory.cc
+++ b/util/process/process_memory.cc
@@ -14,14 +14,23 @@
#include "util/process/process_memory.h"
+#include <algorithm>
+
#include "base/logging.h"
+#include "util/numeric/safe_assignment.h"
namespace crashpad {
-bool ProcessMemory::Read(VMAddress address, size_t size, void* buffer) const {
+bool ProcessMemory::Read(VMAddress address, VMSize size, void* buffer) const {
+ size_t local_size;
+ if (!AssignIfInRange(&local_size, size)) {
+ LOG(ERROR) << "size " << size << " out of bounds for size_t";
+ return false;
+ }
+
char* buffer_c = static_cast<char*>(buffer);
- while (size > 0) {
- ssize_t bytes_read = ReadUpTo(address, size, buffer_c);
+ while (local_size > 0) {
+ ssize_t bytes_read = ReadUpTo(address, local_size, buffer_c);
if (bytes_read < 0) {
return false;
}
@@ -29,8 +38,8 @@
LOG(ERROR) << "short read";
return false;
}
- DCHECK_LE(static_cast<size_t>(bytes_read), size);
- size -= bytes_read;
+ DCHECK_LE(static_cast<size_t>(bytes_read), local_size);
+ local_size -= bytes_read;
address += bytes_read;
buffer_c += bytes_read;
}
@@ -39,15 +48,21 @@
bool ProcessMemory::ReadCStringInternal(VMAddress address,
bool has_size,
- size_t size,
+ VMSize size,
std::string* string) const {
+ size_t local_size;
+ if (!AssignIfInRange(&local_size, size)) {
+ LOG(ERROR) << "size " << size << " out of bounds for size_t";
+ return false;
+ }
+
string->clear();
char buffer[4096];
do {
size_t read_size;
if (has_size) {
- read_size = std::min(sizeof(buffer), size);
+ read_size = std::min(sizeof(buffer), local_size);
} else {
read_size = sizeof(buffer);
}
@@ -68,8 +83,8 @@
string->append(buffer, bytes_read);
address += bytes_read;
- size -= bytes_read;
- } while (!has_size || size > 0);
+ local_size -= bytes_read;
+ } while (!has_size || local_size > 0);
LOG(ERROR) << "unterminated string";
return false;
diff --git a/util/process/process_memory.h b/util/process/process_memory.h
index d67c382..eeb78e9 100644
--- a/util/process/process_memory.h
+++ b/util/process/process_memory.h
@@ -19,8 +19,14 @@
#include <string>
+#include "build/build_config.h"
#include "util/misc/address_types.h"
+#if defined(OS_WIN)
+#include <basetsd.h>
+typedef SSIZE_T ssize_t;
+#endif // defined(OS_WIN)
+
namespace crashpad {
//! \brief Abstract base class for accessing the memory of another process.
@@ -40,7 +46,7 @@
//!
//! \return `true` on success, with \a buffer filled appropriately. `false` on
//! failure, with a message logged.
- bool Read(VMAddress address, size_t size, void* buffer) const;
+ bool Read(VMAddress address, VMSize size, void* buffer) const;
//! \brief Reads a `NUL`-terminated C string from the target process into a
//! string in the current process.
@@ -73,7 +79,7 @@
//! a `NUL` terminator is not found within \a size bytes, or when
//! encountering unmapped or unreadable pages.
bool ReadCStringSizeLimited(VMAddress address,
- size_t size,
+ VMSize size,
std::string* string) const {
return ReadCStringInternal(address, true, size, string);
}
@@ -118,8 +124,11 @@
//! encountering unmapped or unreadable pages.
virtual bool ReadCStringInternal(VMAddress address,
bool has_size,
- size_t size,
+ VMSize size,
std::string* string) const;
+
+ // Allow ProcessMemorySanitized to call ReadUpTo.
+ friend class ProcessMemorySanitized;
};
} // namespace crashpad
diff --git a/util/process/process_memory_fuchsia.cc b/util/process/process_memory_fuchsia.cc
index 212e1c6..b9c4a0c 100644
--- a/util/process/process_memory_fuchsia.cc
+++ b/util/process/process_memory_fuchsia.cc
@@ -14,8 +14,6 @@
#include "util/process/process_memory_fuchsia.h"
-#include <zircon/syscalls.h>
-
#include <limits>
#include "base/logging.h"
@@ -28,9 +26,13 @@
ProcessMemoryFuchsia::~ProcessMemoryFuchsia() {}
-bool ProcessMemoryFuchsia::Initialize(zx_handle_t process) {
+bool ProcessMemoryFuchsia::Initialize(const zx::unowned_process& process) {
+ return Initialize(*process);
+}
+
+bool ProcessMemoryFuchsia::Initialize(const zx::process& process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
- process_ = process;
+ process_ = zx::unowned_process(process);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
@@ -42,8 +44,7 @@
DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});
size_t actual;
- zx_status_t status =
- zx_process_read_memory(process_, address, buffer, size, &actual);
+ zx_status_t status = process_->read_memory(address, buffer, size, &actual);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_process_read_memory";
diff --git a/util/process/process_memory_fuchsia.h b/util/process/process_memory_fuchsia.h
index e6cdd3e..6c9ceba 100644
--- a/util/process/process_memory_fuchsia.h
+++ b/util/process/process_memory_fuchsia.h
@@ -15,7 +15,7 @@
#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_
#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_
-#include <zircon/types.h>
+#include <lib/zx/process.h>
#include <string>
@@ -40,12 +40,15 @@
//! \param[in] process The handle to the target process.
//!
//! \return `true` on success, `false` on failure with a message logged.
- bool Initialize(zx_handle_t process);
+ bool Initialize(const zx::process& process);
+ // TODO(wez): Remove this overload when zx::unowned_process allows implicit
+ // copy.
+ bool Initialize(const zx::unowned_process& process);
private:
ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
- zx_handle_t process_;
+ zx::unowned_process process_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryFuchsia);
diff --git a/util/process/process_memory_mac.cc b/util/process/process_memory_mac.cc
new file mode 100644
index 0000000..29357f3
--- /dev/null
+++ b/util/process/process_memory_mac.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/process/process_memory_mac.h"
+
+#include <mach/mach_vm.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "util/stdlib/strnlen.h"
+
+namespace crashpad {
+
+ProcessMemoryMac::MappedMemory::~MappedMemory() {}
+
+bool ProcessMemoryMac::MappedMemory::ReadCString(size_t offset,
+ std::string* string) const {
+ if (offset >= user_size_) {
+ LOG(WARNING) << "offset out of range";
+ return false;
+ }
+
+ const char* string_base = reinterpret_cast<const char*>(data_) + offset;
+ size_t max_length = user_size_ - offset;
+ size_t string_length = strnlen(string_base, max_length);
+ if (string_length == max_length) {
+ LOG(WARNING) << "unterminated string";
+ return false;
+ }
+
+ string->assign(string_base, string_length);
+ return true;
+}
+
+ProcessMemoryMac::MappedMemory::MappedMemory(vm_address_t vm_address,
+ size_t vm_size,
+ size_t user_offset,
+ size_t user_size)
+ : vm_(vm_address, vm_size),
+ data_(reinterpret_cast<const void*>(vm_address + user_offset)),
+ user_size_(user_size) {
+ vm_address_t vm_end = vm_address + vm_size;
+ vm_address_t user_address = reinterpret_cast<vm_address_t>(data_);
+ vm_address_t user_end = user_address + user_size;
+ DCHECK_GE(user_address, vm_address);
+ DCHECK_LE(user_address, vm_end);
+ DCHECK_GE(user_end, vm_address);
+ DCHECK_LE(user_end, vm_end);
+}
+
+ProcessMemoryMac::ProcessMemoryMac() : task_(TASK_NULL), initialized_() {}
+
+bool ProcessMemoryMac::Initialize(task_t task) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ task_ = task;
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+std::unique_ptr<ProcessMemoryMac::MappedMemory> ProcessMemoryMac::ReadMapped(
+ mach_vm_address_t address,
+ size_t size) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (size == 0) {
+ return std::unique_ptr<MappedMemory>(new MappedMemory(0, 0, 0, 0));
+ }
+
+ mach_vm_address_t region_address = mach_vm_trunc_page(address);
+ mach_vm_size_t region_size =
+ mach_vm_round_page(address - region_address + size);
+
+ vm_offset_t region;
+ mach_msg_type_number_t region_count;
+ kern_return_t kr =
+ mach_vm_read(task_, region_address, region_size, ®ion, ®ion_count);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(WARNING, kr) << base::StringPrintf(
+ "mach_vm_read(0x%llx, 0x%llx)", region_address, region_size);
+ return std::unique_ptr<MappedMemory>();
+ }
+ if (region_count != region_size) {
+ LOG(ERROR) << base::StringPrintf(
+ "mach_vm_read() unexpected read: 0x%x != 0x%llx bytes",
+ region_count,
+ region_size);
+ if (region_count)
+ vm_deallocate(mach_task_self(), region, region_count);
+ return std::unique_ptr<MappedMemory>();
+ }
+
+ return std::unique_ptr<MappedMemory>(
+ new MappedMemory(region, region_size, address - region_address, size));
+}
+
+ssize_t ProcessMemoryMac::ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());
+
+ std::unique_ptr<MappedMemory> memory = ReadMapped(address, size);
+ if (!memory) {
+ // If we can not read the entire mapping, try to perform a short read of the
+ // first page instead. This is necessary to support ReadCString().
+ size_t short_read = PAGE_SIZE - (address % PAGE_SIZE);
+ if (short_read >= size)
+ return -1;
+
+ memory = ReadMapped(address, short_read);
+ if (!memory)
+ return -1;
+
+ size = short_read;
+ }
+
+ memcpy(buffer, memory->data(), size);
+ return static_cast<ssize_t>(size);
+}
+
+} // namespace crashpad
diff --git a/util/process/process_memory_mac.h b/util/process/process_memory_mac.h
new file mode 100644
index 0000000..214e489
--- /dev/null
+++ b/util/process/process_memory_mac.h
@@ -0,0 +1,135 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_
+#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_
+
+#include <mach/mach.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+
+#include "base/mac/scoped_mach_vm.h"
+#include "base/macros.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_memory.h"
+
+namespace crashpad {
+
+//! \brief Accesses the memory of another Mach task.
+class ProcessMemoryMac : public ProcessMemory {
+ public:
+ //! \brief A memory region mapped from another Mach task.
+ //!
+ //! The mapping is maintained until this object is destroyed.
+ class MappedMemory {
+ public:
+ ~MappedMemory();
+
+ //! \brief Returns a pointer to the data requested by the user.
+ //!
+ //! This is the value of the \a vm_address + \a user_offset parameters
+ //! passed to the constructor, casted to `const void*`.
+ const void* data() const { return data_; }
+
+ //! \brief Reads a `NUL`-terminated C string from the mapped region.
+ //!
+ //! This method will read contiguous memory until a `NUL` terminator is
+ //! found.
+ //!
+ //! \param[in] offset The offset into data() of the string to be read.
+ //! \param[out] string The string, whose contents begin at data() and
+ //! continue up to a `NUL` terminator.
+ //!
+ //! \return `true` on success, with \a string set appropriately. If \a
+ //! offset is greater than or equal to the \a user_size constructor
+ //! parameter, or if no `NUL` terminator was found in data() after \a
+ //! offset, returns `false` with an appropriate warning logged.
+ bool ReadCString(size_t offset, std::string* string) const;
+
+ private:
+ //! \brief Creates an object that owns a memory region mapped from another
+ //! Mach task.
+ //!
+ //! \param[in] vm_address The address in this process’ address space where
+ //! the mapping begins. This must be page-aligned.
+ //! \param[in] vm_size The total size of the mapping that begins at \a
+ //! vm_address. This must be page-aligned.
+ //! \param[in] user_offset The offset into the mapped region where the data
+ //! requested by the user begins. This accounts for the fact that a
+ //! mapping must be page-aligned but the user data may not be. This
+ //! parameter must be equal to or less than \a vm_size.
+ //! \param[in] user_size The size of the data requested by the user. This
+ //! parameter can be used to compute the end address of user data, which
+ //! must be within the mapped region.
+ MappedMemory(vm_address_t vm_address,
+ size_t vm_size,
+ size_t user_offset,
+ size_t user_size);
+
+ base::mac::ScopedMachVM vm_;
+ const void* data_;
+ size_t user_size_;
+
+ // The outer class needs to be able to call this class’ private constructor.
+ friend class ProcessMemoryMac;
+
+ DISALLOW_COPY_AND_ASSIGN(MappedMemory);
+ };
+
+ ProcessMemoryMac();
+ ~ProcessMemoryMac() {}
+
+ //! \brief Initializes this object to read the memory of a task with the
+ //! provided task port.
+ //!
+ //! This method must be called successfully prior to calling any other method
+ //! in this class.
+ //!
+ //! \param[in] task A send right to the target task's task port. This object
+ //! does not take ownership of the send right.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(task_t task);
+
+ //! \brief Maps memory from the target task into the current task.
+ //!
+ //! This interface is an alternative to Read() that does not require the
+ //! caller to provide a buffer to fill. This avoids copying memory, which can
+ //! offer a performance improvement.
+ //!
+ //! \param[in] address The address, in the target task’s address space, of the
+ //! memory region to map.
+ //! \param[in] size The size, in bytes, of the memory region to map.
+ //!
+ //! \return On success, a MappedMemory object that provides access to the data
+ //! requested. On faliure, `nullptr`, with a warning logged. Failures can
+ //! occur, for example, when encountering unmapped or unreadable pages.
+ std::unique_ptr<MappedMemory> ReadMapped(mach_vm_address_t address,
+ size_t size) const;
+
+ private:
+ ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
+
+ task_t task_; // weak
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMac);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_
diff --git a/util/process/process_memory_mac_test.cc b/util/process/process_memory_mac_test.cc
new file mode 100644
index 0000000..d801bb1
--- /dev/null
+++ b/util/process/process_memory_mac_test.cc
@@ -0,0 +1,340 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/process/process_memory_mac.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "gtest/gtest.h"
+#include "test/mac/mach_errors.h"
+#include "util/misc/from_pointer_cast.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ProcessMemoryMac, ReadMappedSelf) {
+ vm_address_t address = 0;
+ constexpr vm_size_t kSize = 4 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ region[index] = (index % 256) ^ ((index >> 8) % 256);
+ }
+
+ ProcessMemoryMac memory;
+ ASSERT_TRUE(memory.Initialize(mach_task_self()));
+
+ std::string result(kSize, '\0');
+ std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;
+
+ // Ensure that the entire region can be read.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_EQ(memcmp(region, mapped->data(), kSize), 0);
+
+ // Ensure that a read of length 0 succeeds and doesn't touch the result.
+ result.assign(kSize, '\0');
+ std::string zeroes = result;
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, 0)));
+ EXPECT_EQ(result, zeroes);
+
+ // Ensure that a read starting at an unaligned address works.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 1), 0);
+
+ // Ensure that a read ending at an unaligned address works.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1)));
+ EXPECT_EQ(memcmp(region, mapped->data(), kSize - 1), 0);
+
+ // Ensure that a read starting and ending at unaligned addresses works.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2)));
+ EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 2), 0);
+
+ // Ensure that a read of exactly one page works.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE)));
+ EXPECT_EQ(memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE), 0);
+
+ // Ensure that a read of a single byte works.
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1)));
+ EXPECT_EQ(reinterpret_cast<const char*>(mapped->data())[0], region[2]);
+
+ // Ensure that a read of length zero works and doesn't touch the data.
+ result[0] = 'M';
+ ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0)));
+ EXPECT_EQ(result[0], 'M');
+}
+
+TEST(ProcessMemoryMac, ReadSelfUnmapped) {
+ vm_address_t address = 0;
+ constexpr vm_size_t kSize = 2 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ // Don't include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ region[index] = (index % 255) + 1;
+ }
+
+ kr = vm_protect(
+ mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect");
+
+ ProcessMemoryMac memory;
+ ASSERT_TRUE(memory.Initialize(mach_task_self()));
+ std::string result(kSize, '\0');
+
+ EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
+ EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
+ EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
+ EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
+
+ // Do the same thing with the ReadMapped() interface.
+ std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;
+ EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
+
+ // Repeat the test with an unmapped page instead of an unreadable one. This
+ // portion of the test may be flaky in the presence of other threads, if
+ // another thread maps something in the region that is deallocated here.
+ kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
+ vm_owner.reset(address, PAGE_SIZE);
+
+ EXPECT_FALSE(memory.Read(address, kSize, &result[0]));
+ EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));
+ EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
+ EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
+ EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
+
+ // Do the same thing with the ReadMapped() interface.
+ EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
+ EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
+ EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
+}
+
+TEST(ProcessMemoryMac, ReadCStringSelfUnmapped) {
+ vm_address_t address = 0;
+ constexpr vm_size_t kSize = 2 * PAGE_SIZE;
+ kern_return_t kr =
+ vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
+ base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));
+
+ char* region = reinterpret_cast<char*>(address);
+ for (size_t index = 0; index < kSize; ++index) {
+ // Don't include any NUL bytes, because ReadCString stops when it encounters
+ // a NUL.
+ region[index] = (index % 255) + 1;
+ }
+
+ kr = vm_protect(
+ mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect");
+
+ ProcessMemoryMac memory;
+ ASSERT_TRUE(memory.Initialize(mach_task_self()));
+ std::string result;
+ EXPECT_FALSE(memory.ReadCString(address, &result));
+
+ // Make sure that if the string is NUL-terminated within the mapped memory
+ // region, it can be read properly.
+ char terminator_or_not = '\0';
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ ASSERT_TRUE(memory.ReadCString(address, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(result.size(), PAGE_SIZE - 1u);
+ EXPECT_EQ(result, region);
+
+ // Repeat the test with an unmapped page instead of an unreadable one. This
+ // portion of the test may be flaky in the presence of other threads, if
+ // another thread maps something in the region that is deallocated here.
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);
+ ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
+ vm_owner.reset(address, PAGE_SIZE);
+
+ EXPECT_FALSE(memory.ReadCString(address, &result));
+
+ // Clear the result before testing that the string can be read. This makes
+ // sure that the result is actually filled in, because it already contains the
+ // expected value from the tests above.
+ result.clear();
+ std::swap(region[PAGE_SIZE - 1], terminator_or_not);
+ ASSERT_TRUE(memory.ReadCString(address, &result));
+ EXPECT_FALSE(result.empty());
+ EXPECT_EQ(result.size(), PAGE_SIZE - 1u);
+ EXPECT_EQ(result, region);
+}
+
+bool IsAddressMapped(vm_address_t address) {
+ vm_address_t region_address = address;
+ vm_size_t region_size;
+ mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+ vm_region_basic_info_64 info;
+ mach_port_t object;
+ kern_return_t kr = vm_region_64(mach_task_self(),
+ ®ion_address,
+ ®ion_size,
+ VM_REGION_BASIC_INFO_64,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &count,
+ &object);
+ if (kr == KERN_SUCCESS) {
+ // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c
+ // vm_map_region()), but the interface acts as if it might carry a send
+ // right, so treat it as documented.
+ base::mac::ScopedMachSendRight object_owner(object);
+
+ return address >= region_address && address <= region_address + region_size;
+ }
+
+ if (kr == KERN_INVALID_ADDRESS) {
+ return false;
+ }
+
+ ADD_FAILURE() << MachErrorMessage(kr, "vm_region_64");
+ return false;
+}
+
+TEST(ProcessMemoryMac, MappedMemoryDeallocates) {
+ // This tests that once a ProcessMemoryMac::MappedMemory object is destroyed,
+ // it releases the mapped memory that it owned. Technically, this test is not
+ // valid because after the mapping is released, something else (on another
+ // thread) might wind up mapped in the same address. In the test environment,
+ // hopefully there are either no other threads or they're all quiescent, so
+ // nothing else should wind up mapped in the address.
+
+ ProcessMemoryMac memory;
+ ASSERT_TRUE(memory.Initialize(mach_task_self()));
+ std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;
+
+ static constexpr char kTestBuffer[] = "hello!";
+ mach_vm_address_t test_address =
+ FromPointerCast<mach_vm_address_t>(&kTestBuffer);
+ ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));
+ EXPECT_EQ(memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)), 0);
+
+ vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
+ EXPECT_TRUE(IsAddressMapped(mapped_address));
+
+ mapped.reset();
+ EXPECT_FALSE(IsAddressMapped(mapped_address));
+
+ // This is the same but with a big buffer that’s definitely larger than a
+ // single page. This makes sure that the whole mapped region winds up being
+ // deallocated.
+ constexpr size_t kBigSize = 4 * PAGE_SIZE;
+ std::unique_ptr<char[]> big_buffer(new char[kBigSize]);
+ test_address = FromPointerCast<mach_vm_address_t>(&big_buffer[0]);
+ ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize)));
+
+ mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
+ vm_address_t mapped_last_address = mapped_address + kBigSize - 1;
+ EXPECT_TRUE(IsAddressMapped(mapped_address));
+ EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE));
+ EXPECT_TRUE(IsAddressMapped(mapped_last_address));
+
+ mapped.reset();
+ EXPECT_FALSE(IsAddressMapped(mapped_address));
+ EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE));
+ EXPECT_FALSE(IsAddressMapped(mapped_last_address));
+}
+
+TEST(ProcessMemoryMac, MappedMemoryReadCString) {
+ // This tests the behavior of ProcessMemoryMac::MappedMemory::ReadCString().
+ ProcessMemoryMac memory;
+ ASSERT_TRUE(memory.Initialize(mach_task_self()));
+ std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;
+
+ static constexpr char kTestBuffer[] = "0\0" "2\0" "45\0" "789";
+ const mach_vm_address_t kTestAddress =
+ FromPointerCast<mach_vm_address_t>(&kTestBuffer);
+ ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));
+
+ std::string string;
+ ASSERT_TRUE(mapped->ReadCString(0, &string));
+ EXPECT_EQ(string, "0");
+ ASSERT_TRUE(mapped->ReadCString(1, &string));
+ EXPECT_EQ(string, "");
+ ASSERT_TRUE(mapped->ReadCString(2, &string));
+ EXPECT_EQ(string, "2");
+ ASSERT_TRUE(mapped->ReadCString(3, &string));
+ EXPECT_EQ(string, "");
+ ASSERT_TRUE(mapped->ReadCString(4, &string));
+ EXPECT_EQ(string, "45");
+ ASSERT_TRUE(mapped->ReadCString(5, &string));
+ EXPECT_EQ(string, "5");
+ ASSERT_TRUE(mapped->ReadCString(6, &string));
+ EXPECT_EQ(string, "");
+
+ // kTestBuffer’s NUL terminator was not read, so these will see an
+ // unterminated string and fail.
+ EXPECT_FALSE(mapped->ReadCString(7, &string));
+ EXPECT_FALSE(mapped->ReadCString(8, &string));
+ EXPECT_FALSE(mapped->ReadCString(9, &string));
+
+ // This is out of the range of what was read, so it will fail.
+ EXPECT_FALSE(mapped->ReadCString(10, &string));
+ EXPECT_FALSE(mapped->ReadCString(11, &string));
+
+ // Read it again, this time with a length long enough to include the NUL
+ // terminator.
+ ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11)));
+
+ ASSERT_TRUE(mapped->ReadCString(6, &string));
+ EXPECT_EQ(string, "");
+
+ // These should now succeed.
+ ASSERT_TRUE(mapped->ReadCString(7, &string));
+ EXPECT_EQ(string, "789");
+ ASSERT_TRUE(mapped->ReadCString(8, &string));
+ EXPECT_EQ(string, "89");
+ ASSERT_TRUE(mapped->ReadCString(9, &string));
+ EXPECT_EQ(string, "9");
+ EXPECT_TRUE(mapped->ReadCString(10, &string));
+ EXPECT_EQ(string, "");
+
+ // These are still out of range.
+ EXPECT_FALSE(mapped->ReadCString(11, &string));
+ EXPECT_FALSE(mapped->ReadCString(12, &string));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/process/process_memory_native.h b/util/process/process_memory_native.h
index 19fa805..39bfc89 100644
--- a/util/process/process_memory_native.h
+++ b/util/process/process_memory_native.h
@@ -18,6 +18,10 @@
#include "util/process/process_memory_fuchsia.h"
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include "util/process/process_memory_linux.h"
+#elif defined(OS_WIN)
+#include "util/process/process_memory_win.h"
+#elif defined(OS_MACOSX)
+#include "util/process/process_memory_mac.h"
#endif
namespace crashpad {
@@ -27,6 +31,10 @@
using ProcessMemoryNative = ProcessMemoryFuchsia;
#elif defined(OS_LINUX) || defined(OS_ANDROID)
using ProcessMemoryNative = ProcessMemoryLinux;
+#elif defined(OS_WIN)
+using ProcessMemoryNative = ProcessMemoryWin;
+#elif defined(OS_MACOSX)
+using ProcessMemoryNative = ProcessMemoryMac;
#else
#error Port.
#endif
diff --git a/util/process/process_memory_range.cc b/util/process/process_memory_range.cc
index 32d805d..caa4315 100644
--- a/util/process/process_memory_range.cc
+++ b/util/process/process_memory_range.cc
@@ -67,7 +67,7 @@
}
bool ProcessMemoryRange::Read(VMAddress address,
- size_t size,
+ VMSize size,
void* buffer) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CheckedVMAddressRange read_range(range_.Is64Bit(), address, size);
@@ -79,14 +79,14 @@
}
bool ProcessMemoryRange::ReadCStringSizeLimited(VMAddress address,
- size_t size,
+ VMSize size,
std::string* string) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (!range_.ContainsValue(address)) {
LOG(ERROR) << "read out of range";
return false;
}
- size = std::min(static_cast<VMSize>(size), range_.End() - address);
+ size = std::min(size, range_.End() - address);
return memory_->ReadCStringSizeLimited(address, size, string);
}
diff --git a/util/process/process_memory_range.h b/util/process/process_memory_range.h
index 2b654ba..aabee49 100644
--- a/util/process/process_memory_range.h
+++ b/util/process/process_memory_range.h
@@ -97,7 +97,7 @@
//!
//! \return `true` on success, with \a buffer filled appropriately. `false` on
//! failure, with a message logged.
- bool Read(VMAddress address, size_t size, void* buffer) const;
+ bool Read(VMAddress address, VMSize size, void* buffer) const;
//! \brief Reads a `NUL`-terminated C string from the target process into a
//! string in the current process.
@@ -113,7 +113,7 @@
//! a `NUL` terminator is not found within \a size bytes, or when
//! encountering unmapped or unreadable pages.
bool ReadCStringSizeLimited(VMAddress address,
- size_t size,
+ VMSize size,
std::string* string) const;
private:
diff --git a/util/process/process_memory_range_test.cc b/util/process/process_memory_range_test.cc
index 1c78506..2df2b5c 100644
--- a/util/process/process_memory_range_test.cc
+++ b/util/process/process_memory_range_test.cc
@@ -14,22 +14,15 @@
#include "util/process/process_memory_range.h"
-#include <unistd.h>
-
#include <limits>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
+#include "test/process_type.h"
#include "util/misc/from_pointer_cast.h"
-
-#if defined(OS_FUCHSIA)
-#include <zircon/process.h>
-
-#include "util/process/process_memory_fuchsia.h"
-#else
-#include "util/process/process_memory_linux.h"
-#endif
+#include "util/process/process_memory_native.h"
namespace crashpad {
namespace test {
@@ -41,21 +34,14 @@
} kTestObject = {"string1", "string2"};
TEST(ProcessMemoryRange, Basic) {
-#if defined(OS_FUCHSIA)
- ProcessMemoryFuchsia memory;
- ASSERT_TRUE(memory.Initialize(zx_process_self()));
- constexpr bool is_64_bit = true;
-#else
- pid_t pid = getpid();
#if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true;
#else
constexpr bool is_64_bit = false;
#endif // ARCH_CPU_64_BITS
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
-#endif // OS_FUCHSIA
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(GetSelfProcess()));
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
@@ -73,28 +59,28 @@
auto string1_addr = FromPointerCast<VMAddress>(kTestObject.string1);
auto string2_addr = FromPointerCast<VMAddress>(kTestObject.string2);
ASSERT_TRUE(range.ReadCStringSizeLimited(
- string1_addr, arraysize(kTestObject.string1), &string));
+ string1_addr, base::size(kTestObject.string1), &string));
EXPECT_STREQ(string.c_str(), kTestObject.string1);
ASSERT_TRUE(range.ReadCStringSizeLimited(
- string2_addr, arraysize(kTestObject.string2), &string));
+ string2_addr, base::size(kTestObject.string2), &string));
EXPECT_STREQ(string.c_str(), kTestObject.string2);
// Limit the range to remove access to string2.
ProcessMemoryRange range2;
ASSERT_TRUE(range2.Initialize(range));
ASSERT_TRUE(
- range2.RestrictRange(string1_addr, arraysize(kTestObject.string1)));
+ range2.RestrictRange(string1_addr, base::size(kTestObject.string1)));
EXPECT_TRUE(range2.ReadCStringSizeLimited(
- string1_addr, arraysize(kTestObject.string1), &string));
+ string1_addr, base::size(kTestObject.string1), &string));
EXPECT_FALSE(range2.ReadCStringSizeLimited(
- string2_addr, arraysize(kTestObject.string2), &string));
+ string2_addr, base::size(kTestObject.string2), &string));
EXPECT_FALSE(range2.Read(object_addr, sizeof(object), &object));
// String reads fail if the NUL terminator is outside the range.
ASSERT_TRUE(range2.RestrictRange(string1_addr, strlen(kTestObject.string1)));
EXPECT_FALSE(range2.ReadCStringSizeLimited(
- string1_addr, arraysize(kTestObject.string1), &string));
+ string1_addr, base::size(kTestObject.string1), &string));
// New range outside the old range.
EXPECT_FALSE(range2.RestrictRange(string1_addr - 1, 1));
diff --git a/util/process/process_memory_sanitized.cc b/util/process/process_memory_sanitized.cc
new file mode 100644
index 0000000..cafe5ae
--- /dev/null
+++ b/util/process/process_memory_sanitized.cc
@@ -0,0 +1,65 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/process/process_memory_sanitized.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+
+ProcessMemorySanitized::ProcessMemorySanitized()
+ : ProcessMemory(), memory_(nullptr), whitelist_() {}
+
+ProcessMemorySanitized::~ProcessMemorySanitized() {}
+
+bool ProcessMemorySanitized::Initialize(
+ const ProcessMemory* memory,
+ const std::vector<std::pair<VMAddress, VMAddress>>* whitelist) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ memory_ = memory;
+ if (whitelist)
+ whitelist_ = *whitelist;
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+ssize_t ProcessMemorySanitized::ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ VMAddress end = address + size;
+ for (auto&& entry : whitelist_) {
+ if (address >= entry.first && address < entry.second &&
+ end >= entry.first && end <= entry.second) {
+ return memory_->ReadUpTo(address, size, buffer);
+ }
+ }
+
+ DLOG(ERROR)
+ << "ProcessMemorySanitized failed to read unwhitelisted region. address="
+ << address << " size=" << size;
+ return 0;
+}
+
+} // namespace crashpad
diff --git a/util/process/process_memory_sanitized.h b/util/process/process_memory_sanitized.h
new file mode 100644
index 0000000..4443963
--- /dev/null
+++ b/util/process/process_memory_sanitized.h
@@ -0,0 +1,62 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_
+#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_
+
+#include <sys/types.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_memory.h"
+
+namespace crashpad {
+
+//! \brief Sanitized access to the memory of another process.
+class ProcessMemorySanitized final : public ProcessMemory {
+ public:
+ ProcessMemorySanitized();
+ ~ProcessMemorySanitized();
+
+ //! \brief Initializes this object to read memory from the underlying
+ //! \a memory object if the memory range is in the provided \a whitelist.
+ //!
+ //! This method must be called successfully prior to calling any other method
+ //! in this class.
+ //!
+ //! \param[in] memory The memory object to read whitelisted regions from.
+ //! \param[in] whitelist A whitelist of memory regions.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(
+ const ProcessMemory* memory,
+ const std::vector<std::pair<VMAddress, VMAddress>>* whitelist);
+
+ private:
+ ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
+
+ const ProcessMemory* memory_;
+ InitializationStateDcheck initialized_;
+ std::vector<std::pair<VMAddress, VMAddress>> whitelist_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemorySanitized);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_
diff --git a/util/process/process_memory_sanitized_test.cc b/util/process/process_memory_sanitized_test.cc
new file mode 100644
index 0000000..ff5c944
--- /dev/null
+++ b/util/process/process_memory_sanitized_test.cc
@@ -0,0 +1,64 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/process/process_memory_sanitized.h"
+
+#include "gtest/gtest.h"
+#include "test/process_type.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/process/process_memory_native.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ProcessMemorySanitized, DenyOnEmptyWhitelist) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(GetSelfProcess()));
+
+ char c = 42;
+ char out;
+
+ ProcessMemorySanitized san_null;
+ san_null.Initialize(&memory, nullptr);
+ EXPECT_FALSE(san_null.Read(FromPointerCast<VMAddress>(&c), 1, &out));
+
+ std::vector<std::pair<VMAddress, VMAddress>> whitelist;
+ ProcessMemorySanitized san_blank;
+ san_blank.Initialize(&memory, &whitelist);
+ EXPECT_FALSE(san_blank.Read(FromPointerCast<VMAddress>(&c), 1, &out));
+}
+
+TEST(ProcessMemorySanitized, WhitelistingWorks) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(GetSelfProcess()));
+
+ char str[4] = "ABC";
+ char out[4];
+
+ std::vector<std::pair<VMAddress, VMAddress>> whitelist;
+ whitelist.push_back(std::make_pair(FromPointerCast<VMAddress>(str + 1),
+ FromPointerCast<VMAddress>(str + 2)));
+
+ ProcessMemorySanitized sanitized;
+ sanitized.Initialize(&memory, &whitelist);
+
+ EXPECT_FALSE(sanitized.Read(FromPointerCast<VMAddress>(str), 1, &out));
+ EXPECT_TRUE(sanitized.Read(FromPointerCast<VMAddress>(str + 1), 1, &out));
+ EXPECT_FALSE(sanitized.Read(FromPointerCast<VMAddress>(str + 2), 1, &out));
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc
index 741e58d..3f5db24 100644
--- a/util/process/process_memory_test.cc
+++ b/util/process/process_memory_test.cc
@@ -15,28 +15,93 @@
#include "util/process/process_memory.h"
#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
#include <memory>
+#include "base/process/process_metrics.h"
+#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/multiprocess.h"
#include "test/multiprocess_exec.h"
#include "test/process_type.h"
+#include "test/scoped_guarded_page.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
-#include "util/posix/scoped_mmap.h"
#include "util/process/process_memory_native.h"
+#if defined(OS_MACOSX)
+#include "test/mac/mach_multiprocess.h"
+#endif // defined(OS_MACOSX)
+
namespace crashpad {
namespace test {
namespace {
+// On macOS the ProcessMemoryTests require accessing the child process' task
+// port which requires root or a code signing entitlement. To account for this
+// we implement an adaptor class that wraps MachMultiprocess on macOS, because
+// it shares the child's task port, and makes it behave like MultiprocessExec.
+#if defined(OS_MACOSX)
+class MultiprocessAdaptor : public MachMultiprocess {
+ public:
+ void SetChildTestMainFunction(const std::string& function_name) {
+ test_function_ = function_name;
+ }
+
+ ProcessType ChildProcess() { return ChildTask(); }
+
+ // Helpers to get I/O handles in the child process
+ static FileHandle OutputHandle() {
+ CHECK_NE(write_pipe_handle_, -1);
+ return write_pipe_handle_;
+ }
+
+ static FileHandle InputHandle() {
+ CHECK_NE(read_pipe_handle_, -1);
+ return read_pipe_handle_;
+ }
+
+ private:
+ virtual void Parent() = 0;
+
+ void MachMultiprocessParent() override { Parent(); }
+
+ void MachMultiprocessChild() override {
+ read_pipe_handle_ = ReadPipeHandle();
+ write_pipe_handle_ = WritePipeHandle();
+ internal::CheckedInvokeMultiprocessChild(test_function_);
+ }
+
+ std::string test_function_;
+
+ static FileHandle read_pipe_handle_;
+ static FileHandle write_pipe_handle_;
+};
+
+FileHandle MultiprocessAdaptor::read_pipe_handle_ = -1;
+FileHandle MultiprocessAdaptor::write_pipe_handle_ = -1;
+#else
+class MultiprocessAdaptor : public MultiprocessExec {
+ public:
+ static FileHandle OutputHandle() {
+ return StdioFileHandle(StdioStream::kStandardOutput);
+ }
+
+ static FileHandle InputHandle() {
+ return StdioFileHandle(StdioStream::kStandardInput);
+ }
+
+ private:
+ virtual void Parent() = 0;
+
+ void MultiprocessParent() override { Parent(); }
+};
+#endif // defined(OS_MACOSX)
+
void DoChildReadTestSetup(size_t* region_size,
std::unique_ptr<char[]>* region) {
- *region_size = 4 * getpagesize();
+ *region_size = 4 * base::GetPageSize();
region->reset(new char[*region_size]);
for (size_t index = 0; index < *region_size; ++index) {
(*region)[index] = index % 256;
@@ -47,17 +112,17 @@
size_t region_size;
std::unique_ptr<char[]> region;
DoChildReadTestSetup(®ion_size, ®ion);
- FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ FileHandle out = MultiprocessAdaptor::OutputHandle();
CheckedWriteFile(out, ®ion_size, sizeof(region_size));
VMAddress address = FromPointerCast<VMAddress>(region.get());
CheckedWriteFile(out, &address, sizeof(address));
- CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());
return 0;
}
-class ReadTest : public MultiprocessExec {
+class ReadTest : public MultiprocessAdaptor {
public:
- ReadTest() : MultiprocessExec() {
+ ReadTest() : MultiprocessAdaptor() {
SetChildTestMainFunction("ReadTestChild");
}
@@ -73,7 +138,7 @@
void RunAgainstChild() { Run(); }
private:
- void MultiprocessParent() override {
+ void Parent() override {
size_t region_size;
VMAddress region;
ASSERT_TRUE(
@@ -120,7 +185,7 @@
}
// Ensure that a read of exactly one page works.
- size_t page_size = getpagesize();
+ size_t page_size = base::GetPageSize();
ASSERT_GE(region_size, page_size + page_size);
ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get()));
for (size_t i = 0; i < page_size; ++i) {
@@ -154,7 +219,7 @@
std::string MakeLongString() {
std::string long_string;
- const size_t kStringLongSize = 4 * getpagesize();
+ const size_t kStringLongSize = 4 * base::GetPageSize();
for (size_t index = 0; index < kStringLongSize; ++index) {
long_string.push_back((index % 255) + 1);
}
@@ -184,23 +249,22 @@
&const_empty, &const_short, &local_empty, &local_short, &long_string);
const auto write_address = [](const char* p) {
VMAddress address = FromPointerCast<VMAddress>(p);
- CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
- &address,
- sizeof(address));
+ CheckedWriteFile(
+ MultiprocessAdaptor::OutputHandle(), &address, sizeof(address));
};
write_address(const_empty);
write_address(const_short);
write_address(local_empty);
write_address(local_short);
write_address(long_string.c_str());
- CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());
return 0;
}
-class ReadCStringTest : public MultiprocessExec {
+class ReadCStringTest : public MultiprocessAdaptor {
public:
ReadCStringTest(bool limit_size)
- : MultiprocessExec(), limit_size_(limit_size) {
+ : MultiprocessAdaptor(), limit_size_(limit_size) {
SetChildTestMainFunction("ReadCStringTestChild");
}
@@ -222,7 +286,7 @@
void RunAgainstChild() { Run(); }
private:
- void MultiprocessParent() override {
+ void Parent() override {
#define DECLARE_AND_READ_ADDRESS(name) \
VMAddress name; \
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name)));
@@ -241,6 +305,23 @@
long_string_address);
}
+ void Compare(ProcessMemory& memory, VMAddress address, const char* str) {
+ std::string result;
+ if (limit_size_) {
+ ASSERT_TRUE(
+ memory.ReadCStringSizeLimited(address, strlen(str) + 1, &result));
+ EXPECT_EQ(result, str);
+ ASSERT_TRUE(
+ memory.ReadCStringSizeLimited(address, strlen(str) + 2, &result));
+ EXPECT_EQ(result, str);
+ EXPECT_FALSE(
+ memory.ReadCStringSizeLimited(address, strlen(str), &result));
+ } else {
+ ASSERT_TRUE(memory.ReadCString(address, &result));
+ EXPECT_EQ(result, str);
+ }
+ }
+
void DoTest(ProcessType process,
VMAddress const_empty_address,
VMAddress const_short_address,
@@ -250,51 +331,12 @@
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(process));
- std::string result;
-
- if (limit_size_) {
- ASSERT_TRUE(memory.ReadCStringSizeLimited(
- const_empty_address, arraysize(kConstCharEmpty), &result));
- EXPECT_EQ(result, kConstCharEmpty);
-
- ASSERT_TRUE(memory.ReadCStringSizeLimited(
- const_short_address, arraysize(kConstCharShort), &result));
- EXPECT_EQ(result, kConstCharShort);
- EXPECT_FALSE(memory.ReadCStringSizeLimited(
- const_short_address, arraysize(kConstCharShort) - 1, &result));
-
- ASSERT_TRUE(
- memory.ReadCStringSizeLimited(local_empty_address, 1, &result));
- EXPECT_EQ(result, "");
-
- ASSERT_TRUE(memory.ReadCStringSizeLimited(
- local_short_address, strlen(SHORT_LOCAL_STRING) + 1, &result));
- EXPECT_EQ(result, SHORT_LOCAL_STRING);
- EXPECT_FALSE(memory.ReadCStringSizeLimited(
- local_short_address, strlen(SHORT_LOCAL_STRING), &result));
-
- std::string long_string_for_comparison = MakeLongString();
- ASSERT_TRUE(memory.ReadCStringSizeLimited(
- long_string_address, long_string_for_comparison.size() + 1, &result));
- EXPECT_EQ(result, long_string_for_comparison);
- EXPECT_FALSE(memory.ReadCStringSizeLimited(
- long_string_address, long_string_for_comparison.size(), &result));
- } else {
- ASSERT_TRUE(memory.ReadCString(const_empty_address, &result));
- EXPECT_EQ(result, kConstCharEmpty);
-
- ASSERT_TRUE(memory.ReadCString(const_short_address, &result));
- EXPECT_EQ(result, kConstCharShort);
-
- ASSERT_TRUE(memory.ReadCString(local_empty_address, &result));
- EXPECT_EQ(result, "");
-
- ASSERT_TRUE(memory.ReadCString(local_short_address, &result));
- EXPECT_EQ(result, SHORT_LOCAL_STRING);
-
- ASSERT_TRUE(memory.ReadCString(long_string_address, &result));
- EXPECT_EQ(result, MakeLongString());
- }
+ Compare(memory, const_empty_address, kConstCharEmpty);
+ Compare(memory, const_short_address, kConstCharShort);
+ Compare(memory, local_empty_address, "");
+ Compare(memory, local_short_address, SHORT_LOCAL_STRING);
+ std::string long_string_for_comparison = MakeLongString();
+ Compare(memory, long_string_address, long_string_for_comparison.c_str());
}
const bool limit_size_;
@@ -322,101 +364,60 @@
test.RunAgainstChild();
}
-void DoReadUnmappedChildMainSetup(ScopedMmap* pages,
- VMAddress* address,
- size_t* page_size,
- size_t* region_size) {
- *page_size = getpagesize();
- *region_size = 2 * (*page_size);
- if (!pages->ResetMmap(nullptr,
- *region_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1,
- 0)) {
- ADD_FAILURE();
- return;
- }
-
- *address = pages->addr_as<VMAddress>();
-
- char* region = pages->addr_as<char*>();
- for (size_t index = 0; index < *region_size; ++index) {
+void DoReadUnmappedChildMainSetup(void* page) {
+ char* region = reinterpret_cast<char*>(page);
+ for (size_t index = 0; index < base::GetPageSize(); ++index) {
region[index] = index % 256;
}
-
- EXPECT_TRUE(pages->ResetAddrLen(region, *page_size));
}
CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) {
- ScopedMmap pages;
- VMAddress address = 0;
- size_t page_size, region_size;
- DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size);
- FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ ScopedGuardedPage pages;
+ VMAddress address = reinterpret_cast<VMAddress>(pages.Pointer());
+ DoReadUnmappedChildMainSetup(pages.Pointer());
+ FileHandle out = MultiprocessAdaptor::OutputHandle();
CheckedWriteFile(out, &address, sizeof(address));
- CheckedWriteFile(out, &page_size, sizeof(page_size));
- CheckedWriteFile(out, ®ion_size, sizeof(region_size));
- CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());
return 0;
}
-class ReadUnmappedTest : public MultiprocessExec {
+// This test only supports running against a child process because
+// ScopedGuardedPage is not thread-safe.
+class ReadUnmappedTest : public MultiprocessAdaptor {
public:
- ReadUnmappedTest() : MultiprocessExec() {
+ ReadUnmappedTest() : MultiprocessAdaptor() {
SetChildTestMainFunction("ReadUnmappedChildMain");
}
- void RunAgainstSelf() {
- ScopedMmap pages;
- VMAddress address = 0;
- size_t page_size, region_size;
- DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size);
- DoTest(GetSelfProcess(), address, page_size, region_size);
- }
-
void RunAgainstChild() { Run(); }
private:
- void MultiprocessParent() override {
+ void Parent() override {
VMAddress address = 0;
- size_t page_size, region_size;
ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address)));
- ASSERT_TRUE(
- ReadFileExactly(ReadPipeHandle(), &page_size, sizeof(page_size)));
- ASSERT_TRUE(
- ReadFileExactly(ReadPipeHandle(), ®ion_size, sizeof(region_size)));
- DoTest(ChildProcess(), address, page_size, region_size);
+ DoTest(ChildProcess(), address);
}
- void DoTest(ProcessType process,
- VMAddress address,
- size_t page_size,
- size_t region_size) {
+ void DoTest(ProcessType process, VMAddress address) {
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(process));
VMAddress page_addr1 = address;
- VMAddress page_addr2 = page_addr1 + page_size;
+ VMAddress page_addr2 = page_addr1 + base::GetPageSize();
- std::unique_ptr<char[]> result(new char[region_size]);
- EXPECT_TRUE(memory.Read(page_addr1, page_size, result.get()));
+ std::unique_ptr<char[]> result(new char[base::GetPageSize() * 2]);
+ EXPECT_TRUE(memory.Read(page_addr1, base::GetPageSize(), result.get()));
EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get()));
- EXPECT_FALSE(memory.Read(page_addr1, region_size, result.get()));
- EXPECT_FALSE(memory.Read(page_addr2, page_size, result.get()));
+ EXPECT_FALSE(
+ memory.Read(page_addr1, base::GetPageSize() * 2, result.get()));
+ EXPECT_FALSE(memory.Read(page_addr2, base::GetPageSize(), result.get()));
EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get()));
}
DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest);
};
-TEST(ProcessMemory, ReadUnmappedSelf) {
- ReadUnmappedTest test;
- ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstSelf();
-}
-
TEST(ProcessMemory, ReadUnmappedChild) {
ReadUnmappedTest test;
ASSERT_FALSE(testing::Test::HasFailure());
@@ -428,9 +429,13 @@
class StringDataInChildProcess {
public:
// This constructor only makes sense in the child process.
- explicit StringDataInChildProcess(const char* cstring)
+ explicit StringDataInChildProcess(const char* cstring, bool valid)
: address_(FromPointerCast<VMAddress>(cstring)) {
- memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
+ if (valid) {
+ memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
+ } else {
+ memset(expected_value_, 0xff, kChildProcessStringLength + 1);
+ }
}
void Write(FileHandle out) {
@@ -457,22 +462,10 @@
};
void DoCStringUnmappedTestSetup(
- ScopedMmap* pages,
+ void* page,
std::vector<StringDataInChildProcess>* strings) {
- const size_t page_size = getpagesize();
- const size_t region_size = 2 * page_size;
- if (!pages->ResetMmap(nullptr,
- region_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1,
- 0)) {
- ADD_FAILURE();
- return;
- }
-
- char* region = pages->addr_as<char*>();
- for (size_t index = 0; index < region_size; ++index) {
+ char* region = reinterpret_cast<char*>(page);
+ for (size_t index = 0; index < base::GetPageSize(); ++index) {
region[index] = 1 + index % 255;
}
@@ -481,63 +474,53 @@
string1[kChildProcessStringLength] = '\0';
// A string near the end of the mapped region
- char* string2 = region + page_size - kChildProcessStringLength * 2;
+ char* string2 = region + base::GetPageSize() - kChildProcessStringLength * 2;
string2[kChildProcessStringLength] = '\0';
// A string that crosses from the mapped into the unmapped region
- char* string3 = region + page_size - kChildProcessStringLength + 1;
- string3[kChildProcessStringLength] = '\0';
+ char* string3 = region + base::GetPageSize() - kChildProcessStringLength + 1;
// A string entirely in the unmapped region
- char* string4 = region + page_size + 10;
- string4[kChildProcessStringLength] = '\0';
+ char* string4 = region + base::GetPageSize() + 10;
- strings->push_back(StringDataInChildProcess(string1));
- strings->push_back(StringDataInChildProcess(string2));
- strings->push_back(StringDataInChildProcess(string3));
- strings->push_back(StringDataInChildProcess(string4));
-
- EXPECT_TRUE(pages->ResetAddrLen(region, page_size));
+ strings->push_back(StringDataInChildProcess(string1, true));
+ strings->push_back(StringDataInChildProcess(string2, true));
+ strings->push_back(StringDataInChildProcess(string3, false));
+ strings->push_back(StringDataInChildProcess(string4, false));
}
CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {
- ScopedMmap pages;
+ ScopedGuardedPage pages;
std::vector<StringDataInChildProcess> strings;
- DoCStringUnmappedTestSetup(&pages, &strings);
- FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ DoCStringUnmappedTestSetup(pages.Pointer(), &strings);
+ FileHandle out = MultiprocessAdaptor::OutputHandle();
strings[0].Write(out);
strings[1].Write(out);
strings[2].Write(out);
strings[3].Write(out);
- CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());
return 0;
}
-class ReadCStringUnmappedTest : public MultiprocessExec {
+// This test only supports running against a child process because
+// ScopedGuardedPage is not thread-safe.
+class ReadCStringUnmappedTest : public MultiprocessAdaptor {
public:
ReadCStringUnmappedTest(bool limit_size)
- : MultiprocessExec(), limit_size_(limit_size) {
+ : MultiprocessAdaptor(), limit_size_(limit_size) {
SetChildTestMainFunction("ReadCStringUnmappedChildMain");
}
- void RunAgainstSelf() {
- ScopedMmap pages;
- std::vector<StringDataInChildProcess> strings;
- DoCStringUnmappedTestSetup(&pages, &strings);
- DoTest(GetSelfProcess(), strings);
- }
-
void RunAgainstChild() { Run(); }
private:
- void MultiprocessParent() override {
+ void Parent() override {
std::vector<StringDataInChildProcess> strings;
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
- ASSERT_NO_FATAL_FAILURE();
- DoTest(ChildProcess(), strings);
+ ASSERT_NO_FATAL_FAILURE(DoTest(ChildProcess(), strings));
}
void DoTest(ProcessType process,
@@ -574,24 +557,12 @@
DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest);
};
-TEST(ProcessMemory, ReadCStringUnmappedSelf) {
- ReadCStringUnmappedTest test(/* limit_size= */ false);
- ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstSelf();
-}
-
TEST(ProcessMemory, ReadCStringUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ false);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstChild();
}
-TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
- ReadCStringUnmappedTest test(/* limit_size= */ true);
- ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstSelf();
-}
-
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ true);
ASSERT_FALSE(testing::Test::HasFailure());
diff --git a/util/process/process_memory_win.cc b/util/process/process_memory_win.cc
new file mode 100644
index 0000000..f716102
--- /dev/null
+++ b/util/process/process_memory_win.cc
@@ -0,0 +1,118 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/process/process_memory_win.h"
+
+#include <windows.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/stringprintf.h"
+
+namespace crashpad {
+
+ProcessMemoryWin::ProcessMemoryWin()
+ : ProcessMemory(), handle_(), process_info_(), initialized_() {}
+
+ProcessMemoryWin::~ProcessMemoryWin() {}
+
+bool ProcessMemoryWin::Initialize(HANDLE handle) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ handle_ = handle;
+ if (!process_info_.Initialize(handle)) {
+ LOG(ERROR) << "Failed to initialize ProcessInfo.";
+ return false;
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+ssize_t ProcessMemoryWin::ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());
+
+ SIZE_T size_out = 0;
+ BOOL success = ReadProcessMemory(
+ handle_, reinterpret_cast<void*>(address), buffer, size, &size_out);
+ if (success)
+ return base::checked_cast<ssize_t>(size_out);
+
+ if (GetLastError() == ERROR_PARTIAL_COPY) {
+ // If we can not read the entire section, perform a short read of the first
+ // page instead. This is necessary to support ReadCString().
+ size_t short_read =
+ base::GetPageSize() - (address & (base::GetPageSize() - 1));
+ success = ReadProcessMemory(handle_,
+ reinterpret_cast<void*>(address),
+ buffer,
+ short_read,
+ &size_out);
+ if (success)
+ return base::checked_cast<ssize_t>(size_out);
+ }
+
+ PLOG(ERROR) << "ReadMemory at 0x" << std::hex << address << std::dec << " of "
+ << size << " bytes failed";
+ return -1;
+}
+
+size_t ProcessMemoryWin::ReadAvailableMemory(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());
+
+ if (size == 0)
+ return 0;
+
+ auto ranges = process_info_.GetReadableRanges(
+ CheckedRange<WinVMAddress, WinVMSize>(address, size));
+
+ // We only read up until the first unavailable byte, so we only read from the
+ // first range. If we have no ranges, then no bytes were accessible anywhere
+ // in the range.
+ if (ranges.empty()) {
+ LOG(ERROR) << base::StringPrintf(
+ "range at 0x%llx, size 0x%zx completely inaccessible", address, size);
+ return 0;
+ }
+
+ // If the start address was adjusted, we couldn't read even the first
+ // requested byte.
+ if (ranges.front().base() != address) {
+ LOG(ERROR) << base::StringPrintf(
+ "start of range at 0x%llx, size 0x%zx inaccessible", address, size);
+ return 0;
+ }
+
+ DCHECK_LE(ranges.front().size(), size);
+
+ ssize_t result = ReadUpTo(ranges.front().base(),
+ base::checked_cast<size_t>(ranges.front().size()),
+ buffer);
+ if (result < 0)
+ return 0;
+
+ return base::checked_cast<size_t>(result);
+}
+
+} // namespace crashpad
diff --git a/util/process/process_memory_win.h b/util/process/process_memory_win.h
new file mode 100644
index 0000000..2856900
--- /dev/null
+++ b/util/process/process_memory_win.h
@@ -0,0 +1,66 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_
+#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_memory.h"
+#include "util/win/process_info.h"
+
+namespace crashpad {
+
+//! \brief Accesses the memory of another Windows process.
+class ProcessMemoryWin final : public ProcessMemory {
+ public:
+ ProcessMemoryWin();
+ ~ProcessMemoryWin();
+
+ //! \brief Initializes this object to read the memory of a process with the
+ //! provided handle.
+ //!
+ //! This method must be called successfully prior to calling any other method
+ //! in this class.
+ //!
+ //! \param[in] handle The HANDLE of a target process.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(HANDLE handle);
+
+ //! \brief Attempts to read \a size bytes from the target process starting at
+ //! address \a address into \a buffer. If some of the specified range is
+ //! not accessible, reads up to the first inaccessible byte.
+ //!
+ //! \return The actual number of bytes read.
+ size_t ReadAvailableMemory(VMAddress address,
+ size_t num_bytes,
+ void* buffer) const;
+
+ private:
+ ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
+
+ HANDLE handle_;
+ ProcessInfo process_info_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryWin);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_
diff --git a/util/stdlib/string_number_conversion.cc b/util/stdlib/string_number_conversion.cc
index 859a833..6421184 100644
--- a/util/stdlib/string_number_conversion.cc
+++ b/util/stdlib/string_number_conversion.cc
@@ -97,15 +97,34 @@
}
};
-struct StringToInt64Traits
- : public StringToSignedIntegerTraits<int64_t, int64_t> {
+struct StringToLongTraits
+ : public StringToSignedIntegerTraits<long, long long> {
static LongType Convert(const char* str, char** end, int base) {
return strtoll(str, end, base);
}
};
-struct StringToUnsignedInt64Traits
- : public StringToUnsignedIntegerTraits<uint64_t, uint64_t> {
+struct StringToUnsignedLongTraits
+ : public StringToUnsignedIntegerTraits<unsigned long, unsigned long long> {
+ static LongType Convert(const char* str, char** end, int base) {
+ if (str[0] == '-') {
+ *end = const_cast<char*>(str);
+ return 0;
+ }
+ return strtoull(str, end, base);
+ }
+};
+
+struct StringToLongLongTraits
+ : public StringToSignedIntegerTraits<long long, long long> {
+ static LongType Convert(const char* str, char** end, int base) {
+ return strtoll(str, end, base);
+ }
+};
+
+struct StringToUnsignedLongLongTraits
+ : public StringToUnsignedIntegerTraits<unsigned long long,
+ unsigned long long> {
static LongType Convert(const char* str, char** end, int base) {
if (str[0] == '-') {
*end = const_cast<char*>(str);
@@ -136,7 +155,7 @@
end != string.data() + string.length()) {
return false;
}
- *number = result;
+ *number = static_cast<IntType>(result);
return true;
}
@@ -152,12 +171,21 @@
return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);
}
-bool StringToNumber(const std::string& string, int64_t* number) {
- return StringToIntegerInternal<StringToInt64Traits>(string, number);
+bool StringToNumber(const std::string& string, long* number) {
+ return StringToIntegerInternal<StringToLongTraits>(string, number);
}
-bool StringToNumber(const std::string& string, uint64_t* number) {
- return StringToIntegerInternal<StringToUnsignedInt64Traits>(string, number);
+bool StringToNumber(const std::string& string, unsigned long* number) {
+ return StringToIntegerInternal<StringToUnsignedLongTraits>(string, number);
+}
+
+bool StringToNumber(const std::string& string, long long* number) {
+ return StringToIntegerInternal<StringToLongLongTraits>(string, number);
+}
+
+bool StringToNumber(const std::string& string, unsigned long long* number) {
+ return StringToIntegerInternal<StringToUnsignedLongLongTraits>(string,
+ number);
}
} // namespace crashpad
diff --git a/util/stdlib/string_number_conversion.h b/util/stdlib/string_number_conversion.h
index b5f1d44..69e7fdd 100644
--- a/util/stdlib/string_number_conversion.h
+++ b/util/stdlib/string_number_conversion.h
@@ -56,8 +56,10 @@
//! where such prefix recognition is desirable.
bool StringToNumber(const std::string& string, int* number);
bool StringToNumber(const std::string& string, unsigned int* number);
-bool StringToNumber(const std::string& string, int64_t* number);
-bool StringToNumber(const std::string& string, uint64_t* number);
+bool StringToNumber(const std::string& string, long* number);
+bool StringToNumber(const std::string& string, unsigned long* number);
+bool StringToNumber(const std::string& string, long long* number);
+bool StringToNumber(const std::string& string, unsigned long long* number);
//! \}
} // namespace crashpad
diff --git a/util/stdlib/string_number_conversion_test.cc b/util/stdlib/string_number_conversion_test.cc
index d855c8d..760dc4a 100644
--- a/util/stdlib/string_number_conversion_test.cc
+++ b/util/stdlib/string_number_conversion_test.cc
@@ -16,30 +16,39 @@
#include <sys/types.h>
+#include <array>
#include <limits>
+#include <type_traits>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
-namespace crashpad {
-namespace test {
-namespace {
+#define STRINGIFY(a) STR(a)
+#define STR(a) #a
-TEST(StringNumberConversion, StringToInt) {
- static constexpr struct {
- const char* string;
- bool valid;
- int value;
- } kTestData[] = {
+template <typename TValueType>
+struct TestSpecification {
+ const char* string;
+ bool valid;
+ TValueType value;
+};
+
+// Signed 32-bit test data
+template <typename TIntType,
+ typename std::enable_if<std::is_integral<TIntType>::value &&
+ std::is_signed<TIntType>::value &&
+ (sizeof(TIntType) == 4)>::type* = nullptr>
+static constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {
+ return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
- {"2147483647", true, std::numeric_limits<int>::max()},
+ {"2147483647", true, std::numeric_limits<TIntType>::max()},
{"2147483648", false, 0},
{"4294967295", false, 0},
{"4294967296", false, 0},
{"-1", true, -1},
- {"-2147483648", true, std::numeric_limits<int>::min()},
+ {"-2147483648", true, std::numeric_limits<TIntType>::min()},
{"-2147483649", false, 0},
{"00", true, 0},
{"01", true, 1},
@@ -50,12 +59,12 @@
{"+0x20", true, 32},
{"0xf", true, 15},
{"0xg", false, 0},
- {"0x7fffffff", true, std::numeric_limits<int>::max()},
- {"0x7FfFfFfF", true, std::numeric_limits<int>::max()},
+ {"0x7fffffff", true, std::numeric_limits<TIntType>::max()},
+ {"0x7FfFfFfF", true, std::numeric_limits<TIntType>::max()},
{"0x80000000", false, 0},
{"0xFFFFFFFF", false, 0},
{"-0x7fffffff", true, -2147483647},
- {"-0x80000000", true, std::numeric_limits<int>::min()},
+ {"-0x80000000", true, std::numeric_limits<TIntType>::min()},
{"-0x80000001", false, 0},
{"-0xffffffff", false, 0},
{"0x100000000", false, 0},
@@ -92,45 +101,22 @@
{"9223372036854775809", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
- };
-
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
- int value;
- bool valid = StringToNumber(kTestData[index].string, &value);
- if (kTestData[index].valid) {
- EXPECT_TRUE(valid) << "index " << index << ", string "
- << kTestData[index].string;
- if (valid) {
- EXPECT_EQ(value, kTestData[index].value)
- << "index " << index << ", string " << kTestData[index].string;
- }
- } else {
- EXPECT_FALSE(valid) << "index " << index << ", string "
- << kTestData[index].string << ", value " << value;
- }
- }
-
- // Ensure that embedded NUL characters are treated as bad input. The string
- // is split to avoid MSVC warning:
- // "decimal digit terminates octal escape sequence".
- static constexpr char input[] = "6\000" "6";
- std::string input_string(input, arraysize(input) - 1);
- int output;
- EXPECT_FALSE(StringToNumber(input_string, &output));
+ }};
}
-TEST(StringNumberConversion, StringToUnsignedInt) {
- static constexpr struct {
- const char* string;
- bool valid;
- unsigned int value;
- } kTestData[] = {
+// Unsigned 32-bit test data
+template <typename TIntType,
+ typename std::enable_if<std::is_integral<TIntType>::value &&
+ !std::is_signed<TIntType>::value &&
+ (sizeof(TIntType) == 4)>::type* = nullptr>
+static constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {
+ return {{
{"", false, 0},
{"0", true, 0},
{"1", true, 1},
{"2147483647", true, 2147483647},
{"2147483648", true, 2147483648},
- {"4294967295", true, std::numeric_limits<unsigned int>::max()},
+ {"4294967295", true, std::numeric_limits<TIntType>::max()},
{"4294967296", false, 0},
{"-1", false, 0},
{"-2147483648", false, 0},
@@ -186,9 +172,121 @@
{"9223372036854775809", false, 0},
{"18446744073709551615", false, 0},
{"18446744073709551616", false, 0},
- };
+ }};
+}
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
+// Signed 64-bit test data
+template <typename TIntType,
+ typename std::enable_if<std::is_integral<TIntType>::value &&
+ std::is_signed<TIntType>::value &&
+ (sizeof(TIntType) == 8)>::type* = nullptr>
+static constexpr std::array<TestSpecification<TIntType>, 24> kTestDataFunc() {
+ return {{
+ {"", false, 0},
+ {"0", true, 0},
+ {"1", true, 1},
+ {"2147483647", true, 2147483647},
+ {"2147483648", true, 2147483648},
+ {"4294967295", true, 4294967295},
+ {"4294967296", true, 4294967296},
+ {"9223372036854775807", true, std::numeric_limits<TIntType>::max()},
+ {"9223372036854775808", false, 0},
+ {"18446744073709551615", false, 0},
+ {"18446744073709551616", false, 0},
+ {"-1", true, -1},
+ {"-2147483648", true, INT64_C(-2147483648)},
+ {"-2147483649", true, INT64_C(-2147483649)},
+ {"-9223372036854775808", true, std::numeric_limits<TIntType>::min()},
+ {"-9223372036854775809", false, 0},
+ {"0x7fffffffffffffff", true, std::numeric_limits<TIntType>::max()},
+ {"0x8000000000000000", false, 0},
+ {"0xffffffffffffffff", false, 0},
+ {"0x10000000000000000", false, 0},
+ {"-0x7fffffffffffffff", true, -9223372036854775807},
+ {"-0x8000000000000000", true, std::numeric_limits<TIntType>::min()},
+ {"-0x8000000000000001", false, 0},
+ {"0x7Fffffffffffffff", true, std::numeric_limits<TIntType>::max()},
+ }};
+}
+
+// Unsigned 64-bit test data
+template <typename TIntType,
+ typename std::enable_if<std::is_integral<TIntType>::value &&
+ !std::is_signed<TIntType>::value &&
+ (sizeof(TIntType) == 8)>::type* = nullptr>
+static constexpr std::array<TestSpecification<TIntType>, 25> kTestDataFunc() {
+ return {{
+ {"", false, 0},
+ {"0", true, 0},
+ {"1", true, 1},
+ {"2147483647", true, 2147483647},
+ {"2147483648", true, 2147483648},
+ {"4294967295", true, 4294967295},
+ {"4294967296", true, 4294967296},
+ {"9223372036854775807", true, 9223372036854775807},
+ {"9223372036854775808", true, 9223372036854775808u},
+ {"18446744073709551615", true, std::numeric_limits<TIntType>::max()},
+ {"18446744073709551616", false, 0},
+ {"-1", false, 0},
+ {"-2147483648", false, 0},
+ {"-2147483649", false, 0},
+ {"-2147483648", false, 0},
+ {"-9223372036854775808", false, 0},
+ {"-9223372036854775809", false, 0},
+ {"0x7fffffffffffffff", true, 9223372036854775807},
+ {"0x8000000000000000", true, 9223372036854775808u},
+ {"0xffffffffffffffff", true, std::numeric_limits<TIntType>::max()},
+ {"0x10000000000000000", false, 0},
+ {"-0x7fffffffffffffff", false, 0},
+ {"-0x8000000000000000", false, 0},
+ {"-0x8000000000000001", false, 0},
+ {"0xFfffffffffffffff", true, std::numeric_limits<TIntType>::max()},
+ }};
+}
+
+// This string is split to avoid MSVC warning:
+// "decimal digit terminates octal escape sequence".
+static constexpr char kEmbeddedNullInputRaw[] = "6\000" "6";
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(StringNumberConversion, StringToInt) {
+ static_assert(sizeof(int) == 4, "Test only configured for 32-bit int.");
+ static constexpr auto kTestData = kTestDataFunc<int>();
+
+ for (size_t index = 0; index < kTestData.size(); ++index) {
+ int value;
+ bool valid = StringToNumber(kTestData[index].string, &value);
+ if (kTestData[index].valid) {
+ EXPECT_TRUE(valid) << "index " << index << ", string "
+ << kTestData[index].string;
+ if (valid) {
+ EXPECT_EQ(value, kTestData[index].value)
+ << "index " << index << ", string " << kTestData[index].string;
+ }
+ } else {
+ EXPECT_FALSE(valid) << "index " << index << ", string "
+ << kTestData[index].string << ", value " << value;
+ }
+ }
+
+ // Ensure that embedded NUL characters are treated as bad input. The string
+ // is split to avoid MSVC warning:
+ // "decimal digit terminates octal escape sequence".
+ int output;
+ std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,
+ base::size(kEmbeddedNullInputRaw) - 1);
+ EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));
+}
+
+TEST(StringNumberConversion, StringToUnsignedInt) {
+ static_assert(sizeof(unsigned int) == 4,
+ "Test only configured for 32-bit unsigned int.");
+ static constexpr auto kTestData = kTestDataFunc<unsigned int>();
+
+ for (size_t index = 0; index < kTestData.size(); ++index) {
unsigned int value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
@@ -207,46 +305,20 @@
// Ensure that embedded NUL characters are treated as bad input. The string
// is split to avoid MSVC warning:
// "decimal digit terminates octal escape sequence".
- static constexpr char input[] = "6\000" "6";
- std::string input_string(input, arraysize(input) - 1);
unsigned int output;
- EXPECT_FALSE(StringToNumber(input_string, &output));
+ std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,
+ base::size(kEmbeddedNullInputRaw) - 1);
+ EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));
}
-TEST(StringNumberConversion, StringToInt64) {
- static constexpr struct {
- const char* string;
- bool valid;
- int64_t value;
- } kTestData[] = {
- {"", false, 0},
- {"0", true, 0},
- {"1", true, 1},
- {"2147483647", true, 2147483647},
- {"2147483648", true, 2147483648},
- {"4294967295", true, 4294967295},
- {"4294967296", true, 4294967296},
- {"9223372036854775807", true, std::numeric_limits<int64_t>::max()},
- {"9223372036854775808", false, 0},
- {"18446744073709551615", false, 0},
- {"18446744073709551616", false, 0},
- {"-1", true, -1},
- {"-2147483648", true, INT64_C(-2147483648)},
- {"-2147483649", true, INT64_C(-2147483649)},
- {"-9223372036854775808", true, std::numeric_limits<int64_t>::min()},
- {"-9223372036854775809", false, 0},
- {"0x7fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
- {"0x8000000000000000", false, 0},
- {"0xffffffffffffffff", false, 0},
- {"0x10000000000000000", false, 0},
- {"-0x7fffffffffffffff", true, -9223372036854775807},
- {"-0x8000000000000000", true, std::numeric_limits<int64_t>::min()},
- {"-0x8000000000000001", false, 0},
- {"0x7Fffffffffffffff", true, std::numeric_limits<int64_t>::max()},
- };
+TEST(StringNumberConversion, StringToLong) {
+ static_assert(
+ sizeof(long) == 4 || sizeof(long) == 8,
+ "Test not configured for " STRINGIFY(__SIZEOF_LONG__) "-byte long");
+ static constexpr auto kTestData = kTestDataFunc<long>();
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
- int64_t value;
+ for (size_t index = 0; index < kTestData.size(); ++index) {
+ long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
@@ -262,41 +334,58 @@
}
}
-TEST(StringNumberConversion, StringToUnsignedInt64) {
- static constexpr struct {
- const char* string;
- bool valid;
- uint64_t value;
- } kTestData[] = {
- {"", false, 0},
- {"0", true, 0},
- {"1", true, 1},
- {"2147483647", true, 2147483647},
- {"2147483648", true, 2147483648},
- {"4294967295", true, 4294967295},
- {"4294967296", true, 4294967296},
- {"9223372036854775807", true, 9223372036854775807},
- {"9223372036854775808", true, 9223372036854775808u},
- {"18446744073709551615", true, std::numeric_limits<uint64_t>::max()},
- {"18446744073709551616", false, 0},
- {"-1", false, 0},
- {"-2147483648", false, 0},
- {"-2147483649", false, 0},
- {"-2147483648", false, 0},
- {"-9223372036854775808", false, 0},
- {"-9223372036854775809", false, 0},
- {"0x7fffffffffffffff", true, 9223372036854775807},
- {"0x8000000000000000", true, 9223372036854775808u},
- {"0xffffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
- {"0x10000000000000000", false, 0},
- {"-0x7fffffffffffffff", false, 0},
- {"-0x8000000000000000", false, 0},
- {"-0x8000000000000001", false, 0},
- {"0xFfffffffffffffff", true, std::numeric_limits<uint64_t>::max()},
- };
+TEST(StringNumberConversion, StringToUnsignedLong) {
+ static_assert(
+ sizeof(long) == 4 || sizeof(long) == 8,
+ "Test not configured for " STRINGIFY(__SIZEOF_LONG__) "-byte long");
+ static constexpr auto kTestData = kTestDataFunc<unsigned long>();
- for (size_t index = 0; index < arraysize(kTestData); ++index) {
- uint64_t value;
+ for (size_t index = 0; index < kTestData.size(); ++index) {
+ unsigned long value;
+ bool valid = StringToNumber(kTestData[index].string, &value);
+ if (kTestData[index].valid) {
+ EXPECT_TRUE(valid) << "index " << index << ", string "
+ << kTestData[index].string;
+ if (valid) {
+ EXPECT_EQ(value, kTestData[index].value)
+ << "index " << index << ", string " << kTestData[index].string;
+ }
+ } else {
+ EXPECT_FALSE(valid) << "index " << index << ", string "
+ << kTestData[index].string << ", value " << value;
+ }
+ }
+}
+
+TEST(StringNumberConversion, StringToLongLong) {
+ static_assert(sizeof(long long) == 8,
+ "Test only configured for 64-bit long long.");
+ static constexpr auto kTestData = kTestDataFunc<long long>();
+
+ for (size_t index = 0; index < kTestData.size(); ++index) {
+ long long value;
+ bool valid = StringToNumber(kTestData[index].string, &value);
+ if (kTestData[index].valid) {
+ EXPECT_TRUE(valid) << "index " << index << ", string "
+ << kTestData[index].string;
+ if (valid) {
+ EXPECT_EQ(value, kTestData[index].value)
+ << "index " << index << ", string " << kTestData[index].string;
+ }
+ } else {
+ EXPECT_FALSE(valid) << "index " << index << ", string "
+ << kTestData[index].string << ", value " << value;
+ }
+ }
+}
+
+TEST(StringNumberConversion, StringToUnsignedLongLong) {
+ static_assert(sizeof(unsigned long long) == 8,
+ "Test only configured for 64-bit unsigned long long.");
+ static constexpr auto kTestData = kTestDataFunc<unsigned long long>();
+
+ for (size_t index = 0; index < kTestData.size(); ++index) {
+ unsigned long long value;
bool valid = StringToNumber(kTestData[index].string, &value);
if (kTestData[index].valid) {
EXPECT_TRUE(valid) << "index " << index << ", string "
diff --git a/util/stdlib/strlcpy_test.cc b/util/stdlib/strlcpy_test.cc
index 5d20e19..bfb00fe 100644
--- a/util/stdlib/strlcpy_test.cc
+++ b/util/stdlib/strlcpy_test.cc
@@ -20,7 +20,7 @@
#include <algorithm>
#include "base/format_macros.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
@@ -64,7 +64,7 @@
static constexpr base::char16 test_characters[] =
{0x4d, 0xe9, 0x100, 0x151, 0x1e18};
- for (size_t index = 0; index < arraysize(test_characters); ++index) {
+ for (size_t index = 0; index < base::size(test_characters); ++index) {
base::char16 test_character = test_characters[index];
SCOPED_TRACE(base::StringPrintf(
"character index %" PRIuS ", character 0x%x", index, test_character));
@@ -78,13 +78,13 @@
EXPECT_EQ(c16lcpy(destination.data,
test_string.c_str(),
- arraysize(destination.data)),
+ base::size(destination.data)),
length);
// Make sure that the destination buffer is NUL-terminated, and that as
// much of the test string was copied as could fit.
size_t expected_destination_length =
- std::min(length, arraysize(destination.data) - 1);
+ std::min(length, base::size(destination.data) - 1);
EXPECT_EQ(destination.data[expected_destination_length], '\0');
EXPECT_EQ(C16Len(destination.data), expected_destination_length);
@@ -97,15 +97,15 @@
// of the buffer passed to c16lcpy.
EXPECT_TRUE(C16Memcmp(expected_untouched.lead_guard,
destination.lead_guard,
- arraysize(destination.lead_guard)) == 0);
+ base::size(destination.lead_guard)) == 0);
size_t expected_untouched_length =
- arraysize(destination.data) - expected_destination_length - 1;
+ base::size(destination.data) - expected_destination_length - 1;
EXPECT_TRUE(C16Memcmp(expected_untouched.data,
&destination.data[expected_destination_length + 1],
expected_untouched_length) == 0);
EXPECT_TRUE(C16Memcmp(expected_untouched.trail_guard,
destination.trail_guard,
- arraysize(destination.trail_guard)) == 0);
+ base::size(destination.trail_guard)) == 0);
}
}
}
diff --git a/util/stdlib/strnlen.cc b/util/stdlib/strnlen.cc
index a238728..7ef8d3b 100644
--- a/util/stdlib/strnlen.cc
+++ b/util/stdlib/strnlen.cc
@@ -14,7 +14,8 @@
#include "util/stdlib/strnlen.h"
-#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+#if defined(OS_MACOSX) && !defined(OS_IOS) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
// Redeclare a method only available on Mac OS X 10.7 and later to suppress a
diff --git a/util/stdlib/strnlen.h b/util/stdlib/strnlen.h
index e85d8c7..1db5f6e 100644
--- a/util/stdlib/strnlen.h
+++ b/util/stdlib/strnlen.h
@@ -20,7 +20,7 @@
#include "build/build_config.h"
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) && !defined(OS_IOS)
#include <AvailabilityMacros.h>
#endif
@@ -38,7 +38,7 @@
//! and not all systems’ standard libraries provide an implementation.
size_t strnlen(const char* string, size_t max_length);
-#if !defined(OS_MACOSX) || \
+#if !defined(OS_MACOSX) || defined(OS_IOS) || \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7
inline size_t strnlen(const char* string, size_t max_length) {
return ::strnlen(string, max_length);
diff --git a/util/stdlib/thread_safe_vector_test.cc b/util/stdlib/thread_safe_vector_test.cc
index 805360f..1d26b09 100644
--- a/util/stdlib/thread_safe_vector_test.cc
+++ b/util/stdlib/thread_safe_vector_test.cc
@@ -14,6 +14,7 @@
#include "util/stdlib/thread_safe_vector.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "util/thread/thread.h"
@@ -53,12 +54,12 @@
EXPECT_TRUE(vector.empty());
ThreadSafeVectorTestThread threads[100];
- for (size_t index = 0; index < arraysize(threads); ++index) {
+ for (size_t index = 0; index < base::size(threads); ++index) {
threads[index].SetTestParameters(
&thread_safe_vector, static_cast<int>(index * kElementsPerThread));
}
- for (size_t index = 0; index < arraysize(threads); ++index) {
+ for (size_t index = 0; index < base::size(threads); ++index) {
threads[index].Start();
if (index % 10 == 0) {
@@ -75,8 +76,8 @@
std::vector<int> drained = thread_safe_vector.Drain();
vector.insert(vector.end(), drained.begin(), drained.end());
- bool found[arraysize(threads) * kElementsPerThread] = {};
- EXPECT_EQ(vector.size(), arraysize(found));
+ bool found[base::size(threads) * kElementsPerThread] = {};
+ EXPECT_EQ(vector.size(), base::size(found));
for (int element : vector) {
EXPECT_FALSE(found[element]) << element;
found[element] = true;
diff --git a/util/stream/base94_output_stream.cc b/util/stream/base94_output_stream.cc
new file mode 100644
index 0000000..a47d774
--- /dev/null
+++ b/util/stream/base94_output_stream.cc
@@ -0,0 +1,173 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/base94_output_stream.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace crashpad {
+
+namespace {
+// To improve the space efficiency, we can encode 14 bits into two symbols
+// if 14-bit number is less than 94^2 which is total number can be encoded
+// by 2 digits of base94 number, in another word, if 13 bit number is
+// smaller than 643, we could read one more bit, because even if the 14th
+// bit is 1, the 14-bit number doesn’t exceed the max value.
+constexpr uint16_t kMaxValueOf14BitEncoding = (94 * 94 - 1) & 0x1FFF;
+
+constexpr size_t kMaxBuffer = 4096;
+
+inline uint8_t EncodeByte(uint8_t byte) {
+ DCHECK(byte < 94);
+ return (byte >= 94u) ? 0xff : (byte + '!');
+}
+
+inline uint8_t DecodeByte(uint8_t byte) {
+ DCHECK(byte >= '!' && byte <= '~');
+ return std::min(static_cast<uint8_t>(byte - '!'), static_cast<uint8_t>(94));
+}
+
+} // namespace
+
+Base94OutputStream::Base94OutputStream(
+ Mode mode,
+ std::unique_ptr<OutputStreamInterface> output_stream)
+ : mode_(mode),
+ output_stream_(std::move(output_stream)),
+ bit_buf_(0),
+ bit_count_(0),
+ symbol_buffer_(0),
+ flush_needed_(false),
+ flushed_(false) {
+ buffer_.reserve(kMaxBuffer);
+}
+
+Base94OutputStream::~Base94OutputStream() {
+ DCHECK(!flush_needed_);
+}
+
+bool Base94OutputStream::Write(const uint8_t* data, size_t size) {
+ DCHECK(!flushed_);
+ flush_needed_ = true;
+ return mode_ == Mode::kEncode ? Encode(data, size) : Decode(data, size);
+}
+
+bool Base94OutputStream::Flush() {
+ flushed_ = true;
+ if (flush_needed_) {
+ flush_needed_ = false;
+ if (!((mode_ == Mode::kEncode) ? FinishEncoding() : FinishDecoding()))
+ return false;
+ }
+ return output_stream_->Flush();
+}
+
+bool Base94OutputStream::Encode(const uint8_t* data, size_t size) {
+ const uint8_t* cur = data;
+ while (size--) {
+ bit_buf_ |= *(cur++) << bit_count_;
+ bit_count_ += 8;
+ if (bit_count_ < 14)
+ continue;
+
+ uint16_t block;
+ // Check if 13-bit or 14-bit data should be encoded.
+ if ((bit_buf_ & 0x1FFF) > kMaxValueOf14BitEncoding) {
+ block = bit_buf_ & 0x1FFF;
+ bit_buf_ >>= 13;
+ bit_count_ -= 13;
+ } else {
+ block = bit_buf_ & 0x3FFF;
+ bit_buf_ >>= 14;
+ bit_count_ -= 14;
+ }
+ buffer_.push_back(EncodeByte(block % 94));
+ buffer_.push_back(EncodeByte(base::saturated_cast<uint8_t>(block / 94)));
+
+ if (buffer_.size() > kMaxBuffer - 2 && !WriteOutputStream())
+ return false;
+ }
+ return WriteOutputStream();
+}
+
+bool Base94OutputStream::Decode(const uint8_t* data, size_t size) {
+ const uint8_t* cur = data;
+ while (size--) {
+ if (DecodeByte(*cur) == 94) {
+ LOG(ERROR) << "Decode: invalid input";
+ return false;
+ }
+ if (symbol_buffer_ == 0) {
+ symbol_buffer_ = *cur;
+ cur++;
+ continue;
+ }
+ uint16_t v = DecodeByte(symbol_buffer_) + DecodeByte(*cur) * 94;
+ cur++;
+ symbol_buffer_ = 0;
+ bit_buf_ |= v << bit_count_;
+ bit_count_ += (v & 0x1FFF) > kMaxValueOf14BitEncoding ? 13 : 14;
+ while (bit_count_ > 7) {
+ buffer_.push_back(bit_buf_ & 0xff);
+ bit_buf_ >>= 8;
+ bit_count_ -= 8;
+ }
+ if (buffer_.size() > kMaxBuffer - 2) {
+ if (!WriteOutputStream())
+ return false;
+ }
+ }
+ return WriteOutputStream();
+}
+
+bool Base94OutputStream::FinishEncoding() {
+ if (bit_count_ == 0)
+ return true;
+ // Up to 13 bits data is left over.
+ buffer_.push_back(EncodeByte(bit_buf_ % 94));
+ if (bit_buf_ > 93 || bit_count_ > 8)
+ buffer_.push_back(EncodeByte(base::saturated_cast<uint8_t>(bit_buf_ / 94)));
+ bit_count_ = 0;
+ bit_buf_ = 0;
+ return WriteOutputStream();
+}
+
+bool Base94OutputStream::FinishDecoding() {
+ // The left over bit is padding and all zero, if there is no symbol
+ // unprocessed.
+ if (symbol_buffer_ == 0) {
+ DCHECK(!bit_buf_);
+ return true;
+ }
+ bit_buf_ |= DecodeByte(symbol_buffer_) << bit_count_;
+ buffer_.push_back(bit_buf_ & 0xff);
+ bit_buf_ >>= 8;
+ // The remaining bits are either encode padding or zeros from bit shift.
+ DCHECK(!bit_buf_);
+ return WriteOutputStream();
+}
+
+bool Base94OutputStream::WriteOutputStream() {
+ if (buffer_.empty())
+ return true;
+
+ bool result = output_stream_->Write(buffer_.data(), buffer_.size());
+ buffer_.clear();
+ return result;
+}
+
+} // namespace crashpad
diff --git a/util/stream/base94_output_stream.h b/util/stream/base94_output_stream.h
new file mode 100644
index 0000000..9d27b2a
--- /dev/null
+++ b/util/stream/base94_output_stream.h
@@ -0,0 +1,79 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_
+#define CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+
+//! \brief This class implements Base94 encoding/decoding, it uses all
+//! printable characters except space for encoding, and no padding is required.
+//!
+//! This implementation uses two base94 symbols to encoding 13 or 14 bit data,
+//! To maximize encoding efficiency, 14-bit data is encoded into two base94
+//! symbols if its low 13-bit is less than 644 ( = 94^2 - 2^13), otherwise
+//! 13-bit data is encoded.
+class Base94OutputStream : public OutputStreamInterface {
+ public:
+ //! \brief Whether this object is configured to encode or decode data.
+ enum class Mode : bool {
+ //! \brief Data passed through this object is encoded.
+ kEncode = false,
+ //! \brief Data passed through this object is decoded.
+ kDecode = true
+ };
+
+ //! \param[in] mode The work mode of this object.
+ //! \param[in] output_stream The output_stream that this object writes to.
+ Base94OutputStream(Mode mode,
+ std::unique_ptr<OutputStreamInterface> output_stream);
+ ~Base94OutputStream() override;
+
+ // OutputStreamInterface:
+ bool Write(const uint8_t* data, size_t size) override;
+ bool Flush() override;
+
+ private:
+ bool Encode(const uint8_t* data, size_t size);
+ bool Decode(const uint8_t* data, size_t size);
+ bool FinishEncoding();
+ bool FinishDecoding();
+ // Write encoded/decoded data to |output_stream_| and empty the |buffer_|.
+ bool WriteOutputStream();
+
+ Mode mode_;
+ std::unique_ptr<OutputStreamInterface> output_stream_;
+ std::vector<uint8_t> buffer_;
+ uint32_t bit_buf_;
+ // The number of valid bit in bit_buf_.
+ size_t bit_count_;
+ char symbol_buffer_;
+ bool flush_needed_;
+ bool flushed_;
+
+ DISALLOW_COPY_AND_ASSIGN(Base94OutputStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_
diff --git a/util/stream/base94_output_stream_test.cc b/util/stream/base94_output_stream_test.cc
new file mode 100644
index 0000000..32decbb
--- /dev/null
+++ b/util/stream/base94_output_stream_test.cc
@@ -0,0 +1,290 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/base94_output_stream.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <sstream>
+
+#include "base/macros.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/stream/test_output_stream.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr size_t kLongDataLength = 4096 * 10;
+
+std::string DumpInput(const uint8_t* input, size_t size) {
+ std::stringstream s;
+ size_t index = 0;
+ size_t byte_count = 0;
+ while (index < size) {
+ s << "0x" << std::hex << static_cast<int>(*(input + index++)) << ",";
+ if (byte_count++ > 1024) {
+ s << "\n";
+ byte_count = 0;
+ }
+ }
+ return s.str();
+}
+
+class Base94OutputStreamTest : public testing::Test {
+ public:
+ Base94OutputStreamTest() {}
+
+ protected:
+ void SetUp() override {
+ auto output_stream = std::make_unique<TestOutputStream>();
+ encode_test_output_stream_ = output_stream.get();
+ encoder_ = std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kEncode, std::move(output_stream));
+ output_stream = std::make_unique<TestOutputStream>();
+ decode_test_output_stream_ = output_stream.get();
+ decoder_ = std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kDecode, std::move(output_stream));
+ output_stream = std::make_unique<TestOutputStream>();
+ round_trip_test_output_stream_ = output_stream.get();
+ round_trip_ = std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kEncode,
+ std::make_unique<Base94OutputStream>(Base94OutputStream::Mode::kDecode,
+ std::move(output_stream)));
+ }
+
+ const uint8_t* BuildDeterministicInput(size_t size) {
+ deterministic_input_ = std::make_unique<uint8_t[]>(size);
+ uint8_t* deterministic_input_base = deterministic_input_.get();
+ while (size-- > 0)
+ deterministic_input_base[size] = static_cast<uint8_t>(size);
+ return deterministic_input_base;
+ }
+
+ const uint8_t* BuildRandomInput(size_t size) {
+ input_ = std::make_unique<uint8_t[]>(size);
+ base::RandBytes(&input_[0], size);
+ return input_.get();
+ }
+
+ Base94OutputStream* round_trip() const { return round_trip_.get(); }
+ const TestOutputStream& round_trip_test_output_stream() const {
+ return *round_trip_test_output_stream_;
+ }
+
+ static void VerifyEncoding(const TestOutputStream& out,
+ const std::string& expected) {
+ EXPECT_EQ(out.all_data().size(), expected.size());
+ EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
+ 0);
+ }
+
+ static void VerifyDecoding(const TestOutputStream& out,
+ const std::vector<uint8_t>& expected) {
+ EXPECT_EQ(out.all_data().size(), expected.size());
+ EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),
+ 0);
+ }
+
+ void RunTest(const std::string& text, const std::vector<uint8_t>& binary) {
+ EXPECT_TRUE(encoder_->Write(binary.data(), binary.size()));
+ EXPECT_TRUE(encoder_->Flush());
+ VerifyEncoding(*encode_test_output_stream_, text);
+ EXPECT_TRUE(decoder_->Write(reinterpret_cast<const uint8_t*>(text.data()),
+ text.size()));
+ EXPECT_TRUE(decoder_->Flush());
+ VerifyDecoding(*decode_test_output_stream_, binary);
+ }
+
+ void VerifyRoundTrip(const std::vector<uint8_t>& expected) {
+ TestOutputStream* out = round_trip_test_output_stream_;
+ EXPECT_EQ(out->all_data().size(), expected.size());
+ EXPECT_EQ(memcmp(out->all_data().data(), expected.data(), expected.size()),
+ 0);
+ }
+
+ private:
+ std::unique_ptr<Base94OutputStream> encoder_;
+ std::unique_ptr<Base94OutputStream> decoder_;
+ std::unique_ptr<Base94OutputStream> round_trip_;
+ TestOutputStream* encode_test_output_stream_;
+ TestOutputStream* decode_test_output_stream_;
+ TestOutputStream* round_trip_test_output_stream_;
+ std::unique_ptr<uint8_t[]> input_;
+ std::unique_ptr<uint8_t[]> deterministic_input_;
+
+ DISALLOW_COPY_AND_ASSIGN(Base94OutputStreamTest);
+};
+
+TEST_F(Base94OutputStreamTest, Encoding) {
+ std::vector<uint8_t> binary = {0x0};
+ std::string text("!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding1) {
+ std::vector<uint8_t> binary = {0x0, 0x0};
+ std::string text("!!!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding2) {
+ std::vector<uint8_t> binary = {0x0, 0x0, 0x0};
+ std::string text("!!!!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding3) {
+ std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0};
+ std::string text("!!!!!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding4) {
+ std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0, 0x0};
+ std::string text("!!!!!!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding10) {
+ std::vector<uint8_t> binary = {0xFF};
+ std::string text("d#");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding11) {
+ std::vector<uint8_t> binary = {0xFF, 0xFF};
+ std::string text(".x(");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding12) {
+ std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF};
+ std::string text(".xj6");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding13) {
+ std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF};
+ std::string text(".x.x`");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Encoding14) {
+ std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ std::string text(".x.x.x\"");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, Printable94) {
+ std::vector<uint8_t> binary = {
+ 0x5e, 0x0, 0x47, 0xa0, 0x1d, 0x60, 0xa, 0xab, 0x41, 0x41, 0xa4, 0x9,
+ 0x64, 0x71, 0x32, 0xc, 0x47, 0xf9, 0x20, 0x22, 0xa3, 0x44, 0xa0, 0x84,
+ 0x15, 0xe0, 0xf2, 0x61, 0xfc, 0x4c, 0xb7, 0xe1, 0x39, 0x9b, 0x47, 0xff,
+ 0x64, 0x21, 0x5c, 0x74, 0x91, 0xec, 0x52, 0x75, 0xa2, 0x51, 0x93, 0x4a,
+ 0x5e, 0x45, 0x2d, 0xd8, 0xf5, 0xc0, 0xdc, 0x58, 0x33, 0x63, 0x69, 0x8b,
+ 0x4d, 0xbd, 0x25, 0x39, 0x54, 0x77, 0xf0, 0xcc, 0x5e, 0xf1, 0x23, 0x81,
+ 0x6, 0x21, 0x71, 0x28, 0x28, 0x2};
+ std::string text(
+ "!\"#$%&'()*+,-./"
+ "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
+ "abcdefghijklmnopqrstuvwxyz{|}~!");
+ RunTest(text, binary);
+}
+
+TEST_F(Base94OutputStreamTest, WriteLongDataMultipleTimes) {
+ const uint8_t* input = BuildRandomInput(kLongDataLength);
+ SCOPED_TRACE(base::StringPrintf("Input: %s",
+ DumpInput(input, kLongDataLength).c_str()));
+ // Call Write() a random number of times.
+ size_t index = 0;
+ while (index < kLongDataLength) {
+ size_t write_length =
+ std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),
+ kLongDataLength - index);
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, write_length %zu", index, write_length));
+ EXPECT_TRUE(round_trip()->Write(input + index, write_length));
+ index += write_length;
+ }
+ EXPECT_TRUE(round_trip()->Flush());
+ EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
+ input,
+ kLongDataLength),
+ 0);
+}
+
+TEST_F(Base94OutputStreamTest, WriteDeterministicLongDataMultipleTimes) {
+ const uint8_t* input = BuildDeterministicInput(kLongDataLength);
+
+ static constexpr size_t kWriteLengths[] = {
+ 4, 96, 40, kLongDataLength - 4 - 96 - 40};
+
+ size_t offset = 0;
+ for (size_t index = 0; index < base::size(kWriteLengths); ++index) {
+ const size_t write_length = kWriteLengths[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "offset %zu, write_length %zu", offset, write_length));
+ EXPECT_TRUE(round_trip()->Write(input + offset, write_length));
+ offset += write_length;
+ }
+ EXPECT_TRUE(round_trip()->Flush());
+ EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),
+ input,
+ kLongDataLength),
+ 0);
+}
+
+TEST_F(Base94OutputStreamTest, NoWriteOrFlush) {
+ EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
+ EXPECT_EQ(round_trip_test_output_stream().flush_count(), 0u);
+ EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
+}
+
+TEST_F(Base94OutputStreamTest, FlushWithoutWrite) {
+ EXPECT_TRUE(round_trip()->Flush());
+ EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
+ EXPECT_EQ(round_trip_test_output_stream().flush_count(), 1u);
+ EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
+}
+
+TEST_F(Base94OutputStreamTest, WriteEmptyData) {
+ std::vector<uint8_t> empty_data;
+ EXPECT_TRUE(round_trip()->Write(
+ static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));
+ EXPECT_TRUE(round_trip()->Flush());
+ EXPECT_TRUE(round_trip()->Flush());
+ EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);
+ EXPECT_EQ(round_trip_test_output_stream().flush_count(), 2u);
+ EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());
+}
+
+TEST_F(Base94OutputStreamTest, Process7bitsInFinishDecoding) {
+ std::vector<uint8_t> input = {
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ EXPECT_TRUE(round_trip()->Write(static_cast<const uint8_t*>(input.data()),
+ input.size()));
+ EXPECT_TRUE(round_trip()->Flush());
+ VerifyRoundTrip(input);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/stream/file_encoder.cc b/util/stream/file_encoder.cc
new file mode 100644
index 0000000..bb2c41a
--- /dev/null
+++ b/util/stream/file_encoder.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/file_encoder.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "util/file/file_io.h"
+#include "util/file/file_reader.h"
+#include "util/file/scoped_remove_file.h"
+#include "util/stream/base94_output_stream.h"
+#include "util/stream/file_output_stream.h"
+#include "util/stream/output_stream_interface.h"
+#include "util/stream/zlib_output_stream.h"
+
+namespace crashpad {
+
+FileEncoder::FileEncoder(Mode mode,
+ const base::FilePath& input_path,
+ const base::FilePath& output_path)
+ : mode_(mode), input_path_(input_path), output_path_(output_path) {}
+
+FileEncoder::~FileEncoder() {}
+
+bool FileEncoder::Process() {
+ ScopedRemoveFile file_remover;
+ ScopedFileHandle write_handle(LoggingOpenFileForWrite(
+ output_path_, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));
+ if (!write_handle.is_valid())
+ return false;
+
+ // Remove the output file on failure.
+ file_remover.reset(output_path_);
+
+ std::unique_ptr<OutputStreamInterface> output;
+ if (mode_ == Mode::kEncode) {
+ output = std::make_unique<ZlibOutputStream>(
+ ZlibOutputStream::Mode::kCompress,
+ std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kEncode,
+ std::make_unique<FileOutputStream>(write_handle.get())));
+ } else {
+ output = std::make_unique<Base94OutputStream>(
+ Base94OutputStream::Mode::kDecode,
+ std::make_unique<ZlibOutputStream>(
+ ZlibOutputStream::Mode::kDecompress,
+ std::make_unique<FileOutputStream>(write_handle.get())));
+ }
+
+ FileReader file_reader;
+ if (!file_reader.Open(input_path_))
+ return false;
+
+ FileOperationResult read_result;
+ do {
+ uint8_t buffer[4096];
+ read_result = file_reader.Read(buffer, sizeof(buffer));
+ if (read_result < 0)
+ return false;
+
+ if (read_result > 0 && (!output->Write(buffer, read_result)))
+ return false;
+ } while (read_result > 0);
+
+ if (!output->Flush())
+ return false;
+
+ ignore_result(file_remover.release());
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/stream/file_encoder.h b/util/stream/file_encoder.h
new file mode 100644
index 0000000..2f34b0b
--- /dev/null
+++ b/util/stream/file_encoder.h
@@ -0,0 +1,59 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_
+#define CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace crashpad {
+
+//! \brief The class is used to compress and base94-encode, or base94-decode
+//! and decompress the given input file to the output file.
+class FileEncoder {
+ public:
+ //! \brief Whether this object is configured to encode or decode data.
+ enum class Mode : bool {
+ //! \brief Data passed through this object is encoded.
+ kEncode = false,
+ //! \brief Data passed through this object is decoded.
+ kDecode = true
+ };
+
+ //! \param[in] mode The work mode of this object.
+ //! \param[in] input_path The input file that this object reads from.
+ //! \param[in] output_path The output file that this object writes to.
+ FileEncoder(Mode mode,
+ const base::FilePath& input_path,
+ const base::FilePath& output_path);
+ ~FileEncoder();
+
+ //! \brief Encode/decode the data from \a input_path_ file according work
+ //! \a mode, and write the result to \a output_path_ on success.
+ //!
+ //! \return `true` on success.
+ bool Process();
+
+ private:
+ Mode mode_;
+ base::FilePath input_path_;
+ base::FilePath output_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileEncoder);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_
diff --git a/util/stream/file_encoder_test.cc b/util/stream/file_encoder_test.cc
new file mode 100644
index 0000000..ff98914
--- /dev/null
+++ b/util/stream/file_encoder_test.cc
@@ -0,0 +1,112 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/file_encoder.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "gtest/gtest.h"
+#include "test/scoped_temp_dir.h"
+#include "util/file/file_io.h"
+#include "util/stream/file_output_stream.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr size_t kBufferSize = 4096;
+
+class FileEncoderTest : public testing::Test {
+ public:
+ FileEncoderTest() {}
+
+ void Verify(size_t size) {
+ std::string contents;
+ ASSERT_TRUE(LoggingReadEntireFile(decoded_, &contents));
+ ASSERT_EQ(contents.size(), size);
+ EXPECT_EQ(memcmp(deterministic_input_.get(), contents.data(), size), 0);
+ }
+
+ const uint8_t* BuildDeterministicInput(size_t size) {
+ deterministic_input_ = std::make_unique<uint8_t[]>(size);
+ uint8_t* deterministic_input_base = deterministic_input_.get();
+ while (size-- > 0)
+ deterministic_input_base[size] = static_cast<uint8_t>(size);
+ return deterministic_input_base;
+ }
+
+ void GenerateOrigFile(size_t size) {
+ ScopedFileHandle write_handle(OpenFileForWrite(
+ orig_, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));
+ ASSERT_TRUE(write_handle.is_valid());
+ FileOutputStream out(write_handle.get());
+ const uint8_t* buf = BuildDeterministicInput(size);
+ while (size > 0) {
+ size_t m = std::min(kBufferSize, size);
+ ASSERT_TRUE(out.Write(buf, m));
+ size -= m;
+ buf += m;
+ }
+ ASSERT_TRUE(out.Flush());
+ }
+
+ FileEncoder* encoder() const { return encoder_.get(); }
+
+ FileEncoder* decoder() const { return decoder_.get(); }
+
+ protected:
+ void SetUp() override {
+ temp_dir_ = std::make_unique<ScopedTempDir>();
+ orig_ = base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL("orig")));
+ encoded_ =
+ base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL("encoded")));
+ decoded_ =
+ base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL("decoded")));
+ encoder_ = std::make_unique<FileEncoder>(
+ FileEncoder::Mode::kEncode, orig_, encoded_);
+ decoder_ = std::make_unique<FileEncoder>(
+ FileEncoder::Mode::kDecode, encoded_, decoded_);
+ }
+
+ private:
+ std::unique_ptr<ScopedTempDir> temp_dir_;
+ base::FilePath orig_;
+ base::FilePath encoded_;
+ base::FilePath decoded_;
+ std::unique_ptr<FileEncoder> encoder_;
+ std::unique_ptr<FileEncoder> decoder_;
+ std::unique_ptr<uint8_t[]> deterministic_input_;
+};
+
+TEST_F(FileEncoderTest, ProcessShortFile) {
+ GenerateOrigFile(kBufferSize - 512);
+ EXPECT_TRUE(encoder()->Process());
+ EXPECT_TRUE(decoder()->Process());
+ Verify(kBufferSize - 512);
+}
+
+TEST_F(FileEncoderTest, ProcessLongFile) {
+ GenerateOrigFile(kBufferSize + 512);
+ EXPECT_TRUE(encoder()->Process());
+ EXPECT_TRUE(decoder()->Process());
+ Verify(kBufferSize + 512);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/stream/file_output_stream.cc b/util/stream/file_output_stream.cc
new file mode 100644
index 0000000..022d91f
--- /dev/null
+++ b/util/stream/file_output_stream.cc
@@ -0,0 +1,45 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/file_output_stream.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+FileOutputStream::FileOutputStream(FileHandle file_handle)
+ : writer_(file_handle), flush_needed_(false), flushed_(false) {}
+
+FileOutputStream::~FileOutputStream() {
+ DCHECK(!flush_needed_);
+}
+
+bool FileOutputStream::Write(const uint8_t* data, size_t size) {
+ DCHECK(!flushed_);
+
+ if (!writer_.Write(data, size)) {
+ LOG(ERROR) << "Write: Failed";
+ return false;
+ }
+ flush_needed_ = true;
+ return true;
+}
+
+bool FileOutputStream::Flush() {
+ flush_needed_ = false;
+ flushed_ = true;
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/stream/file_output_stream.h b/util/stream/file_output_stream.h
new file mode 100644
index 0000000..128ccd5
--- /dev/null
+++ b/util/stream/file_output_stream.h
@@ -0,0 +1,46 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_
+#define CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_
+
+#include "base/macros.h"
+#include "util/file/file_io.h"
+#include "util/file/file_writer.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+
+//! \brief The class is used to write data to a file.
+class FileOutputStream : public OutputStreamInterface {
+ public:
+ //! \param[in] file_handle The file that this object writes to.
+ explicit FileOutputStream(FileHandle file_handle);
+ ~FileOutputStream();
+
+ // OutputStream.
+ bool Write(const uint8_t* data, size_t size) override;
+ bool Flush() override;
+
+ private:
+ WeakFileHandleFileWriter writer_;
+ bool flush_needed_;
+ bool flushed_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileOutputStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_
diff --git a/util/stream/log_output_stream.cc b/util/stream/log_output_stream.cc
new file mode 100644
index 0000000..03c0a5a
--- /dev/null
+++ b/util/stream/log_output_stream.cc
@@ -0,0 +1,130 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/log_output_stream.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+#if defined(OS_ANDROID)
+#include <android/log.h>
+#endif
+
+namespace crashpad {
+
+namespace {
+
+// Most minidumps are expected to be compressed and encoded into less than 128k.
+constexpr size_t kOutputCap = 128 * 1024;
+
+// From Android NDK r20 <android/log.h>, log message text may be truncated to
+// less than an implementation-specific limit (1023 bytes), for sake of safe
+// and being easy to read in logcat, choose 512.
+constexpr size_t kLineBufferSize = 512;
+
+} // namespace
+
+LogOutputStream::LogOutputStream()
+ : output_count_(0), flush_needed_(false), flushed_(false) {
+ buffer_.reserve(kLineBufferSize);
+}
+
+LogOutputStream::~LogOutputStream() {
+ DCHECK(!flush_needed_);
+}
+
+bool LogOutputStream::Write(const uint8_t* data, size_t size) {
+ DCHECK(!flushed_);
+ flush_needed_ = true;
+ while (size > 0) {
+ size_t m = std::min(kLineBufferSize - buffer_.size(), size);
+ buffer_.append(reinterpret_cast<const char*>(data), m);
+ data += m;
+ size -= m;
+ if (buffer_.size() == kLineBufferSize && !WriteBuffer()) {
+ flush_needed_ = false;
+ LOG(ERROR) << "Write: exceeds cap.";
+ if (output_stream_for_testing_)
+ output_stream_for_testing_->Flush();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LogOutputStream::WriteBuffer() {
+ if (output_count_ == 0) {
+ if (!WriteToLog("-----BEGIN CRASHPAD MINIDUMP-----"))
+ return false;
+ }
+
+ if (buffer_.empty())
+ return true;
+
+ output_count_ += buffer_.size();
+ if (output_count_ > kOutputCap) {
+ WriteToLog("-----ABORT CRASHPAD MINIDUMP-----");
+ return false;
+ }
+
+ bool result = WriteToLog(buffer_.c_str());
+ buffer_.clear();
+ return result;
+}
+
+bool LogOutputStream::WriteToLog(const char* buf) {
+#if defined(OS_ANDROID)
+ int ret =
+ __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, "crashpad", buf);
+ if (ret < 0) {
+ errno = -ret;
+ PLOG(ERROR) << "__android_log_buf_write";
+ return false;
+ }
+#endif
+ // For testing.
+ if (output_stream_for_testing_) {
+ return output_stream_for_testing_->Write(
+ reinterpret_cast<const uint8_t*>(buf), strlen(buf));
+ }
+ return true;
+}
+
+bool LogOutputStream::Flush() {
+ flush_needed_ = false;
+ flushed_ = true;
+
+ bool result = true;
+ if (WriteBuffer()) {
+ result = WriteToLog("-----END CRASHPAD MINIDUMP-----");
+ } else {
+ LOG(ERROR) << "Flush: exceeds cap.";
+ result = false;
+ }
+
+ // Since output_stream_for_testing_'s Write() method has been called, its
+ // Flush() shall always be invoked.
+ if (output_stream_for_testing_)
+ output_stream_for_testing_->Flush();
+
+ return result;
+}
+
+void LogOutputStream::SetOutputStreamForTesting(
+ std::unique_ptr<OutputStreamInterface> stream) {
+ output_stream_for_testing_ = std::move(stream);
+}
+
+} // namespace crashpad
diff --git a/util/stream/log_output_stream.h b/util/stream/log_output_stream.h
new file mode 100644
index 0000000..4a91f5c
--- /dev/null
+++ b/util/stream/log_output_stream.h
@@ -0,0 +1,61 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_
+#define CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+
+//! \brief This class output the received data to Android log, NOP in other
+//! platform.
+//!
+//! To avoid overflowing Android log, total 128k log data is allowed, after
+//! that cap, the output is aborted.
+class LogOutputStream : public OutputStreamInterface {
+ public:
+ LogOutputStream();
+ ~LogOutputStream() override;
+
+ // OutputStreamInterface:
+ bool Write(const uint8_t* data, size_t size) override;
+ bool Flush() override;
+
+ void SetOutputStreamForTesting(std::unique_ptr<OutputStreamInterface> stream);
+
+ private:
+ // Flush the |buffer_|, return false if kOutputCap meet.
+ bool WriteBuffer();
+ bool WriteToLog(const char* buf);
+
+ std::string buffer_;
+ size_t output_count_;
+ bool flush_needed_;
+ bool flushed_;
+ std::unique_ptr<OutputStreamInterface> output_stream_for_testing_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogOutputStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_
diff --git a/util/stream/log_output_stream_test.cc b/util/stream/log_output_stream_test.cc
new file mode 100644
index 0000000..510df76
--- /dev/null
+++ b/util/stream/log_output_stream_test.cc
@@ -0,0 +1,126 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/log_output_stream.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "gtest/gtest.h"
+#include "util/stream/test_output_stream.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr size_t kOutputCap = 128 * 1024;
+constexpr size_t kLineBufferSize = 512;
+const char* kBeginGuard = "-----BEGIN CRASHPAD MINIDUMP-----";
+const char* kEndGuard = "-----END CRASHPAD MINIDUMP-----";
+const char* kAbortGuard = "-----ABORT CRASHPAD MINIDUMP-----";
+
+std::string ConvertToString(const std::vector<uint8_t>& src) {
+ return std::string(reinterpret_cast<const char*>(src.data()), src.size());
+}
+
+class LogOutputStreamTest : public testing::Test {
+ public:
+ LogOutputStreamTest() : test_output_stream_(nullptr) {}
+
+ protected:
+ void SetUp() override {
+ std::unique_ptr<TestOutputStream> output_stream =
+ std::make_unique<TestOutputStream>();
+ test_output_stream_ = output_stream.get();
+ log_stream_ = std::make_unique<LogOutputStream>();
+ log_stream_->SetOutputStreamForTesting(std::move(output_stream));
+ }
+
+ const uint8_t* BuildDeterministicInput(size_t size) {
+ deterministic_input_ = std::make_unique<uint8_t[]>(size);
+ uint8_t* deterministic_input_base = deterministic_input_.get();
+ while (size-- > 0)
+ deterministic_input_base[size] = static_cast<uint8_t>('a');
+ return deterministic_input_base;
+ }
+
+ TestOutputStream* test_output_stream() const { return test_output_stream_; }
+
+ LogOutputStream* log_stream() const { return log_stream_.get(); }
+
+ private:
+ std::unique_ptr<LogOutputStream> log_stream_;
+ TestOutputStream* test_output_stream_;
+ std::unique_ptr<uint8_t[]> deterministic_input_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogOutputStreamTest);
+};
+
+TEST_F(LogOutputStreamTest, VerifyGuards) {
+ log_stream()->Flush();
+ // Verify OutputStream wrote 2 guards.
+ EXPECT_EQ(test_output_stream()->write_count(), 2u);
+ EXPECT_EQ(test_output_stream()->flush_count(), 1u);
+ EXPECT_FALSE(test_output_stream()->all_data().empty());
+ EXPECT_EQ(ConvertToString(test_output_stream()->all_data()),
+ std::string(kBeginGuard).append(kEndGuard));
+}
+
+TEST_F(LogOutputStreamTest, WriteShortLog) {
+ const uint8_t* input = BuildDeterministicInput(2);
+ EXPECT_TRUE(log_stream()->Write(input, 2));
+ EXPECT_TRUE(log_stream()->Flush());
+ // Verify OutputStream wrote 2 guards and data.
+ EXPECT_EQ(test_output_stream()->write_count(), 3u);
+ EXPECT_EQ(test_output_stream()->flush_count(), 1u);
+ EXPECT_FALSE(test_output_stream()->all_data().empty());
+ EXPECT_EQ(ConvertToString(test_output_stream()->all_data()),
+ std::string(kBeginGuard).append("aa").append(kEndGuard));
+}
+
+TEST_F(LogOutputStreamTest, WriteLongLog) {
+ size_t input_length = kLineBufferSize + kLineBufferSize / 2;
+ const uint8_t* input = BuildDeterministicInput(input_length);
+ // Verify OutputStream wrote 2 guards and data.
+ EXPECT_TRUE(log_stream()->Write(input, input_length));
+ EXPECT_TRUE(log_stream()->Flush());
+ EXPECT_EQ(test_output_stream()->write_count(),
+ 2 + input_length / kLineBufferSize + 1);
+ EXPECT_EQ(test_output_stream()->flush_count(), 1u);
+ EXPECT_EQ(test_output_stream()->all_data().size(),
+ strlen(kBeginGuard) + strlen(kEndGuard) + input_length);
+}
+
+TEST_F(LogOutputStreamTest, WriteAbort) {
+ size_t input_length = kOutputCap + kLineBufferSize;
+ const uint8_t* input = BuildDeterministicInput(input_length);
+ EXPECT_FALSE(log_stream()->Write(input, input_length));
+ std::string data(ConvertToString(test_output_stream()->all_data()));
+ EXPECT_EQ(data.substr(data.size() - strlen(kAbortGuard)), kAbortGuard);
+}
+
+TEST_F(LogOutputStreamTest, FlushAbort) {
+ size_t input_length = kOutputCap + kLineBufferSize / 2;
+ const uint8_t* input = BuildDeterministicInput(input_length);
+ EXPECT_TRUE(log_stream()->Write(input, input_length));
+ EXPECT_FALSE(log_stream()->Flush());
+ std::string data(ConvertToString(test_output_stream()->all_data()));
+ EXPECT_EQ(data.substr(data.size() - strlen(kAbortGuard)), kAbortGuard);
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/stream/output_stream_interface.h b/util/stream/output_stream_interface.h
new file mode 100644
index 0000000..397860e
--- /dev/null
+++ b/util/stream/output_stream_interface.h
@@ -0,0 +1,64 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_
+#define CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace crashpad {
+
+//! \brief The interface for an output stream pipeline.
+//!
+//! Example:
+//! <code>
+//! class OutputStreamInterfaceImpl : public OutputStreamInterface {
+//! ...
+//! };
+//!
+//! // Create a OutputStream.
+//! OutputStreamInterfaceImpl impl(...);
+//! // Write the data multiple times.
+//! while (has_data) {
+//! impl.Write(data, size);
+//! ...
+//! }
+//! // Flush internal buffer to indicate all data has been written.
+//! impl.Flush();
+//! </code>
+//!
+class OutputStreamInterface {
+ public:
+ virtual ~OutputStreamInterface() = default;
+
+ //! \brief Writes \a data to this stream. This method may be called multiple
+ //! times for streaming.
+ //!
+ //! \param[in] data The data that should be written.
+ //! \param[in] size The size of \a data.
+ //!
+ //! \return `true` on success.
+ virtual bool Write(const uint8_t* data, size_t size) = 0;
+
+ //! \brief Flush the internal buffer after all data has been written.
+ //!
+ //! Write() can't be called afterwards.
+ //! \return `true` on success.
+ virtual bool Flush() = 0;
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_
diff --git a/util/stream/test_output_stream.cc b/util/stream/test_output_stream.cc
new file mode 100644
index 0000000..04ef700
--- /dev/null
+++ b/util/stream/test_output_stream.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/test_output_stream.h"
+#include "base/logging.h"
+
+namespace crashpad {
+namespace test {
+
+TestOutputStream::TestOutputStream()
+ : last_written_data_(),
+ all_data_(),
+ write_count_(0),
+ flush_count_(0),
+ flush_needed_(false) {}
+
+TestOutputStream::~TestOutputStream() {
+ DCHECK(!flush_needed_);
+}
+
+bool TestOutputStream::Write(const uint8_t* data, size_t size) {
+ last_written_data_.assign(data, data + size);
+ all_data_.insert(all_data_.end(), data, data + size);
+
+ flush_needed_ = true;
+ write_count_++;
+ return true;
+}
+
+bool TestOutputStream::Flush() {
+ flush_needed_ = false;
+ flush_count_++;
+ return true;
+}
+
+} // namespace test
+} // namespace crashpad
diff --git a/util/stream/test_output_stream.h b/util/stream/test_output_stream.h
new file mode 100644
index 0000000..dcd0d45
--- /dev/null
+++ b/util/stream/test_output_stream.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_
+#define CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+namespace test {
+
+//! \brief The help class for \a OutputStreamInterface related tests.
+class TestOutputStream : public OutputStreamInterface {
+ public:
+ TestOutputStream();
+ ~TestOutputStream() override;
+
+ // OutputStreamInterface:
+ bool Write(const uint8_t* data, size_t size) override;
+ bool Flush() override;
+
+ //! \return the data that has been received by the last call of Write().
+ const std::vector<uint8_t>& last_written_data() const {
+ return last_written_data_;
+ }
+
+ //! \return all data that has been received.
+ const std::vector<uint8_t>& all_data() const { return all_data_; }
+
+ //! \return the number of times Write() has been called.
+ size_t write_count() const { return write_count_; }
+
+ //! \return the number of times Flush() has been called.
+ size_t flush_count() const { return flush_count_; }
+
+ private:
+ std::vector<uint8_t> last_written_data_;
+ std::vector<uint8_t> all_data_;
+ size_t write_count_;
+ size_t flush_count_;
+ bool flush_needed_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestOutputStream);
+};
+
+} // namespace test
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_
diff --git a/util/stream/zlib_output_stream.cc b/util/stream/zlib_output_stream.cc
new file mode 100644
index 0000000..88861db
--- /dev/null
+++ b/util/stream/zlib_output_stream.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/zlib_output_stream.h"
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/stl_util.h"
+#include "util/misc/zlib.h"
+
+namespace crashpad {
+
+ZlibOutputStream::ZlibOutputStream(
+ Mode mode,
+ std::unique_ptr<OutputStreamInterface> output_stream)
+ : output_stream_(std::move(output_stream)),
+ mode_(mode),
+ initialized_(),
+ flush_needed_(false) {}
+
+ZlibOutputStream::~ZlibOutputStream() {
+ if (!initialized_.is_valid())
+ return;
+ DCHECK(!flush_needed_);
+ if (mode_ == Mode::kCompress) {
+ if (deflateEnd(&zlib_stream_) != Z_OK)
+ LOG(ERROR) << "deflateEnd: " << zlib_stream_.msg;
+ } else if (mode_ == Mode::kDecompress) {
+ if (inflateEnd(&zlib_stream_) != Z_OK)
+ LOG(ERROR) << "inflateEnd: " << zlib_stream_.msg;
+ }
+}
+
+bool ZlibOutputStream::Write(const uint8_t* data, size_t size) {
+ if (initialized_.is_uninitialized()) {
+ initialized_.set_invalid();
+
+ zlib_stream_.zalloc = Z_NULL;
+ zlib_stream_.zfree = Z_NULL;
+ zlib_stream_.opaque = Z_NULL;
+
+ if (mode_ == Mode::kDecompress) {
+ int result = inflateInit(&zlib_stream_);
+ if (result != Z_OK) {
+ LOG(ERROR) << "inflateInit: " << ZlibErrorString(result);
+ return false;
+ }
+ } else if (mode_ == Mode::kCompress) {
+ int result = deflateInit(&zlib_stream_, Z_BEST_COMPRESSION);
+ if (result != Z_OK) {
+ LOG(ERROR) << "deflateInit: " << ZlibErrorString(result);
+ return false;
+ }
+ }
+ zlib_stream_.next_out = buffer_;
+ zlib_stream_.avail_out = base::saturated_cast<uInt>(base::size(buffer_));
+ initialized_.set_valid();
+ }
+
+ if (!initialized_.is_valid())
+ return false;
+
+ zlib_stream_.next_in = data;
+ zlib_stream_.avail_in = base::saturated_cast<uInt>(size);
+ flush_needed_ = false;
+ while (zlib_stream_.avail_in > 0) {
+ if (mode_ == Mode::kCompress) {
+ if (deflate(&zlib_stream_, Z_NO_FLUSH) != Z_OK) {
+ LOG(ERROR) << "deflate: " << zlib_stream_.msg;
+ return false;
+ }
+ } else if (mode_ == Mode::kDecompress) {
+ int result = inflate(&zlib_stream_, Z_NO_FLUSH);
+ if (result == Z_STREAM_END) {
+ if (zlib_stream_.avail_in > 0) {
+ LOG(ERROR) << "inflate: unconsumed input";
+ return false;
+ }
+ } else if (result != Z_OK) {
+ LOG(ERROR) << "inflate: " << zlib_stream_.msg;
+ return false;
+ }
+ }
+
+ if (!WriteOutputStream())
+ return false;
+ }
+ flush_needed_ = true;
+ return true;
+}
+
+bool ZlibOutputStream::Flush() {
+ if (initialized_.is_valid() && flush_needed_) {
+ flush_needed_ = false;
+ int result = Z_OK;
+ do {
+ if (mode_ == Mode::kCompress) {
+ result = deflate(&zlib_stream_, Z_FINISH);
+ if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {
+ LOG(ERROR) << "deflate: " << zlib_stream_.msg;
+ return false;
+ }
+ } else if (mode_ == Mode::kDecompress) {
+ result = inflate(&zlib_stream_, Z_FINISH);
+ if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {
+ LOG(ERROR) << "inflate: " << zlib_stream_.msg;
+ return false;
+ }
+ }
+ if (!WriteOutputStream())
+ return false;
+ } while (result != Z_STREAM_END);
+ }
+ return output_stream_->Flush();
+}
+
+bool ZlibOutputStream::WriteOutputStream() {
+ auto valid_size = base::size(buffer_) - zlib_stream_.avail_out;
+ if (valid_size > 0 && !output_stream_->Write(buffer_, valid_size))
+ return false;
+
+ zlib_stream_.next_out = buffer_;
+ zlib_stream_.avail_out = base::saturated_cast<uInt>(base::size(buffer_));
+
+ return true;
+}
+
+} // namespace crashpad
diff --git a/util/stream/zlib_output_stream.h b/util/stream/zlib_output_stream.h
new file mode 100644
index 0000000..d8e62a6
--- /dev/null
+++ b/util/stream/zlib_output_stream.h
@@ -0,0 +1,82 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_
+#define CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "third_party/zlib/zlib_crashpad.h"
+#include "util/misc/initialization_state.h"
+#include "util/stream/output_stream_interface.h"
+
+namespace crashpad {
+
+//! \brief The class wraps zlib into \a OutputStreamInterface.
+class ZlibOutputStream : public OutputStreamInterface {
+ public:
+ //! \brief Whether this object is configured to compress or decompress data.
+ enum class Mode : bool {
+ //! \brief Data passed through this object is compressed.
+ kCompress = false,
+ //! \brief Data passed through this object is decompressed.
+ kDecompress = true
+ };
+
+ //! \param[in] mode The work mode of this object.
+ //! \param[in] output_stream The output_stream that this object writes to.
+ //!
+ //! To construct an output pipeline, the output stream needs an output stream
+ //! to write the result to. For example, the code below constructs a
+ //! compress->base94-encoding->log output stream pipline.
+ //!
+ //! <code>
+ //! ZlibOutputStream zlib_output_stream(
+ //! ZlibOutputStream::Mode::kDeflate,
+ //! std::make_unique<Base94OutputStream>(
+ //! Base94OutputStream::Mode::kEncode,
+ //! std::make_unique<LogOutputStream>()));
+ //! </code>
+ //!
+ //!
+ ZlibOutputStream(Mode mode,
+ std::unique_ptr<OutputStreamInterface> output_stream);
+ ~ZlibOutputStream() override;
+
+ // OutputStreamInterface:
+ bool Write(const uint8_t* data, size_t size) override;
+ bool Flush() override;
+
+ private:
+ // Write compressed/decompressed data to |output_stream_| and empty the output
+ // buffer in |zlib_stream_|.
+ bool WriteOutputStream();
+
+ uint8_t buffer_[4096];
+ z_stream zlib_stream_;
+ std::unique_ptr<OutputStreamInterface> output_stream_;
+ Mode mode_;
+ InitializationState initialized_; // protects zlib_stream_
+ bool flush_needed_;
+
+ DISALLOW_COPY_AND_ASSIGN(ZlibOutputStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_
diff --git a/util/stream/zlib_output_stream_test.cc b/util/stream/zlib_output_stream_test.cc
new file mode 100644
index 0000000..dfa935b
--- /dev/null
+++ b/util/stream/zlib_output_stream_test.cc
@@ -0,0 +1,186 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/stream/zlib_output_stream.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/stream/test_output_stream.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr size_t kShortDataLength = 10;
+constexpr size_t kLongDataLength = 4096 * 10;
+
+class ZlibOutputStreamTest : public testing::Test {
+ public:
+ ZlibOutputStreamTest() : input_(), deterministic_input_() {
+ auto test_output_stream = std::make_unique<TestOutputStream>();
+ test_output_stream_ = test_output_stream.get();
+ zlib_output_stream_ = std::make_unique<ZlibOutputStream>(
+ ZlibOutputStream::Mode::kCompress,
+ std::make_unique<ZlibOutputStream>(ZlibOutputStream::Mode::kDecompress,
+ std::move(test_output_stream)));
+ }
+
+ const uint8_t* BuildDeterministicInput(size_t size) {
+ deterministic_input_ = std::make_unique<uint8_t[]>(size);
+ uint8_t* deterministic_input_base = deterministic_input_.get();
+ while (size-- > 0)
+ deterministic_input_base[size] = static_cast<uint8_t>(size);
+ return deterministic_input_base;
+ }
+
+ const uint8_t* BuildRandomInput(size_t size) {
+ input_ = std::make_unique<uint8_t[]>(size);
+ base::RandBytes(&input_[0], size);
+ return input_.get();
+ }
+
+ const TestOutputStream& test_output_stream() const {
+ return *test_output_stream_;
+ }
+
+ ZlibOutputStream* zlib_output_stream() const {
+ return zlib_output_stream_.get();
+ }
+
+ private:
+ std::unique_ptr<ZlibOutputStream> zlib_output_stream_;
+ std::unique_ptr<uint8_t[]> input_;
+ std::unique_ptr<uint8_t[]> deterministic_input_;
+ TestOutputStream* test_output_stream_; // weak, owned by zlib_output_stream_
+
+ DISALLOW_COPY_AND_ASSIGN(ZlibOutputStreamTest);
+};
+
+TEST_F(ZlibOutputStreamTest, WriteDeterministicShortData) {
+ const uint8_t* input = BuildDeterministicInput(kShortDataLength);
+ EXPECT_TRUE(zlib_output_stream()->Write(input, kShortDataLength));
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().last_written_data().size(), kShortDataLength);
+ EXPECT_EQ(memcmp(test_output_stream().last_written_data().data(),
+ input,
+ kShortDataLength),
+ 0);
+}
+
+TEST_F(ZlibOutputStreamTest, WriteDeterministicLongDataOneTime) {
+ const uint8_t* input = BuildDeterministicInput(kLongDataLength);
+ EXPECT_TRUE(zlib_output_stream()->Write(input, kLongDataLength));
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(
+ memcmp(test_output_stream().all_data().data(), input, kLongDataLength),
+ 0);
+}
+
+TEST_F(ZlibOutputStreamTest, WriteDeterministicLongDataMultipleTimes) {
+ const uint8_t* input = BuildDeterministicInput(kLongDataLength);
+
+ static constexpr size_t kWriteLengths[] = {
+ 4, 96, 40, kLongDataLength - 4 - 96 - 40};
+
+ size_t offset = 0;
+ for (size_t index = 0; index < base::size(kWriteLengths); ++index) {
+ const size_t write_length = kWriteLengths[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "offset %zu, write_length %zu", offset, write_length));
+ EXPECT_TRUE(zlib_output_stream()->Write(input + offset, write_length));
+ offset += write_length;
+ }
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(
+ memcmp(test_output_stream().all_data().data(), input, kLongDataLength),
+ 0);
+}
+
+TEST_F(ZlibOutputStreamTest, WriteShortData) {
+ const uint8_t* input = BuildRandomInput(kShortDataLength);
+ EXPECT_TRUE(zlib_output_stream()->Write(input, kShortDataLength));
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(memcmp(test_output_stream().last_written_data().data(),
+ input,
+ kShortDataLength),
+ 0);
+ EXPECT_EQ(test_output_stream().last_written_data().size(), kShortDataLength);
+}
+
+TEST_F(ZlibOutputStreamTest, WriteLongDataOneTime) {
+ const uint8_t* input = BuildRandomInput(kLongDataLength);
+ EXPECT_TRUE(zlib_output_stream()->Write(input, kLongDataLength));
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(
+ memcmp(test_output_stream().all_data().data(), input, kLongDataLength),
+ 0);
+}
+
+TEST_F(ZlibOutputStreamTest, WriteLongDataMultipleTimes) {
+ const uint8_t* input = BuildRandomInput(kLongDataLength);
+
+ // Call Write() a random number of times.
+ size_t index = 0;
+ while (index < kLongDataLength) {
+ size_t write_length =
+ std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),
+ kLongDataLength - index);
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, write_length %zu", index, write_length));
+ EXPECT_TRUE(zlib_output_stream()->Write(input + index, write_length));
+ index += write_length;
+ }
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);
+ EXPECT_EQ(
+ memcmp(test_output_stream().all_data().data(), input, kLongDataLength),
+ 0);
+}
+
+TEST_F(ZlibOutputStreamTest, NoWriteOrFlush) {
+ EXPECT_EQ(test_output_stream().write_count(), 0u);
+ EXPECT_EQ(test_output_stream().flush_count(), 0u);
+ EXPECT_TRUE(test_output_stream().all_data().empty());
+}
+
+TEST_F(ZlibOutputStreamTest, FlushWithoutWrite) {
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().write_count(), 0u);
+ EXPECT_EQ(test_output_stream().flush_count(), 1u);
+ EXPECT_TRUE(test_output_stream().all_data().empty());
+}
+
+TEST_F(ZlibOutputStreamTest, WriteEmptyData) {
+ std::vector<uint8_t> empty_data;
+ EXPECT_TRUE(zlib_output_stream()->Write(
+ static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_TRUE(zlib_output_stream()->Flush());
+ EXPECT_EQ(test_output_stream().write_count(), 0u);
+ EXPECT_EQ(test_output_stream().flush_count(), 2u);
+ EXPECT_TRUE(test_output_stream().all_data().empty());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/synchronization/semaphore_test.cc b/util/synchronization/semaphore_test.cc
index 10f7546..4f1c1cd 100644
--- a/util/synchronization/semaphore_test.cc
+++ b/util/synchronization/semaphore_test.cc
@@ -16,7 +16,7 @@
#include <sys/types.h>
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#if defined(OS_POSIX)
@@ -126,7 +126,7 @@
Semaphore semaphore(5);
ThreadMainInfo info[10];
size_t iterations = 0;
- for (size_t index = 0; index < arraysize(info); ++index) {
+ for (size_t index = 0; index < base::size(info); ++index) {
info[index].semaphore = &semaphore;
info[index].iterations = index;
iterations += info[index].iterations;
@@ -138,7 +138,7 @@
semaphore.Signal();
}
- for (size_t index = 0; index < arraysize(info); ++index) {
+ for (size_t index = 0; index < base::size(info); ++index) {
JoinThread(&info[index]);
}
}
diff --git a/util/thread/thread_log_messages.cc b/util/thread/thread_log_messages.cc
index 60be08b..60c8248 100644
--- a/util/thread/thread_log_messages.cc
+++ b/util/thread/thread_log_messages.cc
@@ -46,10 +46,6 @@
private:
ThreadLogMessagesMaster() {
- DCHECK(!tls_.initialized());
- tls_.Initialize(nullptr);
- DCHECK(tls_.initialized());
-
DCHECK(!logging::GetLogMessageHandler());
logging::SetLogMessageHandler(LogMessageHandler);
}
@@ -62,7 +58,7 @@
size_t message_start,
const std::string& string) {
std::vector<std::string>* log_messages =
- reinterpret_cast<std::vector<std::string>*>(tls_.Get());
+ reinterpret_cast<std::vector<std::string>*>(GetInstance()->tls_.Get());
if (log_messages) {
log_messages->push_back(string);
}
@@ -72,15 +68,11 @@
return false;
}
- static base::ThreadLocalStorage::StaticSlot tls_;
+ base::ThreadLocalStorage::Slot tls_;
DISALLOW_COPY_AND_ASSIGN(ThreadLogMessagesMaster);
};
-// static
-base::ThreadLocalStorage::StaticSlot ThreadLogMessagesMaster::tls_
- = TLS_INITIALIZER;
-
} // namespace
ThreadLogMessages::ThreadLogMessages() : log_messages_() {
diff --git a/util/thread/thread_log_messages_test.cc b/util/thread/thread_log_messages_test.cc
index 00b612c..94920e7 100644
--- a/util/thread/thread_log_messages_test.cc
+++ b/util/thread/thread_log_messages_test.cc
@@ -18,6 +18,7 @@
#include <sys/types.h>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/thread/thread.h"
@@ -93,8 +94,8 @@
const std::vector<std::string>& log_messages =
thread_log_messages.log_messages();
- EXPECT_EQ(log_messages.size(), arraysize(kMessages));
- for (size_t index = 0; index < arraysize(kMessages); ++index) {
+ EXPECT_EQ(log_messages.size(), base::size(kMessages));
+ for (size_t index = 0; index < base::size(kMessages); ++index) {
EXPECT_EQ(MessageString(log_messages[index]), kMessages[index])
<< "index " << index;
}
@@ -173,7 +174,7 @@
LoggingTestThread threads[20];
int start = 0;
- for (size_t index = 0; index < arraysize(threads); ++index) {
+ for (size_t index = 0; index < base::size(threads); ++index) {
threads[index].Initialize(
index, static_cast<int>(start), static_cast<int>(index));
start += static_cast<int>(index);
diff --git a/util/thread/thread_test.cc b/util/thread/thread_test.cc
index d92544b..47a711c 100644
--- a/util/thread/thread_test.cc
+++ b/util/thread/thread_test.cc
@@ -14,7 +14,6 @@
#include "util/thread/thread.h"
-#include "base/macros.h"
#include "gtest/gtest.h"
#include "util/synchronization/semaphore.h"
diff --git a/util/thread/worker_thread.cc b/util/thread/worker_thread.cc
index 3cf4897..b5dcdb2 100644
--- a/util/thread/worker_thread.cc
+++ b/util/thread/worker_thread.cc
@@ -33,8 +33,9 @@
if (initial_work_delay_ > 0)
semaphore_.TimedWait(initial_work_delay_);
- while (self_->running_) {
+ while (self_->running_ || self_->do_work_now_) {
self_->delegate_->DoWork(self_);
+ self_->do_work_now_ = false;
semaphore_.TimedWait(self_->work_interval_);
}
}
@@ -57,7 +58,8 @@
: work_interval_(work_interval),
delegate_(delegate),
impl_(),
- running_(false) {}
+ running_(false),
+ do_work_now_(false) {}
WorkerThread::~WorkerThread() {
DCHECK(!running_);
@@ -88,6 +90,7 @@
void WorkerThread::DoWorkNow() {
DCHECK(running_);
+ do_work_now_ = true;
impl_->SignalSemaphore();
}
diff --git a/util/thread/worker_thread.h b/util/thread/worker_thread.h
index 97fb6ec..0fae009 100644
--- a/util/thread/worker_thread.h
+++ b/util/thread/worker_thread.h
@@ -92,6 +92,7 @@
Delegate* delegate_; // weak
std::unique_ptr<internal::WorkerThreadImpl> impl_;
bool running_;
+ bool do_work_now_;
DISALLOW_COPY_AND_ASSIGN(WorkerThread);
};
diff --git a/util/util.gyp b/util/util.gyp
index 1ffb752..78a432a 100644
--- a/util/util.gyp
+++ b/util/util.gyp
@@ -24,7 +24,9 @@
'../compat/compat.gyp:crashpad_compat',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../third_party/zlib/zlib.gyp:zlib',
+ '../third_party/lss/lss.gyp:lss',
],
+ 'defines': [ 'ZLIB_CONST' ],
'include_dirs': [
'..',
'<(INTERMEDIATE_DIR)',
@@ -48,6 +50,8 @@
'file/filesystem_win.cc',
'file/file_writer.cc',
'file/file_writer.h',
+ 'file/output_stream_file_writer.cc',
+ 'file/output_stream_file_writer.h',
'file/scoped_remove_file.cc',
'file/scoped_remove_file.h',
'file/string_file.cc',
@@ -63,10 +67,14 @@
'linux/exception_handler_protocol.cc',
'linux/exception_handler_protocol.h',
'linux/exception_information.h',
+ 'linux/initial_signal_dispositions.cc',
+ 'linux/initial_signal_dispositions.h',
'linux/memory_map.cc',
'linux/memory_map.h',
'linux/proc_stat_reader.cc',
'linux/proc_stat_reader.h',
+ 'linux/proc_task_reader.cc',
+ 'linux/proc_task_reader.h',
'linux/ptrace_broker.cc',
'linux/ptrace_broker.h',
'linux/ptrace_client.cc',
@@ -74,10 +82,14 @@
'linux/ptrace_connection.h',
'linux/ptracer.cc',
'linux/ptracer.h',
+ 'linux/scoped_pr_set_dumpable.cc',
+ 'linux/scoped_pr_set_dumpable.h',
'linux/scoped_pr_set_ptracer.cc',
'linux/scoped_pr_set_ptracer.h',
'linux/scoped_ptrace_attach.cc',
'linux/scoped_ptrace_attach.h',
+ 'linux/socket.cc',
+ 'linux/socket.h',
'linux/thread_info.cc',
'linux/thread_info.h',
'linux/traits.h',
@@ -122,11 +134,9 @@
'mach/symbolic_constants_mach.h',
'mach/task_for_pid.cc',
'mach/task_for_pid.h',
- 'mach/task_memory.cc',
- 'mach/task_memory.h',
'misc/address_sanitizer.h',
'misc/address_types.h',
- 'misc/arraysize_unsafe.h',
+ 'misc/arraysize.h',
'misc/as_underlying_type.h',
'misc/capture_context.h',
'misc/capture_context_linux.S',
@@ -163,6 +173,7 @@
'misc/symbolic_constants_common.h',
'misc/time.cc',
'misc/time.h',
+ 'misc/time_linux.cc',
'misc/time_win.cc',
'misc/tri_state.h',
'misc/uuid.cc',
@@ -179,7 +190,6 @@
'net/http_transport.cc',
'net/http_transport.h',
'net/http_transport_mac.mm',
- 'net/http_transport_none.cc',
'net/http_transport_win.cc',
'net/url.cc',
'net/url.h',
@@ -209,10 +219,13 @@
'posix/signals.h',
'posix/symbolic_constants_posix.cc',
'posix/symbolic_constants_posix.h',
+ 'process/process_id.h',
'process/process_memory.cc',
'process/process_memory.h',
'process/process_memory_linux.cc',
'process/process_memory_linux.h',
+ 'process/process_memory_mac.cc',
+ 'process/process_memory_mac.h',
'process/process_memory_native.h',
'process/process_memory_range.cc',
'process/process_memory_range.h',
@@ -227,6 +240,17 @@
'stdlib/strnlen.cc',
'stdlib/strnlen.h',
'stdlib/thread_safe_vector.h',
+ 'stream/base94_output_stream.cc',
+ 'stream/base94_output_stream.h',
+ 'stream/file_encoder.cc',
+ 'stream/file_encoder.h',
+ 'stream/file_output_stream.cc',
+ 'stream/file_output_stream.h',
+ 'stream/log_output_stream.cc',
+ 'stream/log_output_stream.h',
+ 'stream/output_stream_interface.h',
+ 'stream/zlib_output_stream.cc',
+ 'stream/zlib_output_stream.h',
'string/split_string.cc',
'string/split_string.h',
'synchronization/semaphore_mac.cc',
@@ -246,6 +270,7 @@
'win/checked_win_address_range.h',
'win/command_line.cc',
'win/command_line.h',
+ 'win/context_wrappers.h',
'win/critical_section_with_debug_info.cc',
'win/critical_section_with_debug_info.h',
'win/exception_handler_server.cc',
@@ -384,20 +409,24 @@
'win/safe_terminate_process.asm',
],
}],
- ['OS=="linux"', {
+ ['OS=="android"', {
+ 'link_settings': {
+ 'libraries': [
+ '-llog',
+ ],
+ },
+ }],
+ ['OS=="linux" or OS=="android"', {
'sources': [
'net/http_transport_socket.cc',
+ 'process/process_memory_sanitized.cc',
+ 'process/process_memory_sanitized.h',
],
}, { # else: OS!="linux"
'sources!': [
'misc/capture_context_linux.S',
],
}],
- ['OS!="android"', {
- 'sources!': [
- 'net/http_transport_none.cc',
- ],
- }],
['OS!="linux" and OS!="android"', {
'sources/': [
['exclude', '^process/'],
@@ -410,10 +439,16 @@
['include', '^linux/'],
['include', '^misc/capture_context_linux\\.S$'],
['include', '^misc/paths_linux\\.cc$'],
+ ['include', '^misc/time_linux\\.cc$'],
['include', '^posix/process_info_linux\\.cc$'],
['include', '^process/process_memory_linux\\.cc$'],
['include', '^process/process_memory_linux\\.h$'],
],
+ }, { # else: OS!="android"
+ 'sources!': [
+ 'stream/log_output_stream.cc',
+ 'stream/log_output_stream.h',
+ ]
}],
],
},
diff --git a/util/util_test.gyp b/util/util_test.gyp
index ff3559e..f65a918 100644
--- a/util/util_test.gyp
+++ b/util/util_test.gyp
@@ -28,6 +28,7 @@
'../test/test.gyp:crashpad_test',
'../third_party/gtest/gmock.gyp:gmock',
'../third_party/gtest/gtest.gyp:gtest',
+ '../third_party/lss/lss.gyp:lss',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../third_party/zlib/zlib.gyp:zlib',
],
@@ -44,9 +45,11 @@
'linux/auxiliary_vector_test.cc',
'linux/memory_map_test.cc',
'linux/proc_stat_reader_test.cc',
+ 'linux/proc_task_reader_test.cc',
'linux/ptrace_broker_test.cc',
'linux/ptracer_test.cc',
'linux/scoped_ptrace_attach_test.cc',
+ 'linux/socket_test.cc',
'mac/launchd_test.mm',
'mac/mac_util_test.mm',
'mac/service_management_test.mm',
@@ -65,8 +68,7 @@
'mach/notify_server_test.cc',
'mach/scoped_task_suspend_test.cc',
'mach/symbolic_constants_mach_test.cc',
- 'mach/task_memory_test.cc',
- 'misc/arraysize_unsafe_test.cc',
+ 'misc/arraysize_test.cc',
'misc/capture_context_test.cc',
'misc/capture_context_test_util.h',
'misc/capture_context_test_util_linux.cc',
@@ -98,6 +100,7 @@
'posix/scoped_mmap_test.cc',
'posix/signals_test.cc',
'posix/symbolic_constants_posix_test.cc',
+ 'process/process_memory_mac_test.cc',
'process/process_memory_range_test.cc',
'process/process_memory_test.cc',
'stdlib/aligned_allocator_test.cc',
@@ -151,6 +154,11 @@
['exclude', '^net/http_transport_test\\.cc$'],
]
}],
+ ['OS=="linux" or OS=="android"', {
+ 'sources': [
+ 'process/process_memory_sanitized_test.cc',
+ ],
+ }],
['OS!="linux" and OS!="android"', {
'sources/': [
['exclude', '^process/'],
@@ -193,6 +201,15 @@
'cflags!': [
'-Wexit-time-destructors',
],
+ 'conditions': [
+ ['OS=="win"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lws2_32.lib',
+ ],
+ },
+ }],
+ ],
},
],
}],
diff --git a/util/win/command_line_test.cc b/util/win/command_line_test.cc
index 025ef8a..032d555 100644
--- a/util/win/command_line_test.cc
+++ b/util/win/command_line_test.cc
@@ -19,8 +19,8 @@
#include <sys/types.h>
#include "base/logging.h"
-#include "base/macros.h"
#include "base/scoped_generic.h"
+#include "base/stl_util.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "util/win/scoped_local_alloc.h"
@@ -65,7 +65,7 @@
L"argument 1",
L"argument 2",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -77,7 +77,7 @@
L"argument 2",
L"\\some\\path with\\spaces",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -89,7 +89,7 @@
L"she said, \"you had me at hello\"",
L"\\some\\path with\\spaces",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -102,7 +102,7 @@
L"argument3",
L"argument4",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -113,7 +113,7 @@
L"\\some\\directory with\\spaces\\",
L"argument2",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -124,7 +124,7 @@
L"",
L"argument2",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
{
@@ -159,7 +159,7 @@
L"\"\"",
L" \t\n\v\"",
};
- AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
+ AppendCommandLineArgumentTest(base::size(kArguments), kArguments);
}
}
diff --git a/util/win/context_wrappers.h b/util/win/context_wrappers.h
new file mode 100644
index 0000000..5d9b183
--- /dev/null
+++ b/util/win/context_wrappers.h
@@ -0,0 +1,40 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
+#define CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
+
+#include <windows.h>
+
+#include "build/build_config.h"
+
+namespace crashpad {
+
+//! \brief Retrieve program counter from `CONTEXT` structure for different
+//! architectures supported by Windows.
+inline void* ProgramCounterFromCONTEXT(const CONTEXT* context) {
+#if defined(ARCH_CPU_X86)
+ return reinterpret_cast<void*>(context->Eip);
+#elif defined(ARCH_CPU_X86_64)
+ return reinterpret_cast<void*>(context->Rip);
+#elif defined(ARCH_CPU_ARM64)
+ return reinterpret_cast<void*>(context->Pc);
+#else
+#error Unsupported Windows Arch
+#endif // ARCH_CPU_X86
+}
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_
diff --git a/util/win/exception_handler_server.cc b/util/win/exception_handler_server.cc
index 6642665..c841f7b 100644
--- a/util/win/exception_handler_server.cc
+++ b/util/win/exception_handler_server.cc
@@ -23,11 +23,9 @@
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "minidump/minidump_file_writer.h"
-#include "snapshot/crashpad_info_client_options.h"
-#include "snapshot/win/process_snapshot_win.h"
#include "util/file/file_writer.h"
#include "util/misc/tri_state.h"
#include "util/misc/uuid.h"
@@ -307,7 +305,7 @@
void ExceptionHandlerServer::Run(Delegate* delegate) {
uint64_t shutdown_token = base::RandUint64();
ScopedKernelHANDLE thread_handles[kPipeInstances];
- for (size_t i = 0; i < arraysize(thread_handles); ++i) {
+ for (size_t i = 0; i < base::size(thread_handles); ++i) {
HANDLE pipe;
if (first_pipe_instance_.is_valid()) {
pipe = first_pipe_instance_.release();
@@ -359,7 +357,7 @@
}
// Signal to the named pipe instances that they should terminate.
- for (size_t i = 0; i < arraysize(thread_handles); ++i) {
+ for (size_t i = 0; i < base::size(thread_handles); ++i) {
ClientToServerMessage message;
memset(&message, 0, sizeof(message));
message.type = ClientToServerMessage::kShutdown;
diff --git a/util/win/get_module_information.cc b/util/win/get_module_information.cc
index 1a9fd0c..850bfe1 100644
--- a/util/win/get_module_information.cc
+++ b/util/win/get_module_information.cc
@@ -22,9 +22,13 @@
HMODULE module,
MODULEINFO* module_info,
DWORD cb) {
+#if PSAPI_VERSION == 1
static const auto get_module_information =
- GET_FUNCTION_REQUIRED(L"psapi.dll", GetModuleInformation);
+ GET_FUNCTION_REQUIRED(L"psapi.dll", GetModuleInformation);
return get_module_information(process, module, module_info, cb);
+#elif PSAPI_VERSION == 2
+ return GetModuleInformation(process, module, module_info, cb);
+#endif
}
} // namespace crashpad
diff --git a/util/win/get_module_information.h b/util/win/get_module_information.h
index c7d1cf1..8fce035 100644
--- a/util/win/get_module_information.h
+++ b/util/win/get_module_information.h
@@ -17,7 +17,9 @@
#include <windows.h>
-#define PSAPI_VERSION 1
+#ifndef PSAPI_VERSION
+#define PSAPI_VERSION 2
+#endif
#include <psapi.h>
namespace crashpad {
diff --git a/util/win/loader_lock.cc b/util/win/loader_lock.cc
new file mode 100644
index 0000000..187ef84
--- /dev/null
+++ b/util/win/loader_lock.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/loader_lock.h"
+
+#include <windows.h>
+
+#include "build/build_config.h"
+#include "util/win/process_structs.h"
+
+namespace crashpad {
+
+namespace {
+
+#ifdef ARCH_CPU_64_BITS
+using NativeTraits = process_types::internal::Traits64;
+#else
+using NativeTraits = process_types::internal::Traits32;
+#endif // ARCH_CPU_64_BITS
+
+using PEB = process_types::PEB<NativeTraits>;
+using TEB = process_types::TEB<NativeTraits>;
+using RTL_CRITICAL_SECTION = process_types::RTL_CRITICAL_SECTION<NativeTraits>;
+
+TEB* GetTeb() {
+ return reinterpret_cast<TEB*>(NtCurrentTeb());
+}
+
+PEB* GetPeb() {
+ return reinterpret_cast<PEB*>(GetTeb()->ProcessEnvironmentBlock);
+}
+
+} // namespace
+
+bool IsThreadInLoaderLock() {
+ RTL_CRITICAL_SECTION* loader_lock =
+ reinterpret_cast<RTL_CRITICAL_SECTION*>(GetPeb()->LoaderLock);
+ return loader_lock->OwningThread == GetTeb()->ClientId.UniqueThread;
+}
+
+} // namespace crashpad
diff --git a/util/net/http_transport_none.cc b/util/win/loader_lock.h
similarity index 66%
rename from util/net/http_transport_none.cc
rename to util/win/loader_lock.h
index 1c08c1f..6c6b311 100644
--- a/util/net/http_transport_none.cc
+++ b/util/win/loader_lock.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
+// Copyright 2019 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "util/net/http_transport.h"
-
-#include "base/logging.h"
+#ifndef CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
+#define CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
namespace crashpad {
-std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
- NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
- return std::unique_ptr<HTTPTransport>();
-}
+//! \return `true` if the current thread holds the loader lock.
+bool IsThreadInLoaderLock();
} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_LOADER_LOCK_H_
diff --git a/util/win/loader_lock_test.cc b/util/win/loader_lock_test.cc
new file mode 100644
index 0000000..d33ea5a
--- /dev/null
+++ b/util/win/loader_lock_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/win/loader_lock.h"
+
+#include "gtest/gtest.h"
+#include "util/win/get_function.h"
+
+extern "C" bool LoaderLockDetected();
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(LoaderLock, Detected) {
+ EXPECT_FALSE(IsThreadInLoaderLock());
+ auto* loader_lock_detected = GET_FUNCTION_REQUIRED(
+ L"crashpad_util_test_loader_lock_test.dll", LoaderLockDetected);
+ EXPECT_TRUE(loader_lock_detected());
+ EXPECT_FALSE(IsThreadInLoaderLock());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/util/win/loader_lock_test_dll.cc b/util/win/loader_lock_test_dll.cc
new file mode 100644
index 0000000..b673ab3
--- /dev/null
+++ b/util/win/loader_lock_test_dll.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <windows.h>
+
+#include "util/win/loader_lock.h"
+
+namespace {
+
+bool g_loader_lock_detected = false;
+
+} // namespace
+
+extern "C" {
+
+__declspec(dllexport) bool LoaderLockDetected() {
+ return g_loader_lock_detected;
+}
+
+} // extern "C"
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ g_loader_lock_detected = crashpad::IsThreadInLoaderLock();
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/util/win/ntstatus_logging.cc b/util/win/ntstatus_logging.cc
index 442e49f..e9a9b61 100644
--- a/util/win/ntstatus_logging.cc
+++ b/util/win/ntstatus_logging.cc
@@ -16,6 +16,7 @@
#include <string>
+#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
namespace {
@@ -29,7 +30,7 @@
ntstatus,
0,
msgbuf,
- arraysize(msgbuf),
+ static_cast<DWORD>(base::size(msgbuf)),
nullptr);
if (len) {
// Most system messages end in a period and a space. Remove the space if
diff --git a/util/win/process_info.cc b/util/win/process_info.cc
index cd6bcd3..49a6f27 100644
--- a/util/win/process_info.cc
+++ b/util/win/process_info.cc
@@ -224,7 +224,7 @@
sizeof(wow64_peb_address),
&bytes_returned);
if (!NT_SUCCESS(status)) {
- NTSTATUS_LOG(ERROR, status), "NtQueryInformationProcess";
+ NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
return false;
}
if (bytes_returned != sizeof(wow64_peb_address)) {
@@ -513,8 +513,14 @@
// distinguish between these two cases.
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
- is_64_bit_ =
- system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+
+#if defined(ARCH_CPU_X86_FAMILY)
+ constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_ARM64;
+#endif
+
+ is_64_bit_ = system_info.wProcessorArchitecture == kNative64BitArchitecture;
}
#if defined(ARCH_CPU_32_BITS)
@@ -565,12 +571,12 @@
return is_wow64_;
}
-pid_t ProcessInfo::ProcessID() const {
+crashpad::ProcessID ProcessInfo::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_id_;
}
-pid_t ProcessInfo::ParentProcessID() const {
+crashpad::ProcessID ProcessInfo::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return inherited_from_process_id_;
}
diff --git a/util/win/process_info.h b/util/win/process_info.h
index 58921c5..afbe146 100644
--- a/util/win/process_info.h
+++ b/util/win/process_info.h
@@ -24,6 +24,7 @@
#include "base/macros.h"
#include "util/misc/initialization_state_dcheck.h"
#include "util/numeric/checked_range.h"
+#include "util/process/process_id.h"
#include "util/stdlib/aligned_allocator.h"
#include "util/win/address_types.h"
@@ -105,10 +106,10 @@
bool IsWow64() const;
//! \return The target process's process ID.
- pid_t ProcessID() const;
+ crashpad::ProcessID ProcessID() const;
//! \return The target process's parent process ID.
- pid_t ParentProcessID() const;
+ crashpad::ProcessID ParentProcessID() const;
//! \return The command line from the target process's Process Environment
//! Block.
@@ -173,8 +174,8 @@
// This function is best-effort under low memory conditions.
std::vector<Handle> BuildHandleVector(HANDLE process) const;
- pid_t process_id_;
- pid_t inherited_from_process_id_;
+ crashpad::ProcessID process_id_;
+ crashpad::ProcessID inherited_from_process_id_;
HANDLE process_;
std::wstring command_line_;
WinVMAddress peb_address_;
diff --git a/util/win/process_info_test.cc b/util/win/process_info_test.cc
index c7abdb6..a43358d 100644
--- a/util/win/process_info_test.cc
+++ b/util/win/process_info_test.cc
@@ -26,7 +26,6 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
-#include "test/gtest_disabled.h"
#include "test/scoped_temp_dir.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
@@ -38,6 +37,7 @@
#include "util/win/get_function.h"
#include "util/win/handle.h"
#include "util/win/scoped_handle.h"
+#include "util/win/scoped_registry_key.h"
namespace crashpad {
namespace test {
@@ -202,7 +202,7 @@
#if defined(ARCH_CPU_64_BITS)
TEST(ProcessInfo, OtherProcessWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
- DISABLED_TEST();
+ GTEST_SKIP();
}
TestOtherProcess(TestPaths::Architecture::k32Bit);
@@ -528,18 +528,6 @@
&bytes_read));
}
-struct ScopedRegistryKeyCloseTraits {
- static HKEY InvalidValue() {
- return nullptr;
- }
- static void Free(HKEY key) {
- RegCloseKey(key);
- }
-};
-
-using ScopedRegistryKey =
- base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;
-
TEST(ProcessInfo, Handles) {
ScopedTempDir temp_dir;
diff --git a/util/win/registration_protocol_win.cc b/util/win/registration_protocol_win.cc
index 4fb536d..64ed518 100644
--- a/util/win/registration_protocol_win.cc
+++ b/util/win/registration_protocol_win.cc
@@ -14,16 +14,63 @@
#include "util/win/registration_protocol_win.h"
-#include <stddef.h>
#include <windows.h>
+#include <aclapi.h>
+#include <sddl.h>
+#include <stddef.h>
#include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
#include "util/win/exception_handler_server.h"
+#include "util/win/loader_lock.h"
#include "util/win/scoped_handle.h"
+#include "util/win/scoped_local_alloc.h"
namespace crashpad {
+namespace {
+
+void* GetSecurityDescriptorWithUser(const base::char16* sddl_string,
+ size_t* size) {
+ if (size)
+ *size = 0;
+
+ PSECURITY_DESCRIPTOR base_sec_desc;
+ if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+ sddl_string, SDDL_REVISION_1, &base_sec_desc, nullptr)) {
+ PLOG(ERROR) << "ConvertStringSecurityDescriptorToSecurityDescriptor";
+ return nullptr;
+ }
+
+ ScopedLocalAlloc base_sec_desc_owner(base_sec_desc);
+ EXPLICIT_ACCESS access;
+ wchar_t username[] = L"CURRENT_USER";
+ BuildExplicitAccessWithName(
+ &access, username, GENERIC_ALL, GRANT_ACCESS, NO_INHERITANCE);
+
+ PSECURITY_DESCRIPTOR user_sec_desc;
+ ULONG user_sec_desc_size;
+ DWORD error = BuildSecurityDescriptor(nullptr,
+ nullptr,
+ 1,
+ &access,
+ 0,
+ nullptr,
+ base_sec_desc,
+ &user_sec_desc_size,
+ &user_sec_desc);
+ if (error != ERROR_SUCCESS) {
+ SetLastError(error);
+ PLOG(ERROR) << "BuildSecurityDescriptor";
+ return nullptr;
+ }
+
+ *size = user_sec_desc_size;
+ return user_sec_desc;
+}
+
+} // namespace
+
bool SendToCrashHandlerServer(const base::string16& pipe_name,
const ClientToServerMessage& message,
ServerToClientMessage* response) {
@@ -124,16 +171,11 @@
security_attributes_pointer);
}
-const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) {
+const void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size) {
// Mandatory Label, no ACE flags, no ObjectType, integrity level untrusted is
- // "S:(ML;;;;;S-1-16-0)". Typically
- // ConvertStringSecurityDescriptorToSecurityDescriptor() would be used to
- // convert from a string representation. However, that function cannot be used
- // because it is in advapi32.dll and CreateNamedPipeInstance() is called from
- // within DllMain() where the loader lock is held. advapi32.dll is delay
- // loaded in chrome_elf.dll because it must avoid loading user32.dll. If an
- // advapi32.dll function were used, it would cause a load of the DLL, which
- // would in turn cause deadlock.
+ // "S:(ML;;;;;S-1-16-0)". This static security descriptor is used as a
+ // fallback if GetSecurityDescriptorWithUser fails, to avoid losing crashes
+ // from non-AppContainer sandboxed applications.
#pragma pack(push, 1)
static constexpr struct SecurityDescriptorBlob {
@@ -168,7 +210,8 @@
ACL_REVISION, // AclRevision.
0, // Sbz1.
sizeof(kSecDescBlob.sacl), // AclSize.
- arraysize(kSecDescBlob.sacl.ace), // AceCount.
+ static_cast<WORD>(
+ base::size(kSecDescBlob.sacl.ace)), // AceCount.
0, // Sbz2.
},
@@ -188,8 +231,9 @@
// sid.
{
SID_REVISION, // Revision.
- // SubAuthorityCount.
- arraysize(kSecDescBlob.sacl.ace[0].sid.SubAuthority),
+ // SubAuthorityCount.
+ static_cast<BYTE>(base::size(
+ kSecDescBlob.sacl.ace[0].sid.SubAuthority)),
// IdentifierAuthority.
{SECURITY_MANDATORY_LABEL_AUTHORITY},
{SECURITY_MANDATORY_UNTRUSTED_RID}, // SubAuthority.
@@ -205,4 +249,23 @@
return reinterpret_cast<const void*>(&kSecDescBlob);
}
+const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) {
+ CHECK(!IsThreadInLoaderLock());
+
+ // Get a security descriptor which grants the current user and SYSTEM full
+ // access to the named pipe. Also grant AppContainer RW access through the ALL
+ // APPLICATION PACKAGES SID (S-1-15-2-1). Finally add an Untrusted Mandatory
+ // Label for non-AppContainer sandboxed users.
+ static size_t sd_size;
+ static void* sec_desc = GetSecurityDescriptorWithUser(
+ L"D:(A;;GA;;;SY)(A;;GWGR;;;S-1-15-2-1)S:(ML;;;;;S-1-16-0)", &sd_size);
+
+ if (!sec_desc)
+ return GetFallbackSecurityDescriptorForNamedPipeInstance(size);
+
+ if (size)
+ *size = sd_size;
+ return sec_desc;
+}
+
} // namespace crashpad
diff --git a/util/win/registration_protocol_win.h b/util/win/registration_protocol_win.h
index 5f04a46..ef8bebc 100644
--- a/util/win/registration_protocol_win.h
+++ b/util/win/registration_protocol_win.h
@@ -145,10 +145,10 @@
HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,
bool first_instance);
-//! \brief Returns the SECURITY_DESCRIPTOR blob that will be used for creating
+//! \brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating
//! the connection pipe in CreateNamedPipeInstance().
//!
-//! This function is exposed for only for testing.
+//! This function is only exposed for testing.
//!
//! \param[out] size The size of the returned blob. May be `nullptr` if not
//! required.
@@ -157,6 +157,19 @@
//! transferred to the caller.
const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size);
+//! \brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating
+//! the connection pipe in CreateNamedPipeInstance() if the full descriptor
+//! can't be created.
+//!
+//! This function is only exposed for testing.
+//!
+//! \param[out] size The size of the returned blob. May be `nullptr` if not
+//! required.
+//!
+//! \return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not
+//! transferred to the caller.
+const void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size);
+
} // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_
diff --git a/util/win/registration_protocol_win_test.cc b/util/win/registration_protocol_win_test.cc
index 6601b4e..334d7d8 100644
--- a/util/win/registration_protocol_win_test.cc
+++ b/util/win/registration_protocol_win_test.cc
@@ -14,18 +14,122 @@
#include "util/win/registration_protocol_win.h"
-#include <windows.h>
+#include <aclapi.h>
#include <sddl.h>
#include <string.h>
+#include <windows.h>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
#include "gtest/gtest.h"
#include "test/errors.h"
+#include "util/win/scoped_handle.h"
#include "util/win/scoped_local_alloc.h"
namespace crashpad {
namespace test {
namespace {
+base::string16 GetStringFromSid(PSID sid) {
+ LPWSTR sid_str;
+ if (!ConvertSidToStringSid(sid, &sid_str)) {
+ PLOG(ERROR) << "ConvertSidToStringSid";
+ return base::string16();
+ }
+ ScopedLocalAlloc sid_str_ptr(sid_str);
+ return sid_str;
+}
+
+base::string16 GetUserSidString() {
+ HANDLE token_handle;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) {
+ PLOG(ERROR) << "OpenProcessToken";
+ return base::string16();
+ }
+
+ ScopedKernelHANDLE token(token_handle);
+ DWORD user_size = 0;
+ GetTokenInformation(token.get(), TokenUser, nullptr, 0, &user_size);
+ if (user_size == 0) {
+ PLOG(ERROR) << "GetTokenInformation Size";
+ return base::string16();
+ }
+
+ std::vector<char> user(user_size);
+ if (!GetTokenInformation(
+ token.get(), TokenUser, user.data(), user_size, &user_size)) {
+ PLOG(ERROR) << "GetTokenInformation";
+ return base::string16();
+ }
+
+ TOKEN_USER* user_ptr = reinterpret_cast<TOKEN_USER*>(user.data());
+ return GetStringFromSid(user_ptr->User.Sid);
+}
+
+void CheckAce(PACL acl,
+ DWORD index,
+ BYTE check_ace_type,
+ ACCESS_MASK check_mask,
+ const base::string16& check_sid) {
+ ASSERT_FALSE(check_sid.empty());
+ void* ace_ptr;
+ ASSERT_TRUE(GetAce(acl, index, &ace_ptr));
+
+ ACE_HEADER* header = static_cast<ACE_HEADER*>(ace_ptr);
+ ASSERT_EQ(check_ace_type, header->AceType);
+ ASSERT_EQ(0, header->AceFlags);
+
+ PSID sid = nullptr;
+ ACCESS_MASK mask = 0;
+ switch (header->AceType) {
+ case ACCESS_ALLOWED_ACE_TYPE: {
+ ACCESS_ALLOWED_ACE* allowed_ace =
+ static_cast<ACCESS_ALLOWED_ACE*>(ace_ptr);
+ sid = &allowed_ace->SidStart;
+ mask = allowed_ace->Mask;
+ } break;
+ case SYSTEM_MANDATORY_LABEL_ACE_TYPE: {
+ SYSTEM_MANDATORY_LABEL_ACE* label_ace =
+ static_cast<SYSTEM_MANDATORY_LABEL_ACE*>(ace_ptr);
+ sid = &label_ace->SidStart;
+ mask = label_ace->Mask;
+ } break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ ASSERT_EQ(check_mask, mask);
+ ASSERT_EQ(check_sid, GetStringFromSid(sid));
+}
+
+TEST(SecurityDescriptor, NamedPipeDefault) {
+ const void* sec_desc = GetSecurityDescriptorForNamedPipeInstance(nullptr);
+
+ PACL acl;
+ BOOL acl_present;
+ BOOL acl_defaulted;
+ ASSERT_TRUE(GetSecurityDescriptorDacl(
+ const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));
+ ASSERT_EQ(3, acl->AceCount);
+ CheckAce(acl, 0, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, GetUserSidString());
+ // Check SYSTEM user SID.
+ CheckAce(acl, 1, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, L"S-1-5-18");
+ // Check ALL APPLICATION PACKAGES group SID.
+ CheckAce(acl,
+ 2,
+ ACCESS_ALLOWED_ACE_TYPE,
+ GENERIC_READ | GENERIC_WRITE,
+ L"S-1-15-2-1");
+
+ ASSERT_TRUE(GetSecurityDescriptorSacl(
+ const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));
+ ASSERT_EQ(1, acl->AceCount);
+ CheckAce(acl, 0, SYSTEM_MANDATORY_LABEL_ACE_TYPE, 0, L"S-1-16-0");
+}
+
TEST(SecurityDescriptor, MatchesAdvapi32) {
// This security descriptor is built manually in the connection code to avoid
// calling the advapi32 functions. Verify that it returns the same thing as
@@ -43,7 +147,7 @@
size_t created_len;
const void* const created =
- GetSecurityDescriptorForNamedPipeInstance(&created_len);
+ GetFallbackSecurityDescriptorForNamedPipeInstance(&created_len);
ASSERT_EQ(created_len, sec_desc_len);
EXPECT_EQ(memcmp(sec_desc, created, sec_desc_len), 0);
}
diff --git a/util/win/safe_terminate_process_test.cc b/util/win/safe_terminate_process_test.cc
index 3aba159..d2e4b6d 100644
--- a/util/win/safe_terminate_process_test.cc
+++ b/util/win/safe_terminate_process_test.cc
@@ -22,6 +22,7 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/stl_util.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
@@ -91,6 +92,8 @@
DISALLOW_COPY_AND_ASSIGN(ScopedExecutablePatch);
};
+// SafeTerminateProcess is calling convention specific only for x86.
+#if defined(ARCH_CPU_X86_FAMILY)
TEST(SafeTerminateProcess, PatchBadly) {
// This is a test of SafeTerminateProcess(), but it doesn’t actually terminate
// anything. Instead, it works with a process handle for the current process
@@ -146,7 +149,7 @@
};
void* target = reinterpret_cast<void*>(TerminateProcess);
- ScopedExecutablePatch executable_patch(target, patch, arraysize(patch));
+ ScopedExecutablePatch executable_patch(target, patch, base::size(patch));
// Make sure that SafeTerminateProcess() can be called. Since it’s been
// patched with a no-op stub, GetLastError() shouldn’t be modified.
@@ -161,6 +164,7 @@
EXPECT_FALSE(SafeTerminateProcess(process, 0));
EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));
}
+#endif // ARCH_CPU_X86_FAMILY
TEST(SafeTerminateProcess, TerminateChild) {
base::FilePath child_executable =
diff --git a/util/win/scoped_registry_key.h b/util/win/scoped_registry_key.h
new file mode 100644
index 0000000..4393dc2
--- /dev/null
+++ b/util/win/scoped_registry_key.h
@@ -0,0 +1,34 @@
+// Copyright 2019 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
+#define CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
+
+#include <windows.h>
+
+#include "base/scoped_generic.h"
+
+namespace crashpad {
+
+struct ScopedRegistryKeyCloseTraits {
+ static HKEY InvalidValue() { return nullptr; }
+ static void Free(HKEY key) { RegCloseKey(key); }
+};
+
+using ScopedRegistryKey =
+ base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_
\ No newline at end of file