From 98984b41da78448df24d8d9ced842066328d5f11 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 17 Jan 2018 12:59:45 -0800 Subject: [PATCH] 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 --- libbacktrace/UnwindStack.cpp | 1 + libbacktrace/include/backtrace/Backtrace.h | 1 + libunwindstack/DwarfSection.cpp | 183 +++++++++++------- libunwindstack/Unwinder.cpp | 1 + .../include/unwindstack/DwarfLocation.h | 2 +- .../include/unwindstack/DwarfSection.h | 2 + libunwindstack/include/unwindstack/Regs.h | 4 + libunwindstack/include/unwindstack/Unwinder.h | 1 + libunwindstack/tests/DwarfSectionImplTest.cpp | 72 ++++++- libunwindstack/tools/unwind.cpp | 5 + 10 files changed, 191 insertions(+), 81 deletions(-) diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index b0345a140..b62ee1659 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -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; diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h index 592266429..f34fb2d5f 100644 --- a/libbacktrace/include/backtrace/Backtrace.h +++ b/libbacktrace/include/backtrace/Backtrace.h @@ -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. diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp index 095418726..91d855b4f 100644 --- a/libunwindstack/DwarfSection.cpp +++ b/libunwindstack/DwarfSection.cpp @@ -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::EvalExpression(const DwarfLocation& loc, uin return true; } +template +struct EvalInfo { + const dwarf_loc_regs_t* loc_regs; + const DwarfCie* cie; + RegsImpl* cur_regs; + Memory* regular_memory; + AddressType cfa; + bool return_address_undefined = false; + uint64_t reg_map = 0; + AddressType reg_values[64]; +}; + +template +bool DwarfSectionImpl::EvalRegister(const DwarfLocation* loc, uint32_t reg, + AddressType* reg_ptr, void* info) { + EvalInfo* eval_info = reinterpret_cast*>(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 bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs, Regs* regs, @@ -114,9 +194,13 @@ bool DwarfSectionImpl::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 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::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::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::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::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; } diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index b0a1c0c0b..d71177290 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -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(); diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h index 3467e6a8f..088118224 100644 --- a/libunwindstack/include/unwindstack/DwarfLocation.h +++ b/libunwindstack/include/unwindstack/DwarfLocation.h @@ -38,7 +38,7 @@ struct DwarfLocation { uint64_t values[2]; }; -typedef std::unordered_map dwarf_loc_regs_t; +typedef std::unordered_map dwarf_loc_regs_t; } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h index 10be6b48f..e0004aa46 100644 --- a/libunwindstack/include/unwindstack/DwarfSection.h +++ b/libunwindstack/include/unwindstack/DwarfSection.h @@ -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; diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h index 1904d4daa..a5ba7a078 100644 --- a/libunwindstack/include/unwindstack/Regs.h +++ b/libunwindstack/include/unwindstack/Regs.h @@ -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 diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index 5adec4f4b..32858ae52 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -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; diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp index d54b0bf59..dfd2ce059 100644 --- a/libunwindstack/tests/DwarfSectionImplTest.cpp +++ b/libunwindstack/tests/DwarfSectionImplTest.cpp @@ -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 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 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); diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index 07e48af60..33e55274d 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -96,8 +96,13 @@ void DoUnwind(pid_t pid) { unwinder.Unwind(); // Print the frames. + const std::vector& 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); + } } }