mac: Crashpad for macOS on arm64, phase 1: build it

This gets all production code for Chrome building, excluding tests.
There aren’t any guarantees that anything works yet.

This is mostly a lot of CPU context shuffling.

In contrast to macOS on x86, there’s no need to support 32-bit arm on
macOS, because this new platform is 64-bit-only from its inception.

Bug: crashpad:345
Change-Id: I0604fe481159d12b3c07ecad051922a427b49849
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2285961
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
GitOrigin-RevId: 809939c9d110e47749e9e83fe5681e2b7c4b7889
diff --git a/client/simulate_crash_mac.cc b/client/simulate_crash_mac.cc
index 8d6e8bd..ae00816 100644
--- a/client/simulate_crash_mac.cc
+++ b/client/simulate_crash_mac.cc
@@ -95,6 +95,15 @@
       state_count = cpu_context.tsh.count;
       break;
 #endif
+#elif defined(ARCH_CPU_ARM64)
+    case ARM_UNIFIED_THREAD_STATE:
+      state = reinterpret_cast<ConstThreadState>(&cpu_context);
+      state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
+      break;
+    case ARM_THREAD_STATE64:
+      state = reinterpret_cast<ConstThreadState>(&cpu_context.ts_64);
+      state_count = cpu_context.ash.count;
+      break;
 #else
 #error Port to your CPU architecture
 #endif
@@ -187,6 +196,13 @@
             implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
   DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),
             x86_THREAD_STATE64_COUNT);
+#elif defined(ARCH_CPU_ARM64)
+  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.ash.flavor),
+            implicit_cast<thread_state_flavor_t>(ARM_THREAD_STATE64));
+  DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.ash.count),
+            ARM_THREAD_STATE64_COUNT);
+#else
+#error Port to your CPU architecture
 #endif
 
   base::mac::ScopedMachSendRight thread(mach_thread_self());
diff --git a/snapshot/ios/exception_snapshot_ios.cc b/snapshot/ios/exception_snapshot_ios.cc
index db9e489..5d6adc8 100644
--- a/snapshot/ios/exception_snapshot_ios.cc
+++ b/snapshot/ios/exception_snapshot_ios.cc
@@ -56,12 +56,16 @@
 #elif defined(ARCH_CPU_ARM64)
   context_.architecture = kCPUArchitectureARM64;
   context_.arm64 = &context_arm64_;
+  arm_debug_state64_t empty_debug_state = {};
   InitializeCPUContextARM64(&context_arm64_,
                             THREAD_STATE_NONE,
                             nullptr,
                             0,
                             &mcontext->__ss,
-                            &mcontext->__ns);
+                            &mcontext->__ns,
+                            &empty_debug_state);
+#else
+#error Port to your CPU architecture
 #endif
 
   // Thread ID.
@@ -127,10 +131,13 @@
 #elif defined(ARCH_CPU_ARM64)
   arm_thread_state64_t thread_state;
   arm_neon_state64_t float_state;
+  arm_debug_state64_t debug_state;
   mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
   mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
+  mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;
   const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
   const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+  const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;
 #endif
 
   kern_return_t kr =
@@ -150,7 +157,6 @@
     MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
   }
 
