libunwindstack: Support signal frame CIEs.

Mark a CIE with a S in its augmentation string as signal frame.
This allows the code to properly handle signal frame data if none
of the signal frame pattern matchers work.

For a signal frame, DwarfSectionImpl<AddressType>::Eval needs to
continue the unwinding even if PC is zero. A zero PC means that the
program has crashed, and we should try to recover the real PC using the
return address on the stack or LR. This behavior is tested by
UnwindOffline.signal_{x86,x86_64}, which modify the libc.so files
so that the signal frame pattern matcher fails and the CIE/FDE
data is used instead.

Test: libunwindstack_test
Change-Id: I4655b070028fd984345311a5e743796f8c30ed36
This commit is contained in:
Ryan Prichard 2020-09-30 18:31:05 -07:00 committed by Christopher Ferris
parent 483364a7ec
commit 9b8f545920
29 changed files with 260 additions and 40 deletions

View file

@ -296,6 +296,8 @@ cc_defaults {
"tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
"tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
"tests/files/offline/signal_load_bias_arm/*",
"tests/files/offline/signal_fde_x86/*",
"tests/files/offline/signal_fde_x86_64/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
],

View file

@ -37,7 +37,8 @@ namespace unwindstack {
DwarfSection::DwarfSection(Memory* memory) : memory_(memory) {}
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
// Lookup the pc in the cache.
auto it = loc_regs_.upper_bound(pc);
if (it == loc_regs_.end() || pc < it->second.pc_start) {
@ -59,6 +60,8 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f
it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
}
*is_signal_frame = it->second.cie->is_signal_frame;
// Now eval the actual registers.
return Eval(it->second.cie, process_memory, it->second, regs, finished);
}
@ -241,6 +244,9 @@ bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
return false;
}
break;
case 'S':
cie->is_signal_frame = true;
break;
}
}
return true;
@ -558,8 +564,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
}
// If the pc was set to zero, consider this the final frame.
*finished = (cur_regs->pc() == 0) ? true : false;
// If the pc was set to zero, consider this the final frame. Exception: if
// this is the sigreturn frame, then we want to try to recover the real PC
// using the return address (from LR or the stack), so keep going.
*finished = (cur_regs->pc() == 0 && !cie->is_signal_frame) ? true : false;
cur_regs->set_sp(eval_info.cfa);

View file

@ -188,14 +188,15 @@ bool Elf::StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memor
}
// The relative pc is always relative to the start of the map from which it comes.
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
if (!valid_) {
return false;
}
// Lock during the step which can update information in the object.
std::lock_guard<std::mutex> guard(lock_);
return interface_->Step(rel_pc, regs, process_memory, finished);
return interface_->Step(rel_pc, regs, process_memory, finished, is_signal_frame);
}
bool Elf::IsValidElf(Memory* memory) {

View file

@ -499,25 +499,27 @@ bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64
return false;
}
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
last_error_.code = ERROR_NONE;
last_error_.address = 0;
// Try the debug_frame first since it contains the most specific unwind
// information.
DwarfSection* debug_frame = debug_frame_.get();
if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
if (debug_frame != nullptr &&
debug_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}
// Try the eh_frame next.
DwarfSection* eh_frame = eh_frame_.get();
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}
if (gnu_debugdata_interface_ != nullptr &&
gnu_debugdata_interface_->Step(pc, regs, process_memory, finished)) {
gnu_debugdata_interface_->Step(pc, regs, process_memory, finished, is_signal_frame)) {
return true;
}

View file

@ -100,12 +100,13 @@ void ElfInterfaceArm::HandleUnknownType(uint32_t type, uint64_t ph_offset, uint6
total_entries_ = ph_filesz / 8;
}
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) {
// Dwarf unwind information is precise about whether a pc is covered or not,
// but arm unwind information only has ranges of pc. In order to avoid
// incorrectly doing a bad unwind using arm unwind information for a
// different function, always try and unwind with the dwarf information first.
return ElfInterface32::Step(pc, regs, process_memory, finished) ||
return ElfInterface32::Step(pc, regs, process_memory, finished, is_signal_frame) ||
StepExidx(pc, regs, process_memory, finished);
}

View file

@ -72,7 +72,8 @@ class ElfInterfaceArm : public ElfInterface32 {
void HandleUnknownType(uint32_t type, uint64_t ph_offset, uint64_t ph_filesz) override;
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame) override;
bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);

View file

