Merge "libunwindstack: Support signal frame CIEs." am: f30b12e6ad

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1443619

Change-Id: I1377383ec5e926ec430711c6e06489d92affd66a
This commit is contained in:
Christopher Ferris 2020-10-05 19:54:11 +00:00 committed by Automerger Merge Worker
commit 27dcf97048
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