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

Commit 559c7f20 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Implement new DEX PC lookup scheme.

GDB wasn't handling the old one gracefully.

- Create a RegsInfo structure that can be used to properly eval expression
  data.
- Remove the versions on Dwarf ops. It doesn't work the in the real world
  and doesn't add useful information.
- Fix dex pc frame number bug.

Test: testrunner.py -j40 --host --cdex-fast -t 137
Test: libunwindstack_test
Test: All unit tests pass.
Change-Id: Iac4fea651b81cb6087fd237a9a5027a352a49245
parent 71fa8125
Loading
Loading
Loading
Loading
+40 −14
Original line number Diff line number Diff line
@@ -36,13 +36,45 @@ template <typename AddressType>
constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];

template <typename AddressType>
bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
  uint32_t iterations = 0;
bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end) {
  is_register_ = false;
  stack_.clear();
  memory_->set_cur_offset(start);
  dex_pc_set_ = false;

  // Unroll the first Decode calls to be able to check for a special
  // sequence of ops and values that indicate this is the dex pc.
  // The pattern is:
  //   OP_const4u (0x0c)  'D' 'E' 'X' '1'
  //   OP_drop (0x13)
  if (memory_->cur_offset() < end) {
    if (!Decode()) {
      return false;
    }
  } else {
    return true;
  }
  bool check_for_drop;
  if (cur_op_ == 0x0c && operands_.back() == 0x31584544) {
    check_for_drop = true;
  } else {
    check_for_drop = false;
  }
  if (memory_->cur_offset() < end) {
    if (!Decode()) {
      return false;
    }
  } else {
    return true;
  }

  if (check_for_drop && cur_op_ == 0x13) {
    dex_pc_set_ = true;
  }

  uint32_t iterations = 2;
  while (memory_->cur_offset() < end) {
    if (!Decode(dwarf_version)) {
    if (!Decode()) {
      return false;
    }
    // To protect against a branch that creates an infinite loop,
@@ -56,7 +88,7 @@ bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_vers
}

template <typename AddressType>
bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
bool DwarfOp<AddressType>::Decode() {
  last_error_.code = DWARF_ERROR_NONE;
  if (!memory_->ReadBytes(&cur_op_, 1)) {
    last_error_.code = DWARF_ERROR_MEMORY_INVALID;
@@ -71,12 +103,6 @@ bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
    return false;
  }

  // Check for an unsupported opcode.
  if (dwarf_version < op->supported_version) {
    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
    return false;
  }

  // Make sure that the required number of stack elements is available.
  if (stack_.size() < op->num_required_stack_values) {
    last_error_.code = DWARF_ERROR_STACK_INDEX_NOT_VALID;
@@ -434,22 +460,22 @@ bool DwarfOp<AddressType>::op_regx() {
template <typename AddressType>
bool DwarfOp<AddressType>::op_breg() {
  uint16_t reg = cur_op() - 0x70;
  if (reg >= regs_->total_regs()) {
  if (reg >= regs_info_->Total()) {
    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
    return false;
  }
  stack_.push_front((*regs_)[reg] + OperandAt(0));
  stack_.push_front(regs_info_->Get(reg) + OperandAt(0));
  return true;
}

template <typename AddressType>
bool DwarfOp<AddressType>::op_bregx() {
  AddressType reg = OperandAt(0);
  if (reg >= regs_->total_regs()) {
  if (reg >= regs_info_->Total()) {
    last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
    return false;
  }
  stack_.push_front((*regs_)[reg] + OperandAt(1));
  stack_.push_front(regs_info_->Get(reg) + OperandAt(1));
  return true;
}

+114 −272

File changed.

Preview size limit exceeded, changes collapsed.

+30 −50
Original line number Diff line number Diff line
@@ -26,16 +26,14 @@
#include <unwindstack/Regs.h>

#include "DwarfCfa.h"
#include "DwarfEncoding.h"
#include "DwarfOp.h"

#include "DwarfDebugFrame.h"
#include "DwarfEhFrame.h"
#include "DwarfEncoding.h"
#include "DwarfOp.h"
#include "RegsInfo.h"

namespace unwindstack {

constexpr uint64_t DEX_PC_REG = 0x20444558;

DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}

const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
@@ -75,14 +73,17 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f
}

template <typename AddressType>
bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
                                                   Memory* regular_memory, AddressType* value) {
bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, Memory* regular_memory,
                                                   AddressType* value,
                                                   RegsInfo<AddressType>* regs_info,
                                                   bool* is_dex_pc) {
  DwarfOp<AddressType> op(&memory_, regular_memory);
  op.set_regs_info(regs_info);

  // Need to evaluate the op data.
  uint64_t start = loc.values[1];
  uint64_t end = start + loc.values[0];
  if (!op.Eval(start, end, version)) {
  uint64_t end = loc.values[1];
  uint64_t start = end - loc.values[0];
  if (!op.Eval(start, end)) {
    last_error_ = op.last_error();
    return false;
  }
@@ -96,6 +97,9 @@ bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uin
    return false;
  }
  *value = op.StackAt(0);
  if (is_dex_pc != nullptr && op.dex_pc_set()) {
    *is_dex_pc = true;
  }
  return true;
}

@@ -103,12 +107,10 @@ template <typename AddressType>
struct EvalInfo {
  const dwarf_loc_regs_t* loc_regs;
  const DwarfCie* cie;
  RegsImpl<AddressType>* cur_regs;
  Memory* regular_memory;
  AddressType cfa;
  bool return_address_undefined = false;
  uint64_t reg_map = 0;
  AddressType reg_values[64];
  RegsInfo<AddressType> regs_info;
};

template <typename AddressType>
@@ -129,32 +131,18 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
      break;
    case DWARF_LOCATION_REGISTER: {
      uint32_t cur_reg = loc->values[0];
      if (cur_reg >= eval_info->cur_regs->total_regs()) {
      if (cur_reg >= eval_info->regs_info.Total()) {
        last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
        return false;
      }
      AddressType* cur_reg_ptr = &(*eval_info->cur_regs)[cur_reg];
      const auto& entry = eval_info->loc_regs->find(cur_reg);
      if (entry != eval_info->loc_regs->end()) {
        if (!(eval_info->reg_map & (1 << cur_reg))) {
          eval_info->reg_map |= 1 << cur_reg;
          eval_info->reg_values[cur_reg] = *cur_reg_ptr;
          if (!EvalRegister(&entry->second, cur_reg, cur_reg_ptr, eval_info)) {
            return false;
          }
        }

        // Use the register value from before any evaluations.
        *reg_ptr = eval_info->reg_values[cur_reg] + loc->values[1];
      } else {
        *reg_ptr = *cur_reg_ptr + loc->values[1];
      }
      *reg_ptr = eval_info->regs_info.Get(cur_reg) + loc->values[1];
      break;
    }
    case DWARF_LOCATION_EXPRESSION:
    case DWARF_LOCATION_VAL_EXPRESSION: {
      AddressType value;
      if (!EvalExpression(*loc, eval_info->cie->version, regular_memory, &value)) {
      bool is_dex_pc = false;
      if (!EvalExpression(*loc, regular_memory, &value, &eval_info->regs_info, &is_dex_pc)) {
        return false;
      }
      if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -165,6 +153,9 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
        }
      } else {
        *reg_ptr = value;
        if (is_dex_pc) {
          eval_info->regs_info.regs->set_dex_pc(value);
        }
      }
      break;
    }
@@ -201,8 +192,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me

  AddressType prev_cfa = regs->sp();

  EvalInfo<AddressType> eval_info{
      .loc_regs = &loc_regs, .cie = cie, .regular_memory = regular_memory, .cur_regs = cur_regs};
  EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
                                  .cie = cie,
                                  .regular_memory = regular_memory,
                                  .regs_info = RegsInfo<AddressType>(cur_regs)};
  const DwarfLocation* loc = &cfa_entry->second;
  // Only a few location types are valid for the cfa.
  switch (loc->type) {
@@ -224,7 +217,7 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
    case DWARF_LOCATION_EXPRESSION:
    case DWARF_LOCATION_VAL_EXPRESSION: {
      AddressType value;
      if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
      if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
        return false;
      }
      if (loc->type == DWARF_LOCATION_EXPRESSION) {
@@ -249,28 +242,15 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
    if (reg == CFA_REG) continue;

    AddressType* reg_ptr;
    AddressType dex_pc = 0;
    if (reg == DEX_PC_REG) {
      // Special register that indicates this is a dex pc.
      dex_pc = 0;
      reg_ptr = &dex_pc;
    } else if (reg >= cur_regs->total_regs() || eval_info.reg_map & (1 << reg)) {
      // Skip this unknown register, or a register that has already been
      // processed.
    if (reg >= cur_regs->total_regs()) {
      // Skip this unknown register.
      continue;
    } else {
      reg_ptr = &(*cur_regs)[reg];
      eval_info.reg_map |= 1 << reg;
      eval_info.reg_values[reg] = *reg_ptr;
    }

    reg_ptr = eval_info.regs_info.Save(reg);
    if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
      return false;
    }

    if (reg == DEX_PC_REG) {
      cur_regs->set_dex_pc(dex_pc);
    }
  }

  // Find the return address location.
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBUNWINDSTACK_REGS_INFO_H
#define _LIBUNWINDSTACK_REGS_INFO_H

#include <stdint.h>

#include <unwindstack/Regs.h>

namespace unwindstack {

template <typename AddressType>
struct RegsInfo {
  RegsInfo(RegsImpl<AddressType>* regs) : regs(regs) {}

  RegsImpl<AddressType>* regs = nullptr;
  uint64_t saved_reg_map = 0;
  AddressType saved_regs[64];

  inline AddressType Get(uint32_t reg) {
    if (IsSaved(reg)) {
      return saved_regs[reg];
    }
    return (*regs)[reg];
  }

  inline AddressType* Save(uint32_t reg) {
    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
      // This should never happen as since all currently supported
      // architectures have the total number of registers < 64.
      abort();
    }
    saved_reg_map |= 1 << reg;
    saved_regs[reg] = (*regs)[reg];
    return &(*regs)[reg];
  }

  inline bool IsSaved(uint32_t reg) {
    if (reg > sizeof(saved_regs) / sizeof(AddressType)) {
      // This should never happen as since all currently supported
      // architectures have the total number of registers < 64.
      abort();
    }
    return saved_reg_map & (1 << reg);
  }

  inline uint16_t Total() { return regs->total_regs(); }
};

}  // namespace unwindstack

#endif  // _LIBUNWINDSTACK_REGS_INFO_H
+1 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ void Unwinder::FillInDexFrame() {
  size_t frame_num = frames_.size();
  frames_.resize(frame_num + 1);
  FrameData* frame = &frames_.at(frame_num);
  frame->num = frame_num;

  uint64_t dex_pc = regs_->dex_pc();
  frame->pc = dex_pc;
Loading