Added MIPS support to crashpad.

Modified gyp/gn files to support MIPS targets.

Implemented thread_info, cpu_context, signal context classes for MIPS target.

Addressed MIPS specific signal ordering.

Added "MIPS Technologies, Inc." to AUTHORS file.

Bug: crashpad:232
Change-Id: Ibfc221ba54088e95f984b9dc6be5fd52f86abcc2
Reviewed-on: https://chromium-review.googlesource.com/1064594
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Djordje Golubovic 2018-07-10 11:17:22 +02:00 committed by Commit Bot
parent 3072b4059f
commit cb41ba7471
37 changed files with 1271 additions and 17 deletions

View File

@ -11,3 +11,4 @@ Intel Corporation
Opera Software ASA
Vewd Software AS
LG Electronics, Inc.
MIPS Technologies, Inc.

View File

@ -30,6 +30,13 @@ static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(22);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
#elif defined(__mips__)
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(25);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA_3264 =
static_cast<__ptrace_request>(0xc4);
#define PTRACE_GET_THREAD_AREA_3264 PTRACE_GET_THREAD_AREA_3264
#endif
#endif // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__)

View File

@ -432,6 +432,138 @@ struct MinidumpContextARM64 {
uint128_struct fpsimd[32];
};
//! \brief 32bit MIPS-specifc flags for MinidumpContextMIPS::context_flags.
//! Based on minidump_cpu_mips.h from breakpad
enum MinidumpContextMIPSFlags : uint32_t {
//! \brief Identifies the context structure as MIPSEL.
kMinidumpContextMIPS = 0x00040000,
//! \brief Indicates the validity of integer registers.
//!
//! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and
//! `cause` are valid.
kMinidumpContextMIPSInteger = kMinidumpContextMIPS | 0x00000002,
//! \brief Indicates the validity of floating point registers.
//!
//! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid
kMinidumpContextMIPSFloatingPoint = kMinidumpContextMIPS | 0x00000004,
//! \brief Indicates the validity of DSP registers.
//!
//! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid
kMinidumpContextMIPSDSP = kMinidumpContextMIPS | 0x00000008,
//! \brief Indicates the validity of all registers.
kMinidumpContextMIPSAll = kMinidumpContextMIPSInteger |
kMinidumpContextMIPSFloatingPoint |
kMinidumpContextMIPSDSP,
};
//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file.
struct MinidumpContextMIPS {
uint32_t context_flags;
//! \brief This padding field is included for breakpad compatibility.
uint32_t _pad0;
//! \brief General purpose registers `0`-`31`.
uint64_t regs[32];
//! \brief Multiply/divide result.
uint64_t mdhi, mdlo;
//! \brief DSP registers.
uint32_t hi[3];
uint32_t lo[3];
uint32_t dsp_control;
//! \brief This padding field is included for breakpad compatibility.
uint32_t _pad1;
// \brief cp0 registers.
uint64_t epc;
uint64_t badvaddr;
uint32_t status;
uint32_t cause;
//! \brief FPU registers.
union {
struct {
float _fp_fregs;
uint32_t _fp_pad;
} fregs[32];
double dregs[32];
} fpregs;
//! \brief FPU status register.
uint32_t fpcsr;
//! \brief FPU implementation register.
uint32_t fir;
};
//! \brief 64bit MIPS-specifc flags for MinidumpContextMIPS64::context_flags.
//! Based on minidump_cpu_mips.h from breakpad
enum MinidumpContextMIPS64Flags : uint32_t {
//! \brief Identifies the context structure as MIPS64EL.
kMinidumpContextMIPS64 = 0x00080000,
//! \brief Indicates the validity of integer registers.
//!
//! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and
//! `cause` are valid.
kMinidumpContextMIPS64Integer = kMinidumpContextMIPS64 | 0x00000002,
//! \brief Indicates the validity of floating point registers.
//!
//! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid
kMinidumpContextMIPS64FloatingPoint = kMinidumpContextMIPS64 | 0x00000004,
//! \brief Indicates the validity of DSP registers.
//!
//! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid.
kMinidumpContextMIPS64DSP = kMinidumpContextMIPS64 | 0x00000008,
//! \brief Indicates the validity of all registers.
kMinidumpContextMIPS64All = kMinidumpContextMIPS64Integer |
kMinidumpContextMIPS64FloatingPoint |
kMinidumpContextMIPS64DSP,
};
//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file.
struct MinidumpContextMIPS64 {
uint64_t context_flags;
//! \brief General purpose registers.
uint64_t regs[32];
//! \brief Multiply/divide result.
uint64_t mdhi, mdlo;
//! \brief DSP registers.
uint64_t hi[3];
uint64_t lo[3];
uint64_t dsp_control;
//! \brief cp0 registers.
uint64_t epc;
uint64_t badvaddr;
uint64_t status;
uint64_t cause;
//! \brief FPU registers.
union {
struct {
float _fp_fregs;
uint32_t _fp_pad;
} fregs[32];
double dregs[32];
} fpregs;
//! \brief FPU status register.
uint64_t fpcsr;
//! \brief FPU implementation register.
uint64_t fir;
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_

View File

@ -87,6 +87,20 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
break;
}
case kCPUArchitectureMIPSEL: {
context = std::make_unique<MinidumpContextMIPSWriter>();
reinterpret_cast<MinidumpContextMIPSWriter*>(context.get())
->InitializeFromSnapshot(context_snapshot->mipsel);
break;
}
case kCPUArchitectureMIPS64EL: {
context = std::make_unique<MinidumpContextMIPS64Writer>();
reinterpret_cast<MinidumpContextMIPS64Writer*>(context.get())
->InitializeFromSnapshot(context_snapshot->mips64);
break;
}
default: {
LOG(ERROR) << "unknown context architecture "
<< context_snapshot->architecture;
@ -339,4 +353,101 @@ size_t MinidumpContextARM64Writer::ContextSize() const {
return sizeof(context_);
}
MinidumpContextMIPSWriter::MinidumpContextMIPSWriter()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextMIPS;
}
MinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default;
void MinidumpContextMIPSWriter::InitializeFromSnapshot(
const CPUContextMIPS* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS);
context_.context_flags = kMinidumpContextMIPSAll;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
context_.mdhi = context_snapshot->mdhi;
context_.mdlo = context_snapshot->mdlo;
context_.epc = context_snapshot->cp0_epc;
context_.badvaddr = context_snapshot->cp0_badvaddr;
context_.status = context_snapshot->cp0_status;
context_.cause = context_snapshot->cp0_cause;
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs));
context_.fpcsr = context_snapshot->fpcsr;
context_.fir = context_snapshot->fir;
for (size_t index = 0; index < 3; ++index) {
context_.hi[index] = context_snapshot->hi[index];
context_.lo[index] = context_snapshot->lo[index];
}
context_.dsp_control = context_snapshot->dsp_control;
}
bool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextMIPSWriter::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
MinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextMIPS64;
}
MinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default;
void MinidumpContextMIPS64Writer::InitializeFromSnapshot(
const CPUContextMIPS64* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64);
context_.context_flags = kMinidumpContextMIPS64All;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
context_.mdhi = context_snapshot->mdhi;
context_.mdlo = context_snapshot->mdlo;
context_.epc = context_snapshot->cp0_epc;
context_.badvaddr = context_snapshot->cp0_badvaddr;
context_.status = context_snapshot->cp0_status;
context_.cause = context_snapshot->cp0_cause;
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(context_.fpregs.dregs,
context_snapshot->fpregs.dregs,
sizeof(context_.fpregs.dregs));
context_.fpcsr = context_snapshot->fpcsr;
context_.fir = context_snapshot->fir;
for (size_t index = 0; index < 3; ++index) {
context_.hi[index] = context_snapshot->hi[index];
context_.lo[index] = context_snapshot->lo[index];
}
context_.dsp_control = context_snapshot->dsp_control;
}
bool MinidumpContextMIPS64Writer::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextMIPS64Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
} // namespace crashpad

