Merge "Add support for getting a dex pc."
This commit is contained in:
commit
5e5056c5a5
|
@ -70,6 +70,7 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
back_frame->rel_pc = frame->rel_pc;
|
||||
back_frame->pc = frame->pc;
|
||||
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_offset = frame->function_offset;
|
||||
|
|
|
@ -81,6 +81,7 @@ struct backtrace_frame_data_t {
|
|||
uintptr_t rel_pc; // The relative pc.
|
||||
uintptr_t sp; // The top of the stack.
|
||||
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.
|
||||
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.
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
namespace unwindstack {
|
||||
|
||||
constexpr uint64_t DEX_PC_REG = 0x20444558;
|
||||
|
||||
DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
|
||||
|
||||
const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
|
||||
|
@ -97,6 +99,84 @@ bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uin
|
|||
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>
|
||||
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
|
||||
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;
|
||||
}
|
||||
|
||||
// Always set the dex pc to zero when evaluating.
|
||||
cur_regs->set_dex_pc(0);
|
||||
|
||||
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;
|
||||
// Only a few location types are valid for the cfa.
|
||||
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
|
||||
// information, use the current cfa value.
|
||||
if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
|
||||
cfa = prev_cfa;
|
||||
eval_info.cfa = prev_cfa;
|
||||
} 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;
|
||||
case DWARF_LOCATION_EXPRESSION:
|
||||
case DWARF_LOCATION_VAL_EXPRESSION: {
|
||||
|
@ -142,12 +226,12 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
|
|||
return false;
|
||||
}
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cfa = value;
|
||||
eval_info.cfa = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -156,81 +240,38 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
|
|||
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) {
|
||||
uint16_t reg = entry.first;
|
||||
uint32_t reg = entry.first;
|
||||
// Already handled the CFA register.
|
||||
if (reg == CFA_REG) continue;
|
||||
|
||||
if (reg >= cur_regs->total_regs()) {
|
||||
// Skip this unknown register.
|
||||
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.
|
||||
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;
|
||||
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;
|
||||
}
|
||||
(*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
|
||||
break;
|
||||
}
|
||||
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;
|
||||
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.
|
||||
if (return_address_undefined) {
|
||||
if (eval_info.return_address_undefined) {
|
||||
cur_regs->set_pc(0);
|
||||
} else {
|
||||
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.
|
||||
*finished = (cur_regs->pc() == 0) ? true : false;
|
||||
|
||||
cur_regs->set_sp(cfa);
|
||||
cur_regs->set_sp(eval_info.cfa);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc
|
|||
frame->num = frame_num;
|
||||
frame->sp = regs_->sp();
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
frame->dex_pc = regs_->dex_pc();
|
||||
|
||||
if (map_info == nullptr) {
|
||||
frame->pc = regs_->pc();
|
||||
|
|
|
@ -38,7 +38,7 @@ struct DwarfLocation {
|
|||
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
|
||||
|
||||
|
|
|
@ -132,6 +132,8 @@ class DwarfSectionImpl : public DwarfSection {
|
|||
|
||||
const DwarfFde* GetFdeFromIndex(size_t index) override;
|
||||
|
||||
bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
|
||||
|
||||
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
|
||||
Regs* regs, bool* finished) override;
|
||||
|
||||
|
|
|
@ -57,6 +57,9 @@ class Regs {
|
|||
virtual uint64_t pc() = 0;
|
||||
virtual uint64_t sp() = 0;
|
||||
|
||||
uint64_t dex_pc() { return dex_pc_; }
|
||||
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
|
||||
|
||||
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
|
||||
|
||||
virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
|
||||
|
@ -79,6 +82,7 @@ class Regs {
|
|||
uint16_t total_regs_;
|
||||
uint16_t sp_reg_;
|
||||
Location return_loc_;
|
||||
uint64_t dex_pc_ = 0;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
|
|
|
@ -41,6 +41,7 @@ struct FrameData {
|
|||
uint64_t rel_pc;
|
||||
uint64_t pc;
|
||||
uint64_t sp;
|
||||
uint64_t dex_pc;
|
||||
|
||||
std::string function_name;
|
||||
uint64_t function_offset;
|
||||
|
|
|
@ -266,13 +266,67 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
|
|||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[1] = 0x100;
|
||||
regs[3] = 0x300;
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
|
||||
loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 1}};
|
||||
loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(0x301U, regs[1]);
|
||||
EXPECT_EQ(0x300U, regs[3]);
|
||||
EXPECT_EQ(0x10U, regs[8]);
|
||||
EXPECT_EQ(0x102U, regs[9]);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsImplFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[0] = 0x10;
|
||||
regs[1] = 0x20;
|
||||
regs[2] = 0x30;
|
||||
regs[3] = 0x40;
|
||||
regs[4] = 0x50;
|
||||
regs[5] = 0x60;
|
||||
regs[8] = 0x20;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
|
||||
loc_regs[2] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
|
||||
loc_regs[3] = DwarfLocation{DWARF_LOCATION_REGISTER, {2, 3}};
|
||||
loc_regs[4] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 4}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 5}};
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(0x10U, regs[0]);
|
||||
EXPECT_EQ(0x11U, regs[1]);
|
||||
EXPECT_EQ(0x22U, regs[2]);
|
||||
EXPECT_EQ(0x33U, regs[3]);
|
||||
EXPECT_EQ(0x44U, regs[4]);
|
||||
EXPECT_EQ(0x55U, regs[5]);
|
||||
EXPECT_EQ(0x20U, regs[8]);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsImplFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[0] = 0x10;
|
||||
regs[8] = 0x20;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[0x20444558] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(0x10U, regs[0]);
|
||||
EXPECT_EQ(0x20U, regs[8]);
|
||||
EXPECT_EQ(0x11U, regs.dex_pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
|
||||
|
@ -840,11 +894,11 @@ REGISTER_TYPED_TEST_CASE_P(
|
|||
DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
|
||||
Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
|
||||
Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
|
||||
Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
|
||||
Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
|
||||
GetCie_fail_should_not_cache, GetCie_32_version_check, GetCie_negative_data_alignment_factor,
|
||||
GetCie_64_no_augment, GetCie_augment, GetCie_version_3, GetCie_version_4,
|
||||
GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
|
||||
Eval_register_reference_chain, Eval_dex_pc, Eval_invalid_register, Eval_different_reg_locations,
|
||||
Eval_return_address_undefined, Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc,
|
||||
Eval_reg_expr, Eval_reg_val_expr, GetCie_fail_should_not_cache, GetCie_32_version_check,
|
||||
GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
|
||||
GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
|
||||
GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
|
||||
GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
|
||||
GetCfaLocationInfo_cie_cached, Log);
|
||||
|
|
|
@ -96,8 +96,13 @@ void DoUnwind(pid_t pid) {
|
|||
unwinder.Unwind();
|
||||
|
||||
// Print the frames.
|
||||
const std::vector<unwindstack::FrameData>& frames = unwinder.frames();
|
||||
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
|
||||
printf("%s\n", unwinder.FormatFrame(i).c_str());
|
||||
const unwindstack::FrameData* frame = &frames[i];
|
||||
if (frame->dex_pc != 0) {
|
||||
printf(" dex pc %" PRIx64 "\n", frame->dex_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue