Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1270608d authored by Elliott Hughes's avatar Elliott Hughes Committed by Gerrit Code Review
Browse files

Merge "libunwindstack: support for Armv8.3-A Pointer Authentication"

parents aa0358b8 6835d019
Loading
Loading
Loading
Loading
+33 −3
Original line number Original line Diff line number Diff line
@@ -26,7 +26,9 @@


#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/Log.h>
#include <unwindstack/MachineArm64.h>


#include "DwarfCfa.h"
#include "DwarfCfa.h"
#include "DwarfEncoding.h"
#include "DwarfEncoding.h"
@@ -204,8 +206,12 @@ template <typename AddressType>
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
                                           uint64_t* cur_pc) {
                                           uint64_t* cur_pc) {
  const auto* cfa = &DwarfCfaInfo::kTable[op];
  const auto* cfa = &DwarfCfaInfo::kTable[op];
  if (cfa->name[0] == '\0') {
  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, "Illegal");
    }
    log(indent, "Raw Data: 0x%02x", op);
    log(indent, "Raw Data: 0x%02x", op);
    return true;
    return true;
  }
  }
@@ -514,6 +520,24 @@ bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* l
  return true;
  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] = {
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
    {
    {
        // 0x00 DW_CFA_nop
        // 0x00 DW_CFA_nop
@@ -699,7 +723,13 @@ const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
    {"", 0, 0, {}, {}},  // 0x2a illegal cfa
    {"", 0, 0, {}, {}},  // 0x2a illegal cfa
    {"", 0, 0, {}, {}},  // 0x2b illegal cfa
    {"", 0, 0, {}, {}},  // 0x2b illegal cfa
    {"", 0, 0, {}, {}},  // 0x2c 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
        "DW_CFA_GNU_args_size",  // 0x2e DW_CFA_GNU_args_size
        2,
        2,
+10 −3
Original line number Original line Diff line number Diff line
@@ -31,6 +31,9 @@


namespace unwindstack {
namespace unwindstack {


// Forward declarations.
enum ArchEnum : uint8_t;

// DWARF Standard home: http://dwarfstd.org/
// DWARF Standard home: http://dwarfstd.org/
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
@@ -72,7 +75,8 @@ class DwarfCfa {
  typedef typename std::make_signed<AddressType>::type SignedType;
  typedef typename std::make_signed<AddressType>::type SignedType;


 public:
 public:
  DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
  DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
      : memory_(memory), fde_(fde), arch_(arch) {}
  virtual ~DwarfCfa() = default;
  virtual ~DwarfCfa() = default;


  bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
  bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -99,6 +103,7 @@ class DwarfCfa {
  DwarfErrorData last_error_;
  DwarfErrorData last_error_;
  DwarfMemory* memory_;
  DwarfMemory* memory_;
  const DwarfFde* fde_;
  const DwarfFde* fde_;
  ArchEnum arch_;


  AddressType cur_pc_;
  AddressType cur_pc_;
  const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
  const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -128,6 +133,7 @@ class DwarfCfa {
  bool cfa_val_offset_sf(dwarf_loc_regs_t*);
  bool cfa_val_offset_sf(dwarf_loc_regs_t*);
  bool cfa_val_expression(dwarf_loc_regs_t*);
  bool cfa_val_expression(dwarf_loc_regs_t*);
  bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
  bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
  bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);


  using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
  using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
  constexpr static process_func kCallbackTable[64] = {
  constexpr static process_func kCallbackTable[64] = {
@@ -221,8 +227,9 @@ class DwarfCfa {
      nullptr,
      nullptr,
      // 0x2c illegal cfa
      // 0x2c illegal cfa
      nullptr,
      nullptr,
      // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
      // 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
      nullptr,
      // DW_CFA_GNU_window_save on other architectures.
      &DwarfCfa::cfa_aarch64_negate_ra_state,
      // 0x2e DW_CFA_GNU_args_size
      // 0x2e DW_CFA_GNU_args_size
      &DwarfCfa::cfa_nop,
      &DwarfCfa::cfa_nop,
      // 0x2f DW_CFA_GNU_negative_offset_extended
      // 0x2f DW_CFA_GNU_negative_offset_extended
+22 −7
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/DwarfStructs.h>
#include <unwindstack/DwarfStructs.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/Log.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f


    // Now get the location information for this pc.
    // Now get the location information for this pc.
    dwarf_loc_regs_t loc_regs;
    dwarf_loc_regs_t loc_regs;
    if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
    if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
      return false;
      return false;
    }
    }
    loc_regs.cie = fde->cie;
    loc_regs.cie = fde->cie;
@@ -464,6 +465,13 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
        eval_info->return_address_undefined = true;
        eval_info->return_address_undefined = true;
      }
      }
      break;
      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:
    default:
      break;
      break;
  }
  }
@@ -491,6 +499,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
  // Always set the dex pc to zero when evaluating.
  // Always set the dex pc to zero when evaluating.
  cur_regs->set_dex_pc(0);
  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,
  EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
                                  .cie = cie,
                                  .cie = cie,
                                  .regular_memory = regular_memory,
                                  .regular_memory = regular_memory,
@@ -527,9 +539,11 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me


    AddressType* reg_ptr;
    AddressType* reg_ptr;
    if (reg >= cur_regs->total_regs()) {
    if (reg >= cur_regs->total_regs()) {
      if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
        // Skip this unknown register.
        // Skip this unknown register.
        continue;
        continue;
      }
      }
    }


    reg_ptr = eval_info.regs_info.Save(reg);
    reg_ptr = eval_info.regs_info.Save(reg);
    if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
    if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
@@ -554,8 +568,8 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me


template <typename AddressType>
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
                                                       dwarf_loc_regs_t* loc_regs) {
                                                       dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
  DwarfCfa<AddressType> cfa(&memory_, fde);
  DwarfCfa<AddressType> cfa(&memory_, fde, arch);


  // Look for the cached copy of the cie data.
  // Look for the cached copy of the cie data.
  auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
  auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@ bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfF
}
}


template <typename AddressType>
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
  DwarfCfa<AddressType> cfa(&memory_, fde);
                                        ArchEnum arch) {
  DwarfCfa<AddressType> cfa(&memory_, fde, arch);


  // Always print the cie information.
  // Always print the cie information.
  const DwarfCie* cie = fde->cie;
  const DwarfCie* cie = fde->cie;
+52 −1
Original line number Original line Diff line number Diff line
@@ -30,7 +30,10 @@
namespace unwindstack {
namespace unwindstack {


RegsArm64::RegsArm64()
RegsArm64::RegsArm64()
    : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
    : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {
  ResetPseudoRegisters();
  pac_mask_ = 0;
}


ArchEnum RegsArm64::Arch() {
ArchEnum RegsArm64::Arch() {
  return ARCH_ARM64;
  return ARCH_ARM64;
@@ -45,6 +48,23 @@ uint64_t RegsArm64::sp() {
}
}


void RegsArm64::set_pc(uint64_t pc) {
void RegsArm64::set_pc(uint64_t pc) {
  // If the target is aarch64 then the return address may have been
  // signed using the Armv8.3-A Pointer Authentication extension. The
  // original return address can be restored by stripping out the
  // authentication code using a mask or xpaclri. xpaclri is a NOP on
  // pre-Armv8.3-A architectures.
  if ((0 != pc) && IsRASigned()) {
    if (pac_mask_) {
      pc &= ~pac_mask_;
#if defined(__aarch64__)
    } else {
      register uint64_t x30 __asm("x30") = pc;
      // This is XPACLRI.
      asm("hint 0x7" : "+r"(x30));
      pc = x30;
#endif
    }
  }
  regs_[ARM64_REG_PC] = pc;
  regs_[ARM64_REG_PC] = pc;
}
}


@@ -144,6 +164,37 @@ bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* proce
  return true;
  return true;
}
}


void RegsArm64::ResetPseudoRegisters(void) {
  // DWARF for AArch64 says RA_SIGN_STATE should be initialized to 0.
  this->SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 0);
}

bool RegsArm64::SetPseudoRegister(uint16_t id, uint64_t value) {
  if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
    pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST] = value;
    return true;
  }
  return false;
}

bool RegsArm64::GetPseudoRegister(uint16_t id, uint64_t* value) {
  if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
    *value = pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST];
    return true;
  }
  return false;
}

bool RegsArm64::IsRASigned() {
  uint64_t value;
  auto result = this->GetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, &value);
  return (result && (value != 0));
}

void RegsArm64::SetPACMask(uint64_t mask) {
  pac_mask_ = mask;
}

Regs* RegsArm64::Clone() {
Regs* RegsArm64::Clone() {
  return new RegsArm64(*this);
  return new RegsArm64(*this);
}
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -33,6 +33,7 @@ enum DwarfLocationEnum : uint8_t {
  DWARF_LOCATION_REGISTER,
  DWARF_LOCATION_REGISTER,
  DWARF_LOCATION_EXPRESSION,
  DWARF_LOCATION_EXPRESSION,
  DWARF_LOCATION_VAL_EXPRESSION,
  DWARF_LOCATION_VAL_EXPRESSION,
  DWARF_LOCATION_PSEUDO_REGISTER,
};
};


struct DwarfLocation {
struct DwarfLocation {
Loading