View File

@ -235,6 +235,86 @@ class MinidumpContextARM64Writer final : public MinidumpContextWriter {
DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer);
};
//! \brief The writer for a MinidumpContextMIPS structure in a minidump file.
class MinidumpContextMIPSWriter final : public MinidumpContextWriter {
public:
MinidumpContextMIPSWriter();
~MinidumpContextMIPSWriter() override;
//! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextMIPS* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextMIPS* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextMIPS context_;
DISALLOW_COPY_AND_ASSIGN(MinidumpContextMIPSWriter);
};
//! \brief The writer for a MinidumpContextMIPS64 structure in a minidump file.
class MinidumpContextMIPS64Writer final : public MinidumpContextWriter {
public:
MinidumpContextMIPS64Writer();
~MinidumpContextMIPS64Writer() override;
//! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot.
//!
//! \param[in] context_snapshot The context snapshot to use as source data.
//!
//! \note Valid in #kStateMutable. No mutation of context() may be done before
//! calling this method, and it is not normally necessary to alter
//! context() after calling this method.
void InitializeFromSnapshot(const CPUContextMIPS64* context_snapshot);
//! \brief Returns a pointer to the context structure that this object will
//! write.
//!
//! \attention This returns a non-`const` pointer to this objects private
//! data so that a caller can populate the context structure directly.
//! This is done because providing setter interfaces to each field in the
//! context structure would be unwieldy and cumbersome. Care must be taken
//! to populate the context structure correctly. The context structure
//! must only be modified while this object is in the #kStateMutable
//! state.
MinidumpContextMIPS64* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextMIPS64 context_;
DISALLOW_COPY_AND_ASSIGN(MinidumpContextMIPS64Writer);
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_

View File

@ -183,6 +183,36 @@ TEST(MinidumpContextWriter, ARM64_FromSnapshot) {
context, ExpectMinidumpContextARM64, kSeed);
}
TEST(MinidumpContextWriter, MIPS_Zeros) {
EmptyContextTest<MinidumpContextMIPSWriter, MinidumpContextMIPS>(
ExpectMinidumpContextMIPS);
}
TEST(MinidumpContextWriter, MIPS64_Zeros) {
EmptyContextTest<MinidumpContextMIPS64Writer, MinidumpContextMIPS64>(
ExpectMinidumpContextMIPS64);
}
TEST(MinidumpContextWriter, MIPS_FromSnapshot) {
constexpr uint32_t kSeed = 32;
CPUContextMIPS context_mips;
CPUContext context;
context.mipsel = &context_mips;
InitializeCPUContextMIPS(&context, kSeed);
FromSnapshotTest<MinidumpContextMIPSWriter, MinidumpContextMIPS>(
context, ExpectMinidumpContextMIPS, kSeed);
}
TEST(MinidumpContextWriter, MIPS64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextMIPS64 context_mips;
CPUContext context;
context.mips64 = &context_mips;
InitializeCPUContextMIPS64(&context, kSeed);
FromSnapshotTest<MinidumpContextMIPS64Writer, MinidumpContextMIPS64>(
context, ExpectMinidumpContextMIPS64, kSeed);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -122,6 +122,10 @@ std::string MinidumpMiscInfoDebugBuildString() {
static constexpr char kCPU[] = "arm";
#elif defined(ARCH_CPU_ARM64)
static constexpr char kCPU[] = "arm64";
#elif defined(ARCH_CPU_MIPSEL)
static constexpr char kCPU[] = "mips";
#elif defined(ARCH_CPU_MIPS64EL)
static constexpr char kCPU[] = "mips64";
#else
#error define kCPU for this CPU
#endif

View File

@ -195,6 +195,80 @@ void InitializeMinidumpContextARM64(MinidumpContextARM64* context,
context->fpcr = value++;
}
void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context,
uint32_t seed) {
if (seed == 0) {
memset(context, 0, sizeof(*context));
context->context_flags = kMinidumpContextMIPS;
return;
}
context->context_flags = kMinidumpContextMIPSAll;
uint32_t value = seed;
for (size_t index = 0; index < arraysize(context->regs); ++index) {
context->regs[index] = value++;
}
context->mdlo = value++;
context->mdhi = value++;
context->epc = value++;
context->badvaddr = value++;
context->status = value++;
context->cause = value++;
for (size_t index = 0; index < arraysize(context->fpregs.fregs); ++index) {
context->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
}
context->fpcsr = value++;
context->fir = value++;
for (size_t index = 0; index < 3; ++index) {
context->hi[index] = value++;
context->lo[index] = value++;
}
context->dsp_control = value++;
}
void InitializeMinidumpContextMIPS64(MinidumpContextMIPS64* context,
uint32_t seed) {
if (seed == 0) {
memset(context, 0, sizeof(*context));
context->context_flags = kMinidumpContextMIPS64;
return;
}
context->context_flags = kMinidumpContextMIPS64All;
uint64_t value = seed;
for (size_t index = 0; index < arraysize(context->regs); ++index) {
context->regs[index] = value++;
}
context->mdlo = value++;
context->mdhi = value++;
context->epc = value++;
context->badvaddr = value++;
context->status = value++;
context->cause = value++;
for (size_t index = 0; index < arraysize(context->fpregs.dregs); ++index) {
context->fpregs.dregs[index] = static_cast<double>(value++);
}
context->fpcsr = value++;
context->fir = value++;
for (size_t index = 0; index < 3; ++index) {
context->hi[index] = value++;
context->lo[index] = value++;
}
context->dsp_control = value++;
}
namespace {
// Using gtest assertions, compares |expected| to |observed|. This is
@ -453,5 +527,70 @@ void ExpectMinidumpContextARM64(uint32_t expect_seed,
}
}
void ExpectMinidumpContextMIPS(uint32_t expect_seed,
const MinidumpContextMIPS* observed,
bool snapshot) {
MinidumpContextMIPS expected;
InitializeMinidumpContextMIPS(&expected, expect_seed);
EXPECT_EQ(observed->context_flags, expected.context_flags);
for (size_t index = 0; index < arraysize(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
EXPECT_EQ(observed->mdlo, expected.mdlo);
EXPECT_EQ(observed->mdhi, expected.mdhi);
EXPECT_EQ(observed->epc, expected.epc);
EXPECT_EQ(observed->badvaddr, expected.badvaddr);
EXPECT_EQ(observed->status, expected.status);
EXPECT_EQ(observed->cause, expected.cause);
for (size_t index = 0; index < arraysize(expected.fpregs.fregs); ++index) {
EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs,
expected.fpregs.fregs[index]._fp_fregs);
}
EXPECT_EQ(observed->fpcsr, expected.fpcsr);
EXPECT_EQ(observed->fir, expected.fir);
for (size_t index = 0; index < 3; ++index) {
EXPECT_EQ(observed->hi[index], expected.hi[index]);
EXPECT_EQ(observed->lo[index], expected.lo[index]);
}
EXPECT_EQ(observed->dsp_control, expected.dsp_control);
}
void ExpectMinidumpContextMIPS64(uint32_t expect_seed,
const MinidumpContextMIPS64* observed,
bool snapshot) {
MinidumpContextMIPS64 expected;
InitializeMinidumpContextMIPS64(&expected, expect_seed);
EXPECT_EQ(observed->context_flags, expected.context_flags);
for (size_t index = 0; index < arraysize(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
EXPECT_EQ(observed->mdlo, expected.mdlo);
EXPECT_EQ(observed->mdhi, expected.mdhi);
EXPECT_EQ(observed->epc, expected.epc);
EXPECT_EQ(observed->badvaddr, expected.badvaddr);
EXPECT_EQ(observed->status, expected.status);
EXPECT_EQ(observed->cause, expected.cause);
for (size_t index = 0; index < arraysize(expected.fpregs.dregs); ++index) {
EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]);
}
EXPECT_EQ(observed->fpcsr, expected.fpcsr);
EXPECT_EQ(observed->fir, expected.fir);
for (size_t index = 0; index < 3; ++index) {
EXPECT_EQ(observed->hi[index], expected.hi[index]);
EXPECT_EQ(observed->lo[index], expected.lo[index]);
}
EXPECT_EQ(observed->dsp_control, expected.dsp_control);
}
} // namespace test
} // namespace crashpad

