diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp index 8b30b768d..b8164c577 100644 --- a/libunwindstack/DwarfSection.cpp +++ b/libunwindstack/DwarfSection.cpp @@ -225,11 +225,13 @@ bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_me // Find the return address location. if (return_address_undefined) { cur_regs->set_pc(0); - *finished = true; } else { cur_regs->set_pc((*cur_regs)[cie->return_address_register]); - *finished = false; } + + // If the pc was set to zero, consider this the final frame. + *finished = (cur_regs->pc() == 0) ? true : false; + cur_regs->set_sp(cfa); // Return false if the unwind is not finished or the cfa and pc didn't change. return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc(); diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp index 30a15328c..170a5cdf7 100644 --- a/libunwindstack/ElfInterfaceArm.cpp +++ b/libunwindstack/ElfInterfaceArm.cpp @@ -121,8 +121,10 @@ bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, } regs_arm->set_sp(arm.cfa()); (*regs_arm)[ARM_REG_SP] = regs_arm->sp(); - *finished = false; return_value = true; + + // If the pc was set to zero, consider this the final frame. + *finished = (regs_arm->pc() == 0) ? true : false; } if (arm.status() == ARM_STATUS_NO_UNWIND) { diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp index c701a295e..7e85bbbf8 100644 --- a/libunwindstack/tests/DwarfSectionImplTest.cpp +++ b/libunwindstack/tests/DwarfSectionImplTest.cpp @@ -340,6 +340,23 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) { EXPECT_EQ(0x10U, regs.sp()); } +TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) { + 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[5] = 0; + regs[8] = 0x10; + loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}}; + bool finished; + ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished)); + EXPECT_TRUE(finished); + EXPECT_EQ(0U, regs.pc()); + EXPECT_EQ(0x10U, regs.sp()); +} + TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) { DwarfCie cie{.return_address_register = 5}; RegsImplFake regs(10, 9); @@ -854,7 +871,7 @@ REGISTER_TYPED_TEST_CASE_P( 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_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr, + Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr, Eval_same_cfa_same_pc, 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, diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp index feb24ce05..4b621c9be 100644 --- a/libunwindstack/tests/ElfInterfaceArmTest.cpp +++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp @@ -401,4 +401,40 @@ TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) { ASSERT_EQ(0x1234U, regs.pc()); } +TEST_F(ElfInterfaceArmTest, StepExidx_pc_zero) { + ElfInterfaceArmFake interface(&memory_); + + interface.FakeSetStartOffset(0x1000); + interface.FakeSetTotalEntries(1); + memory_.SetData32(0x1000, 0x6000); + // Set the pc using a pop r15 command. + memory_.SetData32(0x1004, 0x808800b0); + + // pc value of zero. + process_memory_.SetData32(0x10000, 0); + + RegsArm regs; + regs[ARM_REG_SP] = 0x10000; + regs[ARM_REG_LR] = 0x20000; + regs.set_sp(regs[ARM_REG_SP]); + regs.set_pc(0x1234); + + bool finished; + ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(finished); + ASSERT_EQ(0U, regs.pc()); + + // Now set the pc from the lr register (pop r14). + memory_.SetData32(0x1004, 0x808400b0); + + regs[ARM_REG_SP] = 0x10000; + regs[ARM_REG_LR] = 0x20000; + regs.set_sp(regs[ARM_REG_SP]); + regs.set_pc(0x1234); + + ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished)); + ASSERT_TRUE(finished); + ASSERT_EQ(0U, regs.pc()); +} + } // namespace unwindstack