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/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 18bd490..9e2a3cd 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -21,6 +21,7 @@
 #include <unwindstack/DwarfMemory.h>
 #include <unwindstack/DwarfSection.h>
 #include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
 #include <unwindstack/Log.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@
 
     // Now get the location information for this pc.
     dwarf_loc_regs_t loc_regs;
-    if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+    if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
       return false;
     }
     loc_regs.cie = fde->cie;
@@ -464,6 +465,13 @@
         eval_info->return_address_undefined = true;
       }
       break;
+    case DWARF_LOCATION_PSEUDO_REGISTER: {
+      if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
+        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+        return false;
+      }
+      break;
+    }
     default:
       break;
   }
@@ -491,6 +499,10 @@
   // Always set the dex pc to zero when evaluating.
   cur_regs->set_dex_pc(0);
 
+  // Reset necessary pseudo registers before evaluation.
+  // This is needed for ARM64, for example.
+  regs->ResetPseudoRegisters();
+
   EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
                                   .cie = cie,
                                   .regular_memory = regular_memory,
@@ -527,8 +539,10 @@
 
     AddressType* reg_ptr;
     if (reg >= cur_regs->total_regs()) {
-      // Skip this unknown register.
-      continue;
+      if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
+        // Skip this unknown register.
+        continue;
+      }
     }
 
     reg_ptr = eval_info.regs_info.Save(reg);
@@ -554,8 +568,8 @@
 
 template <typename AddressType>
 bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
-                                                       dwarf_loc_regs_t* loc_regs) {
-  DwarfCfa<AddressType> cfa(&memory_, fde);
+                                                       dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
+  DwarfCfa<AddressType> cfa(&memory_, fde, arch);
 
   // Look for the cached copy of the cie data.
   auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
-  DwarfCfa<AddressType> cfa(&memory_, fde);
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
+                                        ArchEnum arch) {
+  DwarfCfa<AddressType> cfa(&memory_, fde, arch);
 
   // Always print the cie information.
   const DwarfCie* cie = fde->cie;