View File

@ -44,6 +44,9 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,
void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed);
void InitializeMinidumpContextARM64(MinidumpContextARM64* context,
uint32_t seed);
void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, uint32_t seed);
void InitializeMinidumpContextMIPS64(MinidumpContextMIPS* context,
uint32_t seed);
//! \}
//! \brief Verifies, via gtest assertions, that a context structure contains
@ -76,6 +79,12 @@ void ExpectMinidumpContextARM(uint32_t expect_seed,
void ExpectMinidumpContextARM64(uint32_t expect_seed,
const MinidumpContextARM64* observed,
bool snapshot);
void ExpectMinidumpContextMIPS(uint32_t expect_seed,
const MinidumpContextMIPS* observed,
bool snapshot);
void ExpectMinidumpContextMIPS64(uint32_t expect_seed,
const MinidumpContextMIPS64* observed,
bool snapshot);
//! \}
} // namespace test

View File

@ -416,7 +416,8 @@ source_set("snapshot_test") {
libs = [ "dl" ]
}
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) &&
target_cpu != "mipsel" && target_cpu != "mips64el") {
data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ]
}
@ -477,7 +478,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module_small") {
deps += [ "../third_party/mini_chromium:base" ]
}
if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) &&
target_cpu != "mipsel" && target_cpu != "mips64el") {
crashpad_loadable_module("crashpad_snapshot_test_both_dt_hash_styles") {
testonly = true
sources = [

View File

@ -106,6 +106,10 @@ void CaptureMemory::PointedToByContext(const CPUContext& context,
MaybeCaptureMemoryAround(delegate, context.arm->regs[i]);
}
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
for (size_t i = 0; i < arraysize(context.mipsel->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]);
}
#else
#error Port.
#endif

View File

@ -37,7 +37,13 @@ enum CPUArchitecture {
kCPUArchitectureARM,
//! \brief 64-bit ARM.
kCPUArchitectureARM64
kCPUArchitectureARM64,
//! \brief 32-bit MIPSEL.
kCPUArchitectureMIPSEL,
//! \brief 64-bit MIPSEL.
kCPUArchitectureMIPS64EL
};
} // namespace crashpad

View File

@ -194,9 +194,11 @@ bool CPUContext::Is64Bit() const {
switch (architecture) {
case kCPUArchitectureX86_64:
case kCPUArchitectureARM64:
case kCPUArchitectureMIPS64EL:
return true;
case kCPUArchitectureX86:
case kCPUArchitectureARM:
case kCPUArchitectureMIPSEL:
return false;
default:
NOTREACHED();

View File

@ -306,6 +306,52 @@ struct CPUContextARM64 {
uint32_t fpcr;
};
//! \brief A context structure carrying MIPS CPU state.
struct CPUContextMIPS {
uint64_t regs[32];
uint32_t mdlo;
uint32_t mdhi;
uint32_t cp0_epc;
uint32_t cp0_badvaddr;
uint32_t cp0_status;
uint32_t cp0_cause;
uint32_t hi[3];
uint32_t lo[3];
uint32_t dsp_control;
union {
double dregs[32];
struct {
float _fp_fregs;
uint32_t _fp_pad;
} fregs[32];
} fpregs;
uint32_t fpcsr;
uint32_t fir;
};
//! \brief A context structure carrying MIPS64 CPU state.
struct CPUContextMIPS64 {
uint64_t regs[32];
uint64_t mdlo;
uint64_t mdhi;
uint64_t cp0_epc;
uint64_t cp0_badvaddr;
uint64_t cp0_status;
uint64_t cp0_cause;
uint64_t hi[3];
uint64_t lo[3];
uint64_t dsp_control;
union {
double dregs[32];
struct {
float _fp_fregs;
uint32_t _fp_pad;
} fregs[32];
} fpregs;
uint64_t fpcsr;
uint64_t fir;
};
//! \brief A context structure capable of carrying the context of any supported
//! CPU architecture.
struct CPUContext {
@ -334,6 +380,8 @@ struct CPUContext {
CPUContextX86_64* x86_64;
CPUContextARM* arm;
CPUContextARM64* arm64;
CPUContextMIPS* mipsel;
CPUContextMIPS64* mips64;
};
};

View File

@ -138,6 +138,42 @@ void InitializeCPUContextARM64_OnlyFPSIMD(
#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN
#if defined(ARCH_CPU_MIPS_FAMILY) || DOXYGEN
//! \brief Initializes a CPUContextMIPS structure from native context
//! structures on Linux.
//!
//! This function has template specializations for MIPSEL and MIPS64EL
//! architecture contexts, using ContextTraits32 or ContextTraits64 as template
//! parameter, respectively.
//!
//! \param[in] thread_context The native thread context.
//! \param[in] float_context The native float context.
//! \param[out] context The CPUContextMIPS structure to initialize.
template <typename Traits>
void InitializeCPUContextMIPS(
const typename Traits::SignalThreadContext& thread_context,
const typename Traits::SignalFloatContext& float_context,
typename Traits::CPUContext* context) {
static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
"registers size mismatch");
static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs),
"fp registers size mismatch");
memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));
context->mdlo = thread_context.lo;
context->mdhi = thread_context.hi;
context->cp0_epc = thread_context.cp0_epc;
context->cp0_badvaddr = thread_context.cp0_badvaddr;
context->cp0_status = thread_context.cp0_status;
context->cp0_cause = thread_context.cp0_cause;
memcpy(&context->fpregs, &float_context.fpregs, sizeof(context->fpregs));
context->fpcsr = float_context.fpcsr;
context->fir = float_context.fpu_id;
};
#endif // ARCH_CPU_MIPS_FAMILY || DOXYGEN
} // namespace internal
} // namespace crashpad