@ -113,9 +113,11 @@ bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_f
step_pc -= pc_adjustment;
bool finished = false;
bool is_signal_frame = false;
if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
step_pc = rel_pc;
} else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished)) {
} else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished,
&is_signal_frame)) {
finished = true;
}

View file

@ -242,18 +242,21 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
// some of the speculative frames.
in_device_map = true;
} else {
bool is_signal_frame = false;
if (elf->StepIfSignalHandler(rel_pc, regs_, process_memory_.get())) {
stepped = true;
if (frame != nullptr) {
// Need to adjust the relative pc because the signal handler
// pc should not be adjusted.
frame->rel_pc = rel_pc;
frame->pc += pc_adjustment;
step_pc = rel_pc;
}
} else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished)) {
is_signal_frame = true;
} else if (elf->Step(step_pc, regs_, process_memory_.get(), &finished,
&is_signal_frame)) {
stepped = true;
}
if (is_signal_frame && frame != nullptr) {
// Need to adjust the relative pc because the signal handler
// pc should not be adjusted.
frame->rel_pc = rel_pc;
frame->pc += pc_adjustment;
step_pc = rel_pc;
}
elf->GetLastError(&last_error_);
}
}

View file

@ -106,7 +106,7 @@ class DwarfSection {
virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished, bool* is_signal_frame);
protected:
DwarfMemory memory_;

View file

@ -35,6 +35,7 @@ struct DwarfCie {
uint64_t code_alignment_factor = 0;
int64_t data_alignment_factor = 0;
uint64_t return_address_register = 0;
bool is_signal_frame = false;
};
struct DwarfFde {

View file

@ -60,7 +60,8 @@ class Elf {
bool StepIfSignalHandler(uint64_t rel_pc, Regs* regs, Memory* process_memory);
bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);

View file

@ -64,7 +64,8 @@ class ElfInterface {
virtual std::string GetBuildID() = 0;
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished,
bool* is_signal_frame);
virtual bool IsValidPc(uint64_t pc);

View file

@ -68,7 +68,8 @@ TEST_F(DwarfSectionTest, Step_fail_fde) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
bool finished;
ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
bool is_signal_frame;
ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_fail_cie_null) {
@ -79,7 +80,8 @@ TEST_F(DwarfSectionTest, Step_fail_cie_null) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
bool finished;
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished));
bool is_signal_frame;
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@ -93,7 +95,8 @@ TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
.WillOnce(::testing::Return(false));
bool finished;
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished));
bool is_signal_frame;
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_pass) {
@ -111,7 +114,8 @@ TEST_F(DwarfSectionTest, Step_pass) {
.WillOnce(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
bool is_signal_frame;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished, &is_signal_frame));
}
static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
@ -137,9 +141,10 @@ TEST_F(DwarfSectionTest, Step_cache) {
.WillRepeatedly(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x1500, &regs_, &process, &finished));
bool is_signal_frame;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished, &is_signal_frame));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished, &is_signal_frame));
ASSERT_TRUE(section_->Step(0x1500, &regs_, &process, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
@ -157,7 +162,8 @@ TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
.WillRepeatedly(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
bool is_signal_frame;
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished, &is_signal_frame));
DwarfFde fde1{};
fde1.pc_start = 0x500;
@ -167,8 +173,8 @@ TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
ASSERT_TRUE(section_->Step(0x600, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x700, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x600, &regs_, &process, &finished, &is_signal_frame));
ASSERT_TRUE(section_->Step(0x700, &regs_, &process, &finished, &is_signal_frame));
}
} // namespace unwindstack

View file

@ -52,7 +52,7 @@ bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* of
return true;
}
bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished, bool* is_signal_frame) {
if (steps_.empty()) {
return false;
}
@ -68,6 +68,7 @@ bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
fake_regs->set_pc(entry.pc);
fake_regs->set_sp(entry.sp);
*finished = entry.finished;
*is_signal_frame = false;
return true;
}

View file