-#if defined(ARCH_CPU_X86_64)
   kr = thread_get_state(exception_thread,
                         kDebugStateFlavor,
                         reinterpret_cast<thread_state_t>(&debug_state),
@@ -158,7 +164,6 @@
   if (kr != KERN_SUCCESS) {
     MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
   }
-#endif
 
 #if defined(ARCH_CPU_X86_64)
   context_.architecture = kCPUArchitectureX86_64;
@@ -173,8 +178,15 @@
 #elif defined(ARCH_CPU_ARM64)
   context_.architecture = kCPUArchitectureARM64;
   context_.arm64 = &context_arm64_;
-  InitializeCPUContextARM64(
-      &context_arm64_, flavor, state, state_count, &thread_state, &float_state);
+  InitializeCPUContextARM64(&context_arm64_,
+                            flavor,
+                            state,
+                            state_count,
+                            &thread_state,
+                            &float_state,
+                            &debug_state);
+#else
+#error Port to your CPU architecture
 #endif
 
   // Thread ID.
diff --git a/snapshot/ios/thread_snapshot_ios.cc b/snapshot/ios/thread_snapshot_ios.cc
index a52be51..ffd5a45 100644
--- a/snapshot/ios/thread_snapshot_ios.cc
+++ b/snapshot/ios/thread_snapshot_ios.cc
@@ -26,6 +26,7 @@
 #elif defined(ARCH_CPU_ARM64)
 const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
 const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;
 #endif
 
 kern_return_t MachVMRegionRecurseDeepest(task_t task,
@@ -324,8 +325,10 @@
 #elif defined(ARCH_CPU_ARM64)
   arm_thread_state64_t thread_state;
   arm_neon_state64_t float_state;
+  arm_debug_state64_t debug_state;
   mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
   mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
+  mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;
 #endif
 
   kern_return_t kr =
@@ -345,7 +348,6 @@
     MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")";
   }
 
-#if defined(ARCH_CPU_X86_64)
   kr = thread_get_state(thread,
                         kDebugStateFlavor,
                         reinterpret_cast<thread_state_t>(&debug_state),
@@ -353,7 +355,6 @@
   if (kr != KERN_SUCCESS) {
     MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")";
   }
-#endif
 
   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
   kr = thread_info(thread,
@@ -388,7 +389,7 @@
 #if defined(ARCH_CPU_X86_64)
   vm_address_t stack_pointer = thread_state.__rsp;
 #elif defined(ARCH_CPU_ARM64)
-  vm_address_t stack_pointer = thread_state.__sp;
+  vm_address_t stack_pointer = arm_thread_state64_get_sp(thread_state);
 #endif
   stack_region_address =
       CalculateStackRegion(stack_pointer, &stack_region_size);
@@ -432,7 +433,10 @@
                             nullptr,
                             0,
                             &thread_state,
-                            &float_state);
+                            &float_state,
+                            &debug_state);
+#else
+#error Port to your CPU architecture
 #endif
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
diff --git a/snapshot/mac/cpu_context_mac.cc b/snapshot/mac/cpu_context_mac.cc
index 6e2950a..0a30fde 100644
--- a/snapshot/mac/cpu_context_mac.cc
+++ b/snapshot/mac/cpu_context_mac.cc
@@ -437,20 +437,22 @@
 
 }  // namespace internal
 
-#elif defined(ARCH_CPU_ARM_FAMILY)
+#elif defined(ARCH_CPU_ARM64)
 
 namespace {
 
 void InitializeCPUContextARM64Thread(
     CPUContextARM64* context,
     const arm_thread_state64_t* arm_thread_state64) {
-  // The structures of context->regs and arm_thread_state64->__x are laid out
-  // identically for this copy, even though the members are organized
-  // differently.  Because of this difference, there can't be a static assert
-  // similar to the one below for fpsimd.
-  memcpy(context->regs, arm_thread_state64->__x, sizeof(context->regs));
-  context->sp = arm_thread_state64->__sp;
-  context->pc = arm_thread_state64->__pc;
+  // The first 29 fields of context->regs is laid out identically to
+  // arm_thread_state64->__x.
+  memcpy(
+      context->regs, arm_thread_state64->__x, sizeof(arm_thread_state64->__x));
+
+  context->regs[29] = arm_thread_state64_get_fp(*arm_thread_state64);
+  context->regs[30] = arm_thread_state64_get_lr(*arm_thread_state64);
+  context->sp = arm_thread_state64_get_sp(*arm_thread_state64);
+  context->pc = arm_thread_state64_get_pc(*arm_thread_state64);
   context->spsr =
       static_cast<decltype(context->spsr)>(arm_thread_state64->__cpsr);
 }
@@ -464,6 +466,12 @@
   context->fpcr = arm_neon_state64->__fpcr;
 }
 
