diff --git a/compat/linux/signal.h b/compat/linux/signal.h index 62b9c086..4e1cdc1f 100644 --- a/compat/linux/signal.h +++ b/compat/linux/signal.h @@ -18,10 +18,43 @@ #include_next // 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_ diff --git a/snapshot/cpu_architecture.h b/snapshot/cpu_architecture.h index 208e98f2..6116d4a1 100644 --- a/snapshot/cpu_architecture.h +++ b/snapshot/cpu_architecture.h @@ -32,6 +32,12 @@ enum CPUArchitecture { //! \brief x86_64. kCPUArchitectureX86_64, + + //! \brief 32-bit ARM. + kCPUArchitectureARM, + + //! \brief 64-bit ARM. + kCPUArchitectureARM64 }; } // namespace crashpad diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index 5c964480..8d83e638 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -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; diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index ba3ac181..3160b0eb 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -18,6 +18,7 @@ #include #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; }; }; diff --git a/snapshot/linux/cpu_context_linux.cc b/snapshot/linux/cpu_context_linux.cc index 26c65be1..1a6589f8 100644 --- a/snapshot/linux/cpu_context_linux.cc +++ b/snapshot/linux/cpu_context_linux.cc @@ -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 diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 092762c2..30bb205d 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -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 diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index 1fcbced4..498b1f79 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -67,7 +67,10 @@ bool ExceptionSnapshotLinux::ReadContext( 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( ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64); return true; } + +#elif defined(ARCH_CPU_ARM_FAMILY) + +template <> +bool ExceptionSnapshotLinux::ReadContext( + 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, 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, 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( + 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, 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, 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, diff --git a/snapshot/linux/exception_snapshot_linux.h b/snapshot/linux/exception_snapshot_linux.h index a744356d..73949668 100644 --- a/snapshot/linux/exception_snapshot_linux.h +++ b/snapshot/linux/exception_snapshot_linux.h @@ -73,12 +73,15 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { template 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 codes_; uint64_t thread_id_; diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index bbccf7d8..24f0ef52 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -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(&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(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( + 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( + 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( + 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 diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index d8d5f69d..0476fdfd 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -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 #include #include +#include + #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 +struct SignalStack { + typename Traits::Address stack_pointer; + uint32_t flags; + typename Traits::UInteger32_64Only padding; + typename Traits::Size size; +}; + +template +struct Sigset {}; + +template +struct Sigset< + Traits, + typename std::enable_if::value>::type> { + uint64_t val; +}; + +template +struct Sigset< + Traits, + typename std::enable_if::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 -struct SignalStack { - typename Traits::Address stack_pointer; - uint32_t flags; - typename Traits::UInteger32_64Only padding; - typename Traits::Size size; -}; - -template -struct Sigset {}; - -template <> -struct Sigset { - uint64_t val; -}; - -#if defined(OS_ANDROID) -template <> -struct Sigset { - uint64_t val; -}; -#else -template <> -struct Sigset { - ContextTraits64::ULong val[16]; -}; -#endif // OS_ANDROID - template 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 +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + typename Traits::MContext32 mcontext32; + Sigset 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, mcontext32) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext, reserved) == + offsetof(ucontext_t, uc_regspace), + "regspace offset mismatch"); + +#elif defined(ARCH_CPU_ARM64) +static_assert(offsetof(UContext, mcontext64) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext, 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_ diff --git a/snapshot/linux/system_snapshot_linux.cc b/snapshot/linux/system_snapshot_linux.cc index f3ed99be..c9c64383 100644 --- a/snapshot/linux/system_snapshot_linux.cc +++ b/snapshot/linux/system_snapshot_linux.cc @@ -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, diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc index dcfe5c40..f465a59c 100644 --- a/snapshot/linux/thread_snapshot_linux.cc +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -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 diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index e0ce7adb..1ba291dc 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -58,14 +58,17 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { std::vector 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_; diff --git a/util/linux/thread_info.h b/util/linux/thread_info.h index 4208be67..94424dd4 100644 --- a/util/linux/thread_info.h +++ b/util/linux/thread_info.h @@ -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; diff --git a/util/linux/traits.h b/util/linux/traits.h index ca957681..dc5463d8 100644 --- a/util/linux/traits.h +++ b/util/linux/traits.h @@ -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;