mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-28 15:50:26 +08:00
809939c9d1
This gets all production code for Chrome building, excluding tests. There aren’t any guarantees that anything works yet. This is mostly a lot of CPU context shuffling. In contrast to macOS on x86, there’s no need to support 32-bit arm on macOS, because this new platform is 64-bit-only from its inception. Bug: crashpad:345 Change-Id: I187239b6a969005a3458af7fe30c44147a57f95f Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2285961 Reviewed-by: Robert Sesek <rsesek@chromium.org> Commit-Queue: Mark Mentovai <mark@chromium.org>
277 lines
9.9 KiB
C++
277 lines
9.9 KiB
C++
// 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_;
|
||
arm_debug_state64_t empty_debug_state = {};
|
||
InitializeCPUContextARM64(&context_arm64_,
|
||
THREAD_STATE_NONE,
|
||
nullptr,
|
||
0,
|
||
&mcontext->__ss,
|
||
&mcontext->__ns,
|
||
&empty_debug_state);
|
||
#else
|
||
#error Port to your CPU architecture
|
||
#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;
|
||
arm_debug_state64_t debug_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;
|
||
mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;
|
||
const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
|
||
const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
|
||
const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_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 << ")";
|
||
}
|
||
|
||
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 << ")";
|
||
}
|
||
|
||
#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,
|
||
&debug_state);
|
||
#else
|
||
#error Port to your CPU architecture
|
||
#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
|