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
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 75a419c..7770b13 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -296,6 +296,8 @@
"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/*",
],
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index bf86e6e..ad25e80 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -37,7 +37,8 @@
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 @@
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 @@
return false;
}
break;
+ case 'S':
+ cie->is_signal_frame = true;
+ break;
}
}
return true;
@@ -558,8 +564,10 @@
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);
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 286febc..e098a58 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -188,14 +188,15 @@
}
// 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) {
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 17470fd..0188def 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -499,25 +499,27 @@
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;
}
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 76f2dc8..9352a5d 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -100,12 +100,13 @@
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);
}
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 1d71cac..fd824f8 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -72,7 +72,8 @@
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);
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
index 05650fb..5f51a73 100644
--- a/libunwindstack/LocalUnwinder.cpp
+++ b/libunwindstack/LocalUnwinder.cpp
@@ -113,9 +113,11 @@
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;
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index bcdbde8..9ffc0f7 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -242,18 +242,21 @@
// 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_);
}
}
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index af823da..f28cf25 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -106,7 +106,7 @@
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_;
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
index 4b481f0..3d8c2db 100644
--- a/libunwindstack/include/unwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -35,6 +35,7 @@
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 {
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 97614b1..e15b221 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -60,7 +60,8 @@
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);
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 0c39b23..5df7ddf 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -64,7 +64,8 @@
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);
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index febd6d3..e5a1aed 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -68,7 +68,8 @@
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 @@
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
+ bool is_signal_frame;
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@@ -93,7 +95,8 @@
.WillOnce(::testing::Return(false));
bool finished;
- ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished));
+ bool is_signal_frame;
+ ASSERT_FALSE(section_->Step(0x1000, ®s_, nullptr, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_pass) {
@@ -111,7 +114,8 @@
.WillOnce(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ bool is_signal_frame;
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished, &is_signal_frame));
}
static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
@@ -137,9 +141,10 @@
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
- ASSERT_TRUE(section_->Step(0x1500, ®s_, &process, &finished));
+ bool is_signal_frame;
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished, &is_signal_frame));
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished, &is_signal_frame));
+ ASSERT_TRUE(section_->Step(0x1500, ®s_, &process, &finished, &is_signal_frame));
}
TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
@@ -157,7 +162,8 @@
.WillRepeatedly(::testing::Return(true));
bool finished;
- ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished));
+ bool is_signal_frame;
+ ASSERT_TRUE(section_->Step(0x1000, ®s_, &process, &finished, &is_signal_frame));
DwarfFde fde1{};
fde1.pc_start = 0x500;
@@ -167,8 +173,8 @@
EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
- ASSERT_TRUE(section_->Step(0x600, ®s_, &process, &finished));
- ASSERT_TRUE(section_->Step(0x700, ®s_, &process, &finished));
+ ASSERT_TRUE(section_->Step(0x600, ®s_, &process, &finished, &is_signal_frame));
+ ASSERT_TRUE(section_->Step(0x700, ®s_, &process, &finished, &is_signal_frame));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 3d5ddd6..b16cd53 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -52,7 +52,7 @@
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 @@
fake_regs->set_pc(entry.pc);
fake_regs->set_sp(entry.sp);
*finished = entry.finished;
+ *is_signal_frame = false;
return true;
}
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 3b6cb80..abda7b8 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -76,7 +76,7 @@
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;
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index f0852a4..d81edbf 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -138,7 +138,8 @@
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 @@
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 @@
MemoryFake process_memory;
bool finished;
- EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished))
+ bool is_signal_frame;
+ EXPECT_CALL(*interface, Step(0x1000, ®s, &process_memory, &finished, &is_signal_frame))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(elf.Step(0x1000, ®s, &process_memory, &finished));
+ ASSERT_TRUE(elf.Step(0x1000, ®s, &process_memory, &finished, &is_signal_frame));
}
TEST_F(ElfTest, get_global_invalid_elf) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 0c6f9f8..ab427b5 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1736,4 +1736,158 @@
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
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
new file mode 100644
index 0000000..5c882e4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
new file mode 100644
index 0000000..8dcff67
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/maps.txt b/libunwindstack/tests/files/offline/signal_fde_x86/maps.txt
new file mode 100644
index 0000000..6166a9d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/maps.txt
@@ -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
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/regs.txt b/libunwindstack/tests/files/offline/signal_fde_x86/regs.txt
new file mode 100644
index 0000000..456b212
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/regs.txt
@@ -0,0 +1,9 @@
+eax: 5aeec4ac
+ebx: 5aeec4ac
+ecx: 0
+edx: 6b
+ebp: ecb37188
+edi: ebecda30
+esi: b
+esp: ecb37188
+eip: 5ae0d4d9
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack0.data b/libunwindstack/tests/files/offline/signal_fde_x86/stack0.data
new file mode 100644
index 0000000..0bbbe22
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack1.data b/libunwindstack/tests/files/offline/signal_fde_x86/stack1.data
new file mode 100644
index 0000000..0873046
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86/stack1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
new file mode 100644
index 0000000..cea7336
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
new file mode 100644
index 0000000..c48e84e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt b/libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt
new file mode 100644
index 0000000..514aa71
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt
@@ -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
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt b/libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt
new file mode 100644
index 0000000..8da7b4e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt
@@ -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
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data b/libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data
new file mode 100644
index 0000000..e19a016
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data b/libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data
new file mode 100644
index 0000000..3435f7c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data
Binary files differ