[Lldb-commits] [lldb] r306397 - Add debug_frame section support

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Tue Jun 27 04:16:27 PDT 2017


Author: labath
Date: Tue Jun 27 04:16:26 2017
New Revision: 306397

URL: http://llvm.org/viewvc/llvm-project?rev=306397&view=rev
Log:
Add debug_frame section support

Summary:
This is a beefed-up version of D33504, which adds support for dwarf 4
debug_frame section format.

The main difference here is that the decision whether to use eh_frame or
debug_frame is done on a per-function basis instead of per-object file.
This is necessary because one module can contain both sections (for
example, the start files added by the linker will typically pull in
eh_frame), but we want to be able to access both, for maximum
information.

I also add unit test for parsing various CFI formats (eh_frame,
debug_frame v3 and debug_frame v4).

Reviewers: jasonmolenda, clayborg

Subscribers: mgorny, aprantl, abidh, lldb-commits, tatyana-krasnukha

Differential Revision: https://reviews.llvm.org/D34613

Added:
    lldb/trunk/unittests/Symbol/Inputs/
    lldb/trunk/unittests/Symbol/Inputs/basic-call-frame-info.yaml
    lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp
Modified:
    lldb/trunk/include/lldb/Symbol/DWARFCallFrameInfo.h
    lldb/trunk/include/lldb/Symbol/FuncUnwinders.h
    lldb/trunk/include/lldb/Symbol/UnwindTable.h
    lldb/trunk/source/Commands/CommandObjectTarget.cpp
    lldb/trunk/source/Symbol/DWARFCallFrameInfo.cpp
    lldb/trunk/source/Symbol/FuncUnwinders.cpp
    lldb/trunk/source/Symbol/UnwindTable.cpp
    lldb/trunk/unittests/Symbol/CMakeLists.txt

Modified: lldb/trunk/include/lldb/Symbol/DWARFCallFrameInfo.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/DWARFCallFrameInfo.h?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/DWARFCallFrameInfo.h (original)
+++ lldb/trunk/include/lldb/Symbol/DWARFCallFrameInfo.h Tue Jun 27 04:16:26 2017
@@ -37,7 +37,7 @@ public:
   DWARFCallFrameInfo(ObjectFile &objfile, lldb::SectionSP &section,
                      lldb::RegisterKind reg_kind, bool is_eh_frame);
 
-  ~DWARFCallFrameInfo();
+  ~DWARFCallFrameInfo() = default;
 
   // Locate an AddressRange that includes the provided Address in this
   // object's eh_frame/debug_info
@@ -74,12 +74,20 @@ public:
 
 private:
   enum { CFI_AUG_MAX_SIZE = 8, CFI_HEADER_SIZE = 8 };
+  enum CFIVersion {
+    CFI_VERSION1 = 1, // DWARF v.2
+    CFI_VERSION3 = 3, // DWARF v.3
+    CFI_VERSION4 = 4  // DWARF v.4, v.5
+  };
 
   struct CIE {
     dw_offset_t cie_offset;
     uint8_t version;
     char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very
                                          // short.
+    uint8_t address_size = sizeof(uint32_t); // The size of a target address.
+    uint8_t segment_size = 0;                // The size of a segment selector.
+
     uint32_t code_align;
     int32_t data_align;
     uint32_t return_addr_reg_num;

Modified: lldb/trunk/include/lldb/Symbol/FuncUnwinders.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/FuncUnwinders.h?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/FuncUnwinders.h (original)
+++ lldb/trunk/include/lldb/Symbol/FuncUnwinders.h Tue Jun 27 04:16:26 2017
@@ -99,6 +99,13 @@ public:
                                                    Thread &thread,
                                                    int current_offset);
 
+  lldb::UnwindPlanSP GetDebugFrameUnwindPlan(Target &target,
+                                             int current_offset);
+
+  lldb::UnwindPlanSP GetDebugFrameAugmentedUnwindPlan(Target &target,
+                                                      Thread &thread,
+                                                      int current_offset);
+
   lldb::UnwindPlanSP GetCompactUnwindUnwindPlan(Target &target,
                                                 int current_offset);
 
@@ -126,10 +133,12 @@ private:
 
   lldb::UnwindPlanSP m_unwind_plan_assembly_sp;
   lldb::UnwindPlanSP m_unwind_plan_eh_frame_sp;
-  lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp; // augmented by
-                                                          // assembly inspection
-                                                          // so it's valid
-                                                          // everywhere
+  lldb::UnwindPlanSP m_unwind_plan_debug_frame_sp;
+
+  // augmented by assembly inspection so it's valid everywhere
+  lldb::UnwindPlanSP m_unwind_plan_eh_frame_augmented_sp;
+  lldb::UnwindPlanSP m_unwind_plan_debug_frame_augmented_sp;
+
   std::vector<lldb::UnwindPlanSP> m_unwind_plan_compact_unwind;
   lldb::UnwindPlanSP m_unwind_plan_arm_unwind_sp;
   lldb::UnwindPlanSP m_unwind_plan_fast_sp;