View File

@ -268,6 +268,61 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
} while (true);
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
template <typename Traits>
static bool ReadContext(ProcessReaderLinux* reader,
LinuxVMAddress context_address,
typename Traits::CPUContext* dest_context) {
ProcessMemory* memory = reader->Memory();
LinuxVMAddress gregs_address = context_address +
offsetof(UContext<Traits>, mcontext) +
offsetof(typename Traits::MContext, gregs);
typename Traits::SignalThreadContext thread_context;
if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) {
LOG(ERROR) << "Couldn't read gregs";
return false;
}
LinuxVMAddress fpregs_address = context_address +
offsetof(UContext<Traits>, mcontext) +
offsetof(typename Traits::MContext, fpregs);
typename Traits::SignalFloatContext fp_context;
if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) {
LOG(ERROR) << "Couldn't read fpregs";
return false;
}
InitializeCPUContextMIPS<Traits>(thread_context, fp_context, dest_context);
return true;
}
template <>
bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(
ProcessReaderLinux* reader,
LinuxVMAddress context_address) {
context_.architecture = kCPUArchitectureMIPSEL;
context_.mipsel = &context_union_.mipsel;
return internal::ReadContext<ContextTraits32>(
reader, context_address, context_.mipsel);
}
template <>
bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
ProcessReaderLinux* reader,
LinuxVMAddress context_address) {
context_.architecture = kCPUArchitectureMIPS64EL;
context_.mips64 = &context_union_.mips64;
return internal::ReadContext<ContextTraits64>(
reader, context_address, context_.mips64);
}
#endif // ARCH_CPU_X86_FAMILY
bool ExceptionSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,

View File

@ -81,6 +81,9 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot {
#elif defined(ARCH_CPU_ARM_FAMILY)
CPUContextARM arm;
CPUContextARM64 arm64;
#elif defined(ARCH_CPU_MIPS_FAMILY)
CPUContextMIPS mipsel;
CPUContextMIPS64 mips64;
#endif
} context_union_;
CPUContext context_;

View File

@ -266,6 +266,36 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
sizeof(actual.arm64->fpsimd)),
0);
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
using NativeCPUContext = ucontext_t;
void InitializeContext(NativeCPUContext* context) {
for (size_t reg = 0; reg < arraysize(context->uc_mcontext.gregs); ++reg) {
context->uc_mcontext.gregs[reg] = reg;
}
memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs));
}
void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
#if defined(ARCH_CPU_MIPSEL)
EXPECT_EQ(actual.architecture, kCPUArchitectureMIPSEL);
#define CPU_ARCH_NAME mipsel
#elif defined(ARCH_CPU_MIPS64EL)
EXPECT_EQ(actual.architecture, kCPUArchitectureMIPS64EL);
#define CPU_ARCH_NAME mips64
#endif
for (size_t reg = 0; reg < arraysize(expected.uc_mcontext.gregs); ++reg) {
EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]);
}
EXPECT_EQ(memcmp(&actual.CPU_ARCH_NAME->fpregs,
&expected.uc_mcontext.fpregs,
sizeof(actual.CPU_ARCH_NAME->fpregs)),
0);
#undef CPU_ARCH_NAME
}
#else
#error Port.
#endif

