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:
Joshua Peraza 2018-01-23 14:14:06 -08:00 committed by Commit Bot
parent c9b41a4d15
commit 24f07f7c43
15 changed files with 750 additions and 42 deletions

View File

@ -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_

View File

@ -32,6 +32,12 @@ enum CPUArchitecture {
//! \brief x86_64.
kCPUArchitectureX86_64,
//! \brief 32-bit ARM.
kCPUArchitectureARM,
//! \brief 64-bit ARM.
kCPUArchitectureARM64
};
} // namespace crashpad

View File

@ -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;

View File

@ -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;
};
};

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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_;

View File

@ -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

View File

@ -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_

View File

@ -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,

View File

@ -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

View File

@ -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_;

View File

@ -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;

View File

@ -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;