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

Commit 98984b41 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Add support for getting a dex pc.

Changes:
- Change the register type from int16_t to uint32_t for the location data
  and the Eval processing. This is because the special dex pc is > 65535.
- Add the ability for Dwarf register location information to point to a
  register that is itself a Dwarf location register.
- Add dex_pc to the frame information.
- Modify the unwind tool to print the dex pc if non-zero.

This does not implement the printing of the dex information in anything
but the unwind tool. It's not the final form of this printing.

Bug: 72070049

Test: Ran new unit tests.
Test: Dumped stack while in interpreter running 137-cfi art test and
Test: verified dex pc is set to non-zero.
Change-Id: I6ce8a6b577fb4f92abacbd433b1f68977e272542
parent 2c4f487d
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -70,6 +70,7 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
    back_frame->rel_pc = frame->rel_pc;
    back_frame->rel_pc = frame->rel_pc;
    back_frame->pc = frame->pc;
    back_frame->pc = frame->pc;
    back_frame->sp = frame->sp;
    back_frame->sp = frame->sp;
    back_frame->dex_pc = frame->dex_pc;


    back_frame->func_name = demangle(frame->function_name.c_str());
    back_frame->func_name = demangle(frame->function_name.c_str());
    back_frame->func_offset = frame->function_offset;
    back_frame->func_offset = frame->function_offset;
+1 −0
Original line number Original line Diff line number Diff line
@@ -81,6 +81,7 @@ struct backtrace_frame_data_t {
  uintptr_t rel_pc;       // The relative pc.
  uintptr_t rel_pc;       // The relative pc.
  uintptr_t sp;           // The top of the stack.
  uintptr_t sp;           // The top of the stack.
  size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
  size_t stack_size;      // The size of the stack, zero indicate an unknown stack size.
  uint64_t dex_pc;        // If non-zero, the Dex PC for the ART interpreter.
  backtrace_map_t map;    // The map associated with the given pc.
  backtrace_map_t map;    // The map associated with the given pc.
  std::string func_name;  // The function name associated with this pc, NULL if not found.
  std::string func_name;  // The function name associated with this pc, NULL if not found.
  uintptr_t func_offset;  // pc relative to the start of the function, only valid if func_name is not NULL.
  uintptr_t func_offset;  // pc relative to the start of the function, only valid if func_name is not NULL.
+112 −71
Original line number Original line Diff line number Diff line
@@ -34,6 +34,8 @@


namespace unwindstack {
namespace unwindstack {


constexpr uint64_t DEX_PC_REG = 0x20444558;

DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}


const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
@@ -97,6 +99,84 @@ bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uin
  return true;
  return true;
}
}


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];
};

template <typename AddressType>
bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
                                                 AddressType* reg_ptr, void* info) {
  EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
  Memory* regular_memory = eval_info->regular_memory;
  switch (loc->type) {
    case DWARF_LOCATION_OFFSET:
      if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
        last_error_ = DWARF_ERROR_MEMORY_INVALID;
        return false;
      }
      break;
    case DWARF_LOCATION_VAL_OFFSET:
      *reg_ptr = eval_info->cfa + loc->values[0];
      break;
    case DWARF_LOCATION_REGISTER: {
      uint32_t cur_reg = loc->values[0];
      if (cur_reg >= eval_info->cur_regs->total_regs()) {
        last_error_ = 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];
      }
      break;
    }
    case DWARF_LOCATION_EXPRESSION:
    case DWARF_LOCATION_VAL_EXPRESSION: {
      AddressType value;
      if (!EvalExpression(*loc, eval_info->cie->version, regular_memory, &value)) {
        return false;
      }
      if (loc->type == DWARF_LOCATION_EXPRESSION) {
        if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
          last_error_ = DWARF_ERROR_MEMORY_INVALID;
          return false;
        }
      } else {
        *reg_ptr = value;
      }
      break;
    }
    case DWARF_LOCATION_UNDEFINED:
      if (reg == eval_info->cie->return_address_register) {
        eval_info->return_address_undefined = true;
      }
    default:
      break;
  }

  return true;
}

template <typename AddressType>
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
                                         const dwarf_loc_regs_t& loc_regs, Regs* regs,
                                         const dwarf_loc_regs_t& loc_regs, Regs* regs,
@@ -114,9 +194,13 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
    return false;
    return false;
  }
  }


  // Always set the dex pc to zero when evaluating.
  cur_regs->set_dex_pc(0);

  AddressType prev_cfa = regs->sp();
  AddressType prev_cfa = regs->sp();


  AddressType cfa;
  EvalInfo<AddressType> eval_info{
      .loc_regs = &loc_regs, .cie = cie, .regular_memory = regular_memory, .cur_regs = cur_regs};
  const DwarfLocation* loc = &cfa_entry->second;
  const DwarfLocation* loc = &cfa_entry->second;
  // Only a few location types are valid for the cfa.
  // Only a few location types are valid for the cfa.
  switch (loc->type) {
  switch (loc->type) {
@@ -129,11 +213,11 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
      // pointer register does not have any associated location
      // pointer register does not have any associated location
      // information, use the current cfa value.
      // information, use the current cfa value.
      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
      if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
        cfa = prev_cfa;
        eval_info.cfa = prev_cfa;
      } else {
      } else {
        cfa = (*cur_regs)[loc->values[0]];
        eval_info.cfa = (*cur_regs)[loc->values[0]];
      }
      }
      cfa += loc->values[1];
      eval_info.cfa += loc->values[1];
      break;
      break;
    case DWARF_LOCATION_EXPRESSION:
    case DWARF_LOCATION_EXPRESSION:
    case DWARF_LOCATION_VAL_EXPRESSION: {
    case DWARF_LOCATION_VAL_EXPRESSION: {
@@ -142,12 +226,12 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
        return false;
        return false;
      }
      }
      if (loc->type == DWARF_LOCATION_EXPRESSION) {
      if (loc->type == DWARF_LOCATION_EXPRESSION) {
        if (!regular_memory->ReadFully(value, &cfa, sizeof(AddressType))) {
        if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
          last_error_ = DWARF_ERROR_MEMORY_INVALID;
          last_error_ = DWARF_ERROR_MEMORY_INVALID;
          return false;
          return false;
        }
        }
      } else {
      } else {
        cfa = value;
        eval_info.cfa = value;
      }
      }
      break;
      break;
    }
    }
