ios: CaptureContext arm64.

Change-Id: I2db8ead3103391af4d198f213524ea34ffef022b
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2167211
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
diff --git a/client/crashpad_client_ios_test.mm b/client/crashpad_client_ios_test.mm
index 002fddb..292b27f 100644
--- a/client/crashpad_client_ios_test.mm
+++ b/client/crashpad_client_ios_test.mm
@@ -32,18 +32,7 @@
   client.StartCrashpadInProcessHandler();
 
   NativeCPUContext context;
-#if defined(ARCH_CPU_X86_64)
   CaptureContext(&context);
-#elif defined(ARCH_CPU_ARM64)
-  // TODO(justincohen): Implement CaptureContext for ARM64.
-  mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
-  kern_return_t kr =
-      thread_get_state(mach_thread_self(),
-                       MACHINE_THREAD_STATE,
-                       reinterpret_cast<thread_state_t>(&context),
-                       &thread_state_count);
-  ASSERT_EQ(kr, KERN_SUCCESS);
-#endif
   client.DumpWithoutCrash(&context);
 }
 
diff --git a/util/BUILD.gn b/util/BUILD.gn
index 9dc80ac..f3ffedd 100644
--- a/util/BUILD.gn
+++ b/util/BUILD.gn
@@ -723,6 +723,7 @@
       "mach/mach_extensions_test.cc",
       "mach/mach_message_test.cc",
       "mach/symbolic_constants_mach_test.cc",
+      "misc/capture_context_test_util_mac.cc",
     ]
   }
 
@@ -740,7 +741,6 @@
       "mach/mach_message_server_test.cc",
       "mach/notify_server_test.cc",
       "mach/scoped_task_suspend_test.cc",
-      "misc/capture_context_test_util_mac.cc",
       "process/process_memory_mac_test.cc",
     ]
   }
@@ -749,7 +749,6 @@
     sources += [ "ios/exception_processor_test.mm" ]
 
     sources -= [
-      "misc/capture_context_test.cc",
       "process/process_memory_range_test.cc",
       "process/process_memory_test.cc",
     ]
diff --git a/util/misc/capture_context_mac.S b/util/misc/capture_context_mac.S
index 39c6ca6..f6acafb 100644
--- a/util/misc/capture_context_mac.S
+++ b/util/misc/capture_context_mac.S
@@ -12,17 +12,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
 
+#if defined(__i386__) || defined(__x86_64__)
 // namespace crashpad {
 // void CaptureContext(x86_thread_state_t* x86_thread_state);
 // }  // namespace crashpad
 #define CAPTURECONTEXT_SYMBOL __ZN8crashpad14CaptureContextEP16x86_thread_state
+#elif defined(__aarch64__)
+// namespace crashpad {
+// void CaptureContext(arm_unified_thread_state_t* arm_unified_thread_state);
+// }  // namespace crashpad
+#define CAPTURECONTEXT_SYMBOL \
+    __ZN8crashpad14CaptureContextEP24arm_unified_thread_state
+#endif
 
   .section __TEXT,__text,regular,pure_instructions
   .private_extern CAPTURECONTEXT_SYMBOL
   .globl CAPTURECONTEXT_SYMBOL
-  .balign 16, 0x90
+#if defined(__i386__) || defined(__x86_64__)
+  .p2align 4, 0x90
+#elif defined(__aarch64__)
+  .p2align 2
+#endif
 CAPTURECONTEXT_SYMBOL:
 
 #if defined(__i386__)
@@ -211,6 +223,64 @@
 
   .cfi_endproc
 