View File

@ -100,6 +100,9 @@ void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) {
#elif defined(ARCH_CPU_ARM_FAMILY)
stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp
: thread_info.thread_context.t32.sp;
#elif defined(ARCH_CPU_MIPS_FAMILY)
stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.regs[29]
: thread_info.thread_context.t32.regs[29];
#else
#error Port.
#endif

View File

@ -41,8 +41,14 @@ union Sigval {
template <class Traits>
struct Siginfo {
int32_t signo;
#ifdef ARCH_CPU_MIPS_FAMILY
// Attribute order for signo_t defined in kernel is different for MIPS.
int32_t code;
int32_t err;
#else
int32_t err;
int32_t code;
#endif
typename Traits::UInteger32_64Only padding;
union {
@ -301,6 +307,121 @@ static_assert(offsetof(UContext<ContextTraits64>, reserved) ==
"reserved space offset mismtach");
#endif
#elif defined(ARCH_CPU_MIPS_FAMILY)
struct MContext32 {
uint32_t regmask;
uint32_t status;
uint64_t pc;
uint64_t gregs[32];
struct {
float _fp_fregs;
unsigned int _fp_pad;
} fpregs[32];
uint32_t fp_owned;
uint32_t fpc_csr;
uint32_t fpc_eir;
uint32_t used_math;
uint32_t dsp;
uint64_t mdhi;
uint64_t mdlo;
uint32_t hi1;
uint32_t lo1;
uint32_t hi2;
uint32_t lo2;
uint32_t hi3;
uint32_t lo3;
};
struct MContext64 {
uint64_t gregs[32];
double fpregs[32];
uint64_t mdhi;
uint64_t hi1;
uint64_t hi2;
uint64_t hi3;
uint64_t mdlo;
uint64_t lo1;
uint64_t lo2;
uint64_t lo3;
uint64_t pc;
uint32_t fpc_csr;
uint32_t used_math;
uint32_t dsp;
uint32_t __glibc_reserved1;
};
struct SignalThreadContext32 {
uint64_t regs[32];
uint32_t lo;
uint32_t hi;
uint32_t cp0_epc;
uint32_t cp0_badvaddr;
uint32_t cp0_status;
uint32_t cp0_cause;
SignalThreadContext32() {}
explicit SignalThreadContext32(
const struct ThreadContext::t32_t& thread_context) {
for (size_t reg = 0; reg < 32; ++reg) {
regs[reg] = thread_context.regs[reg];
}
lo = thread_context.lo;
hi = thread_context.hi;
cp0_epc = thread_context.cp0_epc;
cp0_badvaddr = thread_context.cp0_badvaddr;
cp0_status = thread_context.cp0_status;
cp0_cause = thread_context.cp0_cause;
}
};
struct ContextTraits32 : public Traits32 {
using MContext = MContext32;
using SignalThreadContext = SignalThreadContext32;
using SignalFloatContext = FloatContext::f32_t;
using CPUContext = CPUContextMIPS;
};
struct ContextTraits64 : public Traits64 {
using MContext = MContext64;
using SignalThreadContext = ThreadContext::t64_t;
using SignalFloatContext = FloatContext::f64_t;
using CPUContext = CPUContextMIPS64;
};
template <typename Traits>
struct UContext {
typename Traits::ULong flags;
typename Traits::Address link;
SignalStack<Traits> stack;
typename Traits::ULong_32Only alignment_padding_;
typename Traits::MContext mcontext;
Sigset<Traits> sigmask;
};
#if defined(ARCH_CPU_MIPSEL)
static_assert(offsetof(UContext<ContextTraits32>, mcontext) ==
offsetof(ucontext_t, uc_mcontext),
"context offset mismatch");
static_assert(offsetof(UContext<ContextTraits32>, mcontext.gregs) ==
offsetof(ucontext_t, uc_mcontext.gregs),
"context offset mismatch");
static_assert(offsetof(UContext<ContextTraits32>, mcontext.fpregs) ==
offsetof(ucontext_t, uc_mcontext.fpregs),
"context offset mismatch");
#elif defined(ARCH_CPU_MIPS64EL)
static_assert(offsetof(UContext<ContextTraits64>, mcontext) ==
offsetof(ucontext_t, uc_mcontext),
"context offset mismtach");
static_assert(offsetof(UContext<ContextTraits64>, mcontext.gregs) ==
offsetof(ucontext_t, uc_mcontext.gregs),
"context offset mismatch");
static_assert(offsetof(UContext<ContextTraits64>, mcontext.fpregs) ==
offsetof(ucontext_t, uc_mcontext.fpregs),
"context offset mismatch");
#endif
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -200,6 +200,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const {
#elif defined(ARCH_CPU_ARM_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureARM64
: kCPUArchitectureARM;
#elif defined(ARCH_CPU_MIPS_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureMIPS64EL
: kCPUArchitectureMIPSEL;
#else
#error port to your architecture
#endif
@ -212,6 +215,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const {
#elif defined(ARCH_CPU_ARM_FAMILY)
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
return 0;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return 0;
#else
#error port to your architecture
#endif
@ -229,6 +235,9 @@ std::string SystemSnapshotLinux::CPUVendor() const {
#elif defined(ARCH_CPU_ARM_FAMILY)
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
return std::string();
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return std::string();
#else
#error port to your architecture
#endif
@ -359,6 +368,9 @@ bool SystemSnapshotLinux::NXEnabled() const {
#elif defined(ARCH_CPU_ARM_FAMILY)
// TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
return false;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return false;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -69,6 +69,22 @@ bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,
thread.thread_info.float_context.f32,
context_.arm);
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
if (process_reader->Is64Bit()) {
context_.architecture = kCPUArchitectureMIPS64EL;
context_.mips64 = &context_union_.mips64;
InitializeCPUContextMIPS<ContextTraits64>(
thread.thread_info.thread_context.t64,
thread.thread_info.float_context.f64,
context_.mips64);
} else {
context_.architecture = kCPUArchitectureMIPSEL;
context_.mipsel = &context_union_.mipsel;
InitializeCPUContextMIPS<ContextTraits32>(
SignalThreadContext32(thread.thread_info.thread_context.t32),
thread.thread_info.float_context.f32,
context_.mipsel);
}
#else
#error Port.
#endif

View File

@ -65,6 +65,9 @@ class ThreadSnapshotLinux final : public ThreadSnapshot {
#elif defined(ARCH_CPU_ARM_FAMILY)
CPUContextARM arm;
CPUContextARM64 arm64;
#elif defined(ARCH_CPU_MIPS_FAMILY)
CPUContextMIPS mipsel;
CPUContextMIPS64 mips64;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -52,7 +52,6 @@
'target_name': 'crashpad_snapshot_test',
'type': 'executable',
'dependencies': [
'crashpad_snapshot_test_both_dt_hash_styles',
'crashpad_snapshot_test_lib',
'crashpad_snapshot_test_module',
'crashpad_snapshot_test_module_large',
@ -104,6 +103,10 @@
'win/system_snapshot_win_test.cc',
],
'conditions': [
# .gnu.hash is incompatible with the MIPS ABI
['target_arch!="mips"', {
'dependencies': ['crashpad_snapshot_test_both_dt_hash_styles']
}],
['OS=="mac"', {
'dependencies': [
'crashpad_snapshot_test_module_crashy_initializer',
@ -228,13 +231,17 @@
{
'target_name': 'crashpad_snapshot_test_both_dt_hash_styles',
'type': 'executable',
'conditions': [
# .gnu.hash is incompatible with the MIPS ABI
['target_arch!="mips"', {
'sources': [
'hash_types_test.cc',
],
'ldflags': [
# This makes `ld` emit both .hash and .gnu.hash sections.
'-Wl,--hash-style=both',
]},
]
],
},
],

View File

@ -220,5 +220,77 @@ void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) {
arm64->fpcr = value++;
}
void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed) {
context->architecture = kCPUArchitectureMIPSEL;
CPUContextMIPS* mipsel = context->mipsel;
if (seed == 0) {
memset(mipsel, 0, sizeof(*mipsel));
return;
}
uint32_t value = seed;
for (size_t index = 0; index < arraysize(mipsel->regs); ++index) {
mipsel->regs[index] = value++;
}
mipsel->mdlo = value++;
mipsel->mdhi = value++;
mipsel->cp0_epc = value++;
mipsel->cp0_badvaddr = value++;
mipsel->cp0_status = value++;
mipsel->cp0_cause = value++;
for (size_t index = 0; index < arraysize(mipsel->fpregs.fregs); ++index) {
mipsel->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);
}
mipsel->fpcsr = value++;
mipsel->fir = value++;
for (size_t index = 0; index < 3; ++index) {
mipsel->hi[index] = value++;
mipsel->lo[index] = value++;
}
mipsel->dsp_control = value++;
}
void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed) {
context->architecture = kCPUArchitectureMIPS64EL;
CPUContextMIPS64* mips64 = context->mips64;
if (seed == 0) {
memset(mips64, 0, sizeof(*mips64));
return;
}
uint64_t value = seed;
for (size_t index = 0; index < arraysize(mips64->regs); ++index) {
mips64->regs[index] = value++;
}
mips64->mdlo = value++;
mips64->mdhi = value++;
mips64->cp0_epc = value++;
mips64->cp0_badvaddr = value++;
mips64->cp0_status = value++;
mips64->cp0_cause = value++;
for (size_t index = 0; index < arraysize(mips64->fpregs.dregs); ++index) {
mips64->fpregs.dregs[index] = static_cast<double>(value++);
}
mips64->fpcsr = value++;
mips64->fir = value++;
for (size_t index = 0; index < 3; ++index) {
mips64->hi[index] = value++;
mips64->lo[index] = value++;
}
mips64->dsp_control = value++;
}
} // namespace test
} // namespace crashpad

View File

@ -61,6 +61,8 @@ void InitializeCPUContextX86(CPUContext* context, uint32_t seed);
void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed);
void InitializeCPUContextARM(CPUContext* context, uint32_t seed);
void InitializeCPUContextARM64(CPUContext* context, uint32_t seed);
void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed);
void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed);
//! \}
} // namespace test

View File

@ -35,6 +35,20 @@ LinuxVMAddress GetTLS() {
tls = tls_32;
#elif defined(ARCH_CPU_X86_64)
asm("movq %%fs:0x0, %0" : "=r"(tls));
#elif defined(ARCH_CPU_MIPSEL)
uint32_t tls_32;
asm("rdhwr $3,$29\n\t"
"move %0,$3\n\t"
: "=r"(tls_32)
:
: "$3");
tls = tls_32;
#elif defined(ARCH_CPU_MIPS64EL)
asm("rdhwr $3,$29\n\t"
"move %0,$3\n\t"
: "=r"(tls)
:
: "$3");
#else
#error Port.
#endif // ARCH_CPU_ARMEL

View File

@ -158,7 +158,7 @@ void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
}
void Multiprocess::SetExpectedChildTerminationBuiltinTrap() {
#if defined(ARCH_CPU_ARM64)
#if defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_MIPS_FAMILY)
SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
#else
SetExpectedChildTermination(kTerminationSignal, SIGILL);

View File

@ -37,7 +37,12 @@
// TODO(jperaza): This symbol isn't defined when building in chromium for
// Android. There may be another symbol to use.
extern "C" {
extern void _start();
#if defined(ARCH_CPU_MIPS_FAMILY)
#define START_SYMBOL __start
#else
#define START_SYMBOL _start
#endif
extern void START_SYMBOL();
} // extern "C"
#endif
@ -70,7 +75,7 @@ void TestAgainstCloneOrSelf(pid_t pid) {
#if !defined(OS_ANDROID)
LinuxVMAddress entry_addr;
ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr));
EXPECT_EQ(entry_addr, FromPointerCast<LinuxVMAddress>(_start));
EXPECT_EQ(entry_addr, FromPointerCast<LinuxVMAddress>(START_SYMBOL));
#endif
uid_t uid;

View File

@ -269,6 +269,131 @@ bool GetThreadArea64(pid_t tid,
}
return true;
}
#elif defined(ARCH_CPU_MIPS_FAMILY)
// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel
// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including
// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for MIPS only since
// Linux 3.13 (c0ff3c53d4f99). Older Linux kernels support PTRACE_GETREGS,
// and PTRACE_GETFPREGS instead, which don't allow checking the size of data
// copied. Also, PTRACE_GETREGS assumes register size of 64 bits even for 32 bit
// MIPS CPU (contrary to PTRACE_GETREGSET behavior), so we need buffer
// structure here.
bool GetGeneralPurposeRegistersLegacy(pid_t tid,
ThreadContext* context,
bool can_log) {
ThreadContext context_buffer;
if (ptrace(PTRACE_GETREGS, tid, nullptr, &context_buffer.t64) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
// Bitness of target process can't be determined through ptrace here, so we
// assume target process has the same as current process, making cross-bit
// ptrace unsupported on MIPS for kernels older than 3.13
#if defined(ARCH_CPU_MIPSEL)
#define THREAD_CONTEXT_FIELD t32
#elif defined(ARCH_CPU_MIPS64EL)
#define THREAD_CONTEXT_FIELD t64
#endif
for (size_t reg = 0; reg < 32; ++reg) {
context->THREAD_CONTEXT_FIELD.regs[reg] = context_buffer.t64.regs[reg];
}
context->THREAD_CONTEXT_FIELD.lo = context_buffer.t64.lo;
context->THREAD_CONTEXT_FIELD.hi = context_buffer.t64.hi;
context->THREAD_CONTEXT_FIELD.cp0_epc = context_buffer.t64.cp0_epc;
context->THREAD_CONTEXT_FIELD.cp0_badvaddr = context_buffer.t64.cp0_badvaddr;
context->THREAD_CONTEXT_FIELD.cp0_status = context_buffer.t64.cp0_status;
context->THREAD_CONTEXT_FIELD.cp0_cause = context_buffer.t64.cp0_cause;
#undef THREAD_CONTEXT_FIELD
return true;
}
bool GetFloatingPointRegistersLegacy(pid_t tid,
FloatContext* context,
bool can_log) {
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
return true;
}
bool GetFloatingPointRegisters32(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = &context->f32.fpregs;
iov.iov_len = sizeof(context->f32.fpregs);
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {
switch (errno) {
case EINVAL:
// fp may not be present
break;
case EIO:
return GetFloatingPointRegistersLegacy(tid, context, can_log);
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
}
return true;
}
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = &context->f64.fpregs;
iov.iov_len = sizeof(context->f64.fpregs);
if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f64.fpregs) != 0) {
switch (errno) {
case EINVAL:
// fp may not be present
break;
case EIO:
return GetFloatingPointRegistersLegacy(tid, context, can_log);
default:
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
}
return true;
}
bool GetThreadArea32(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
#if defined(ARCH_CPU_MIPSEL)
void* result;
if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
#else
return false;
#endif
}
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
void* result;
#if defined(ARCH_CPU_MIPSEL)
if (ptrace(PTRACE_GET_THREAD_AREA_3264, tid, nullptr, &result) != 0) {
#else
if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {
#endif
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
*address = FromPointerCast<LinuxVMAddress>(result);
return true;
}
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -283,7 +408,7 @@ size_t GetGeneralPurposeRegistersAndLength(pid_t tid,
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=
0) {
switch (errno) {
#if defined(ARCH_CPU_ARMEL)
#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPS_FAMILY)
case EIO:
return GetGeneralPurposeRegistersLegacy(tid, context, can_log)
? sizeof(context->t32)

View File

@ -67,6 +67,18 @@ union ThreadContext {
uint32_t pc;
uint32_t cpsr;
uint32_t orig_r0;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Reflects output format of static int gpr32_get(), defined in
// arch/mips/kernel/ptrace.c in kernel source
uint32_t padding0_[6];
uint32_t regs[32];
uint32_t lo;
uint32_t hi;
uint32_t cp0_epc;
uint32_t cp0_badvaddr;
uint32_t cp0_status;
uint32_t cp0_cause;
uint32_t padding1_;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -110,6 +122,16 @@ union ThreadContext {
uint64_t sp;
uint64_t pc;
uint64_t pstate;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Reflects output format of static int gpr64_get(), defined in
// arch/mips/kernel/ptrace.c in kernel source
uint64_t regs[32];
uint64_t lo;
uint64_t hi;
uint64_t cp0_epc;
uint64_t cp0_badvaddr;
uint64_t cp0_status;
uint64_t cp0_cause;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -119,15 +141,19 @@ union ThreadContext {
using NativeThreadContext = user_regs_struct;
#elif defined(ARCH_CPU_ARMEL)
using NativeThreadContext = user_regs;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// No appropriate NativeThreadsContext type available for MIPS
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64
#if !defined(ARCH_CPU_MIPS_FAMILY)
#if defined(ARCH_CPU_32_BITS)
static_assert(sizeof(t32_t) == sizeof(NativeThreadContext), "Size mismatch");
#else // ARCH_CPU_64_BITS
static_assert(sizeof(t64_t) == sizeof(NativeThreadContext), "Size mismatch");
#endif // ARCH_CPU_32_BITS
#endif // !ARCH_CPU_MIPS_FAMILY
};
static_assert(std::is_standard_layout<ThreadContext>::value,
"Not standard layout");
@ -183,6 +209,15 @@ union FloatContext {
bool have_fpregs;
bool have_vfp;
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Reflects data format filled by ptrace_getfpregs() in
// arch/mips/kernel/ptrace.c
struct {
float _fp_fregs;
unsigned int _fp_pad;
} fpregs[32];
uint32_t fpcsr;
uint32_t fpu_id;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -211,6 +246,12 @@ union FloatContext {
uint32_t fpsr;
uint32_t fpcr;
uint8_t padding[8];
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Reflects data format filled by ptrace_getfpregs() in
// arch/mips/kernel/ptrace.c
double fpregs[32];
uint32_t fpcsr;
uint32_t fpu_id;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -237,6 +278,8 @@ union FloatContext {
#endif
#elif defined(ARCH_CPU_ARM64)
static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch");
#elif defined(ARCH_CPU_MIPS_FAMILY)
// No appropriate floating point context native type for available MIPS.
#else
#error Port.
#endif // ARCH_CPU_X86

View File

@ -65,6 +65,7 @@ using NativeCPUContext = ucontext_t;
//! Win | x86_64 | `%%rcx`
//! macOS/Linux/Fuchsia | x86_64 | `%%rdi`
//! Linux | ARM/ARM64 | `r0`/`x0`
//! Linux | MIPS/MIPS64 | `$a0`
//!
//! Additionally, the value `LR` on ARM/ARM64 will be the return address of
//! this function.

View File

@ -28,7 +28,7 @@
.globl CAPTURECONTEXT_SYMBOL2
#if defined(__i386__) || defined(__x86_64__)
.balign 16, 0x90
#elif defined(__arm__) || defined(__aarch64__)
#elif defined(__arm__) || defined(__aarch64__) || defined(__mips__)
.balign 4, 0x0
#endif
@ -331,5 +331,94 @@ CAPTURECONTEXT_SYMBOL2:
// TODO(jperaza): save floating-point registers.
ret
#elif defined(__mips__)
.set noat
#if _MIPS_SIM == _ABIO32
#define STORE sw
#define MCONTEXT_FPREG_SIZE 4
#define MCONTEXT_PC_OFFSET 32
#else
#define STORE sd
#define MCONTEXT_FPREG_SIZE 8
#define MCONTEXT_PC_OFFSET 616
#endif
#define MCONTEXT_REG_SIZE 8
#define MCONTEXT_GREGS_OFFSET 40
#define MCONTEXT_FPREGS_OFFSET 296
// Value of register 0 is always 0.
// Registers 26 and 27 are reserved for kernel, and shouldn't be used.
STORE $1, (1 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $2, (2 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $3, (3 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $4, (4 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $5, (5 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $6, (6 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $7, (7 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $8, (8 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $9, (9 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $10, (10 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $11, (11 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $12, (12 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $13, (13 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $14, (14 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $15, (15 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $16, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $17, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $18, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $19, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $20, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $21, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $22, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $23, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)
STORE $31, (MCONTEXT_PC_OFFSET)($a0)
#ifdef __mips_hard_float
s.d $f0, (0 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f2, (2 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f4, (4 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f6, (6 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f8, (8 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f10, (10 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f12, (12 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f14, (14 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f16, (16 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f18, (18 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f20, (20 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f22, (22 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f24, (24 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f26, (26 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f28, (28 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f30, (30 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
#if _MIPS_SIM != _ABIO32
s.d $f1, (1 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f3, (3 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f5, (5 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f7, (7 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f9, (9 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f11, (11 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f13, (13 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f15, (15 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f17, (17 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f19, (19 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f21, (21 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f23, (23 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f25, (25 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f27, (27 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f29, (29 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
s.d $f31, (31 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)
#endif // _MIPS_SIM != _ABIO32
#endif // __mips_hard_float
jr $ra
.set at
#endif // __i386__

View File

@ -49,7 +49,7 @@ void TestCaptureContext() {
// reference program counter.
uintptr_t pc = ProgramCounterFromContext(context_1);
#if !defined(ADDRESS_SANITIZER)
#if !defined(ADDRESS_SANITIZER) && !defined(ARCH_CPU_MIPS_FAMILY)
// AddressSanitizer can cause enough code bloat that the “nearby” check would
// likely fail.
const uintptr_t kReferencePC =

View File

@ -34,6 +34,8 @@ void SanityCheckContext(const NativeCPUContext& context) {
EXPECT_EQ(context.uc_mcontext.arm_r0, FromPointerCast<uintptr_t>(&context));
#elif defined(ARCH_CPU_ARM64)
EXPECT_EQ(context.uc_mcontext.regs[0], FromPointerCast<uintptr_t>(&context));
#elif defined(ARCH_CPU_MIPS_FAMILY)
EXPECT_EQ(context.uc_mcontext.gregs[4], FromPointerCast<uintptr_t>(&context));
#endif
}
@ -46,6 +48,8 @@ uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {
return context.uc_mcontext.arm_pc;
#elif defined(ARCH_CPU_ARM64)
return context.uc_mcontext.pc;
#elif defined(ARCH_CPU_MIPS_FAMILY)
return context.uc_mcontext.pc;
#endif
}
@ -58,6 +62,8 @@ uintptr_t StackPointerFromContext(const NativeCPUContext& context) {
return context.uc_mcontext.arm_sp;
#elif defined(ARCH_CPU_ARM64)
return context.uc_mcontext.sp;
#elif defined(ARCH_CPU_MIPS_FAMILY)
return context.uc_mcontext.gregs[29];
#endif
}

View File

@ -65,6 +65,39 @@ constexpr const char* kSignalNames[] = {
"USR1",
"USR2",
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(ARCH_CPU_MIPS_FAMILY)
"HUP",
"INT",
"QUIT",
"ILL",
"TRAP",
"ABRT",
"EMT",
"FPE",
"KILL",
"BUS",
"SEGV",
"SYS",
"PIPE",
"ALRM",
"TERM",
"USR1",
"USR2",
"CHLD",
"PWR",
"WINCH",
"URG",
"IO",
"STOP",
"TSTP",
"CONT",
"TTIN",
"TTOU",
"VTALRM",
"PROF",
"XCPU",
"XFSZ",
#else
// sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p'
// /usr/include/asm-generic/signal.h
// and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN.
@ -99,6 +132,7 @@ constexpr const char* kSignalNames[] = {
"IO",
"PWR",
"SYS",
#endif // defined(ARCH_CPU_MIPS_FAMILY)
#endif
};
#if defined(OS_LINUX) || defined(OS_ANDROID)

View File

@ -67,8 +67,10 @@ constexpr struct {
{SIGINFO, "SIGINFO", "INFO"},
#elif defined(OS_LINUX) || defined(OS_ANDROID)
{SIGPWR, "SIGPWR", "PWR"},
#if !defined(ARCH_CPU_MIPS_FAMILY)
{SIGSTKFLT, "SIGSTKFLT", "STKFLT"},
#endif
#endif
};
// If |expect| is nullptr, the conversion is expected to fail. If |expect| is