@@ -156,81 +240,38 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
      return false;
      return false;
  }
  }


  // This code is not guaranteed to work in cases where a register location
  // is a double indirection to the actual value. For example, if r3 is set
  // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
  // because it does not guarantee that r5 is evaluated before r3.
  // Check that this case does not exist, and error if it does.
  bool return_address_undefined = false;
  for (const auto& entry : loc_regs) {
  for (const auto& entry : loc_regs) {
    uint16_t reg = entry.first;
    uint32_t reg = entry.first;
    // Already handled the CFA register.
    // Already handled the CFA register.
    if (reg == CFA_REG) continue;
    if (reg == CFA_REG) continue;


    if (reg >= cur_regs->total_regs()) {
    AddressType* reg_ptr;
      // Skip this unknown register.
    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.
      continue;
      continue;
    } else {
      reg_ptr = &(*cur_regs)[reg];
      eval_info.reg_map |= 1 << reg;
      eval_info.reg_values[reg] = *reg_ptr;
    }
    }


    const DwarfLocation* loc = &entry.second;
    if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
    switch (loc->type) {
      case DWARF_LOCATION_OFFSET:
        if (!regular_memory->ReadFully(cfa + loc->values[0], &(*cur_regs)[reg],
                                       sizeof(AddressType))) {
          last_error_ = DWARF_ERROR_MEMORY_INVALID;
          return false;
        }
        break;
      case DWARF_LOCATION_VAL_OFFSET:
        (*cur_regs)[reg] = cfa + loc->values[0];
        break;
      case DWARF_LOCATION_REGISTER: {
        uint16_t cur_reg = loc->values[0];
        if (cur_reg >= cur_regs->total_regs()) {
          last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
          return false;
        }
        if (loc_regs.find(cur_reg) != loc_regs.end()) {
          // This is a double indirection, a register definition references
          // another register which is also defined as something other
          // than a register.
          log(0,
              "Invalid indirection: register %d references register %d which is "
              "not a plain register.\n",
              reg, cur_reg);
          last_error_ = DWARF_ERROR_ILLEGAL_STATE;
      return false;
      return false;
    }
    }
        (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];

        break;
    if (reg == DEX_PC_REG) {
      }
      cur_regs->set_dex_pc(dex_pc);
      case DWARF_LOCATION_EXPRESSION:
      case DWARF_LOCATION_VAL_EXPRESSION: {
        AddressType value;
        if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
          return false;
        }
        if (loc->type == DWARF_LOCATION_EXPRESSION) {
          if (!regular_memory->ReadFully(value, &(*cur_regs)[reg], sizeof(AddressType))) {
            last_error_ = DWARF_ERROR_MEMORY_INVALID;
            return false;
          }
        } else {
          (*cur_regs)[reg] = value;
        }
        break;
      }
      case DWARF_LOCATION_UNDEFINED:
        if (reg == cie->return_address_register) {
          return_address_undefined = true;
        }
      default:
        break;
    }
    }
  }
  }


  // Find the return address location.
  // Find the return address location.
  if (return_address_undefined) {
  if (eval_info.return_address_undefined) {
    cur_regs->set_pc(0);
    cur_regs->set_pc(0);
  } else {
  } else {
    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
    cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
@@ -239,7 +280,7 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
  // If the pc was set to zero, consider this the final frame.
  // If the pc was set to zero, consider this the final frame.
  *finished = (cur_regs->pc() == 0) ? true : false;
  *finished = (cur_regs->pc() == 0) ? true : false;


  cur_regs->set_sp(cfa);
  cur_regs->set_sp(eval_info.cfa);


  return true;
  return true;
}
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc
  frame->num = frame_num;
  frame->num = frame_num;
  frame->sp = regs_->sp();
  frame->sp = regs_->sp();
  frame->rel_pc = adjusted_rel_pc;
  frame->rel_pc = adjusted_rel_pc;
  frame->dex_pc = regs_->dex_pc();


  if (map_info == nullptr) {
  if (map_info == nullptr) {
    frame->pc = regs_->pc();
    frame->pc = regs_->pc();
+1 −1
Original line number Original line Diff line number Diff line
@@ -38,7 +38,7 @@ struct DwarfLocation {
  uint64_t values[2];
  uint64_t values[2];
};
};


typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
typedef std::unordered_map<uint32_t, DwarfLocation> dwarf_loc_regs_t;


}  // namespace unwindstack
}  // namespace unwindstack


Loading