libunwindstack: support for Armv8.3-A Pointer Authentication

This patch adds support for handling return addresses signed with
pointer authentication. It simply strips the authentication code
without verifying its correctness, and thus works with both A and B
keys and through key-change boundaries.

Additons:
  * DW_CFA_AARCH64_negate_ra_state: new CFA operation.
  * RA_SIGN_STATE: new pseudo register.
  * Pass the arch to DwarfCfa so that the new op is only executed
    on aarch64.

The stripping uses the xpaclri instruction. This is a hint space
instruction which is compatible with pre Armv8.3-A devices. For cases
where it cannot be used, a mask can be set instead.

Test: libunwindstack_test
      Without this patch all UnwindTest.* testcases should fail if
      compiled with Pointer Authentication.

The tests should be executed with both -mbranch-protection=pac-ret and
pac-ret+leaf flags so that either some or all functions have pointer
authentication instructions.

Change-Id: Id7c3f1d0e2fc7fccb19bd1430826264405a9df7c
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index c128b9b..c6db209 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -26,7 +26,9 @@
 
 #include <unwindstack/DwarfError.h>
 #include <unwindstack/DwarfLocation.h>
+#include <unwindstack/Elf.h>
 #include <unwindstack/Log.h>
+#include <unwindstack/MachineArm64.h>
 
 #include "DwarfCfa.h"
 #include "DwarfEncoding.h"
@@ -204,8 +206,12 @@
 bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
                                            uint64_t* cur_pc) {
   const auto* cfa = &DwarfCfaInfo::kTable[op];
-  if (cfa->name[0] == '\0') {
-    log(indent, "Illegal");
+  if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
+    if (op == 0x2d) {
+      log(indent, "Illegal (Only valid on aarch64)");
+    } else {
+      log(indent, "Illegal");
+    }
     log(indent, "Raw Data: 0x%02x", op);
     return true;
   }
@@ -514,6 +520,24 @@
   return true;
 }
 
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
+  // Only supported on aarch64.
+  if (arch_ != ARCH_ARM64) {
+    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+    return false;
+  }
+
+  auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
+  if (cfa_location == loc_regs->end()) {
+    (*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
+                                                       .values = {1}};
+  } else {
+    cfa_location->second.values[0] ^= 1;
+  }
+  return true;
+}
+
 const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
     {
         // 0x00 DW_CFA_nop
@@ -699,7 +723,13 @@
     {"", 0, 0, {}, {}},  // 0x2a illegal cfa
     {"", 0, 0, {}, {}},  // 0x2b illegal cfa
     {"", 0, 0, {}, {}},  // 0x2c illegal cfa
-    {"", 0, 0, {}, {}},  // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+    {
+        "DW_CFA_AARCH64_negate_ra_state",  // 0x2d DW_CFA_AARCH64_negate_ra_state
+        3,
+        0,
+        {},
+        {},
+    },
     {
         "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
         2,