Blob Blame History Raw
From cda9400739dfa064907d822f00578bb51b24a404 Mon Sep 17 00:00:00 2001
From: Shawn Anastasio <shawnanastasio@yahoo.com>
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 = &regs;
@@ -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 <sys/user.h>
   struct user_regs_struct regs;
   struct user_fpsimd_struct fpregs;
+#elif defined(__powerpc64__)
+  // Use the structures defined in <sys/ucontext.h>.
+  mcontext_t mcontext;
+  struct _libc_vrstate vregs;
 #elif defined(__mips__) || defined(__riscv)
   // Use the structure defined in <sys/ucontext.h>.
   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<void*>(context.context.uc_mcontext.pc);
+#elif defined(__powerpc64__)
+  context.siginfo.si_addr =
+      reinterpret_cast<void*>(context.context.uc_mcontext.gp_regs[PT_NIP]);
 #elif defined(__riscv)
   context.siginfo.si_addr =
       reinterpret_cast<void*>(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<uint8_t*>(
         info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]);
+#elif defined(__powerpc64__)
+  stack_pointer =
+      reinterpret_cast<uint8_t*>(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<uint8_t*>(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<uint8_t*>(
       info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]);
+#elif defined(__powerpc64__)
+  stack_pointer =
+      reinterpret_cast<uint8_t*>(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<pid_t*>(one_thread.mcontext.__gregs[4]);
+#elif defined(__powerpc64__)
+    pid_t* process_tid_location =
+        reinterpret_cast<pid_t*>(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<fpregset_t>::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<char*>(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<uint8_t*>(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<uint8_t*>(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<MDRawContextPPC64>(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