@@ -139,7 +148,9 @@ private:
   // Fetching the UnwindPlans can be expensive - if we've already attempted
   // to get one & failed, don't try again.
   bool m_tried_unwind_plan_assembly : 1, m_tried_unwind_plan_eh_frame : 1,
+      m_tried_unwind_plan_debug_frame : 1,
       m_tried_unwind_plan_eh_frame_augmented : 1,
+      m_tried_unwind_plan_debug_frame_augmented : 1,
       m_tried_unwind_plan_compact_unwind : 1,
       m_tried_unwind_plan_arm_unwind : 1, m_tried_unwind_fast : 1,
       m_tried_unwind_arch_default : 1,

Modified: lldb/trunk/include/lldb/Symbol/UnwindTable.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/UnwindTable.h?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/UnwindTable.h (original)
+++ lldb/trunk/include/lldb/Symbol/UnwindTable.h Tue Jun 27 04:16:26 2017
@@ -27,6 +27,7 @@ public:
   ~UnwindTable();
 
   lldb_private::DWARFCallFrameInfo *GetEHFrameInfo();
+  lldb_private::DWARFCallFrameInfo *GetDebugFrameInfo();
 
   lldb_private::CompactUnwindInfo *GetCompactUnwindInfo();
 
@@ -58,6 +59,8 @@ private:
   void Dump(Stream &s);
 
   void Initialize();
+  llvm::Optional<AddressRange> GetAddressRange(const Address &addr,
+                                               SymbolContext &sc);
 
   typedef std::map<lldb::addr_t, lldb::FuncUnwindersSP> collection;
   typedef collection::iterator iterator;
@@ -70,6 +73,7 @@ private:
   std::mutex m_mutex;
 
   std::unique_ptr<DWARFCallFrameInfo> m_eh_frame_up;
+  std::unique_ptr<DWARFCallFrameInfo> m_debug_frame_up;
   std::unique_ptr<CompactUnwindInfo> m_compact_unwind_up;
   std::unique_ptr<ArmUnwindInfo> m_arm_unwind_up;
 

Modified: lldb/trunk/source/Commands/CommandObjectTarget.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectTarget.cpp?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectTarget.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectTarget.cpp Tue Jun 27 04:16:26 2017
@@ -3426,6 +3426,23 @@ protected:
         result.GetOutputStream().Printf("\n");
       }
 
