From cda9400739dfa064907d822f00578bb51b24a404 Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Fri, 17 Aug 2018 14:18:33 -0500 Subject: [PATCH] Implement support for ppc64 on Linux This patch implements support for the ppc64 architecture on Linux systems. Notable changes include: * Modification of tests to support non-4K page sizes * minidump_writer: Determine size of stack to capture based on page size * dump_writer_common: Introduce member function GetVectorRegisters to ThreadInfo on ppc64 systems. This allows Altivec/VMX registers to be dumped like they are on OS X. linux_ptrace_dumper has been updated to utilize this function along with the ptrace mode NT_PPC_VMX. * processor/exploitability_unittest.cc: Tests were disabled on non-x86 systems. They assume the system objdump is capable of disassembling x86 binaries which is not the case on other architectures. To-do: * tools/linux/md2core has been updated as well, but functionality has not been confirmed and restoration of Altivec/VMX registers has not been implemented Note that proper functionality depends on updates to third_party/LSS that introduce PPC64 support. An in-progress patch that allows breakpad to build and run successfully is available at: https://wiki.raptorcs.com/wiki/Porting/Chromium --- .../dump_writer_common/raw_context_cpu.h | 2 + .../linux/dump_writer_common/thread_info.cc | 56 ++++++++++++++++++- .../linux/dump_writer_common/thread_info.h | 9 +++ .../dump_writer_common/ucontext_reader.cc | 42 ++++++++++++++ .../dump_writer_common/ucontext_reader.h | 3 + src/client/linux/handler/exception_handler.cc | 22 +++++++- src/client/linux/handler/exception_handler.h | 6 +- .../handler/exception_handler_unittest.cc | 8 ++- .../microdump_writer/microdump_writer.cc | 14 ++++- .../microdump_writer_unittest.cc | 15 ++++- .../minidump_writer/linux_core_dumper.cc | 8 ++- .../linux/minidump_writer/linux_dumper.cc | 4 +- .../linux/minidump_writer/linux_dumper.h | 3 +- .../linux_dumper_unittest_helper.cc | 2 + .../minidump_writer/linux_ptrace_dumper.cc | 19 +++++-- .../linux_ptrace_dumper_unittest.cc | 5 ++ .../linux/minidump_writer/minidump_writer.cc | 18 ++++-- .../linux/minidump_writer/minidump_writer.h | 2 + .../minidump_writer_unittest.cc | 3 + src/common/linux/memory_mapped_file.cc | 3 +- .../linux/memory_mapped_file_unittest.cc | 7 ++- src/common/memory_allocator_unittest.cc | 3 +- src/processor/exploitability_linux.cc | 2 + src/processor/exploitability_unittest.cc | 15 +++-- src/tools/linux/md2core/minidump-2-core.cc | 45 +++++++++++++++ 25 files changed, 281 insertions(+), 35 deletions(-) --- a/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h +++ b/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h @@ -51,6 +51,8 @@ typedef MDRawContextRISCV64 RawContextCP # else # error "Unexpected __riscv_xlen" # endif +#elif defined(__powerpc64__) +typedef MDRawContextPPC64 RawContextCPU; #else #error "This code has not been ported to your platform yet." #endif --- a/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.cc +++ b/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.cc @@ -336,7 +336,42 @@ void ThreadInfo::FillCPUContext(RawConte #error "Unexpected __riscv_xlen" #endif } -#endif // __riscv + +#elif defined(__powerpc64__) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return mcontext.gp_regs[PT_NIP]; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { + out->context_flags = MD_CONTEXT_PPC64_FULL; + for (int i = 0; i < MD_CONTEXT_PPC64_GPR_COUNT; i++) + out->gpr[i] = mcontext.gp_regs[i]; + + out->lr = mcontext.gp_regs[PT_LNK]; + out->srr0 = mcontext.gp_regs[PT_NIP]; + out->srr1 = mcontext.gp_regs[PT_MSR]; + out->cr = mcontext.gp_regs[PT_CCR]; + out->xer = mcontext.gp_regs[PT_XER]; + out->ctr = mcontext.gp_regs[PT_CTR]; + + for (int i = 0; i < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; i++) + out->float_save.fpregs[i] = mcontext.fp_regs[i]; + + out->float_save.fpscr = mcontext.fp_regs[NFPREG-1]; + + for (int i = 0; i < MD_VECTORSAVEAREA_PPC_VR_COUNT; i++) + out->vector_save.save_vr[i] = \ + {(((uint64_t)vregs.vrregs[i][0]) << 32) + | vregs.vrregs[i][1], + (((uint64_t)vregs.vrregs[i][2]) << 32) + | vregs.vrregs[i][3]}; + + out->vrsave = vregs.vrsave; + out->vector_save.save_vscr = {0, vregs.vscr.vscr_word}; + out->vector_save.save_vrvalid = 0xFFFFFFFF; +} +#endif // __powerpc64__ void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { assert(gp_regs || size); @@ -350,6 +385,11 @@ void ThreadInfo::GetGeneralPurposeRegist *gp_regs = mcontext.__gregs; if (size) *size = sizeof(mcontext.__gregs); +#elif defined(__powerpc64__) + if (gp_regs) + *gp_regs = mcontext.gp_regs; + if (size) + *size = sizeof(mcontext.gp_regs); #else if (gp_regs) *gp_regs = ®s; @@ -384,6 +424,11 @@ void ThreadInfo::GetFloatingPointRegiste # else # error "Unexpected __riscv_flen" # endif +#elif defined(__powerpc64__) + if (fp_regs) + *fp_regs = &mcontext.fp_regs; + if (size) + *size = sizeof(mcontext.fp_regs); #else if (fp_regs) *fp_regs = &fpregs; @@ -392,4 +437,13 @@ void ThreadInfo::GetFloatingPointRegiste #endif } +#if defined(__powerpc64__) +void ThreadInfo::GetVectorRegisters(void** v_regs, size_t* size) { + if (v_regs) + *v_regs = &vregs; + if (size) + *size = sizeof(vregs); +} +#endif + } // namespace google_breakpad --- a/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.h +++ b/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/thread_info.h @@ -67,6 +67,10 @@ struct ThreadInfo { // Use the structures defined in struct user_regs_struct regs; struct user_fpsimd_struct fpregs; +#elif defined(__powerpc64__) + // Use the structures defined in . + mcontext_t mcontext; + struct _libc_vrstate vregs; #elif defined(__mips__) || defined(__riscv) // Use the structure defined in . mcontext_t mcontext; @@ -83,6 +87,11 @@ struct ThreadInfo { // Returns the pointer and size of float point register area. void GetFloatingPointRegisters(void** fp_regs, size_t* size); + +#if defined(__powerpc64__) + // Returns the pointer and size of the vector register area. (PPC64 only) + void GetVectorRegisters(void** v_regs, size_t* size); +#endif }; } // namespace google_breakpad --- a/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc +++ b/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc @@ -324,6 +324,48 @@ void UContextReader::FillCPUContext(RawC #error "Unexpected __riscv_xlen" #endif } + +#elif defined(__powerpc64__) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gp_regs[MD_CONTEXT_PPC64_REG_SP]; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gp_regs[PT_NIP]; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const struct _libc_vrstate* vregs) { + out->context_flags = MD_CONTEXT_PPC64_FULL; + + for (int i = 0; i < MD_CONTEXT_PPC64_GPR_COUNT; i++) + out->gpr[i] = uc->uc_mcontext.gp_regs[i]; + + out->lr = uc->uc_mcontext.gp_regs[PT_LNK]; + out->srr0 = uc->uc_mcontext.gp_regs[PT_NIP]; + out->srr1 = uc->uc_mcontext.gp_regs[PT_MSR]; + out->cr = uc->uc_mcontext.gp_regs[PT_CCR]; + out->xer = uc->uc_mcontext.gp_regs[PT_XER]; + out->ctr = uc->uc_mcontext.gp_regs[PT_CTR]; + + for (int i = 0; i < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; i++) + out->float_save.fpregs[i] = uc->uc_mcontext.fp_regs[i]; + + out->float_save.fpscr = uc->uc_mcontext.fp_regs[NFPREG-1]; + + for (int i = 0; i < MD_VECTORSAVEAREA_PPC_VR_COUNT; i++) + out->vector_save.save_vr[i] = + {(((uint64_t)vregs->vrregs[i][0]) << 32) + | vregs->vrregs[i][1], + (((uint64_t)vregs->vrregs[i][2]) << 32) + | vregs->vrregs[i][3]}; + + out->vrsave = vregs->vrsave; + out->vector_save.save_vscr = {0, vregs->vscr.vscr_word}; + out->vector_save.save_vrvalid = 0xFFFFFFFF; +} + #endif } // namespace google_breakpad --- a/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h +++ b/third_party/breakpad/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h @@ -54,6 +54,9 @@ struct UContextReader { #elif defined(__aarch64__) static void FillCPUContext(RawContextCPU* out, const ucontext_t* uc, const struct fpsimd_context* fpregs); +#elif defined(__powerpc64__) + static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc, + const struct _libc_vrstate* vregs); #else static void FillCPUContext(RawContextCPU* out, const ucontext_t* uc); #endif --- a/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.cc +++ b/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.cc @@ -464,6 +464,13 @@ bool ExceptionHandler::HandleSignal(int memcpy(&g_crash_context_.float_state, fp_ptr, sizeof(g_crash_context_.float_state)); } +#elif defined(__powerpc64__) + // On PPC64, we must copy VR state + ucontext_t* uc_ptr = (ucontext_t*)uc; + if (uc_ptr->uc_mcontext.v_regs) { + memcpy(&g_crash_context_.vector_state, uc_ptr->uc_mcontext.v_regs, + sizeof(g_crash_context_.vector_state)); + } #elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE ucontext_t* uc_ptr = (ucontext_t*)uc; if (uc_ptr->uc_mcontext.fpregs) { @@ -701,10 +708,18 @@ bool ExceptionHandler::WriteMinidump() { } #endif -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE && !defined(__aarch64__) +#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE && !defined(__aarch64__) \ + && !defined(__powerpc64__) memcpy(&context.float_state, context.context.uc_mcontext.fpregs, sizeof(context.float_state)); #endif + +#if defined(__powerpc64__) + // Vector registers must be copied on PPC64 + memcpy(&context.vector_state, context.context.uc_mcontext.v_regs, + sizeof(context.vector_state)); +#endif + context.tid = sys_gettid(); // Add an exception stream to the minidump for better reporting. @@ -725,6 +740,9 @@ bool ExceptionHandler::WriteMinidump() { #elif defined(__mips__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.pc); +#elif defined(__powerpc64__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.gp_regs[PT_NIP]); #elif defined(__riscv) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.__gregs[REG_PC]); --- a/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h +++ b/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler.h @@ -200,7 +200,11 @@ class ExceptionHandler { siginfo_t siginfo; pid_t tid; // the crashing thread. ucontext_t context; -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + // PPC64's FP state is a part of ucontext_t like MIPS but the vector + // state is not, so a struct is needed. + vstate_t vector_state; +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE fpstate_t float_state; #endif }; --- a/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler_unittest.cc +++ b/third_party/breakpad/breakpad/src/client/linux/handler/exception_handler_unittest.cc @@ -321,7 +321,7 @@ TEST(ExceptionHandlerTest, ParallelChild ASSERT_EQ(SIGSEGV, WTERMSIG(status)); return; } else { - usleep(100000); + usleep(200000); } } @@ -576,6 +576,8 @@ const unsigned char kIllegalInstruction[ #if defined(__mips__) // mfc2 zero,Impl - usually illegal in userspace. 0x48, 0x00, 0x00, 0x48 +#elif defined(__powerpc64__) + 0x01, 0x01, 0x01, 0x01 // Crashes on a tested POWER9 cpu #else // This crashes with SIGILL on x86/x86-64/arm. 0xff, 0xff, 0xff, 0xff @@ -771,10 +773,10 @@ TEST(ExceptionHandlerTest, InstructionPo // These are defined here so the parent can use them to check the // data from the minidump afterwards. - // Use 4k here because the OS will hand out a single page even + // Use the page size here because the OS will hand out a single page even // if a smaller size is requested, and this test wants to // test the upper bound of the memory range. - const uint32_t kMemorySize = 4096; // bytes + const uint32_t kMemorySize = getpagesize(); // bytes const int kOffset = kMemorySize - sizeof(kIllegalInstruction); const pid_t child = fork(); --- a/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer.cc +++ b/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer.cc @@ -141,7 +141,9 @@ class MicrodumpWriter { const MicrodumpExtraInfo& microdump_extra_info, LinuxDumper* dumper) : ucontext_(context ? &context->context : NULL), -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + vector_state_(context ? &context->vector_state : NULL), +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE float_state_(context ? &context->float_state : NULL), #endif dumper_(dumper), @@ -348,6 +350,8 @@ class MicrodumpWriter { # else # error "Unexpected __riscv_xlen" # endif +#elif defined(__powerpc64__) + const char kArch[] = "ppc64"; #else # error "This code has not been ported to your platform yet" #endif @@ -420,7 +424,9 @@ class MicrodumpWriter { void DumpCPUState() { RawContextCPU cpu; my_memset(&cpu, 0, sizeof(RawContextCPU)); -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + UContextReader::FillCPUContext(&cpu, ucontext_, vector_state_); +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); #else UContextReader::FillCPUContext(&cpu, ucontext_); @@ -616,7 +622,9 @@ class MicrodumpWriter { void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } const ucontext_t* const ucontext_; -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + const google_breakpad::vstate_t* const vector_state_; +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE const google_breakpad::fpstate_t* const float_state_; #endif LinuxDumper* dumper_; --- a/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc +++ b/third_party/breakpad/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc @@ -282,10 +282,19 @@ TEST(MicrodumpWriterTest, BasicWithMappi CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf); ASSERT_TRUE(ContainsMicrodump(buf)); + int page_size = getpagesize(); #ifdef __LP64__ - ASSERT_NE(std::string::npos, - buf.find("M 0000000000001000 000000000000002A 0000000000001000 " - "33221100554477668899AABBCCDDEEFF0 libfoo.so")); + // This test is only available for the following page sizes + ASSERT_TRUE((page_size == 4096) || (page_size == 65536)); + if (page_size == 4096) { + ASSERT_NE(std::string::npos, + buf.find("M 0000000000001000 000000000000002A 0000000000001000 " + "33221100554477668899AABBCCDDEEFF0 libfoo.so")); + } else { + ASSERT_NE(std::string::npos, + buf.find("M 0000000000010000 000000000000002A 0000000000010000 " + "33221100554477668899AABBCCDDEEFF0 libfoo.so")); + } #else ASSERT_NE(std::string::npos, buf.find("M 00001000 0000002A 00001000 " --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -118,6 +118,9 @@ bool LinuxCoreDumper::GetThreadInfoByInd #elif defined(__riscv) stack_pointer = reinterpret_cast( info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]); +#elif defined(__powerpc64__) + stack_pointer = + reinterpret_cast(info->mcontext.gp_regs[MD_CONTEXT_PPC64_REG_SP]); #else # error "This code hasn't been ported to your platform yet." #endif @@ -213,7 +216,10 @@ bool LinuxCoreDumper::EnumerateThreads() memset(&info, 0, sizeof(ThreadInfo)); info.tgid = status->pr_pgrp; info.ppid = status->pr_ppid; -#if defined(__mips__) +#if defined(__powerpc64__) + for (int i = 0; i < 31; i++) + info.mcontext.gp_regs[i] = status->pr_reg[i]; +#elif defined(__mips__) # if defined(__ANDROID__) for (int i = EF_R0; i <= EF_R31; i++) info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.cc @@ -770,7 +770,9 @@ bool LinuxDumper::GetStackInfo(const voi reinterpret_cast(int_stack_pointer & ~(page_size - 1)); // The number of bytes of stack which we try to capture. - static const ptrdiff_t kStackToCapture = 32 * 1024; + // This now depends on page_size to avoid missing data + // on systems with larger page sizes. + static const ptrdiff_t kStackToCapture = 8 * page_size; const MappingInfo* mapping = FindMapping(stack_pointer); if (!mapping) --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper.h @@ -64,7 +64,8 @@ namespace google_breakpad { typedef Elf32_auxv_t elf_aux_entry; #elif defined(__x86_64) || defined(__aarch64__) || \ (defined(__mips__) && _MIPS_SIM != _ABIO32) || \ - (defined(__riscv) && __riscv_xlen == 64) + (defined(__riscv) && __riscv_xlen == 64) || \ + defined(__powerpc64__) typedef Elf64_auxv_t elf_aux_entry; #endif --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -56,6 +56,8 @@ #define TID_PTR_REGISTER "$1" #elif defined(__riscv) #define TID_PTR_REGISTER "x4" +#elif defined(__powerpc64__) +#define TID_PTR_REGISTER "r8" #else #error This test has not been ported to this platform. #endif --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -189,12 +189,12 @@ bool LinuxPtraceDumper::ReadRegisterSet( #ifdef PTRACE_GETREGSET struct iovec io; info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len); - if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { + if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { return false; } info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len); - if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { + if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { // We are going to check if we can read VFP registers on ARM32. // Currently breakpad does not support VFP registers to be a part of minidump, // so this is only to confirm that we can actually read FP registers. @@ -214,6 +214,15 @@ bool LinuxPtraceDumper::ReadRegisterSet( } #endif // defined(__arm__) } + +#if defined(__powerpc64__) + // Grab the vector registers on PPC64 too + info->GetVectorRegisters(&io.iov_base, &io.iov_len); + if (ptrace(PTRACE_GETREGSET, tid, (void*)NT_PPC_VMX, (void*)&io) == -1) { + return false; + } +#endif // defined(__powerpc64__) + return true; #else return false; @@ -378,6 +387,9 @@ bool LinuxPtraceDumper::GetThreadInfoByI #elif defined(__riscv) stack_pointer = reinterpret_cast( info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]); +#elif defined(__powerpc64__) + stack_pointer = + reinterpret_cast(info->mcontext.gp_regs[MD_CONTEXT_PPC64_REG_SP]); #else # error "This code hasn't been ported to your platform yet." #endif --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -470,6 +470,9 @@ TEST(LinuxPtraceDumperTest, VerifyStackR #elif defined(__riscv) pid_t* process_tid_location = reinterpret_cast(one_thread.mcontext.__gregs[4]); +#elif defined(__powerpc64__) + pid_t* process_tid_location = + reinterpret_cast(one_thread.mcontext.gp_regs[8]); #else #error This test has not been ported to this platform. #endif @@ -569,6 +572,8 @@ TEST_F(LinuxPtraceDumperTest, SanitizeSt uintptr_t heap_addr = thread_info.mcontext.gregs[1]; #elif defined(__riscv) uintptr_t heap_addr = thread_info.mcontext.__gregs[4]; +#elif defined(__powerpc64__) + uintptr_t heap_addr = thread_info.mcontext.gp_regs[8]; #else #error This test has not been ported to this platform. #endif --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.cc @@ -144,7 +144,9 @@ class MinidumpWriter { : fd_(minidump_fd), path_(minidump_path), ucontext_(context ? &context->context : NULL), -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + vector_state_(context ? &context->vector_state : NULL), +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE float_state_(context ? &context->float_state : NULL), #endif dumper_(dumper), @@ -476,7 +478,9 @@ class MinidumpWriter { if (!cpu.Allocate()) return false; my_memset(cpu.get(), 0, sizeof(RawContextCPU)); -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + UContextReader::FillCPUContext(cpu.get(), ucontext_, vector_state_); +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_); #else UContextReader::FillCPUContext(cpu.get(), ucontext_); @@ -953,7 +957,7 @@ class MinidumpWriter { dirent->location.rva = 0; } -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) +#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || defined(__powerpc64__) bool WriteCPUInformation(MDRawSystemInfo* sys_info) { char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; static const char vendor_id_name[] = "vendor_id"; @@ -973,7 +977,9 @@ class MinidumpWriter { // processor_architecture should always be set, do this first sys_info->processor_architecture = -#if defined(__mips__) +#if defined(__powerpc64__) + MD_CPU_ARCHITECTURE_PPC64; +#elif defined(__mips__) # if _MIPS_SIM == _ABIO32 MD_CPU_ARCHITECTURE_MIPS; # elif _MIPS_SIM == _ABI64 @@ -1440,7 +1446,9 @@ class MinidumpWriter { const char* path_; // Path to the file where the minidum should be written. const ucontext_t* const ucontext_; // also from the signal handler -#if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE +#if defined(__powerpc64__) + const google_breakpad::vstate_t* const vector_state_; +#elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE const google_breakpad::fpstate_t* const float_state_; // ditto #endif LinuxDumper* dumper_; --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer.h @@ -47,6 +47,8 @@ class ExceptionHandler; #if defined(__aarch64__) typedef struct fpsimd_context fpstate_t; +#elif defined(__powerpc64__) +typedef struct _libc_vrstate vstate_t; #elif !defined(__ARM_EABI__) && !defined(__mips__) typedef std::remove_pointer::type fpstate_t; #endif --- a/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/third_party/breakpad/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -723,6 +723,9 @@ TEST(MinidumpWriterTest, InvalidStackPoi #elif defined(__riscv) context.context.uc_mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP] = invalid_stack_pointer; +#elif defined(__powerpc64__) + context.context.uc_mcontext.gp_regs[MD_CONTEXT_PPC64_REG_SP] = + invalid_stack_pointer; #else # error "This code has not been ported to your platform yet." #endif --- a/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file.cc +++ b/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file.cc @@ -72,8 +72,7 @@ bool MemoryMappedFile::Map(const char* p #if defined(__x86_64__) || defined(__aarch64__) || \ (defined(__mips__) && _MIPS_SIM == _ABI64) || \ - (defined(__riscv) && __riscv_xlen == 64) - + (defined(__riscv) && __riscv_xlen == 64) || defined(__powerpc64__) struct kernel_stat st; if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { #else --- a/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file_unittest.cc +++ b/third_party/breakpad/breakpad/src/common/linux/memory_mapped_file_unittest.cc @@ -179,9 +179,10 @@ TEST_F(MemoryMappedFileTest, RemapAfterM TEST_F(MemoryMappedFileTest, MapWithOffset) { // Put more data in the test file this time. Offsets can only be // done on page boundaries, so we need a two page file to test this. - const int page_size = 4096; - char data1[2 * page_size]; - size_t data1_size = sizeof(data1); + const int page_size = getpagesize(); + char *data1 = static_cast(malloc(2 * page_size)); + EXPECT_TRUE(data1 != NULL); + size_t data1_size = (2 * page_size); for (size_t i = 0; i < data1_size; ++i) { data1[i] = i & 0x7f; } --- a/third_party/breakpad/breakpad/src/common/memory_allocator_unittest.cc +++ b/third_party/breakpad/breakpad/src/common/memory_allocator_unittest.cc @@ -60,8 +60,9 @@ TEST(PageAllocatorTest, LargeObject) { EXPECT_EQ(0U, allocator.pages_allocated()); uint8_t* p = reinterpret_cast(allocator.Alloc(10000)); + uint64_t expected_pages = 1 + ((10000 - 1) / getpagesize()); ASSERT_FALSE(p == NULL); - EXPECT_EQ(3U, allocator.pages_allocated()); + EXPECT_EQ(expected_pages, allocator.pages_allocated()); for (unsigned i = 1; i < 10; ++i) { uint8_t* p = reinterpret_cast(allocator.Alloc(i)); ASSERT_FALSE(p == NULL); --- a/third_party/breakpad/breakpad/src/tools/linux/md2core/minidump-2-core.cc +++ b/third_party/breakpad/breakpad/src/tools/linux/md2core/minidump-2-core.cc @@ -82,6 +82,8 @@ #define ELF_ARCH EM_AARCH64 #elif defined(__riscv) #define ELF_ARCH EM_RISCV +#elif defined(__powerpc64__) + #define ELF_ARCH EM_PPC64 #endif #if defined(__arm__) @@ -92,6 +94,8 @@ typedef user_regs user_regs_struct; #elif defined (__mips__) || defined(__riscv) // This file-local typedef simplifies the source code. typedef gregset_t user_regs_struct; +#elif defined(__powerpc64__) +typedef struct pt_regs user_regs_struct; #endif using google_breakpad::MDTypeHelper; @@ -324,6 +328,9 @@ struct CrashedProcess { #if defined(__aarch64__) user_fpsimd_struct fpregs; #endif +#if defined(__powerpc64__) + mcontext_t mcontext; +#endif uintptr_t stack_addr; const uint8_t* stack; size_t stack_length; @@ -599,6 +606,38 @@ ParseThreadRegisters(CrashedProcess::Thr #error "Unexpected __riscv_xlen" #endif } +#elif defined(__powerpc64__) +static void +ParseThreadRegisters(CrashedProcess::Thread* thread, + const MinidumpMemoryRange& range) { + const MDRawContextPPC64* rawregs = range.GetData(0); + + for (int i = 0; i < MD_CONTEXT_PPC64_GPR_COUNT; i++) + thread->mcontext.gp_regs[i] = rawregs->gpr[i]; + + thread->mcontext.gp_regs[PT_LNK] = rawregs->lr; + thread->mcontext.gp_regs[PT_NIP] = rawregs->srr0; + thread->mcontext.gp_regs[PT_MSR] = rawregs->srr1; + thread->mcontext.gp_regs[PT_CCR] = rawregs->cr; + thread->mcontext.gp_regs[PT_XER] = rawregs->xer; + thread->mcontext.gp_regs[PT_CTR] = rawregs->ctr; + thread->mcontext.v_regs->vrsave = rawregs->vrsave; + + for (int i = 0; i < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; i++) + thread->mcontext.fp_regs[i] = rawregs->float_save.fpregs[i]; + + thread->mcontext.fp_regs[NFPREG-1] = rawregs->float_save.fpscr; + + for (int i = 0; i < MD_VECTORSAVEAREA_PPC_VR_COUNT; i++) { + thread->mcontext.v_regs->vrregs[i][0] = rawregs->vector_save.save_vr[i].high >> 32; + thread->mcontext.v_regs->vrregs[i][1] = rawregs->vector_save.save_vr[i].high; + thread->mcontext.v_regs->vrregs[i][2] = rawregs->vector_save.save_vr[i].low >> 32; + thread->mcontext.v_regs->vrregs[i][3] = rawregs->vector_save.save_vr[i].low; + } + + thread->mcontext.v_regs->vscr.vscr_word = rawregs->vector_save.save_vscr.low & 0xFFFFFFFF; +} + #else #error "This code has not been ported to your platform yet" #endif @@ -704,6 +743,12 @@ ParseSystemInfo(const Options& options, # else # error "Unexpected __riscv_xlen" # endif +#elif defined(__powerpc64__) + if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_PPC64) { + fprintf(stderr, + "This version of minidump-2-core only supports PPC64.\n"); + exit(1); + } #else #error "This code has not been ported to your platform yet" #endif