[riscv] Add RISC-V Linux support

Only RV64GC is supported.

Bug: fuchsia:127655

Tested: `python build/run_tests.py` on RISC-V emulator
Tested: Created minidump via self-induced crash on RISC-V emulator,
ran through Breakpad stackwalker

Change-Id: I713797cd623b0a758269048e01696cbce502ca6c
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4581050
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Thomas Gales 2023-06-08 21:08:54 +00:00 committed by Joshua Peraza
parent 656fc62589
commit 4f5dd67229
37 changed files with 662 additions and 5 deletions

View File

@ -637,6 +637,56 @@ struct MinidumpContextMIPS64 {
uint64_t fir;
};
//! \brief 64-bit RISCV-specific flags for
//! MinidumpContextRISCV64::context_flags.
enum MinidumpContextRISCV64Flags : uint32_t {
//! \brief Identifies the context structure as RISCV64.
kMinidumpContextRISCV64 = 0x08000000,
//! \brief Indicates the validity of integer registers.
//!
//! Registers 'pc' and `x1`-`x31` are valid.
kMinidumpContextRISCV64Integer = kMinidumpContextRISCV64 | 0x00000001,
//! \brief Indicates the validity of floating point registers.
//!
//! Floating point registers `f0`-`f31` are valid.
kMinidumpContextRISCV64FloatingPoint = kMinidumpContextRISCV64 | 0x00000002,
//! \brief Indicates the validity of all registers.
kMinidumpContextRISCV64All = kMinidumpContextRISCV64Integer |
kMinidumpContextRISCV64FloatingPoint,
};
//! \brief A 64-bit RISC-V CPU context (register state) carried in a minidump
//! file.
//!
//! This structure is versioned. Increment |kVersion| when changing this
//! structure.
struct MinidumpContextRISCV64 {
//! \brief The structures currently-defined version number.
static constexpr uint32_t kVersion = 1;
//! \brief Indicates the validity of fields in this structure.
uint32_t context_flags;
//! \brief The structures version number.
uint32_t version;
//! \brief The program counter register.
uint64_t pc;
//! \brief The integer registers, x1 through x31.
uint64_t regs[31];
//! \brief The floating point registers.
uint64_t fpregs[32];
//! \brief The floating point control and status register.
uint32_t fcsr;
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_

View File

@ -102,6 +102,13 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
break;
}
case kCPUArchitectureRISCV64: {
context = std::make_unique<MinidumpContextRISCV64Writer>();
reinterpret_cast<MinidumpContextRISCV64Writer*>(context.get())
->InitializeFromSnapshot(context_snapshot->riscv64);
break;
}
default: {
LOG(ERROR) << "unknown context architecture "
<< context_snapshot->architecture;
@ -556,4 +563,42 @@ size_t MinidumpContextMIPS64Writer::ContextSize() const {
return sizeof(context_);
}
MinidumpContextRISCV64Writer::MinidumpContextRISCV64Writer()
: MinidumpContextWriter(), context_() {
context_.context_flags = kMinidumpContextRISCV64;
context_.version = MinidumpContextRISCV64::kVersion;
}
MinidumpContextRISCV64Writer::~MinidumpContextRISCV64Writer() = default;
void MinidumpContextRISCV64Writer::InitializeFromSnapshot(
const CPUContextRISCV64* context_snapshot) {
DCHECK_EQ(state(), kStateMutable);
DCHECK_EQ(context_.context_flags, kMinidumpContextRISCV64);
context_.context_flags = kMinidumpContextRISCV64All;
context_.version = MinidumpContextRISCV64::kVersion;
context_.pc = context_snapshot->pc;
static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
"GPRs size mismatch");
memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),
"FPRs size mismatch");
memcpy(context_.fpregs, context_snapshot->fpregs, sizeof(context_.fpregs));
context_.fcsr = context_snapshot->fcsr;
}
bool MinidumpContextRISCV64Writer::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
return file_writer->Write(&context_, sizeof(context_));
}
size_t MinidumpContextRISCV64Writer::ContextSize() const {
DCHECK_GE(state(), kStateFrozen);
return sizeof(context_);
}
} // namespace crashpad

View File