+#elif defined(__aarch64__)
+
+  .cfi_startproc
+
+  // Save general-purpose registers in arm_unified_thread_state->ts_64.__x[0].
+  // The original x0 can't be recovered.
+  stp x0, x1, [x0, #0x8]
+  stp x2, x3, [x0, #0x18]
+  stp x4, x5, [x0, #0x28]
+  stp x6, x7, [x0, #0x38]
+  stp x8, x9, [x0, #0x48]
+  stp x10, x11, [x0, #0x58]
+  stp x12, x13, [x0, #0x68]
+  stp x14, x15, [x0, #0x78]
+  stp x16, x17, [x0, #0x88]
+  stp x18, x19, [x0, #0x98]
+  stp x20, x21, [x0, #0xa8]
+  stp x22, x23, [x0, #0xb8]
+  stp x24, x25, [x0, #0xc8]
+  stp x26, x27, [x0, #0xd8]
+
+  // Save the last general-purpose register (x28) and the frame pointer (x29).
+  stp x28, x29, [x0, #0xe8]  // __x[28] and __fp
+
+  // Save the link register (x30) and the stack pointer (using x1 as a scratch
+  // register)
+  mov x1, sp
+  stp x30, x1, [x0, #0xf8]  // __lr and __sp
+
+  // The link register (x30) holds the return address for this function.
+  // __cpsr should hold current program status register, but nzcv are the only
+  // bits we know about (saved using x1 as a scratch register). The 64-bit x1
+  // covers both the 32-bit __cpsr (which receives the nzcv bits) and __pad
+  // (which will be zeroed).
+  mrs x1, nzcv
+  stp x30, x1, [x0, #0x108]  // __pc and __cpsr and __pad
+
+  // Initialize the header identifying the arm_unified_thread_state structure as
+  // carrying an arm_thread_state64_t (flavor ARM_THREAD_STATE64) of size
+  // ARM_THREAD_STATE64_COUNT 32-bit values.
+  mov x1, #6
+  movk x1, #68, lsl #32
+  str x1, [x0, #0x0]  // arm_thread_state->ash.flavor and count
+
+  // Restore x1 from the saved context.
+  ldr x1, [x0, #0x10]
+
+  // TODO(justincohen): Consider saving floating-point registers into
+  // arm_neon_state64_t as second parameter, or as a a second function call
+  // after all of the general-purpose state is captured, or as a new struct that
+  // has both arm_unified_state_t and arm_neon_state64_t members. That may be
+  // better than a second parameter (which occupies another register) and better
+  // than a second function call.
+
+  ret
+
+  .cfi_endproc
+
 #endif
 
 .subsections_via_symbols
diff --git a/util/misc/capture_context_test_util_mac.cc b/util/misc/capture_context_test_util_mac.cc
index afe0916..6d4143f 100644
--- a/util/misc/capture_context_test_util_mac.cc
+++ b/util/misc/capture_context_test_util_mac.cc
@@ -31,6 +31,11 @@
             implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));
   ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),
             implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));
+#elif defined(ARCH_CPU_ARM64)
+  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.ash.flavor),
+            implicit_cast<thread_state_flavor_t>(ARM_THREAD_STATE64));
+  ASSERT_EQ(implicit_cast<uint32_t>(context.ash.count),
+            implicit_cast<uint32_t>(ARM_THREAD_STATE64_COUNT));
 #endif
 
 #if defined(ARCH_CPU_X86_FAMILY)
@@ -61,6 +66,18 @@
   EXPECT_EQ(context.uts.ts64.__gs & ~UINT64_C(0xffff), 0u);
   EXPECT_EQ(context.uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a), 2u);
 #endif
+
+#elif defined(ARCH_CPU_ARM64)
+  // Check that the bits other than nzcv in __cpsr read 0.
+  EXPECT_EQ(context.ts_64.__cpsr & 0x0fffffff, 0u);
+
+  // Check that __pad is entirely zeroed out.
+  EXPECT_EQ(context.ts_64.__pad, 0u);
+
+  // Because ARM ret doesn’t change %lr, and because CaptureContext captures the
+  // the state at CaptureContext’s return to its caller, check for equivalence
+  // of __lr and __pc.
+  EXPECT_EQ(context.ts_64.__lr, context.ts_64.__pc);
 #endif
 }
 
@@ -69,6 +86,8 @@
   return context.uts.ts32.__eip;
 #elif defined(ARCH_CPU_X86_64)
   return context.uts.ts64.__rip;
+#elif defined(ARCH_CPU_ARM64)
+  return context.ts_64.__pc;
 #endif
 }
 
@@ -77,6 +96,8 @@
   return context.uts.ts32.__esp;
 #elif defined(ARCH_CPU_X86_64)
   return context.uts.ts64.__rsp;
+#elif defined(ARCH_CPU_ARM64)
+  return context.ts_64.__sp;
 #endif
 }