+void InitializeCPUContextARM64Debug(
+    CPUContextARM64* context,
+    const arm_debug_state64_t* arm_debug_state64) {
+  // TODO(macos_arm64): Create a spot in CPUContextARM64 to keep this.
+}
+
 thread_state_flavor_t InitializeCPUContextARM64Flavor(
     CPUContextARM64* context,
     thread_state_flavor_t flavor,
@@ -471,8 +479,8 @@
     mach_msg_type_number_t state_count) {
   mach_msg_type_number_t expected_state_count;
   switch (flavor) {
-    case ARM_THREAD_STATE:
-      expected_state_count = ARM_THREAD_STATE_COUNT;
+    case ARM_UNIFIED_THREAD_STATE:
+      expected_state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
       break;
     case ARM_THREAD_STATE64:
       expected_state_count = ARM_THREAD_STATE64_COUNT;
@@ -480,6 +488,9 @@
     case ARM_NEON_STATE64:
       expected_state_count = ARM_NEON_STATE64_COUNT;
       break;
+    case ARM_DEBUG_STATE64:
+      expected_state_count = ARM_DEBUG_STATE64_COUNT;
+      break;
     case THREAD_STATE_NONE: {
       // This may happen without error when called without exception-style
       // flavor data, or even from an exception handler when the exception
@@ -498,7 +509,7 @@
   }
 
   switch (flavor) {
-    case ARM_THREAD_STATE: {
+    case ARM_UNIFIED_THREAD_STATE: {
       const arm_unified_thread_state_t* arm_thread_state =
           reinterpret_cast<const arm_unified_thread_state_t*>(state);
       if (arm_thread_state->ash.flavor != ARM_THREAD_STATE64) {
@@ -527,6 +538,13 @@
       return ARM_NEON_STATE64;
     }
 
+    case ARM_DEBUG_STATE64: {
+      const arm_debug_state64_t* arm_debug_state =
+          reinterpret_cast<const arm_debug_state64_t*>(state);
+      InitializeCPUContextARM64Debug(context, arm_debug_state);
+      return ARM_DEBUG_STATE64;
+    }
+
     case THREAD_STATE_NONE: {
       // This may happen without error when called without exception-style
       // flavor data, or even from an exception handler when the exception
@@ -550,7 +568,8 @@
                                ConstThreadState state,
                                mach_msg_type_number_t state_count,
                                const arm_thread_state64_t* arm_thread_state64,
-                               const arm_neon_state64_t* arm_neon_state64) {
+                               const arm_neon_state64_t* arm_neon_state64,
+                               const arm_debug_state64_t* arm_debug_state64) {
   thread_state_flavor_t set_flavor = THREAD_STATE_NONE;
   if (flavor != THREAD_STATE_NONE) {
     set_flavor =
@@ -563,6 +582,9 @@
   if (set_flavor != ARM_NEON_STATE64) {
     InitializeCPUContextARM64Neon(context, arm_neon_state64);
   }
+  if (set_flavor != ARM_DEBUG_STATE64) {
+    InitializeCPUContextARM64Debug(context, arm_debug_state64);
+  }
 }
 
 }  // namespace internal
diff --git a/snapshot/mac/cpu_context_mac.h b/snapshot/mac/cpu_context_mac.h
index 05c035a..154dccd 100644
--- a/snapshot/mac/cpu_context_mac.h
+++ b/snapshot/mac/cpu_context_mac.h
@@ -108,9 +108,9 @@
                                 const x86_float_state64_t* x86_float_state64,
                                 const x86_debug_state64_t* x86_debug_state64);
 
-#elif defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
+#elif defined(ARCH_CPU_ARM64) || DOXYGEN
 //! \brief Initializes a CPUContextARM64 structure from native context
-//!     structures on iOS.
+//!     structures on macOS or iOS.
 //!
 //! \a flavor, \a state, and \a state_count may be supplied by exception
 //! handlers in order for the \a context parameter to be initialized by the
@@ -140,12 +140,14 @@
 //! \param[in] arm_thread_state64 The state of the thread’s integer registers.
 //! \param[in] arm_neon_state64 The state of the thread’s floating-point
 //!     registers.
+//! \param[in] arm_debug_state64 The state of the thread’s debug registers.
 void InitializeCPUContextARM64(CPUContextARM64* context,
                                thread_state_flavor_t flavor,
                                ConstThreadState state,
                                mach_msg_type_number_t state_count,
                                const arm_thread_state64_t* arm_thread_state64,
-                               const arm_neon_state64_t* arm_neon_state64);
+                               const arm_neon_state64_t* arm_neon_state64,
+                               const arm_debug_state64_t* arm_debug_state64);
 #endif
 
 }  // namespace internal
diff --git a/snapshot/mac/exception_snapshot_mac.cc b/snapshot/mac/exception_snapshot_mac.cc
index 8eefac9..39d8fb2 100644
--- a/snapshot/mac/exception_snapshot_mac.cc
+++ b/snapshot/mac/exception_snapshot_mac.cc
@@ -187,6 +187,18 @@
        exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
     code_1_is_exception_address = false;
   }
+#elif defined(ARCH_CPU_ARM64)
+  context_.architecture = kCPUArchitectureARM64;
+  context_.arm64 = &context_union_.arm64;
+  InitializeCPUContextARM64(context_.arm64,
+                            flavor,
+                            state,
+                            state_count,
+                            &thread->thread_context,
+                            &thread->float_context,
+                            &thread->debug_context);
+#else
+#error Port to your architecture
 #endif
 
   if (code_1_is_exception_address) {
diff --git a/snapshot/mac/exception_snapshot_mac.h b/snapshot/mac/exception_snapshot_mac.h
index 52ef519..7c0b87c 100644
--- a/snapshot/mac/exception_snapshot_mac.h
+++ b/snapshot/mac/exception_snapshot_mac.h
@@ -79,12 +79,16 @@
   virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;
 
  private:
-#if defined(ARCH_CPU_X86_FAMILY)
   union {
+#if defined(ARCH_CPU_X86_FAMILY)
     CPUContextX86 x86;
     CPUContextX86_64 x86_64;
-  } context_union_;
+#elif defined(ARCH_CPU_ARM64)
+    CPUContextARM64 arm64;
+#else
+#error Port to your CPU architecture
 #endif
+  } context_union_;
   CPUContext context_;
   std::vector<uint64_t> codes_;
   uint64_t thread_id_;
diff --git a/snapshot/mac/process_reader_mac.cc b/snapshot/mac/process_reader_mac.cc
index 61dc3a1..ca2c1f7 100644
--- a/snapshot/mac/process_reader_mac.cc
+++ b/snapshot/mac/process_reader_mac.cc
@@ -280,6 +280,15 @@
         Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32;
     mach_msg_type_number_t debug_state_count =
         Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT;
+#elif defined(ARCH_CPU_ARM64)
+    const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
+    mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
+
+    const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;
+    mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
+
+    const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;
+    mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;
 #endif
 
     kr = thread_get_state(
@@ -366,6 +375,9 @@
     mach_vm_address_t stack_pointer = Is64Bit()
                                           ? thread.thread_context.t64.__rsp
                                           : thread.thread_context.t32.__esp;
+#elif defined(ARCH_CPU_ARM64)
+    mach_vm_address_t stack_pointer =
+        arm_thread_state64_get_sp(thread.thread_context);
 #endif
 
     thread.stack_region_address =
diff --git a/snapshot/mac/process_reader_mac.h b/snapshot/mac/process_reader_mac.h
index 9feb8b0..ac57a06 100644
--- a/snapshot/mac/process_reader_mac.h
+++ b/snapshot/mac/process_reader_mac.h
@@ -55,6 +55,10 @@
       x86_debug_state64_t d64;
       x86_debug_state32_t d32;
     };
+#elif defined(ARCH_CPU_ARM64)
+    using ThreadContext = arm_thread_state64_t;
+    using FloatContext = arm_neon_state64_t;
+    using DebugContext = arm_debug_state64_t;
 #endif
 
     Thread();
diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc
index 5410be0..2d6620d 100644
--- a/snapshot/mac/system_snapshot_mac.cc
+++ b/snapshot/mac/system_snapshot_mac.cc
@@ -23,6 +23,7 @@
 #include <algorithm>
 
 #include "base/logging.h"
+#include "base/notreached.h"
 #include "base/scoped_clear_last_error.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
@@ -157,6 +158,8 @@
 #if defined(ARCH_CPU_X86_FAMILY)
   return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
                                     : kCPUArchitectureX86;
+#elif defined(ARCH_CPU_ARM64)
+  return kCPUArchitectureARM64;
 #else
 #error port to your architecture
 #endif
@@ -174,6 +177,11 @@
   uint8_t stepping = CastIntSysctlByName<uint8_t>("machdep.cpu.stepping", 0);
 
   return (family << 16) | (model << 8) | stepping;
+#elif defined(ARCH_CPU_ARM64)
+  // TODO(macos_arm64): Verify that this is correct, and pack more information
+  // if feasible. The Apple A12Z returns hw.cputype = 0x100000c and
+  // hw.cpusubtype = 2.
+  return CastIntSysctlByName<uint32_t>("hw.cputype", 0);
 #else
 #error port to your architecture
 #endif
@@ -189,6 +197,8 @@
 
 #if defined(ARCH_CPU_X86_FAMILY)
   return ReadStringSysctlByName("machdep.cpu.vendor");
+#elif defined(ARCH_CPU_ARM64)
+  return ReadStringSysctlByName("machdep.cpu.brand_string");
 #else
 #error port to your architecture
 #endif
diff --git a/snapshot/mac/thread_snapshot_mac.cc b/snapshot/mac/thread_snapshot_mac.cc
index ddb4def..d261f19 100644
--- a/snapshot/mac/thread_snapshot_mac.cc
+++ b/snapshot/mac/thread_snapshot_mac.cc
@@ -75,6 +75,18 @@
                             &process_reader_thread.float_context.f32,
                             &process_reader_thread.debug_context.d32);
   }
