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, &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 @@
       .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 @@
       .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 @@
       .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 @@
       .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 @@
   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
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, &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) {
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