+      if (UnwindPlanSP plan_sp =
+              func_unwinders_sp->GetDebugFrameUnwindPlan(*target, 0)) {
+        result.GetOutputStream().Printf("debug_frame UnwindPlan:\n");
+        plan_sp->Dump(result.GetOutputStream(), thread.get(),
+                      LLDB_INVALID_ADDRESS);
+        result.GetOutputStream().Printf("\n");
+      }
+
+      if (UnwindPlanSP plan_sp =
+              func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target,
+                                                                  *thread, 0)) {
+        result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n");
+        plan_sp->Dump(result.GetOutputStream(), thread.get(),
+                      LLDB_INVALID_ADDRESS);
+        result.GetOutputStream().Printf("\n");
+      }
+
       UnwindPlanSP arm_unwind_sp =
           func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0);
       if (arm_unwind_sp) {

Modified: lldb/trunk/source/Symbol/DWARFCallFrameInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/DWARFCallFrameInfo.cpp?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/DWARFCallFrameInfo.cpp (original)
+++ lldb/trunk/source/Symbol/DWARFCallFrameInfo.cpp Tue Jun 27 04:16:26 2017
@@ -161,8 +161,6 @@ DWARFCallFrameInfo::DWARFCallFrameInfo(O
       m_fde_index(), m_fde_index_initialized(false),
       m_is_eh_frame(is_eh_frame) {}
 
-DWARFCallFrameInfo::~DWARFCallFrameInfo() {}
-
 bool DWARFCallFrameInfo::GetUnwindPlan(Address addr, UnwindPlan &unwind_plan) {
   FDEEntryMap::Entry fde_entry;
 
@@ -276,6 +274,12 @@ DWARFCallFrameInfo::ParseCIE(const dw_of
     //    cie.cieID = cieID;
     cie_sp->ptr_encoding = DW_EH_PE_absptr; // default
     cie_sp->version = m_cfi_data.GetU8(&offset);
+    if (cie_sp->version > CFI_VERSION4) {
+      Host::SystemLog(Host::eSystemLogError,
+                      "CIE parse error: CFI version %d is not supported\n",
+                      cie_sp->version);
+      return nullptr;
+    }
 
     for (i = 0; i < CFI_AUG_MAX_SIZE; ++i) {
       cie_sp->augmentation[i] = m_cfi_data.GetU8(&offset);
@@ -294,11 +298,23 @@ DWARFCallFrameInfo::ParseCIE(const dw_of
                       "CIE parse error: CIE augmentation string was too large "
                       "for the fixed sized buffer of %d bytes.\n",
                       CFI_AUG_MAX_SIZE);
-      return cie_sp;
+      return nullptr;
+    }
+
+    // m_cfi_data uses address size from target architecture of the process
+    // may ignore these fields?
+    if (!m_is_eh_frame && cie_sp->version >= CFI_VERSION4) {
+      cie_sp->address_size = m_cfi_data.GetU8(&offset);
+      cie_sp->segment_size = m_cfi_data.GetU8(&offset);
     }
+
     cie_sp->code_align = (uint32_t)m_cfi_data.GetULEB128(&offset);
     cie_sp->data_align = (int32_t)m_cfi_data.GetSLEB128(&offset);
-    cie_sp->return_addr_reg_num = m_cfi_data.GetU8(&offset);
+
+    cie_sp->return_addr_reg_num =
+        !m_is_eh_frame && cie_sp->version >= CFI_VERSION3
+            ? static_cast<uint32_t>(m_cfi_data.GetULEB128(&offset))
+            : m_cfi_data.GetU8(&offset);
 
     if (cie_sp->augmentation[0]) {
       // Get the length of the eh_frame augmentation data
@@ -461,11 +477,33 @@ void DWARFCallFrameInfo::GetFDEIndex() {
       m_fde_index_initialized = true;
       return;
     }
+
+    // An FDE entry contains CIE_pointer in debug_frame in same place as cie_id
+    // in eh_frame. CIE_pointer is an offset into the .debug_frame section.
+    // So, variable cie_offset should be equal to cie_id for debug_frame.
+    // FDE entries with cie_id == 0 shouldn't be ignored for it.
+    if ((cie_id == 0 && m_is_eh_frame) || cie_id == UINT32_MAX || len == 0) {
+      auto cie_sp = ParseCIE(current_entry);
+      if (!cie_sp) {
+        // Cannot parse, the reason is already logged
+        m_fde_index.Clear();
+        m_fde_index_initialized = true;
+        return;
+      }
+
+      m_cie_map[current_entry] = std::move(cie_sp);
+      offset = next_entry;
+      continue;
+    }
+
+    if (!m_is_eh_frame)
+      cie_offset = cie_id;
+
     if (cie_offset > m_cfi_data.GetByteSize()) {
-      Host::SystemLog(
-          Host::eSystemLogError,
-          "error: Invalid cie offset of 0x%x found in cie/fde at 0x%x\n",
-          cie_offset, current_entry);
+      Host::SystemLog(Host::eSystemLogError,
+                      "error: Invalid cie offset of 0x%x "
+                      "found in cie/fde at 0x%x\n",
+                      cie_offset, current_entry);
       // Don't trust anything in this eh_frame section if we find blatantly
       // invalid data.
       m_fde_index.Clear();
@@ -473,12 +511,6 @@ void DWARFCallFrameInfo::GetFDEIndex() {
       return;
     }
 
-    if (cie_id == 0 || cie_id == UINT32_MAX || len == 0) {
-      m_cie_map[current_entry] = ParseCIE(current_entry);
-      offset = next_entry;
-      continue;
-    }
-
     const CIE *cie = GetCIE(cie_offset);
     if (cie) {
       const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress();
@@ -531,7 +563,8 @@ bool DWARFCallFrameInfo::FDEToUnwindPlan
     cie_offset = m_cfi_data.GetU32(&offset);
   }
 
-  assert(cie_offset != 0 && cie_offset != UINT32_MAX);
+  // FDE entries with zeroth cie_offset may occur for debug_frame.
+  assert(!(m_is_eh_frame && 0 == cie_offset) && cie_offset != UINT32_MAX);
 
   // Translate the CIE_id from the eh_frame format, which
   // is relative to the FDE offset, into a __eh_frame section

Modified: lldb/trunk/source/Symbol/FuncUnwinders.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/FuncUnwinders.cpp?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/FuncUnwinders.cpp (original)
+++ lldb/trunk/source/Symbol/FuncUnwinders.cpp Tue Jun 27 04:16:26 2017
@@ -39,7 +39,9 @@ FuncUnwinders::FuncUnwinders(UnwindTable
       m_unwind_plan_arch_default_sp(),
       m_unwind_plan_arch_default_at_func_entry_sp(),
       m_tried_unwind_plan_assembly(false), m_tried_unwind_plan_eh_frame(false),
+      m_tried_unwind_plan_debug_frame(false),
       m_tried_unwind_plan_eh_frame_augmented(false),
+      m_tried_unwind_plan_debug_frame_augmented(false),
       m_tried_unwind_plan_compact_unwind(false),
       m_tried_unwind_plan_arm_unwind(false), m_tried_unwind_fast(false),
       m_tried_unwind_arch_default(false),
@@ -56,17 +58,14 @@ UnwindPlanSP FuncUnwinders::GetUnwindPla
                                                     int current_offset) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
 
-  UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan(target, current_offset);
-  if (unwind_plan_sp)
-    return unwind_plan_sp;
-
-  unwind_plan_sp = GetCompactUnwindUnwindPlan(target, current_offset);
-  if (unwind_plan_sp)
-    return unwind_plan_sp;
-
-  unwind_plan_sp = GetArmUnwindUnwindPlan(target, current_offset);
-  if (unwind_plan_sp)
-    return unwind_plan_sp;
+  if (UnwindPlanSP plan_sp = GetEHFrameUnwindPlan(target, current_offset))
+    return plan_sp;
+  if (UnwindPlanSP plan_sp = GetDebugFrameUnwindPlan(target, current_offset))
+    return plan_sp;
+  if (UnwindPlanSP plan_sp = GetCompactUnwindUnwindPlan(target, current_offset))
+    return plan_sp;
+  if (UnwindPlanSP plan_sp = GetArmUnwindUnwindPlan(target, current_offset))
+    return plan_sp;
 
   return nullptr;
 }
@@ -121,6 +120,29 @@ UnwindPlanSP FuncUnwinders::GetEHFrameUn
   return m_unwind_plan_eh_frame_sp;
 }
 
+UnwindPlanSP FuncUnwinders::GetDebugFrameUnwindPlan(Target &target,
+                                                    int current_offset) {
+  std::lock_guard<std::recursive_mutex> guard(m_mutex);
+  if (m_unwind_plan_debug_frame_sp || m_tried_unwind_plan_debug_frame)
+    return m_unwind_plan_debug_frame_sp;
+
+  m_tried_unwind_plan_debug_frame = true;
+  if (m_range.GetBaseAddress().IsValid()) {
+    Address current_pc(m_range.GetBaseAddress());
+    if (current_offset != -1)
+      current_pc.SetOffset(current_pc.GetOffset() + current_offset);
+    DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo();
+    if (debug_frame) {
+      m_unwind_plan_debug_frame_sp.reset(
+          new UnwindPlan(lldb::eRegisterKindGeneric));
+      if (!debug_frame->GetUnwindPlan(current_pc,
+                                      *m_unwind_plan_debug_frame_sp))
+        m_unwind_plan_debug_frame_sp.reset();
+    }
+  }
+  return m_unwind_plan_debug_frame_sp;
+}
+
 UnwindPlanSP FuncUnwinders::GetArmUnwindUnwindPlan(Target &target,
                                                    int current_offset) {
   std::lock_guard<std::recursive_mutex> guard(m_mutex);
@@ -187,6 +209,48 @@ UnwindPlanSP FuncUnwinders::GetEHFrameAu
   return m_unwind_plan_eh_frame_augmented_sp;
 }
 
+UnwindPlanSP
+FuncUnwinders::GetDebugFrameAugmentedUnwindPlan(Target &target, Thread &thread,
+                                                int current_offset) {
+  std::lock_guard<std::recursive_mutex> guard(m_mutex);
+  if (m_unwind_plan_debug_frame_augmented_sp.get() ||
+      m_tried_unwind_plan_debug_frame_augmented)
+    return m_unwind_plan_debug_frame_augmented_sp;
+
+  // Only supported on x86 architectures where we get debug_frame from the
+  // compiler that describes the prologue instructions perfectly, and sometimes
+  // the epilogue instructions too.
+  if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
+      target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
+      target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
+    m_tried_unwind_plan_debug_frame_augmented = true;
+    return m_unwind_plan_debug_frame_augmented_sp;
+  }
+
+  m_tried_unwind_plan_debug_frame_augmented = true;
+
+  UnwindPlanSP debug_frame_plan =
+      GetDebugFrameUnwindPlan(target, current_offset);
+  if (!debug_frame_plan)
+    return m_unwind_plan_debug_frame_augmented_sp;
+
+  m_unwind_plan_debug_frame_augmented_sp.reset(
+      new UnwindPlan(*debug_frame_plan));
+
+  // Augment the debug_frame instructions with epilogue descriptions if
+  // necessary so the UnwindPlan can be used at any instruction in the function.
+
+  UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
+  if (assembly_profiler_sp) {
+    if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite(
+            m_range, thread, *m_unwind_plan_debug_frame_augmented_sp)) {
+      m_unwind_plan_debug_frame_augmented_sp.reset();
+    }
+  } else
+    m_unwind_plan_debug_frame_augmented_sp.reset();
+  return m_unwind_plan_debug_frame_augmented_sp;
+}
+
 UnwindPlanSP FuncUnwinders::GetAssemblyUnwindPlan(Target &target,
                                                   Thread &thread,
                                                   int current_offset) {
@@ -248,6 +312,8 @@ UnwindPlanSP FuncUnwinders::GetUnwindPla
                                                        Thread &thread,
                                                        int current_offset) {
   UnwindPlanSP eh_frame_sp = GetEHFrameUnwindPlan(target, current_offset);
+  if (!eh_frame_sp)
+    eh_frame_sp = GetDebugFrameUnwindPlan(target, current_offset);
   UnwindPlanSP arch_default_at_entry_sp =
       GetUnwindPlanArchitectureDefaultAtFunctionEntry(thread);
   UnwindPlanSP arch_default_sp = GetUnwindPlanArchitectureDefault(thread);
@@ -255,28 +321,22 @@ UnwindPlanSP FuncUnwinders::GetUnwindPla
       GetAssemblyUnwindPlan(target, thread, current_offset);
 
   // This point of this code is to detect when a function is using a
-  // non-standard ABI, and the eh_frame
-  // correctly describes that alternate ABI.  This is addressing a specific
-  // situation on x86_64 linux
-  // systems where one function in a library pushes a value on the stack and
-  // jumps to another function.
-  // So using an assembly instruction based unwind will not work when you're in
-  // the second function -
-  // the stack has been modified in a non-ABI way.  But we have eh_frame that
-  // correctly describes how to
-  // unwind from this location.  So we're looking to see if the initial pc
-  // register save location from
-  // the eh_frame is different from the assembly unwind, the arch default
-  // unwind, and the arch default at
-  // initial function entry.
+  // non-standard ABI, and the eh_frame correctly describes that alternate ABI.
+  // This is addressing a specific situation on x86_64 linux systems where one
+  // function in a library pushes a value on the stack and jumps to another
+  // function.  So using an assembly instruction based unwind will not work when
+  // you're in the second function - the stack has been modified in a non-ABI
+  // way.  But we have eh_frame that correctly describes how to unwind from this
+  // location.  So we're looking to see if the initial pc register save location
+  // from the eh_frame is different from the assembly unwind, the arch default
+  // unwind, and the arch default at initial function entry.
   //
   // We may have eh_frame that describes the entire function -- or we may have
-  // eh_frame that only describes
-  // the unwind after the prologue has executed -- so we need to check both the
-  // arch default (once the prologue
-  // has executed) and the arch default at initial function entry.  And we may
-  // be running on a target where
-  // we have only some of the assembly/arch default unwind plans available.
+  // eh_frame that only describes the unwind after the prologue has executed --
+  // so we need to check both the arch default (once the prologue has executed)
+  // and the arch default at initial function entry.  And we may be running on a
+  // target where we have only some of the assembly/arch default unwind plans
+  // available.
 
   if (CompareUnwindPlansForIdenticalInitialPCLocation(
           thread, eh_frame_sp, arch_default_at_entry_sp) == eLazyBoolNo &&
@@ -287,11 +347,12 @@ UnwindPlanSP FuncUnwinders::GetUnwindPla
     return eh_frame_sp;
   }
 
-  UnwindPlanSP eh_frame_augmented_sp =
-      GetEHFrameAugmentedUnwindPlan(target, thread, current_offset);
-  if (eh_frame_augmented_sp) {
-    return eh_frame_augmented_sp;
-  }
+  if (UnwindPlanSP plan_sp =
+          GetEHFrameAugmentedUnwindPlan(target, thread, current_offset))
+    return plan_sp;
+  if (UnwindPlanSP plan_sp =
+          GetDebugFrameAugmentedUnwindPlan(target, thread, current_offset))
+    return plan_sp;
 
   return assembly_sp;
 }

Modified: lldb/trunk/source/Symbol/UnwindTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/UnwindTable.cpp?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/UnwindTable.cpp (original)
+++ lldb/trunk/source/Symbol/UnwindTable.cpp Tue Jun 27 04:16:26 2017
@@ -44,38 +44,64 @@ void UnwindTable::Initialize() {
 
   if (m_initialized) // check again once we've acquired the lock
     return;
+  m_initialized = true;
 
   SectionList *sl = m_object_file.GetSectionList();
-  if (sl) {
-    SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
-    if (sect.get()) {
-      m_eh_frame_up.reset(new DWARFCallFrameInfo(m_object_file, sect,
-                                                 eRegisterKindEHFrame, true));
-    }
-    sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
-    if (sect.get()) {
-      m_compact_unwind_up.reset(new CompactUnwindInfo(m_object_file, sect));
-    }
-    sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
-    if (sect.get()) {
-      SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
-      if (sect_extab.get()) {
-        m_arm_unwind_up.reset(
-            new ArmUnwindInfo(m_object_file, sect, sect_extab));
-      }
-    }
+  if (!sl)
+    return;
+
+  SectionSP sect = sl->FindSectionByType(eSectionTypeEHFrame, true);
+  if (sect.get()) {
+    m_eh_frame_up.reset(new DWARFCallFrameInfo(m_object_file, sect,
+                                               eRegisterKindEHFrame, true));
   }
 
-  m_initialized = true;
+  sect = sl->FindSectionByType(eSectionTypeDWARFDebugFrame, true);
+  if (sect) {
+    m_debug_frame_up.reset(
+        new DWARFCallFrameInfo(m_object_file, sect, eRegisterKindDWARF, false));
+  }
+
+  sect = sl->FindSectionByType(eSectionTypeCompactUnwind, true);
+  if (sect) {
+    m_compact_unwind_up.reset(new CompactUnwindInfo(m_object_file, sect));
+  }
+
+  sect = sl->FindSectionByType(eSectionTypeARMexidx, true);
+  if (sect) {
+    SectionSP sect_extab = sl->FindSectionByType(eSectionTypeARMextab, true);
+    if (sect_extab.get()) {
+      m_arm_unwind_up.reset(new ArmUnwindInfo(m_object_file, sect, sect_extab));
+    }
+  }
 }
 
 UnwindTable::~UnwindTable() {}
 
+llvm::Optional<AddressRange> UnwindTable::GetAddressRange(const Address &addr,
+                                                          SymbolContext &sc) {
+  AddressRange range;
+
+  // First check the symbol context
+  if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
+                         false, range) &&
+      range.GetBaseAddress().IsValid())
+    return range;
+
+  // Does the eh_frame unwind info has a function bounds for this addr?
+  if (m_eh_frame_up && m_eh_frame_up->GetAddressRange(addr, range))
+    return range;
+
+  // Try debug_frame as well
+  if (m_debug_frame_up && m_debug_frame_up->GetAddressRange(addr, range))
+    return range;
+
+  return llvm::None;
+}
+
 FuncUnwindersSP
 UnwindTable::GetFuncUnwindersContainingAddress(const Address &addr,
                                                SymbolContext &sc) {
-  FuncUnwindersSP no_unwind_found;
-
   Initialize();
 
   std::lock_guard<std::mutex> guard(m_mutex);
@@ -96,23 +122,14 @@ UnwindTable::GetFuncUnwindersContainingA
       return pos->second;
   }
 
-  AddressRange range;
-  if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
-                          false, range) ||
-      !range.GetBaseAddress().IsValid()) {
-    // Does the eh_frame unwind info has a function bounds for this addr?
-    if (m_eh_frame_up == nullptr ||
-        !m_eh_frame_up->GetAddressRange(addr, range)) {
-      return no_unwind_found;
-    }
-  }
+  auto range_or = GetAddressRange(addr, sc);
+  if (!range_or)
+    return nullptr;
 
