mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
linux: Enable ARM family exception and thread snapshots
ARM references: http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/asm/ucontext.h http://elixir.free-electrons.com/linux/latest/source/arch/arm/kernel/signal.c#L185 ARM64 references: http://elixir.free-electrons.com/linux/latest/source/arch/arm64/include/uapi/asm/sigcontext.h http://elixir.free-electrons.com/linux/latest/source/arch/arm64/kernel/signal.c#L371 Bug: crashpad:30 Change-Id: I53f235b5826607db260bd1e43a819a93284843f5 Reviewed-on: https://chromium-review.googlesource.com/865435 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
c9b41a4d15
commit
24f07f7c43
@ -18,10 +18,43 @@
|
||||
#include_next <signal.h>
|
||||
|
||||
// Missing from glibc and bionic-x86_64
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#if !defined(X86_FXSR_MAGIC)
|
||||
#define X86_FXSR_MAGIC 0x0000
|
||||
#endif
|
||||
#endif // __x86_64__ || __i386__
|
||||
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
|
||||
#if !defined(FPSIMD_MAGIC)
|
||||
#define FPSIMD_MAGIC 0x46508001
|
||||
#endif
|
||||
|
||||
#if !defined(ESR_MAGIC)
|
||||
#define ESR_MAGIC 0x45535201
|
||||
#endif
|
||||
|
||||
#if !defined(EXTRA_MAGIC)
|
||||
#define EXTRA_MAGIC 0x45585401
|
||||
#endif
|
||||
|
||||
#if !defined(VFP_MAGIC)
|
||||
#define VFP_MAGIC 0x56465001
|
||||
#endif
|
||||
|
||||
#if !defined(CRUNCH_MAGIC)
|
||||
#define CRUNCH_MAGIC 0x5065cf03
|
||||
#endif
|
||||
|
||||
#if !defined(DUMMY_MAGIC)
|
||||
#define DUMMY_MAGIC 0xb0d9ed01
|
||||
#endif
|
||||
|
||||
#if !defined(IWMMXT_MAGIC)
|
||||
#define IWMMXT_MAGIC 0x12ef842a
|
||||
#endif
|
||||
|
||||
#endif // __aarch64__ || __arm__
|
||||
|
||||
#endif // CRASHPAD_COMPAT_LINUX_SIGNAL_H_
|
||||
|
@ -32,6 +32,12 @@ enum CPUArchitecture {
|
||||
|
||||
//! \brief x86_64.
|
||||
kCPUArchitectureX86_64,
|
||||
|
||||
//! \brief 32-bit ARM.
|
||||
kCPUArchitectureARM,
|
||||
|
||||
//! \brief 64-bit ARM.
|
||||
kCPUArchitectureARM64
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -164,6 +164,10 @@ uint64_t CPUContext::InstructionPointer() const {
|
||||
return x86->eip;
|
||||
case kCPUArchitectureX86_64:
|
||||
return x86_64->rip;
|
||||
case kCPUArchitectureARM:
|
||||
return arm->pc;
|
||||
case kCPUArchitectureARM64:
|
||||
return arm64->pc;
|
||||
default:
|
||||
NOTREACHED();
|
||||
return ~0ull;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "snapshot/cpu_architecture.h"
|
||||
#include "util/numeric/int128.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -258,6 +259,53 @@ struct CPUContextX86_64 {
|
||||
uint64_t dr7;
|
||||
};
|
||||
|
||||
//! \brief A context structure carrying ARM CPU state.
|
||||
struct CPUContextARM {
|
||||
uint32_t regs[11];
|
||||
uint32_t fp; // r11
|
||||
uint32_t ip; // r12
|
||||
uint32_t sp; // r13
|
||||
uint32_t lr; // r14
|
||||
uint32_t pc; // r15
|
||||
uint32_t cpsr;
|
||||
|
||||
struct {
|
||||
struct fp_reg {
|
||||
uint32_t sign1 : 1;
|
||||
uint32_t unused : 15;
|
||||
uint32_t sign2 : 1;
|
||||
uint32_t exponent : 14;
|
||||
uint32_t j : 1;
|
||||
uint32_t mantissa1 : 31;
|
||||
uint32_t mantisss0 : 32;
|
||||
} fpregs[8];
|
||||
uint32_t fpsr : 32;
|
||||
uint32_t fpcr : 32;
|
||||
uint8_t type[8];
|
||||
uint32_t init_flag;
|
||||
} fpa_regs;
|
||||
|
||||
struct {
|
||||
uint64_t vfp[32];
|
||||
uint32_t fpscr;
|
||||
} vfp_regs;
|
||||
|
||||
bool have_fpa_regs;
|
||||
bool have_vfp_regs;
|
||||
};
|
||||
|
||||
//! \brief A context structure carrying ARM64 CPU state.
|
||||
struct CPUContextARM64 {
|
||||
uint64_t regs[31];
|
||||
uint64_t sp;
|
||||
uint64_t pc;
|
||||
uint64_t pstate;
|
||||
|
||||
uint128_struct fpsimd[32];
|
||||
uint32_t fpsr;
|
||||
uint32_t fpcr;
|
||||
};
|
||||
|
||||
//! \brief A context structure capable of carrying the context of any supported
|
||||
//! CPU architecture.
|
||||
struct CPUContext {
|
||||
@ -274,6 +322,8 @@ struct CPUContext {
|
||||
union {
|
||||
CPUContextX86* x86;
|
||||
CPUContextX86_64* x86_64;
|
||||
CPUContextARM* arm;
|
||||
CPUContextARM64* arm64;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -151,9 +151,92 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,
|
||||
context->dr6 = 0;
|
||||
context->dr7 = 0;
|
||||
}
|
||||
#else
|
||||
#error Port.
|
||||
#endif // ARCH_CPU_X86_FAMILY || DOXYGEN
|
||||
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,
|
||||
const FloatContext::f32_t& float_context,
|
||||
CPUContextARM* context) {
|
||||
static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
|
||||
"registers size mismatch");
|
||||
memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));
|
||||
context->fp = thread_context.fp;
|
||||
context->ip = thread_context.ip;
|
||||
context->sp = thread_context.sp;
|
||||
context->lr = thread_context.lr;
|
||||
context->pc = thread_context.pc;
|
||||
context->cpsr = thread_context.cpsr;
|
||||
|
||||
static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp),
|
||||
"vfp size mismatch");
|
||||
context->have_vfp_regs = float_context.have_vfp;
|
||||
if (float_context.have_vfp) {
|
||||
memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs));
|
||||
}
|
||||
|
||||
static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs),
|
||||
"fpregs size mismatch");
|
||||
context->have_fpa_regs = float_context.have_fpregs;
|
||||
if (float_context.have_fpregs) {
|
||||
memcpy(
|
||||
&context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs));
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM_NoFloatingPoint(
|
||||
const SignalThreadContext32& thread_context,
|
||||
CPUContextARM* context) {
|
||||
static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
|
||||
"registers size mismatch");
|
||||
memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));
|
||||
context->fp = thread_context.fp;
|
||||
context->ip = thread_context.ip;
|
||||
context->sp = thread_context.sp;
|
||||
context->lr = thread_context.lr;
|
||||
context->pc = thread_context.pc;
|
||||
context->cpsr = thread_context.cpsr;
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,
|
||||
const FloatContext::f64_t& float_context,
|
||||
CPUContextARM64* context) {
|
||||
InitializeCPUContextARM64_NoFloatingPoint(thread_context, context);
|
||||
|
||||
static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),
|
||||
"fpsimd context size mismatch");
|
||||
memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));
|
||||
context->fpsr = float_context.fpsr;
|
||||
context->fpcr = float_context.fpcr;
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM64_NoFloatingPoint(
|
||||
const ThreadContext::t64_t& thread_context,
|
||||
CPUContextARM64* context) {
|
||||
static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
|
||||
"gpr context size mismtach");
|
||||
memcpy(context->regs, thread_context.regs, sizeof(context->regs));
|
||||
context->sp = thread_context.sp;
|
||||
context->pc = thread_context.pc;
|
||||
context->pstate = thread_context.pstate;
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM64_OnlyFPSIMD(
|
||||
const SignalFPSIMDContext& float_context,
|
||||
CPUContextARM64* context) {
|
||||
static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),
|
||||
"fpsimd context size mismatch");
|
||||
memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));
|
||||
context->fpsr = float_context.fpsr;
|
||||
context->fpcr = float_context.fpcr;
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context) {
|
||||
memset(context->fpsimd, 0, sizeof(context->fpsimd));
|
||||
context->fpsr = 0;
|
||||
context->fpcr = 0;
|
||||
}
|
||||
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
@ -68,10 +68,73 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,
|
||||
const SignalFloatContext64& float_context,
|
||||
CPUContextX86_64* context);
|
||||
//! \}
|
||||
#else
|
||||
#error Port. // TODO(jperaza): ARM
|
||||
|
||||
#endif // ARCH_CPU_X86_FAMILY || DOXYGEN
|
||||
|
||||
#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
|
||||
|
||||
//! \brief Initializes a CPUContextARM structure from native context structures
|
||||
//! on Linux.
|
||||
//!
|
||||
//! \param[in] thread_context The native thread context.
|
||||
//! \param[in] float_context The native float context.
|
||||
//! \param[out] context The CPUContextARM structure to initialize.
|
||||
void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,
|
||||
const FloatContext::f32_t& float_context,
|
||||
CPUContextARM* context);
|
||||
|
||||
//! \brief Initializes GPR state in a CPUContextARM from a native signal context
|
||||
//! structure on Linux.
|
||||
//!
|
||||
//! Floating point state is not initialized.
|
||||
//!
|
||||
//! \param[in] thread_context The native thread context.
|
||||
//! \param[out] context The CPUContextARM structure to initialize.
|
||||
void InitializeCPUContextARM_NoFloatingPoint(
|
||||
const SignalThreadContext32& thread_context,
|
||||
CPUContextARM* context);
|
||||
|
||||
//! \brief Initializes a CPUContextARM64 structure from native context
|
||||
//! structures on Linux.
|
||||
//!
|
||||
//! \param[in] thread_context The native thread context.
|
||||
//! \param[in] float_context The native float context.
|
||||
//! \param[out] context The CPUContextARM64 structure to initialize.
|
||||
void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,
|
||||
const FloatContext::f64_t& float_context,
|
||||
CPUContextARM64* context);
|
||||
|
||||
//! \brief Initializes GPR state in a CPUContextARM64 from a native context
|
||||
//! structure on Linux.
|
||||
//!
|
||||
//! Floating point state is not initialized.
|
||||
//!
|
||||
//! \param[in] thread_context The native thread context.
|
||||
//! \param[out] context The CPUContextARM64 structure to initialize.
|
||||
void InitializeCPUContextARM64_NoFloatingPoint(
|
||||
const ThreadContext::t64_t& thread_context,
|
||||
CPUContextARM64* context);
|
||||
|
||||
//! \brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd
|
||||
//! signal context structure on Linux.
|
||||
//!
|
||||
//! General purpose registers are not initialized.
|
||||
//!
|
||||
//! \param[in] thread_context The native fpsimd context.
|
||||
//! \param[out] context The CPUContextARM64 structure to initialize.
|
||||
void InitializeCPUContextARM64_OnlyFPSIMD(
|
||||
const SignalFPSIMDContext& float_context,
|
||||
CPUContextARM64* context);
|
||||
|
||||
//! \brief Initializes FPSIMD state in a CPUContextARM64 to zero.
|
||||
//!
|
||||
//! General purpose registers are not initialized.
|
||||
//!
|
||||
//! \param[out] context The CPUContextARM64 structure to initialize.
|
||||
void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context);
|
||||
|
||||
#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
|
@ -67,7 +67,10 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(
|
||||
context_.x86);
|
||||
|
||||
} else {
|
||||
DCHECK_EQ(ucontext.fprs.magic, 0xffff);
|
||||
if (ucontext.fprs.magic != 0xffff) {
|
||||
LOG(ERROR) << "unexpected magic 0x" << std::hex << ucontext.fprs.magic;
|
||||
return false;
|
||||
}
|
||||
InitializeCPUContextX86(
|
||||
ucontext.mcontext.gprs, ucontext.fprs, context_.x86);
|
||||
}
|
||||
@ -91,6 +94,163 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
|
||||
ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64);
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
template <>
|
||||
bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(
|
||||
ProcessReader* reader,
|
||||
LinuxVMAddress context_address) {
|
||||
context_.architecture = kCPUArchitectureARM;
|
||||
context_.arm = &context_union_.arm;
|
||||
|
||||
CPUContextARM* dest_context = context_.arm;
|
||||
ProcessMemory* memory = reader->Memory();
|
||||
|
||||
LinuxVMAddress gprs_address =
|
||||
context_address + offsetof(UContext<ContextTraits32>, mcontext32) +
|
||||
offsetof(MContext32, gprs);
|
||||
|
||||
SignalThreadContext32 thread_context;
|
||||
if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {
|
||||
LOG(ERROR) << "Couldn't read gprs";
|
||||
return false;
|
||||
}
|
||||
InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context);
|
||||
|
||||
dest_context->have_fpa_regs = false;
|
||||
|
||||
LinuxVMAddress reserved_address =
|
||||
context_address + offsetof(UContext<ContextTraits32>, reserved);
|
||||
if ((reserved_address & 7) != 0) {
|
||||
LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr VMSize kMaxContextSpace = 1024;
|
||||
|
||||
ProcessMemoryRange range;
|
||||
if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest_context->have_vfp_regs = false;
|
||||
do {
|
||||
CoprocessorContextHead head;
|
||||
if (!range.Read(reserved_address, sizeof(head), &head)) {
|
||||
LOG(ERROR) << "missing context terminator";
|
||||
return false;
|
||||
}
|
||||
reserved_address += sizeof(head);
|
||||
|
||||
switch (head.magic) {
|
||||
case VFP_MAGIC:
|
||||
if (head.size != sizeof(SignalVFPContext) + sizeof(head)) {
|
||||
LOG(ERROR) << "unexpected vfp context size " << head.size;
|
||||
return false;
|
||||
}
|
||||
static_assert(
|
||||
sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs),
|
||||
"vfp context size mismatch");
|
||||
if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp),
|
||||
sizeof(dest_context->vfp_regs),
|
||||
&dest_context->vfp_regs)) {
|
||||
LOG(ERROR) << "Couldn't read vfp";
|
||||
return false;
|
||||
}
|
||||
dest_context->have_vfp_regs = true;
|
||||
return true;
|
||||
|
||||
case CRUNCH_MAGIC:
|
||||
case IWMMXT_MAGIC:
|
||||
case DUMMY_MAGIC:
|
||||
reserved_address += head.size - sizeof(head);
|
||||
continue;
|
||||
|
||||
case 0:
|
||||
return true;
|
||||
|
||||
default:
|
||||
LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic;
|
||||
return false;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
|
||||
ProcessReader* reader,
|
||||
LinuxVMAddress context_address) {
|
||||
context_.architecture = kCPUArchitectureARM64;
|
||||
context_.arm64 = &context_union_.arm64;
|
||||
|
||||
CPUContextARM64* dest_context = context_.arm64;
|
||||
ProcessMemory* memory = reader->Memory();
|
||||
|
||||
LinuxVMAddress gprs_address =
|
||||
context_address + offsetof(UContext<ContextTraits64>, mcontext64) +
|
||||
offsetof(MContext64, gprs);
|
||||
|
||||
ThreadContext::t64_t thread_context;
|
||||
if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {
|
||||
LOG(ERROR) << "Couldn't read gprs";
|
||||
return false;
|
||||
}
|
||||
InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context);
|
||||
|
||||
LinuxVMAddress reserved_address =
|
||||
context_address + offsetof(UContext<ContextTraits64>, reserved);
|
||||
if ((reserved_address & 15) != 0) {
|
||||
LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address;
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr VMSize kMaxContextSpace = 4096;
|
||||
|
||||
ProcessMemoryRange range;
|
||||
if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
CoprocessorContextHead head;
|
||||
if (!range.Read(reserved_address, sizeof(head), &head)) {
|
||||
LOG(ERROR) << "missing context terminator";
|
||||
return false;
|
||||
}
|
||||
reserved_address += sizeof(head);
|
||||
|
||||
switch (head.magic) {
|
||||
case FPSIMD_MAGIC:
|
||||
if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) {
|
||||
LOG(ERROR) << "unexpected fpsimd context size " << head.size;
|
||||
return false;
|
||||
}
|
||||
SignalFPSIMDContext fpsimd;
|
||||
if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) {
|
||||
LOG(ERROR) << "Couldn't read fpsimd " << head.size;
|
||||
return false;
|
||||
}
|
||||
InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context);
|
||||
return true;
|
||||
|
||||
case ESR_MAGIC:
|
||||
case EXTRA_MAGIC:
|
||||
reserved_address += head.size - sizeof(head);
|
||||
continue;
|
||||
|
||||
case 0:
|
||||
LOG(WARNING) << "fpsimd not found";
|
||||
InitializeCPUContextARM64_ClearFPSIMD(dest_context);
|
||||
return true;
|
||||
|
||||
default:
|
||||
LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic;
|
||||
return false;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
|
||||
bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader,
|
||||
|
@ -73,12 +73,15 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot {
|
||||
template <typename Traits>
|
||||
bool ReadContext(ProcessReader* reader, LinuxVMAddress context_address);
|
||||
|
||||
#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_ARM_FAMILY)
|
||||
CPUContextARM arm;
|
||||
CPUContextARM64 arm64;
|
||||
#endif
|
||||
} context_union_;
|
||||
CPUContext context_;
|
||||
std::vector<uint64_t> codes_;
|
||||
uint64_t thread_id_;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/cpu_architecture.h"
|
||||
#include "snapshot/linux/process_reader.h"
|
||||
#include "snapshot/linux/signal_context.h"
|
||||
#include "sys/syscall.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/linux/fake_ptrace_connection.h"
|
||||
@ -92,6 +93,176 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
|
||||
reinterpret_cast<const char*>(&expected.__fpregs_mem)[byte_offset]);
|
||||
}
|
||||
}
|
||||
#elif defined(ARCH_CPU_ARMEL)
|
||||
// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of
|
||||
// the different possible coprocessor contexts at once. However, the ABI allows
|
||||
// it and the native regspace may be expanded in the future. Append some extra
|
||||
// space so this is testable now.
|
||||
struct NativeCPUContext {
|
||||
ucontext_t ucontext;
|
||||
char extra[1024];
|
||||
};
|
||||
|
||||
struct CrunchContext {
|
||||
uint32_t mvdx[16][2];
|
||||
uint32_t mvax[4][3];
|
||||
uint32_t dspsc[2];
|
||||
};
|
||||
|
||||
struct IWMMXTContext {
|
||||
uint32_t save[38];
|
||||
};
|
||||
|
||||
struct TestCoprocessorContext {
|
||||
struct {
|
||||
internal::CoprocessorContextHead head;
|
||||
CrunchContext context;
|
||||
} crunch;
|
||||
struct {
|
||||
internal::CoprocessorContextHead head;
|
||||
IWMMXTContext context;
|
||||
} iwmmxt;
|
||||
struct {
|
||||
internal::CoprocessorContextHead head;
|
||||
IWMMXTContext context;
|
||||
} dummy;
|
||||
struct {
|
||||
internal::CoprocessorContextHead head;
|
||||
internal::SignalVFPContext context;
|
||||
} vfp;
|
||||
internal::CoprocessorContextHead terminator;
|
||||
};
|
||||
|
||||
void InitializeContext(NativeCPUContext* context) {
|
||||
memset(context, 'x', sizeof(*context));
|
||||
|
||||
for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address -
|
||||
&context->ucontext.uc_mcontext.arm_r0);
|
||||
++index) {
|
||||
(&context->ucontext.uc_mcontext.arm_r0)[index] = index;
|
||||
}
|
||||
|
||||
static_assert(
|
||||
sizeof(TestCoprocessorContext) <=
|
||||
sizeof(context->ucontext.uc_regspace) + sizeof(context->extra),
|
||||
"Insufficient context space");
|
||||
auto test_context =
|
||||
reinterpret_cast<TestCoprocessorContext*>(context->ucontext.uc_regspace);
|
||||
|
||||
test_context->crunch.head.magic = CRUNCH_MAGIC;
|
||||
test_context->crunch.head.size = sizeof(test_context->crunch);
|
||||
memset(
|
||||
&test_context->crunch.context, 'c', sizeof(test_context->crunch.context));
|
||||
|
||||
test_context->iwmmxt.head.magic = IWMMXT_MAGIC;
|
||||
test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt);
|
||||
memset(
|
||||
&test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context));
|
||||
|
||||
test_context->dummy.head.magic = DUMMY_MAGIC;
|
||||
test_context->dummy.head.size = sizeof(test_context->dummy);
|
||||
memset(
|
||||
&test_context->dummy.context, 'd', sizeof(test_context->dummy.context));
|
||||
|
||||
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);
|
||||
++reg) {
|
||||
test_context->vfp.context.vfp.fpregs[reg] = reg;
|
||||
}
|
||||
test_context->vfp.context.vfp.fpscr = 42;
|
||||
|
||||
test_context->terminator.magic = 0;
|
||||
test_context->terminator.size = 0;
|
||||
}
|
||||
|
||||
void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
|
||||
EXPECT_EQ(actual.architecture, kCPUArchitectureARM);
|
||||
|
||||
EXPECT_EQ(memcmp(actual.arm->regs,
|
||||
&expected.ucontext.uc_mcontext.arm_r0,
|
||||
sizeof(actual.arm->regs)),
|
||||
0);
|
||||
EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp);
|
||||
EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip);
|
||||
EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp);
|
||||
EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr);
|
||||
EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc);
|
||||
EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr);
|
||||
|
||||
EXPECT_FALSE(actual.arm->have_fpa_regs);
|
||||
|
||||
EXPECT_TRUE(actual.arm->have_vfp_regs);
|
||||
|
||||
auto test_context = reinterpret_cast<const TestCoprocessorContext*>(
|
||||
expected.ucontext.uc_regspace);
|
||||
|
||||
EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp,
|
||||
&test_context->vfp.context.vfp,
|
||||
sizeof(actual.arm->vfp_regs.vfp)),
|
||||
0);
|
||||
}
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
using NativeCPUContext = ucontext_t;
|
||||
|
||||
struct TestCoprocessorContext {
|
||||
esr_context esr;
|
||||
fpsimd_context fpsimd;
|
||||
_aarch64_ctx terminator;
|
||||
};
|
||||
|
||||
void InitializeContext(NativeCPUContext* context) {
|
||||
memset(context, 'x', sizeof(*context));
|
||||
|
||||
for (size_t index = 0; index < arraysize(context->uc_mcontext.regs);
|
||||
++index) {
|
||||
context->uc_mcontext.regs[index] = index;
|
||||
}
|
||||
context->uc_mcontext.sp = 1;
|
||||
context->uc_mcontext.pc = 2;
|
||||
context->uc_mcontext.pstate = 3;
|
||||
|
||||
auto test_context = reinterpret_cast<TestCoprocessorContext*>(
|
||||
context->uc_mcontext.__reserved);
|
||||
|
||||
test_context->esr.head.magic = ESR_MAGIC;
|
||||
test_context->esr.head.size = sizeof(test_context->esr);
|
||||
memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr));
|
||||
|
||||
test_context->fpsimd.head.magic = FPSIMD_MAGIC;
|
||||
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) {
|
||||
test_context->fpsimd.vregs[reg] = reg;
|
||||
}
|
||||
|
||||
test_context->terminator.magic = 0;
|
||||
test_context->terminator.size = 0;
|
||||
}
|
||||
|
||||
void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
|
||||
EXPECT_EQ(actual.architecture, kCPUArchitectureARM64);
|
||||
|
||||
EXPECT_EQ(memcmp(actual.arm64->regs,
|
||||
expected.uc_mcontext.regs,
|
||||
sizeof(actual.arm64->regs)),
|
||||
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);
|
||||
|
||||
auto test_context = reinterpret_cast<const TestCoprocessorContext*>(
|
||||
expected.uc_mcontext.__reserved);
|
||||
|
||||
EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr);
|
||||
EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr);
|
||||
EXPECT_EQ(memcmp(actual.arm64->fpsimd,
|
||||
&test_context->fpsimd.vregs,
|
||||
sizeof(actual.arm64->fpsimd)),
|
||||
0);
|
||||
}
|
||||
#else
|
||||
#error Port.
|
||||
#endif
|
||||
|
@ -12,13 +12,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
|
||||
#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
|
||||
#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
|
||||
#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "util/linux/thread_info.h"
|
||||
#include "util/linux/traits.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -85,6 +89,35 @@ struct Siginfo {
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct SignalStack {
|
||||
typename Traits::Address stack_pointer;
|
||||
uint32_t flags;
|
||||
typename Traits::UInteger32_64Only padding;
|
||||
typename Traits::Size size;
|
||||
};
|
||||
|
||||
template <typename Traits, typename Enable = void>
|
||||
struct Sigset {};
|
||||
|
||||
template <typename Traits>
|
||||
struct Sigset<
|
||||
Traits,
|
||||
typename std::enable_if<std::is_base_of<Traits32, Traits>::value>::type> {
|
||||
uint64_t val;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct Sigset<
|
||||
Traits,
|
||||
typename std::enable_if<std::is_base_of<Traits64, Traits>::value>::type> {
|
||||
#if defined(OS_ANDROID)
|
||||
uint64_t val;
|
||||
#else
|
||||
typename Traits::ULong val[16];
|
||||
#endif // OS_ANDROID
|
||||
};
|
||||
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
|
||||
struct SignalThreadContext32 {
|
||||
@ -166,34 +199,6 @@ struct MContext {
|
||||
typename Traits::ULong_64Only reserved[8];
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct SignalStack {
|
||||
typename Traits::Address stack_pointer;
|
||||
uint32_t flags;
|
||||
typename Traits::UInteger32_64Only padding;
|
||||
typename Traits::Size size;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct Sigset {};
|
||||
|
||||
template <>
|
||||
struct Sigset<ContextTraits32> {
|
||||
uint64_t val;
|
||||
};
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
template <>
|
||||
struct Sigset<ContextTraits64> {
|
||||
uint64_t val;
|
||||
};
|
||||
#else
|
||||
template <>
|
||||
struct Sigset<ContextTraits64> {
|
||||
ContextTraits64::ULong val[16];
|
||||
};
|
||||
#endif // OS_ANDROID
|
||||
|
||||
template <typename Traits>
|
||||
struct UContext {
|
||||
typename Traits::ULong flags;
|
||||
@ -204,6 +209,96 @@ struct UContext {
|
||||
typename Traits::FloatContext fprs;
|
||||
};
|
||||
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
struct CoprocessorContextHead {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct SignalFPSIMDContext {
|
||||
uint32_t fpsr;
|
||||
uint32_t fpcr;
|
||||
uint128_struct vregs[32];
|
||||
};
|
||||
|
||||
struct SignalVFPContext {
|
||||
FloatContext::f32_t::vfp_t vfp;
|
||||
struct vfp_exc {
|
||||
uint32_t fpexc;
|
||||
uint32_t fpinst;
|
||||
uint32_t fpinst2;
|
||||
} vfp_exc;
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
struct SignalThreadContext32 {
|
||||
uint32_t regs[11];
|
||||
uint32_t fp;
|
||||
uint32_t ip;
|
||||
uint32_t sp;
|
||||
uint32_t lr;
|
||||
uint32_t pc;
|
||||
uint32_t cpsr;
|
||||
};
|
||||
|
||||
using SignalThreadContext64 = ThreadContext::t64_t;
|
||||
|
||||
struct MContext32 {
|
||||
uint32_t trap_no;
|
||||
uint32_t error_code;
|
||||
uint32_t oldmask;
|
||||
SignalThreadContext32 gprs;
|
||||
uint32_t fault_address;
|
||||
};
|
||||
|
||||
struct MContext64 {
|
||||
uint64_t fault_address;
|
||||
SignalThreadContext64 gprs;
|
||||
};
|
||||
|
||||
struct ContextTraits32 : public Traits32 {
|
||||
using MContext32 = MContext32;
|
||||
using MContext64 = Nothing;
|
||||
};
|
||||
|
||||
struct ContextTraits64 : public Traits64 {
|
||||
using MContext32 = Nothing;
|
||||
using MContext64 = MContext64;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct UContext {
|
||||
typename Traits::ULong flags;
|
||||
typename Traits::Address link;
|
||||
SignalStack<Traits> stack;
|
||||
typename Traits::MContext32 mcontext32;
|
||||
Sigset<Traits> sigmask;
|
||||
char padding[128 - sizeof(sigmask)];
|
||||
typename Traits::Char_64Only padding2[8];
|
||||
typename Traits::MContext64 mcontext64;
|
||||
typename Traits::Char_64Only padding3[8];
|
||||
char reserved[0];
|
||||
};
|
||||
|
||||
#if defined(ARCH_CPU_ARMEL)
|
||||
static_assert(offsetof(UContext<ContextTraits32>, mcontext32) ==
|
||||
offsetof(ucontext_t, uc_mcontext),
|
||||
"context offset mismatch");
|
||||
static_assert(offsetof(UContext<ContextTraits32>, reserved) ==
|
||||
offsetof(ucontext_t, uc_regspace),
|
||||
"regspace offset mismatch");
|
||||
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
static_assert(offsetof(UContext<ContextTraits64>, mcontext64) ==
|
||||
offsetof(ucontext_t, uc_mcontext),
|
||||
"context offset mismtach");
|
||||
static_assert(offsetof(UContext<ContextTraits64>, reserved) ==
|
||||
offsetof(ucontext_t, uc_mcontext) +
|
||||
offsetof(mcontext_t, __reserved),
|
||||
"reserved space offset mismtach");
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error Port.
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
@ -213,4 +308,4 @@ struct UContext {
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
|
||||
#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
|
||||
|
@ -197,6 +197,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const {
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
|
||||
: kCPUArchitectureX86;
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
return process_reader_->Is64Bit() ? kCPUArchitectureARM64
|
||||
: kCPUArchitectureARM;
|
||||
#else
|
||||
#error port to your architecture
|
||||
#endif
|
||||
@ -206,6 +209,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
return cpuid_.Revision();
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
|
||||
return 0;
|
||||
#else
|
||||
#error port to your architecture
|
||||
#endif
|
||||
@ -220,6 +226,9 @@ std::string SystemSnapshotLinux::CPUVendor() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
return cpuid_.Vendor();
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
|
||||
return std::string();
|
||||
#else
|
||||
#error port to your architecture
|
||||
#endif
|
||||
@ -264,7 +273,12 @@ uint64_t SystemSnapshotLinux::CPUX86Features() const {
|
||||
|
||||
uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
return cpuid_.ExtendedFeatures();
|
||||
#else
|
||||
NOTREACHED();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const {
|
||||
@ -340,7 +354,14 @@ std::string SystemSnapshotLinux::MachineDescription() const {
|
||||
|
||||
bool SystemSnapshotLinux::NXEnabled() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
return cpuid_.NXEnabled();
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
|
||||
return false;
|
||||
#else
|
||||
#error Port.
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
}
|
||||
|
||||
void SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status,
|
||||
|
@ -56,6 +56,20 @@ bool ThreadSnapshotLinux::Initialize(
|
||||
thread.thread_info.float_context.f32,
|
||||
context_.x86);
|
||||
}
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
if (process_reader->Is64Bit()) {
|
||||
context_.architecture = kCPUArchitectureARM64;
|
||||
context_.arm64 = &context_union_.arm64;
|
||||
InitializeCPUContextARM64(thread.thread_info.thread_context.t64,
|
||||
thread.thread_info.float_context.f64,
|
||||
context_.arm64);
|
||||
} else {
|
||||
context_.architecture = kCPUArchitectureARM;
|
||||
context_.arm = &context_union_.arm;
|
||||
InitializeCPUContextARM(thread.thread_info.thread_context.t32,
|
||||
thread.thread_info.float_context.f32,
|
||||
context_.arm);
|
||||
}
|
||||
#else
|
||||
#error Port.
|
||||
#endif
|
||||
|
@ -58,14 +58,17 @@ class ThreadSnapshotLinux final : public ThreadSnapshot {
|
||||
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_ARM_FAMILY)
|
||||
CPUContextARM arm;
|
||||
CPUContextARM64 arm64;
|
||||
#else
|
||||
#error Port.
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
} context_union_;
|
||||
CPUContext context_;
|
||||
MemorySnapshotLinux stack_;
|
||||
LinuxVMAddress thread_specific_data_address_;
|
||||
|
@ -176,7 +176,7 @@ union FloatContext {
|
||||
} fpregs;
|
||||
|
||||
// Reflects user_vfp in sys/user.h.
|
||||
struct vfp {
|
||||
struct vfp_t {
|
||||
uint64_t fpregs[32];
|
||||
uint32_t fpscr;
|
||||
} vfp;
|
||||
|
@ -26,6 +26,7 @@ struct Traits32 {
|
||||
using ULong = uint32_t;
|
||||
using Clock = Long;
|
||||
using Size = uint32_t;
|
||||
using Char_64Only = Nothing;
|
||||
using ULong_32Only = ULong;
|
||||
using ULong_64Only = Nothing;
|
||||
using UInteger32_64Only = Nothing;
|
||||
@ -38,6 +39,7 @@ struct Traits64 {
|
||||
using ULong = uint64_t;
|
||||
using Clock = Long;
|
||||
using Size = uint64_t;
|
||||
using Char_64Only = char;
|
||||
using ULong_32Only = Nothing;
|
||||
using ULong_64Only = ULong;
|
||||
using UInteger32_64Only = uint32_t;
|
||||
|
Loading…
x
Reference in New Issue
Block a user