@ -76,7 +76,7 @@ class ElfInterfaceFake : public ElfInterface {
bool GetGlobalVariable(const std::string&, uint64_t*) override;
std::string GetBuildID() override { return fake_build_id_; }
bool Step(uint64_t, Regs*, Memory*, bool*) override;
bool Step(uint64_t, Regs*, Memory*, bool*, bool*) override;
void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
globals_[global] = offset;

View file

@ -138,7 +138,8 @@ TEST_F(ElfTest, elf_invalid) {
EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
bool finished;
ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
bool is_signal_frame;
ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished, &is_signal_frame));
EXPECT_EQ(ERROR_INVALID_ELF, elf.GetLastErrorCode());
}
@ -327,7 +328,7 @@ class ElfInterfaceMock : public ElfInterface {
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
std::string GetBuildID() override { return ""; }
MOCK_METHOD(bool, Step, (uint64_t, Regs*, Memory*, bool*), (override));
MOCK_METHOD(bool, Step, (uint64_t, Regs*, Memory*, bool*, bool*), (override));
MOCK_METHOD(bool, GetGlobalVariable, (const std::string&, uint64_t*), (override));
MOCK_METHOD(bool, IsValidPc, (uint64_t), (override));
@ -351,10 +352,11 @@ TEST_F(ElfTest, step_in_interface) {
MemoryFake process_memory;
bool finished;
EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished))
bool is_signal_frame;
EXPECT_CALL(*interface, Step(0x1000, &regs, &process_memory, &finished, &is_signal_frame))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x1000, &regs, &process_memory, &finished));
ASSERT_TRUE(elf.Step(0x1000, &regs, &process_memory, &finished, &is_signal_frame));
}
TEST_F(ElfTest, get_global_invalid_elf) {

View file

@ -1736,4 +1736,158 @@ TEST_F(UnwindOfflineTest, empty_arm64) {
EXPECT_EQ(0x7ffb6c0f30U, unwinder.frames()[6].sp);
}
// This test has a libc.so where the __restore has been changed so
// that the signal handler match does not occur and it uses the
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86) {
ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86/", ARCH_X86));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(20U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 007914d9 libunwindstack_test (SignalInnerFunction+25)\n"
" #01 pc 007914fc libunwindstack_test (SignalMiddleFunction+28)\n"
" #02 pc 0079152c libunwindstack_test (SignalOuterFunction+28)\n"
" #03 pc 0079af62 libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, "
"void*)+50)\n"
" #04 pc 00058fb0 libc.so (__restore)\n"
" #05 pc 00000000 <unknown>\n"
" #06 pc 0079161a libunwindstack_test (InnerFunction+218)\n"
" #07 pc 007923aa libunwindstack_test (MiddleFunction+42)\n"
" #08 pc 007923ea libunwindstack_test (OuterFunction+42)\n"
" #09 pc 00797444 libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned "
"int)+868)\n"
" #10 pc 007985b8 libunwindstack_test "
"(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+56)\n"
" #11 pc 00817a19 libunwindstack_test\n"
" #12 pc 008178c5 libunwindstack_test (testing::Test::Run()+277)\n"
" #13 pc 00818d3e libunwindstack_test (testing::TestInfo::Run()+318)\n"
" #14 pc 008198b4 libunwindstack_test (testing::TestSuite::Run()+436)\n"
" #15 pc 00828cb0 libunwindstack_test "
"(testing::internal::UnitTestImpl::RunAllTests()+1216)\n"
" #16 pc 0082870f libunwindstack_test (testing::UnitTest::Run()+367)\n"
" #17 pc 0084031e libunwindstack_test (IsolateMain+2334)\n"
" #18 pc 0083f9e9 libunwindstack_test (main+41)\n"
" #19 pc 00050646 libc.so (__libc_init+118)\n",
frame_info);
EXPECT_EQ(0x5ae0d4d9U, unwinder.frames()[0].pc);
EXPECT_EQ(0xecb37188U, unwinder.frames()[0].sp);
EXPECT_EQ(0x5ae0d4fcU, unwinder.frames()[1].pc);
EXPECT_EQ(0xecb37190U, unwinder.frames()[1].sp);
EXPECT_EQ(0x5ae0d52cU, unwinder.frames()[2].pc);
EXPECT_EQ(0xecb371b0U, unwinder.frames()[2].sp);
EXPECT_EQ(0x5ae16f62U, unwinder.frames()[3].pc);
EXPECT_EQ(0xecb371d0U, unwinder.frames()[3].sp);
EXPECT_EQ(0xec169fb0U, unwinder.frames()[4].pc);
EXPECT_EQ(0xecb371f0U, unwinder.frames()[4].sp);
EXPECT_EQ(0x0U, unwinder.frames()[5].pc);
EXPECT_EQ(0xffcfac6cU, unwinder.frames()[5].sp);
EXPECT_EQ(0x5ae0d61aU, unwinder.frames()[6].pc);
EXPECT_EQ(0xffcfac6cU, unwinder.frames()[6].sp);
EXPECT_EQ(0x5ae0e3aaU, unwinder.frames()[7].pc);
EXPECT_EQ(0xffcfad60U, unwinder.frames()[7].sp);
EXPECT_EQ(0x5ae0e3eaU, unwinder.frames()[8].pc);
EXPECT_EQ(0xffcfad90U, unwinder.frames()[8].sp);
EXPECT_EQ(0x5ae13444U, unwinder.frames()[9].pc);
EXPECT_EQ(0xffcfadc0U, unwinder.frames()[9].sp);
EXPECT_EQ(0x5ae145b8U, unwinder.frames()[10].pc);
EXPECT_EQ(0xffcfb020U, unwinder.frames()[10].sp);
EXPECT_EQ(0x5ae93a19U, unwinder.frames()[11].pc);
EXPECT_EQ(0xffcfb050U, unwinder.frames()[11].sp);
EXPECT_EQ(0x5ae938c5U, unwinder.frames()[12].pc);
EXPECT_EQ(0xffcfb090U, unwinder.frames()[12].sp);
EXPECT_EQ(0x5ae94d3eU, unwinder.frames()[13].pc);
EXPECT_EQ(0xffcfb0f0U, unwinder.frames()[13].sp);
EXPECT_EQ(0x5ae958b4U, unwinder.frames()[14].pc);
EXPECT_EQ(0xffcfb160U, unwinder.frames()[14].sp);
EXPECT_EQ(0x5aea4cb0U, unwinder.frames()[15].pc);
EXPECT_EQ(0xffcfb1d0U, unwinder.frames()[15].sp);
EXPECT_EQ(0x5aea470fU, unwinder.frames()[16].pc);
EXPECT_EQ(0xffcfb270U, unwinder.frames()[16].sp);
EXPECT_EQ(0x5aebc31eU, unwinder.frames()[17].pc);
EXPECT_EQ(0xffcfb2c0U, unwinder.frames()[17].sp);
EXPECT_EQ(0x5aebb9e9U, unwinder.frames()[18].pc);
EXPECT_EQ(0xffcfc3c0U, unwinder.frames()[18].sp);
EXPECT_EQ(0xec161646U, unwinder.frames()[19].pc);
EXPECT_EQ(0xffcfc3f0U, unwinder.frames()[19].sp);
}
// This test has a libc.so where the __restore_rt has been changed so
// that the signal handler match does not occur and it uses the
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86_64) {
ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86_64/", ARCH_X86_64));
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(18U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 000000000058415b libunwindstack_test (SignalInnerFunction+11)\n"
" #01 pc 0000000000584168 libunwindstack_test (SignalMiddleFunction+8)\n"
" #02 pc 0000000000584178 libunwindstack_test (SignalOuterFunction+8)\n"
" #03 pc 000000000058ac77 libunwindstack_test (unwindstack::SignalCallerHandler(int, "
"siginfo*, void*)+23)\n"
" #04 pc 0000000000057d10 libc.so (__restore_rt)\n"
" #05 pc 0000000000000000 <unknown>\n"
" #06 pc 0000000000584244 libunwindstack_test (InnerFunction+196)\n"
" #07 pc 0000000000584b44 libunwindstack_test (MiddleFunction+20)\n"
" #08 pc 0000000000584b64 libunwindstack_test (OuterFunction+20)\n"
" #09 pc 0000000000588457 libunwindstack_test (unwindstack::RemoteThroughSignal(int, "
"unsigned int)+583)\n"
" #10 pc 0000000000588f67 libunwindstack_test "
"(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+23)\n"
" #11 pc 00000000005d9c38 libunwindstack_test (testing::Test::Run()+216)\n"
" #12 pc 00000000005daf9a libunwindstack_test (testing::TestInfo::Run()+266)\n"
" #13 pc 00000000005dba46 libunwindstack_test (testing::TestSuite::Run()+390)\n"
" #14 pc 00000000005ea4c6 libunwindstack_test "
"(testing::internal::UnitTestImpl::RunAllTests()+1190)\n"
" #15 pc 00000000005e9f61 libunwindstack_test (testing::UnitTest::Run()+337)\n"
" #16 pc 0000000000600155 libunwindstack_test (IsolateMain+2037)\n"
" #17 pc 000000000004e405 libc.so (__libc_init+101)\n",
frame_info);
EXPECT_EQ(0x5bb41271e15bU, unwinder.frames()[0].pc);
EXPECT_EQ(0x707eb5aa8320U, unwinder.frames()[0].sp);
EXPECT_EQ(0x5bb41271e168U, unwinder.frames()[1].pc);
EXPECT_EQ(0x707eb5aa8330U, unwinder.frames()[1].sp);
EXPECT_EQ(0x5bb41271e178U, unwinder.frames()[2].pc);
EXPECT_EQ(0x707eb5aa8340U, unwinder.frames()[2].sp);
EXPECT_EQ(0x5bb412724c77U, unwinder.frames()[3].pc);
EXPECT_EQ(0x707eb5aa8350U, unwinder.frames()[3].sp);
EXPECT_EQ(0x707eb2ca5d10U, unwinder.frames()[4].pc);
EXPECT_EQ(0x707eb5aa8380U, unwinder.frames()[4].sp);
EXPECT_EQ(0x0U, unwinder.frames()[5].pc);
EXPECT_EQ(0x7ffcaadde078U, unwinder.frames()[5].sp);
EXPECT_EQ(0x5bb41271e244U, unwinder.frames()[6].pc);
EXPECT_EQ(0x7ffcaadde078U, unwinder.frames()[6].sp);
EXPECT_EQ(0x5bb41271eb44U, unwinder.frames()[7].pc);
EXPECT_EQ(0x7ffcaadde1a0U, unwinder.frames()[7].sp);
EXPECT_EQ(0x5bb41271eb64U, unwinder.frames()[8].pc);
EXPECT_EQ(0x7ffcaadde1c0U, unwinder.frames()[8].sp);
EXPECT_EQ(0x5bb412722457U, unwinder.frames()[9].pc);
EXPECT_EQ(0x7ffcaadde1e0U, unwinder.frames()[9].sp);
EXPECT_EQ(0x5bb412722f67U, unwinder.frames()[10].pc);
EXPECT_EQ(0x7ffcaadde510U, unwinder.frames()[10].sp);
EXPECT_EQ(0x5bb412773c38U, unwinder.frames()[11].pc);
EXPECT_EQ(0x7ffcaadde530U, unwinder.frames()[11].sp);
EXPECT_EQ(0x5bb412774f9aU, unwinder.frames()[12].pc);
EXPECT_EQ(0x7ffcaadde560U, unwinder.frames()[12].sp);
EXPECT_EQ(0x5bb412775a46U, unwinder.frames()[13].pc);
EXPECT_EQ(0x7ffcaadde5b0U, unwinder.frames()[13].sp);
EXPECT_EQ(0x5bb4127844c6U, unwinder.frames()[14].pc);
EXPECT_EQ(0x7ffcaadde5f0U, unwinder.frames()[14].sp);
EXPECT_EQ(0x5bb412783f61U, unwinder.frames()[15].pc);
EXPECT_EQ(0x7ffcaadde6c0U, unwinder.frames()[15].sp);
EXPECT_EQ(0x5bb41279a155U, unwinder.frames()[16].pc);
EXPECT_EQ(0x7ffcaadde720U, unwinder.frames()[16].sp);
EXPECT_EQ(0x707eb2c9c405U, unwinder.frames()[17].pc);
EXPECT_EQ(0x7ffcaaddf870U, unwinder.frames()[17].sp);
}
} // namespace unwindstack

