Merge "logd: add locks around ~SerializedFlushToState"
diff --git a/include/android/log.h b/include/android/log.h
new file mode 120000
index 0000000..736c448
--- /dev/null
+++ b/include/android/log.h
@@ -0,0 +1 @@
+../../liblog/include/android/log.h
\ No newline at end of file
diff --git a/include/log b/include/log
new file mode 120000
index 0000000..714065f
--- /dev/null
+++ b/include/log
@@ -0,0 +1 @@
+../liblog/include/log
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log.h b/liblog/include_vndk/log/log.h
index ab4adc4..c305661 100644
--- a/liblog/include_vndk/log/log.h
+++ b/liblog/include_vndk/log/log.h
@@ -3,8 +3,9 @@
 #ifndef _LIBS_LOG_LOG_H
 #define _LIBS_LOG_LOG_H
 
-/* Historically vendors have depended on this header being included. */
+/* Historically vendors have depended on these headers being included. */
 #include <fcntl.h>
+#include <unistd.h>
 
 #include <android/log.h>
 #include <log/log_id.h>
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