@ -369,6 +369,50 @@ class MinidumpContextMIPS64Writer final : public MinidumpContextWriter {
MinidumpContextMIPS64 context_;
};
//! \brief The writer for a MinidumpContextRISCV64 structure in a minidump file.
class MinidumpContextRISCV64Writer final : public MinidumpContextWriter {
public:
MinidumpContextRISCV64Writer();
MinidumpContextRISCV64Writer(const MinidumpContextRISCV64Writer&) = delete;
MinidumpContextRISCV64Writer& operator=(const MinidumpContextRISCV64Writer&) =
delete;
~MinidumpContextRISCV64Writer() override;
//! \brief Initializes the MinidumpContextRISCV64 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 CPUContextRISCV64* 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.
MinidumpContextRISCV64* context() { return &context_; }
protected:
// MinidumpWritable:
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpContextWriter:
size_t ContextSize() const override;
private:
MinidumpContextRISCV64 context_;
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_

View File

@ -143,6 +143,49 @@ TYPED_TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
}
}
TYPED_TEST(MinidumpContextWriter, MinidumpContextRISCV64Writer) {
{
// Make sure that a heap-allocated context writer has the proper alignment,
// because it may be nonstandard.
auto context_writer = std::make_unique<MinidumpContextRISCV64Writer>();
EXPECT_EQ(reinterpret_cast<uintptr_t>(context_writer.get()) &
(alignof(MinidumpContextRISCV64Writer) - 1),
0u);
}
StringFile string_file;
{
// Make sure that a context writer thats untouched writes a zeroed-out
// context.
SCOPED_TRACE("zero");
EmptyContextTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(ExpectMinidumpContextRISCV64);
}
{
SCOPED_TRACE("nonzero");
string_file.Reset();
constexpr uint32_t kSeed = 0x808664;
MinidumpContextRISCV64Writer context_writer;
InitializeMinidumpContextRISCV64(context_writer.context(), kSeed);
EXPECT_TRUE(context_writer.WriteEverything(&string_file));
ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextRISCV64));
const MinidumpContextRISCV64* observed =
MinidumpWritableAtRVA<MinidumpContextRISCV64>(string_file.string(),
TypeParam(0));
ASSERT_TRUE(observed);
ExpectMinidumpContextRISCV64(kSeed, observed, false);
}
}
template <typename Writer, typename Context, typename RVAType>
void FromSnapshotTest(const CPUContext& snapshot_context,
void (*expect_context)(uint32_t, const Context*, bool),
@ -268,6 +311,23 @@ TYPED_TEST(MinidumpContextWriter, MIPS64_FromSnapshot) {
TypeParam>(context, ExpectMinidumpContextMIPS64, kSeed);
}
TYPED_TEST(MinidumpContextWriter, RISCV64_Zeros) {
EmptyContextTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(ExpectMinidumpContextRISCV64);
}
TYPED_TEST(MinidumpContextWriter, RISCV64_FromSnapshot) {
constexpr uint32_t kSeed = 64;
CPUContextRISCV64 context_riscv64;
CPUContext context;
context.riscv64 = &context_riscv64;
InitializeCPUContextRISCV64(&context, kSeed);
FromSnapshotTest<MinidumpContextRISCV64Writer,
MinidumpContextRISCV64,
TypeParam>(context, ExpectMinidumpContextRISCV64, kSeed);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -210,6 +210,9 @@ enum MinidumpCPUArchitecture : uint16_t {
//! \deprecated Use #kMinidumpCPUArchitectureARM64 instead.
kMinidumpCPUArchitectureARM64Breakpad = 0x8003,
//! \brief Used by Breakpad for 64-bit RISC-V.
kMinidumpCPUArchitectureRISCV64Breakpad = 0x8006,
//! \brief Unknown CPU architecture.
kMinidumpCPUArchitectureUnknown = PROCESSOR_ARCHITECTURE_UNKNOWN,
};

View File

@ -175,6 +175,8 @@ std::string MinidumpMiscInfoDebugBuildString() {
static constexpr char kCPU[] = "mips";
#elif defined(ARCH_CPU_MIPS64EL)
static constexpr char kCPU[] = "mips64";
#elif defined(ARCH_CPU_RISCV64)
static constexpr char kCPU[] = "riscv64";
#else
#error define kCPU for this CPU
#endif

View File

@ -132,6 +132,9 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot(
case kCPUArchitectureARM64:
cpu_architecture = kMinidumpCPUArchitectureARM64;
break;
case kCPUArchitectureRISCV64:
cpu_architecture = kMinidumpCPUArchitectureRISCV64Breakpad;
break;
default:
NOTREACHED();
cpu_architecture = kMinidumpCPUArchitectureUnknown;

View File

@ -272,6 +272,31 @@ void InitializeMinidumpContextMIPS64(MinidumpContextMIPS64* context,
context->dsp_control = value++;
}
void InitializeMinidumpContextRISCV64(MinidumpContextRISCV64* context,
uint32_t seed) {
if (seed == 0) {
memset(context, 0, sizeof(*context));
context->context_flags = kMinidumpContextRISCV64;
context->version = MinidumpContextRISCV64::kVersion;
return;
}
context->context_flags = kMinidumpContextRISCV64All;
context->version = MinidumpContextRISCV64::kVersion;
uint32_t value = seed;
context->pc = value++;
for (size_t index = 0; index < std::size(context->regs); ++index) {
context->regs[index] = value++;
}
for (size_t index = 0; index < std::size(context->fpregs); ++index) {
context->fpregs[index] = value++;
}
context->fcsr = value++;
}
namespace {
// Using Google Test assertions, compares |expected| to |observed|. This is
@ -601,5 +626,24 @@ void ExpectMinidumpContextMIPS64(uint32_t expect_seed,
EXPECT_EQ(observed->dsp_control, expected.dsp_control);
}
void ExpectMinidumpContextRISCV64(uint32_t expect_seed,
const MinidumpContextRISCV64* observed,
bool snapshot) {
MinidumpContextRISCV64 expected;
InitializeMinidumpContextRISCV64(&expected, expect_seed);
EXPECT_EQ(observed->context_flags, expected.context_flags);
EXPECT_EQ(observed->version, expected.version);
for (size_t index = 0; index < std::size(expected.regs); ++index) {
EXPECT_EQ(observed->regs[index], expected.regs[index]);
}
for (size_t index = 0; index < std::size(expected.fpregs); ++index) {
EXPECT_EQ(observed->fpregs[index], expected.fpregs[index]);
}
EXPECT_EQ(observed->fcsr, expected.fcsr);
}
} // namespace test
} // namespace crashpad

View File

@ -47,6 +47,8 @@ void InitializeMinidumpContextARM64(MinidumpContextARM64* context,
void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, uint32_t seed);
void InitializeMinidumpContextMIPS64(MinidumpContextMIPS* context,
uint32_t seed);
void InitializeMinidumpContextRISCV64(MinidumpContextRISCV64* context,
uint32_t seed);
//! \}
//! \brief Verifies, via Google Test assertions, that a context structure
@ -85,6 +87,9 @@ void ExpectMinidumpContextMIPS(uint32_t expect_seed,
void ExpectMinidumpContextMIPS64(uint32_t expect_seed,
const MinidumpContextMIPS64* observed,
bool snapshot);
void ExpectMinidumpContextRISCV64(uint32_t expect_seed,
const MinidumpContextRISCV64* observed,
bool snapshot);
//! \}
} // namespace test

View File

@ -117,6 +117,11 @@ void CaptureMemory::PointedToByContext(const CPUContext& context,
for (size_t i = 0; i < std::size(context.mipsel->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]);
}
#elif defined(ARCH_CPU_RISCV64)
MaybeCaptureMemoryAround(delegate, context.riscv64->pc);
for (size_t i = 0; i < std::size(context.riscv64->regs); ++i) {
MaybeCaptureMemoryAround(delegate, context.riscv64->regs[i]);
}
#else
#error Port.
#endif

View File

@ -43,7 +43,10 @@ enum CPUArchitecture {
kCPUArchitectureMIPSEL,
//! \brief 64-bit MIPSEL.
kCPUArchitectureMIPS64EL
kCPUArchitectureMIPS64EL,
//! \brief 64-bit RISC-V.
kCPUArchitectureRISCV64,
};
} // namespace crashpad

View File

@ -20,6 +20,7 @@
#include <iterator>
#include "base/notreached.h"
#include "cpu_architecture.h"
#include "util/misc/arraysize.h"
#include "util/misc/implicit_cast.h"
@ -170,6 +171,8 @@ uint64_t CPUContext::InstructionPointer() const {
return arm->pc;
case kCPUArchitectureARM64:
return arm64->pc;
case kCPUArchitectureRISCV64:
return riscv64->pc;
default:
NOTREACHED();
return ~0ull;
@ -186,6 +189,8 @@ uint64_t CPUContext::StackPointer() const {
return arm->sp;
case kCPUArchitectureARM64:
return arm64->sp;
case kCPUArchitectureRISCV64:
return riscv64->regs[1];
default:
NOTREACHED();
return ~0ull;
@ -226,6 +231,7 @@ bool CPUContext::Is64Bit() const {
case kCPUArchitectureX86_64:
case kCPUArchitectureARM64:
case kCPUArchitectureMIPS64EL:
case kCPUArchitectureRISCV64:
return true;
case kCPUArchitectureX86:
case kCPUArchitectureARM:

View File

@ -362,6 +362,15 @@ struct CPUContextMIPS64 {
uint64_t fir;
};
//! \brief A context structure carrying RISCV64 CPU state.
struct CPUContextRISCV64 {
uint64_t pc;
uint64_t regs[31];
uint64_t fpregs[32];
uint32_t fcsr;
};
//! \brief A context structure capable of carrying the context of any supported
//! CPU architecture.
struct CPUContext {
@ -402,6 +411,7 @@ struct CPUContext {
CPUContextARM64* arm64;
CPUContextMIPS* mipsel;
CPUContextMIPS64* mips64;
CPUContextRISCV64* riscv64;
};
};

View File

@ -733,8 +733,11 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag,
if (!dynamic_array_->GetValue(tag, log, address)) {
return false;
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
// The GNU loader updates the dynamic array according to the load bias.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \
(defined(__GLIBC__) && defined(ARCH_CPU_RISCV64))
// The GNU loader updates the dynamic array according to the load bias (except
// for RISC-V: https://sourceware.org/bugzilla/show_bug.cgi?id=24484).
// The Android and Fuchsia loaders only update the debug address.
if (tag != DT_DEBUG) {
*address += GetLoadBias();

View File

@ -266,6 +266,21 @@ void InitializeCPUContextARM64_OnlyFPSIMD(
context->fpcr = float_context.fpcr;
}
#elif defined(ARCH_CPU_RISCV64)
void InitializeCPUContextRISCV64(const ThreadContext::t64_t& thread_context,
const FloatContext::f64_t& float_context,
CPUContextRISCV64* context) {
context->pc = thread_context.pc;
static_assert(sizeof(context->regs) == sizeof(thread_context.regs));
memcpy(context->regs, thread_context.regs, sizeof(context->regs));
static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs));
memcpy(context->fpregs, float_context.fpregs, sizeof(context->fpregs));
context->fcsr = float_context.fcsr;
}
#endif // ARCH_CPU_X86_FAMILY
} // namespace internal

View File

@ -174,6 +174,20 @@ void InitializeCPUContextMIPS(
#endif // ARCH_CPU_MIPS_FAMILY || DOXYGEN
#if defined(ARCH_CPU_RISCV64) || DOXYGEN
//! \brief Initializes a CPUContextRISCV64 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 CPUContextRISCV64 structure to initialize.
void InitializeCPUContextRISCV64(const ThreadContext::t64_t& thread_context,
const FloatContext::f64_t& float_context,
CPUContextRISCV64* context);
#endif // ARCH_CPU_RISCV64 || DOXYGEN
} // namespace internal
} // namespace crashpad

View File

@ -325,6 +325,48 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
reader, context_address, context_.mips64);
}
#elif defined(ARCH_CPU_RISCV64)
static bool ReadContext(ProcessReaderLinux* reader,
LinuxVMAddress context_address,
typename ContextTraits64::CPUContext* dest_context) {
const ProcessMemory* memory = reader->Memory();
LinuxVMAddress gregs_address = context_address +
offsetof(UContext<ContextTraits64>, mcontext) +
offsetof(MContext64, regs);
typename ContextTraits64::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<ContextTraits64>, mcontext) +
offsetof(MContext64, fpregs);
typename ContextTraits64::SignalFloatContext fp_context;
if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) {
LOG(ERROR) << "Couldn't read fpregs";
return false;
}
InitializeCPUContextRISCV64(thread_context, fp_context, dest_context);
return true;
}
template <>
bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
ProcessReaderLinux* reader,
LinuxVMAddress context_address) {
context_.architecture = kCPUArchitectureRISCV64;
context_.riscv64 = &context_union_.riscv64;
return internal::ReadContext(reader, context_address, context_.riscv64);
}
#endif // ARCH_CPU_X86_FAMILY
bool ExceptionSnapshotLinux::Initialize(
@ -355,10 +397,12 @@ bool ExceptionSnapshotLinux::Initialize(
return false;
}
} else {
#if !defined(ARCH_CPU_RISCV64)
if (!ReadContext<ContextTraits32>(process_reader, context_address) ||
!ReadSiginfo<Traits32>(process_reader, siginfo_address)) {
return false;
}
#endif
}
CaptureMemoryDelegateLinux capture_memory_delegate(

View File

@ -89,6 +89,8 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot {
#elif defined(ARCH_CPU_MIPS_FAMILY)
CPUContextMIPS mipsel;
CPUContextMIPS64 mips64;
#elif defined(ARCH_CPU_RISCV64)
CPUContextRISCV64 riscv64;
#endif
} context_union_;
CPUContext context_;

View File

@ -297,6 +297,34 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
#undef CPU_ARCH_NAME
}
#elif defined(ARCH_CPU_RISCV64)
using NativeCPUContext = ucontext_t;
void InitializeContext(NativeCPUContext* context) {
for (size_t reg = 0; reg < std::size(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) {
EXPECT_EQ(actual.architecture, kCPUArchitectureRISCV64);
EXPECT_EQ(actual.riscv64->pc, expected.uc_mcontext.__gregs[0]);
for (size_t reg = 0; reg < std::size(actual.riscv64->regs); ++reg) {
EXPECT_EQ(actual.riscv64->regs[reg], expected.uc_mcontext.__gregs[reg + 1]);
}
EXPECT_EQ(memcmp(&actual.riscv64->fpregs,
&expected.uc_mcontext.__fpregs,
sizeof(actual.riscv64->fpregs)),
0);
}
#else
#error Port.
#endif

View File

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

View File

@ -422,6 +422,40 @@ static_assert(offsetof(UContext<ContextTraits64>, mcontext.fpregs) ==
"context offset mismatch");
#endif
#elif defined(ARCH_CPU_RISCV64)
struct ContextTraits64 : public Traits64 {
using SignalThreadContext = ThreadContext::t64_t;
using SignalFloatContext = FloatContext::f64_t;
using CPUContext = CPUContextRISCV64;
};
struct MContext64 {
ThreadContext::t64_t regs;
FloatContext::f64_t fpregs;
};
template <typename Traits>
struct UContext {
typename Traits::ULong flags;
typename Traits::Address link;
SignalStack<Traits> stack;
Sigset<Traits> sigmask;
char alignment_padding_[8];
char padding[128 - sizeof(Sigset<Traits>)];
MContext64 mcontext;
};
static_assert(offsetof(UContext<ContextTraits64>, mcontext) ==
offsetof(ucontext_t, uc_mcontext),
"context offset mismatch");
static_assert(offsetof(UContext<ContextTraits64>, mcontext.regs) ==
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");
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -205,6 +205,8 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const {
#elif defined(ARCH_CPU_MIPS_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureMIPS64EL
: kCPUArchitectureMIPSEL;
#elif defined(ARCH_CPU_RISCV64)
return kCPUArchitectureRISCV64;
#else
#error port to your architecture
#endif
@ -220,6 +222,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const {
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return 0;
#elif defined(ARCH_CPU_RISCV64)
// Not implemented
return 0;
#else
#error port to your architecture
#endif
@ -240,6 +245,9 @@ std::string SystemSnapshotLinux::CPUVendor() const {
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return std::string();
#elif defined(ARCH_CPU_RISCV64)
// Not implemented
return std::string();
#else
#error port to your architecture
#endif
@ -373,6 +381,9 @@ bool SystemSnapshotLinux::NXEnabled() const {
#elif defined(ARCH_CPU_MIPS_FAMILY)
// Not implementable on MIPS
return false;
#elif defined(ARCH_CPU_RISCV64)
// Not implemented
return false;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -110,6 +110,13 @@ bool WriteTestModule(const base::FilePath& module_path,
module.ehdr.e_machine = EM_AARCH64;
#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL)
module.ehdr.e_machine = EM_MIPS;
#elif defined(ARCH_CPU_RISCV64)
module.ehdr.e_machine = EM_RISCV;
#endif
#if defined(ARCH_CPU_RISCV64)
// Crashpad supports RV64GC
module.ehdr.e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE;
#endif
module.ehdr.e_version = EV_CURRENT;

View File

@ -190,6 +190,12 @@ bool ThreadSnapshotLinux::Initialize(
thread.thread_info.float_context.f32,
context_.mipsel);
}
#elif defined(ARCH_CPU_RISCV64)
context_.architecture = kCPUArchitectureRISCV64;
context_.riscv64 = &context_union_.riscv64;
InitializeCPUContextRISCV64(thread.thread_info.thread_context.t64,
thread.thread_info.float_context.f64,
context_.riscv64);
#else
#error Port.
#endif

View File

@ -74,6 +74,8 @@ class ThreadSnapshotLinux final : public ThreadSnapshot {
#elif defined(ARCH_CPU_MIPS_FAMILY)
CPUContextMIPS mipsel;
CPUContextMIPS64 mips64;
#elif defined(ARCH_CPU_RISCV64)
CPUContextRISCV64 riscv64;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY

View File

@ -266,6 +266,33 @@ bool MinidumpContextConverter::Initialize(
context_.mips64->fir = src->fir;
memcpy(&context_.mips64->fpregs, &src->fpregs, sizeof(src->fpregs));
} else if (context_.architecture ==
CPUArchitecture::kCPUArchitectureRISCV64) {
context_memory_.resize(sizeof(CPUContextRISCV64));
context_.riscv64 =
reinterpret_cast<CPUContextRISCV64*>(context_memory_.data());
const MinidumpContextRISCV64* src =
reinterpret_cast<const MinidumpContextRISCV64*>(
minidump_context.data());
if (minidump_context.size() < sizeof(MinidumpContextRISCV64)) {
return false;
}
if (!(src->context_flags & kMinidumpContextRISCV64)) {
return false;
}
context_.riscv64->pc = src->pc;
static_assert(sizeof(context_.riscv64->regs) == sizeof(src->regs),
"GPR size mismatch");
memcpy(&context_.riscv64->regs, &src->regs, sizeof(src->regs));
static_assert(sizeof(context_.riscv64->fpregs) == sizeof(src->fpregs),
"FPR size mismatch");
memcpy(&context_.riscv64->fpregs, &src->fpregs, sizeof(src->fpregs));
context_.riscv64->fcsr = src->fcsr;
} else {
// Architecture is listed as "unknown".
DLOG(ERROR) << "Unknown architecture";

View File

@ -68,6 +68,8 @@ CPUArchitecture SystemSnapshotMinidump::GetCPUArchitecture() const {
case kMinidumpCPUArchitectureMIPS:
return kCPUArchitectureMIPSEL;
// No word on how MIPS64 is signalled
case kMinidumpCPUArchitectureRISCV64Breakpad:
return kCPUArchitectureRISCV64;
default:
return CPUArchitecture::kCPUArchitectureUnknown;

View File

@ -295,5 +295,27 @@ void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed) {
mips64->dsp_control = value++;
}
void InitializeCPUContextRISCV64(CPUContext* context, uint32_t seed) {
context->architecture = kCPUArchitectureRISCV64;
CPUContextRISCV64* riscv64 = context->riscv64;
if (seed == 0) {
memset(riscv64, 0, sizeof(*riscv64));
return;
}
uint32_t value = seed;
riscv64->pc = value++;
for (size_t index = 0; index < std::size(riscv64->regs); ++index) {
riscv64->regs[index] = value++;
}
for (size_t index = 0; index < std::size(riscv64->fpregs); ++index) {
riscv64->fpregs[index] = value++;
}
riscv64->fcsr = value++;
}
} // namespace test
} // namespace crashpad

View File

@ -63,6 +63,7 @@ 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);
void InitializeCPUContextRISCV64(CPUContext* context, uint32_t seed);
//! \}
} // namespace test

View File

@ -49,6 +49,8 @@ LinuxVMAddress GetTLS() {
: "=r"(tls)
:
: "$3");
#elif defined(ARCH_CPU_RISCV64)
asm("mv %0, tp" : "=r"(tls));
#else
#error Port.
#endif // ARCH_CPU_ARMEL

View File

@ -96,10 +96,15 @@ void TestAgainstCloneOrSelf(pid_t pid) {
ProcessMemoryLinux memory(&connection);
// AT_PLATFORM is null for RISC-V:
// https://elixir.bootlin.com/linux/v6.4-rc4/C/ident/ELF_PLATFORM
#if !defined(ARCH_CPU_RISCV64)
LinuxVMAddress platform_addr;
ASSERT_TRUE(aux.GetValue(AT_PLATFORM, &platform_addr));
std::string platform;
ASSERT_TRUE(memory.ReadCStringSizeLimited(platform_addr, 10, &platform));
#endif // ARCH_CPU_RISCV64
#if defined(ARCH_CPU_X86)
EXPECT_STREQ(platform.c_str(), "i686");
#elif defined(ARCH_CPU_X86_64)

View File

@ -398,6 +398,37 @@ bool GetThreadArea64(pid_t tid,
return true;
}
#elif defined(ARCH_CPU_RISCV64)
bool GetFloatingPointRegisters64(pid_t tid,
FloatContext* context,
bool can_log) {
iovec iov;
iov.iov_base = context;
iov.iov_len = sizeof(*context);
if (ptrace(
PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=
0) {
PLOG_IF(ERROR, can_log) << "ptrace";
return false;
}
if (iov.iov_len != sizeof(context->f64)) {
LOG_IF(ERROR, can_log) << "Unexpected registers size " << iov.iov_len
<< " != " << sizeof(context->f64);
return false;
}
return true;
}
bool GetThreadArea64(pid_t tid,
const ThreadContext& context,
LinuxVMAddress* address,
bool can_log) {
// Thread pointer register
*address = context.t64.regs[3];
return true;
}
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -426,6 +457,7 @@ size_t GetGeneralPurposeRegistersAndLength(pid_t tid,
return iov.iov_len;
}
#if !defined(ARCH_CPU_RISCV64)
bool GetGeneralPurposeRegisters32(pid_t tid,
ThreadContext* context,
bool can_log) {
@ -437,6 +469,7 @@ bool GetGeneralPurposeRegisters32(pid_t tid,
}
return true;
}
#endif
bool GetGeneralPurposeRegisters64(pid_t tid,
ThreadContext* context,
@ -500,12 +533,16 @@ bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) {
can_log_);
}
#if !defined(ARCH_CPU_RISCV64)
return GetGeneralPurposeRegisters32(tid, &info->thread_context, can_log_) &&
GetFloatingPointRegisters32(tid, &info->float_context, can_log_) &&
GetThreadArea32(tid,
info->thread_context,
&info->thread_specific_data_address,
can_log_);
#else
return false;
#endif
}
ssize_t Ptracer::ReadUpTo(pid_t pid,

View File

@ -29,6 +29,11 @@
#include <android/api-level.h>
#endif
// x86_64 has compilation errors if asm/ptrace.h is #included.
#if defined(ARCH_CPU_RISCV64)
#include <asm/ptrace.h>
#endif
namespace crashpad {
//! \brief The set of general purpose registers for an architecture family.
@ -80,6 +85,8 @@ union ThreadContext {
uint32_t cp0_status;
uint32_t cp0_cause;
uint32_t padding1_;
#elif defined(ARCH_CPU_RISCV64)
// 32 bit RISC-V not supported
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -133,12 +140,17 @@ union ThreadContext {
uint64_t cp0_badvaddr;
uint64_t cp0_status;
uint64_t cp0_cause;
#elif defined(ARCH_CPU_RISCV64)
// Reflects user_regs_struct in asm/ptrace.h.
uint64_t pc;
uint64_t regs[31];
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
} t64;
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)
#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64) || \
defined(ARCH_CPU_RISCV64)
using NativeThreadContext = user_regs_struct;
#elif defined(ARCH_CPU_ARMEL)
using NativeThreadContext = user_regs;
@ -146,7 +158,7 @@ union ThreadContext {
// No appropriate NativeThreadsContext type available for MIPS
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64
#endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64 || ARCH_CPU_RISCV64
#if !defined(ARCH_CPU_MIPS_FAMILY)
#if defined(ARCH_CPU_32_BITS)
@ -219,6 +231,8 @@ union FloatContext {
} fpregs[32];
uint32_t fpcsr;
uint32_t fpu_id;
#elif defined(ARCH_CPU_RISCV64)
// 32 bit RISC-V not supported
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -253,6 +267,10 @@ union FloatContext {
double fpregs[32];
uint32_t fpcsr;
uint32_t fpu_id;
#elif defined(ARCH_CPU_RISCV64)
// Reflects __riscv_d_ext_state in asm/ptrace.h
uint64_t fpregs[32];
uint64_t fcsr;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@ -282,6 +300,8 @@ union FloatContext {
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.
#elif defined(ARCH_CPU_RISCV64)
static_assert(sizeof(f64) == sizeof(__riscv_d_ext_state), "Size mismatch");
#else
#error Port.
#endif // ARCH_CPU_X86

View File

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

View File

@ -36,6 +36,8 @@
.type CAPTURECONTEXT_SYMBOL2, %function
#elif defined(__mips__)
.balign 4, 0x0
#elif defined(__riscv)
.balign 4, 0x0
#endif
CAPTURECONTEXT_SYMBOL:
@ -427,4 +429,85 @@ CAPTURECONTEXT_SYMBOL2:
jr $ra
.set at
#elif defined(__riscv)
#define MCONTEXT_GREGS_OFFSET 176
// x1/ra is the return address. Store it as the pc.
// The original x10/a0 can't be recovered.
sd x1, (0 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x1, (1 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x2, (2 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x3, (3 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x4, (4 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x5, (5 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x6, (6 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x7, (7 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x8, (8 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x9, (9 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x10, (10 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x11, (11 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x12, (12 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x13, (13 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x14, (14 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x15, (15 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x16, (16 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x17, (17 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x18, (18 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x19, (19 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x20, (20 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x21, (21 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x22, (22 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x23, (23 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x24, (24 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x25, (25 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x26, (26 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x27, (27 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x28, (28 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x29, (29 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x30, (30 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
sd x31, (31 * 8 + MCONTEXT_GREGS_OFFSET)(a0)
#define MCONTEXT_FPREGS_OFFSET MCONTEXT_GREGS_OFFSET + 32*8
// Use x31/t6 as scratch register.
frcsr x31
sw x31, (32 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f0, (0 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f1, (1 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f2, (2 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f3, (3 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f4, (4 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f5, (5 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f6, (6 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f7, (7 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f8, (8 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f9, (9 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f10, (10 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f11, (11 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f12, (12 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f13, (13 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f14, (14 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f15, (15 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f16, (16 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f17, (17 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f18, (18 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f19, (19 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f20, (20 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f21, (21 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f22, (22 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f23, (23 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f24, (24 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f25, (25 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f26, (26 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f27, (27 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f28, (28 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f29, (29 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f30, (30 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
fsd f31, (31 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)
ret
#endif // __i386__

View File

@ -35,6 +35,9 @@ void SanityCheckContext(const NativeCPUContext& context) {
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));
#elif defined(ARCH_CPU_RISCV64)
EXPECT_EQ(context.uc_mcontext.__gregs[10],
FromPointerCast<uintptr_t>(&context));
#endif
}
@ -49,6 +52,8 @@ uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {
return context.uc_mcontext.pc;
#elif defined(ARCH_CPU_MIPS_FAMILY)
return context.uc_mcontext.pc;
#elif defined(ARCH_CPU_RISCV64)
return context.uc_mcontext.__gregs[0];
#endif
}
@ -63,6 +68,8 @@ uintptr_t StackPointerFromContext(const NativeCPUContext& context) {
return context.uc_mcontext.sp;
#elif defined(ARCH_CPU_MIPS_FAMILY)
return context.uc_mcontext.gregs[29];
#elif defined(ARCH_CPU_RISCV64)
return context.uc_mcontext.__gregs[2];
#endif
}

View File

@ -237,6 +237,8 @@ std::string UserAgent() {
#elif defined(ARCH_CPU_BIG_ENDIAN)
static constexpr char arch[] = "aarch64_be";
#endif
#elif defined (ARCH_CPU_RISCV64)
static constexpr char arch[] = "riscv64";
#else
#error Port
#endif