-  FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, range));
+  FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, *range_or));
   m_unwinds.insert(insert_pos,
-                   std::make_pair(range.GetBaseAddress().GetFileAddress(),
+                   std::make_pair(range_or->GetBaseAddress().GetFileAddress(),
                                   func_unwinder_sp));
-  //    StreamFile s(stdout, false);
-  //    Dump (s);
   return func_unwinder_sp;
 }
 
@@ -121,26 +138,16 @@ UnwindTable::GetFuncUnwindersContainingA
 // UnwindTable.  This is intended for use by target modules show-unwind where we
 // want to create
 // new UnwindPlans, not re-use existing ones.
-
 FuncUnwindersSP
 UnwindTable::GetUncachedFuncUnwindersContainingAddress(const Address &addr,
                                                        SymbolContext &sc) {
-  FuncUnwindersSP no_unwind_found;
   Initialize();
 
-  AddressRange range;
-  if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
-                          false, range) ||
-      !range.GetBaseAddress().IsValid()) {
-    // Does the eh_frame unwind info has a function bounds for this addr?
-    if (m_eh_frame_up == nullptr ||
-        !m_eh_frame_up->GetAddressRange(addr, range)) {
-      return no_unwind_found;
-    }
-  }
+  auto range_or = GetAddressRange(addr, sc);
+  if (!range_or)
+    return nullptr;
 