View file

@ -0,0 +1,4 @@
5a67c000-5a7ba000 r--p 0 00:00 0 libunwindstack_test
5a7ba000-5aedd000 r-xp 13d000 00:00 0 libunwindstack_test
ec111000-ec153000 r--p 0 00:00 0 libc.so
ec153000-ec200000 r-xp 41000 00:00 0 libc.so

View file

@ -0,0 +1,9 @@
eax: 5aeec4ac
ebx: 5aeec4ac
ecx: 0
edx: 6b
ebp: ecb37188
edi: ebecda30
esi: b
esp: ecb37188
eip: 5ae0d4d9

View file

@ -0,0 +1,4 @@
5bb41219a000-5bb4122cd000 r--p 0 00:00 0 libunwindstack_test
5bb4122cd000-5bb4127b9000 r-xp 132000 00:00 0 libunwindstack_test
707eb2c4e000-707eb2c91000 r--p 0 00:00 0 libc.so
707eb2c91000-707eb2d1b000 r-xp 42000 00:00 0 libc.so

View file

@ -0,0 +1,17 @@
rax: 0
rbx: 707d82c59c60
rcx: 4
rdx: 707eb5aa8380
r8: 7ffcaadde470
r9: 7ffcaadde478
r10: 8
r11: 206
r12: 707cb2c64330
r13: 0
r14: 174e9096a8f
r15: 707d52c96cb0
rdi: b
rsi: 707eb5aa84b0
rbp: 707eb5aa8320
rsp: 707eb5aa8320
rip: 5bb41271e15b