+#elif defined(ARCH_CPU_ARM64)
+  context_.architecture = kCPUArchitectureARM64;
+  context_.arm64 = &context_union_.arm64;
+  InitializeCPUContextARM64(context_.arm64,
+                            THREAD_STATE_NONE,
+                            nullptr,
+                            0,
+                            &process_reader_thread.thread_context,
+                            &process_reader_thread.float_context,
+                            &process_reader_thread.debug_context);
+#else
+#error Port to your CPU architecture
 #endif
 
   INITIALIZATION_STATE_SET_VALID(initialized_);
diff --git a/snapshot/mac/thread_snapshot_mac.h b/snapshot/mac/thread_snapshot_mac.h
index 946b008..06331fa 100644
--- a/snapshot/mac/thread_snapshot_mac.h
+++ b/snapshot/mac/thread_snapshot_mac.h
@@ -63,12 +63,16 @@
   std::vector<const MemorySnapshot*> ExtraMemory() const override;
 
  private:
-#if defined(ARCH_CPU_X86_FAMILY)
   union {
+#if defined(ARCH_CPU_X86_FAMILY)
     CPUContextX86 x86;
     CPUContextX86_64 x86_64;
-  } context_union_;
+#elif defined(ARCH_CPU_ARM64)
+    CPUContextARM64 arm64;
+#else
+#error Port to your CPU architecture
 #endif
+  } context_union_;
   CPUContext context_;
   MemorySnapshotGeneric stack_;
   uint64_t thread_id_;
diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm
index a433bb3..8faa1b8 100644
--- a/util/net/http_transport_mac.mm
+++ b/util/net/http_transport_mac.mm
@@ -91,6 +91,8 @@
     NSString* arch = @"i386";
 #elif defined(ARCH_CPU_X86_64)
     NSString* arch = @"x86_64";
+#elif defined(ARCH_CPU_ARM64)
+    NSString* arch = @"arm64";
 #else
 #error Port
 #endif