-  FuncUnwindersSP func_unwinder_sp(new FuncUnwinders(*this, range));
-  return func_unwinder_sp;
+  return std::make_shared<FuncUnwinders>(*this, *range_or);
 }
 
 void UnwindTable::Dump(Stream &s) {
@@ -161,6 +168,11 @@ DWARFCallFrameInfo *UnwindTable::GetEHFr
   return m_eh_frame_up.get();
 }
 
+DWARFCallFrameInfo *UnwindTable::GetDebugFrameInfo() {
+  Initialize();
+  return m_debug_frame_up.get();
+}
+
 CompactUnwindInfo *UnwindTable::GetCompactUnwindInfo() {
   Initialize();
   return m_compact_unwind_up.get();

Modified: lldb/trunk/unittests/Symbol/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Symbol/CMakeLists.txt?rev=306397&r1=306396&r2=306397&view=diff
==============================================================================
--- lldb/trunk/unittests/Symbol/CMakeLists.txt (original)
+++ lldb/trunk/unittests/Symbol/CMakeLists.txt Tue Jun 27 04:16:26 2017
@@ -1,8 +1,16 @@
 add_lldb_unittest(SymbolTests
   TestClangASTContext.cpp
+  TestDWARFCallFrameInfo.cpp
   TestType.cpp
 
   LINK_LIBS
     lldbHost
     lldbSymbol
   )
+
+add_dependencies(SymbolTests yaml2obj)
+add_definitions(-DYAML2OBJ="$<TARGET_FILE:yaml2obj>")
+set(test_inputs
+  basic-call-frame-info.yaml
+  )
+add_unittest_inputs(SymbolTests "${test_inputs}")

Added: lldb/trunk/unittests/Symbol/Inputs/basic-call-frame-info.yaml
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Symbol/Inputs/basic-call-frame-info.yaml?rev=306397&view=auto
==============================================================================
--- lldb/trunk/unittests/Symbol/Inputs/basic-call-frame-info.yaml (added)
+++ lldb/trunk/unittests/Symbol/Inputs/basic-call-frame-info.yaml Tue Jun 27 04:16:26 2017
@@ -0,0 +1,138 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+  Entry:           0x0000000000000260
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x0000000000000260
+    AddressAlign:    0x0000000000000010
+    Content:         554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC30F1F4000554889E5897DFC8B45FC5DC3
+#0000000000000260 <eh_frame>:
+# 260:	55                   	push   %rbp
+# 261:	48 89 e5             	mov    %rsp,%rbp
+# 264:	89 7d fc             	mov    %edi,-0x4(%rbp)
+# 267:	8b 45 fc             	mov    -0x4(%rbp),%eax
+# 26a:	5d                   	pop    %rbp
+# 26b:	c3                   	retq
+# 26c:	0f 1f 40 00          	nopl   0x0(%rax)
+#
+#0000000000000270 <debug_frame3>:
+# 270:	55                   	push   %rbp
+# 271:	48 89 e5             	mov    %rsp,%rbp
+# 274:	89 7d fc             	mov    %edi,-0x4(%rbp)
+# 277:	8b 45 fc             	mov    -0x4(%rbp),%eax
+# 27a:	5d                   	pop    %rbp
+# 27b:	c3                   	retq
+# 27c:	0f 1f 40 00          	nopl   0x0(%rax)
+#
+#0000000000000280 <debug_frame4>:
+# 280:	55                   	push   %rbp
+# 281:	48 89 e5             	mov    %rsp,%rbp
+# 284:	89 7d fc             	mov    %edi,-0x4(%rbp)
+# 287:	8b 45 fc             	mov    -0x4(%rbp),%eax
+# 28a:	5d                   	pop    %rbp
+# 28b:	c3                   	retq
+  - Name:            .eh_frame
+    Type:            SHT_X86_64_UNWIND
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x0000000000000290
+    AddressAlign:    0x0000000000000008
+    Content:         1400000000000000017A5200017810011B0C0708900100001C0000001C000000B0FFFFFF0C00000000410E108602430D0600000000000000
+#00000000 0000000000000014 00000000 CIE
+#  Version:               1
+#  Augmentation:          "zR"
+#  Code alignment factor: 1
+#  Data alignment factor: -8
+#  Return address column: 16
+#  Augmentation data:     1b
+#
+#  DW_CFA_def_cfa: r7 (rsp) ofs 8
+#  DW_CFA_offset: r16 (rip) at cfa-8
+#  DW_CFA_nop
+#  DW_CFA_nop
+#
+#00000018 000000000000001c 0000001c FDE cie=00000000 pc=ffffffffffffffd0..ffffffffffffffdc
+#  DW_CFA_advance_loc: 1 to ffffffffffffffd1
+#  DW_CFA_def_cfa_offset: 16
+#  DW_CFA_offset: r6 (rbp) at cfa-16
+#  DW_CFA_advance_loc: 3 to ffffffffffffffd4
+#  DW_CFA_def_cfa_register: r6 (rbp)
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+  - Name:            .debug_frame
+    Type:            SHT_PROGBITS
+    AddressAlign:    0x0000000000000008
+    Content:         14000000FFFFFFFF03000178100C070890010000000000001C0000000000000070020000000000000C00000000000000410E108602430D0614000000FFFFFFFF040008000178100C07089001000000001C0000003800000080020000000000000C00000000000000410E108602430D06
+#00000000 0000000000000014 ffffffff CIE
+#  Version:               3
+#  Augmentation:          ""
+#  Code alignment factor: 1
+#  Data alignment factor: -8
+#  Return address column: 16
+#
+#  DW_CFA_def_cfa: r7 (rsp) ofs 8
+#  DW_CFA_offset: r16 (rip) at cfa-8
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#
+#00000018 000000000000001c 00000000 FDE cie=00000000 pc=0000000000000270..000000000000027c
+#  DW_CFA_advance_loc: 1 to 0000000000000271
+#  DW_CFA_def_cfa_offset: 16
+#  DW_CFA_offset: r6 (rbp) at cfa-16
+#  DW_CFA_advance_loc: 3 to 0000000000000274
+#  DW_CFA_def_cfa_register: r6 (rbp)
+#
+#00000038 0000000000000014 ffffffff CIE
+#  Version:               4
+#  Augmentation:          ""
+#  Pointer Size:          8
+#  Segment Size:          0
+#  Code alignment factor: 1
+#  Data alignment factor: -8
+#  Return address column: 16
+#
+#  DW_CFA_def_cfa: r7 (rsp) ofs 8
+#  DW_CFA_offset: r16 (rip) at cfa-8
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#  DW_CFA_nop
+#
+#00000050 000000000000001c 00000038 FDE cie=00000038 pc=0000000000000280..000000000000028c
+#  DW_CFA_advance_loc: 1 to 0000000000000281
+#  DW_CFA_def_cfa_offset: 16
+#  DW_CFA_offset: r6 (rbp) at cfa-16
+#  DW_CFA_advance_loc: 3 to 0000000000000284
+#  DW_CFA_def_cfa_register: r6 (rbp)
+Symbols:
+  Global:
+    - Name:            eh_frame
+      Type:            STT_FUNC
+      Section:         .text
+      Value:           0x0000000000000260
+      Size:            0x000000000000000C
+    - Name:            debug_frame3
+      Type:            STT_FUNC
+      Section:         .text
+      Value:           0x0000000000000270
+      Size:            0x000000000000000C
+    - Name:            debug_frame4
+      Type:            STT_FUNC
+      Section:         .text
+      Value:           0x0000000000000280
+      Size:            0x000000000000000C
+...

Added: lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp?rev=306397&view=auto
==============================================================================
--- lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp (added)
+++ lldb/trunk/unittests/Symbol/TestDWARFCallFrameInfo.cpp Tue Jun 27 04:16:26 2017
@@ -0,0 +1,147 @@
+//===-- TestDWARFCallFrameInfo.cpp ------------------------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "Plugins/Process/Utility/RegisterContext_x86.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/DWARFCallFrameInfo.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+
+extern const char *TestMainArgv0;
+
+using namespace lldb_private;
+using namespace lldb;
+
+class DWARFCallFrameInfoTest : public testing::Test {
+public:
+  void SetUp() override {
+    HostInfo::Initialize();
+    ObjectFileELF::Initialize();
+
+    m_inputs_folder = llvm::sys::path::parent_path(TestMainArgv0);
+    llvm::sys::path::append(m_inputs_folder, "Inputs");
+    llvm::sys::fs::make_absolute(m_inputs_folder);
+  }
+
+  void TearDown() override {
+    ObjectFileELF::Terminate();
+    HostInfo::Terminate();
+  }
+
+protected:
+  llvm::SmallString<128> m_inputs_folder;
+
+  void TestBasic(bool eh_frame, llvm::StringRef symbol);
+};
+
+#define ASSERT_NO_ERROR(x)                                                     \
+  if (std::error_code ASSERT_NO_ERROR_ec = x) {                                \
+    llvm::SmallString<128> MessageStorage;                                     \
+    llvm::raw_svector_ostream Message(MessageStorage);                         \
+    Message << #x ": did not return errc::success.\n"                          \
+            << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
+            << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
+    GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
+  } else {                                                                     \
+  }
+
+namespace lldb_private {
+static std::ostream &operator<<(std::ostream &OS, const UnwindPlan::Row &row) {
+  StreamString SS;
+  row.Dump(SS, nullptr, nullptr, 0);
+  return OS << SS.GetData();
+}
+} // namespace lldb_private
+
+static UnwindPlan::Row GetExpectedRow0() {
+  UnwindPlan::Row row;
+  row.SetOffset(0);
+  row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 8);
+  row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
+  return row;
+}
+
+static UnwindPlan::Row GetExpectedRow1() {
+  UnwindPlan::Row row;
+  row.SetOffset(1);
+  row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 16);
+  row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
+  row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
+  return row;
+}
+
+static UnwindPlan::Row GetExpectedRow2() {
+  UnwindPlan::Row row;
+  row.SetOffset(4);
+  row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rbp_x86_64, 16);
+  row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false);
+  row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rbp_x86_64, -16, false);
+  return row;
+}
+
+void DWARFCallFrameInfoTest::TestBasic(bool eh_frame, llvm::StringRef symbol) {
+  llvm::SmallString<128> yaml = m_inputs_folder;
+  llvm::sys::path::append(yaml, "basic-call-frame-info.yaml");
+  llvm::SmallString<128> obj = m_inputs_folder;
+
+  ASSERT_NO_ERROR(llvm::sys::fs::createTemporaryFile(
+      "basic-call-frame-info-%%%%%%", "obj", obj));
+  llvm::FileRemover obj_remover(obj);
+
+  const char *args[] = {YAML2OBJ, yaml.c_str(), nullptr};
+  llvm::StringRef obj_ref = obj;
+  const llvm::StringRef *redirects[] = {nullptr, &obj_ref, nullptr};
+  ASSERT_EQ(0, llvm::sys::ExecuteAndWait(YAML2OBJ, args, nullptr, redirects));
+
+  uint64_t size;
+  ASSERT_NO_ERROR(llvm::sys::fs::file_size(obj, size));
+  ASSERT_GT(size, 0u);
+
+  auto module_sp = std::make_shared<Module>(ModuleSpec(FileSpec(obj, false)));
+  SectionList *list = module_sp->GetSectionList();
+  ASSERT_NE(nullptr, list);
+
+  auto section_sp = list->FindSectionByType(
+      eh_frame ? eSectionTypeEHFrame : eSectionTypeDWARFDebugFrame, false);
+  ASSERT_NE(nullptr, section_sp);
+
+  DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp,
+                         eh_frame ? eRegisterKindEHFrame : eRegisterKindDWARF,
+                         eh_frame);
+
+  const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType(
+      ConstString(symbol), eSymbolTypeAny);
+  ASSERT_NE(nullptr, sym);
+
+  UnwindPlan plan(eRegisterKindGeneric);
+  ASSERT_TRUE(cfi.GetUnwindPlan(sym->GetAddress(), plan));
+  ASSERT_EQ(3, plan.GetRowCount());
+  EXPECT_EQ(GetExpectedRow0(), *plan.GetRowAtIndex(0));
+  EXPECT_EQ(GetExpectedRow1(), *plan.GetRowAtIndex(1));
+  EXPECT_EQ(GetExpectedRow2(), *plan.GetRowAtIndex(2));
+}
+
+TEST_F(DWARFCallFrameInfoTest, Basic_dwarf3) {
+  TestBasic(false, "debug_frame3");
+}
+
+TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) {
+  TestBasic(false, "debug_frame4");
+}
+
+TEST_F(DWARFCallFrameInfoTest, Basic_eh) { TestBasic(true, "eh_frame"); }




More information about the lldb-commits mailing list