Index: vendor/lldb/dist/Makefile =================================================================== --- vendor/lldb/dist/Makefile (revision 295597) +++ vendor/lldb/dist/Makefile (revision 295598) @@ -1,120 +1,112 @@ ##===- Makefile --------------------------------------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## # If LLDB_LEVEL is not set, then we are the top-level Makefile. Otherwise, we # are being included from a subdirectory makefile. ifndef LLDB_LEVEL IS_TOP_LEVEL := 1 LLDB_LEVEL := . DIRS := include scripts source lib tools PARALLEL_DIRS := endif ### # Common Makefile code, shared by all LLDB Makefiles. # Set LLVM source root level. LEVEL := $(LLDB_LEVEL)/../.. # Include LLVM common makefile. include $(LEVEL)/Makefile.common # Set common LLDB build flags. CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/include CPP.Flags += -I$(PROJ_OBJ_DIR)/$(LLDB_LEVEL)/include CPP.Flags += -I$(LLVM_SRC_ROOT)/tools/clang/include CPP.Flags += -I$(LLVM_OBJ_ROOT)/tools/clang/include CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Utility CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/Utility CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLDB_LEVEL)/source/Plugins/Process/POSIX # Disable python and curses on mingw build ifeq ($(HOST_OS),MingW) CXXFLAGS += -DLLDB_DISABLE_PYTHON -DLLDB_DISABLE_CURSES endif ifeq (,$(findstring -DLLDB_DISABLE_PYTHON,$(CXXFLAGS))) # Set Python include directory PYTHON_CONFIG?= python-config PYTHON_INC_DIR = $(shell $(PYTHON_CONFIG) --includes) CPP.Flags += $(PYTHON_INC_DIR) endif ifeq ($(HOST_OS),Darwin) CPP.Flags += $(subst -I,-I$(SDKROOT),$(PYTHON_INC_DIR)) CPP.Flags += -F$(SDKROOT)/System/Library/Frameworks CPP.Flags += -F$(SDKROOT)/System/Library/PrivateFrameworks CPP.Flags += -I$(SDKROOT)/usr/include/libxml2 endif ifdef LLDB_VENDOR CPP.Flags += -DLLDB_VENDOR='"$(LLDB_VENDOR) "' endif # If building on a 32-bit system, make sure off_t can store offsets > 2GB ifneq "$(HOST_ARCH)" "x86_64" CPP.Flags += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 endif # Disable -fstrict-aliasing. Darwin disables it by default (and LLVM doesn't # work with it enabled with GCC), Clang/llvm-gc don't support it yet, and newer # GCC's have false positive warnings with it on Linux (which prove a pain to # fix). For example: # http://gcc.gnu.org/PR41874 # http://gcc.gnu.org/PR41838 # # We can revisit this when LLVM/Clang support it. CXX.Flags += -fno-strict-aliasing # Do not warn about pragmas. In particular, we are looking to ignore the # "#pragma mark" construct which GCC warns about on platforms other than Darwin. EXTRA_OPTIONS += -Wno-unknown-pragmas # Drop -Wsign-compare, which we are not currently clean with. EXTRA_OPTIONS += -Wno-sign-compare ### # LLDB Top Level specific stuff. ifeq ($(IS_TOP_LEVEL),1) -ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT)) -$(RecursiveTargets):: - $(Verb) if [ ! -f test/Makefile ]; then \ - $(MKDIR) test; \ - $(CP) $(PROJ_SRC_DIR)/test/Makefile test/Makefile; \ - fi -endif - test:: @ $(MAKE) -C test #report:: # @ $(MAKE) -C test report #clean:: # @ $(MAKE) -C test clean tags:: $(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \ grep -v /lib/Headers | grep -v /test/` cscope.files: find tools lib include -name '*.cpp' \ -or -name '*.def' \ -or -name '*.td' \ -or -name '*.h' > cscope.files .PHONY: test report clean cscope.files endif Index: vendor/lldb/dist/include/lldb/API/SBInstruction.h =================================================================== --- vendor/lldb/dist/include/lldb/API/SBInstruction.h (revision 295597) +++ vendor/lldb/dist/include/lldb/API/SBInstruction.h (revision 295598) @@ -1,94 +1,97 @@ //===-- SBInstruction.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_SBInstruction_h_ #define LLDB_SBInstruction_h_ #include "lldb/API/SBDefines.h" #include "lldb/API/SBData.h" #include // There's a lot to be fixed here, but need to wait for underlying insn implementation // to be revised & settle down first. namespace lldb { class LLDB_API SBInstruction { public: SBInstruction (); SBInstruction (const SBInstruction &rhs); const SBInstruction & operator = (const SBInstruction &rhs); ~SBInstruction (); bool IsValid(); SBAddress GetAddress(); lldb::AddressClass GetAddressClass (); const char * GetMnemonic (lldb::SBTarget target); const char * GetOperands (lldb::SBTarget target); const char * GetComment (lldb::SBTarget target); lldb::SBData GetData (lldb::SBTarget target); size_t GetByteSize (); bool DoesBranch (); + bool + HasDelaySlot (); + void Print (FILE *out); bool GetDescription (lldb::SBStream &description); bool EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); bool DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' bool TestEmulation (lldb::SBStream &output_stream, const char *test_file); protected: friend class SBInstructionList; SBInstruction (const lldb::InstructionSP &inst_sp); void SetOpaque (const lldb::InstructionSP &inst_sp); private: lldb::InstructionSP m_opaque_sp; }; } // namespace lldb #endif // LLDB_SBInstruction_h_ Index: vendor/lldb/dist/include/lldb/Core/RangeMap.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/RangeMap.h (revision 295597) +++ vendor/lldb/dist/include/lldb/Core/RangeMap.h (revision 295598) @@ -1,1483 +1,1502 @@ //===-- RangeMap.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_RangeMap_h_ #define liblldb_RangeMap_h_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "llvm/ADT/SmallVector.h" // Project includes #include "lldb/lldb-private.h" // Uncomment to make sure all Range objects are sorted when needed //#define ASSERT_RANGEMAP_ARE_SORTED namespace lldb_private { //---------------------------------------------------------------------- // Templatized classes for dealing with generic ranges and also // collections of ranges, or collections of ranges that have associated // data. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // A simple range class where you get to define the type of the range // base "B", and the type used for the range byte size "S". //---------------------------------------------------------------------- template struct Range { typedef B BaseType; typedef S SizeType; BaseType base; SizeType size; Range () : base (0), size (0) { } Range (BaseType b, SizeType s) : base (b), size (s) { } void Clear (BaseType b = 0) { base = b; size = 0; } // Set the start value for the range, and keep the same size BaseType GetRangeBase () const { return base; } void SetRangeBase (BaseType b) { base = b; } void Slide (BaseType slide) { base += slide; } BaseType GetRangeEnd () const { return base + size; } void SetRangeEnd (BaseType end) { if (end > base) size = end - base; else size = 0; } SizeType GetByteSize () const { return size; } void SetByteSize (SizeType s) { size = s; } bool IsValid() const { return size > 0; } bool Contains (BaseType r) const { return (GetRangeBase() <= r) && (r < GetRangeEnd()); } bool ContainsEndInclusive (BaseType r) const { return (GetRangeBase() <= r) && (r <= GetRangeEnd()); } bool Contains (const Range& range) const { return Contains(range.GetRangeBase()) && ContainsEndInclusive(range.GetRangeEnd()); } // Returns true if the two ranges adjoing or intersect bool DoesAdjoinOrIntersect (const Range &rhs) const { const BaseType lhs_base = this->GetRangeBase(); const BaseType rhs_base = rhs.GetRangeBase(); const BaseType lhs_end = this->GetRangeEnd(); const BaseType rhs_end = rhs.GetRangeEnd(); bool result = (lhs_base <= rhs_end) && (lhs_end >= rhs_base); return result; } // Returns true if the two ranges intersect bool DoesIntersect (const Range &rhs) const { const BaseType lhs_base = this->GetRangeBase(); const BaseType rhs_base = rhs.GetRangeBase(); const BaseType lhs_end = this->GetRangeEnd(); const BaseType rhs_end = rhs.GetRangeEnd(); bool result = (lhs_base < rhs_end) && (lhs_end > rhs_base); return result; } bool operator < (const Range &rhs) const { if (base == rhs.base) return size < rhs.size; return base < rhs.base; } bool operator == (const Range &rhs) const { return base == rhs.base && size == rhs.size; } bool operator != (const Range &rhs) const { return base != rhs.base || size != rhs.size; } }; //---------------------------------------------------------------------- // A range array class where you get to define the type of the ranges // that the collection contains. //---------------------------------------------------------------------- template class RangeArray { public: typedef B BaseType; typedef S SizeType; typedef Range Entry; typedef llvm::SmallVector Collection; RangeArray() = default; ~RangeArray() = default; void Append (const Entry &entry) { m_entries.push_back (entry); } bool RemoveEntrtAtIndex (uint32_t idx) { if (idx < m_entries.size()) { m_entries.erase (m_entries.begin() + idx); return true; } return false; } void Sort () { if (m_entries.size() > 1) std::stable_sort (m_entries.begin(), m_entries.end()); } #ifdef ASSERT_RANGEMAP_ARE_SORTED bool IsSorted () const { typename Collection::const_iterator pos, end, prev; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && *pos < *prev) return false; } return true; } #endif void CombineConsecutiveRanges () { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif // Can't combine if ranges if we have zero or one range if (m_entries.size() > 1) { // The list should be sorted prior to calling this function typename Collection::iterator pos; typename Collection::iterator end; typename Collection::iterator prev; bool can_combine = false; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->DoesAdjoinOrIntersect(*pos)) { can_combine = true; break; } } // We we can combine at least one entry, then we make a new collection // and populate it accordingly, and then swap it into place. if (can_combine) { Collection minimal_ranges; for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->DoesAdjoinOrIntersect(*pos)) minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); else minimal_ranges.push_back (*pos); } // Use the swap technique in case our new vector is much smaller. // We must swap when using the STL because std::vector objects never // release or reduce the memory once it has been allocated/reserved. m_entries.swap (minimal_ranges); } } } BaseType GetMinRangeBase (BaseType fail_value) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (m_entries.empty()) return fail_value; // m_entries must be sorted, so if we aren't empty, we grab the // first range's base return m_entries.front().GetRangeBase(); } BaseType GetMaxRangeEnd (BaseType fail_value) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (m_entries.empty()) return fail_value; // m_entries must be sorted, so if we aren't empty, we grab the // last range's end return m_entries.back().GetRangeEnd(); } void Slide (BaseType slide) { typename Collection::iterator pos, end; for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) pos->Slide (slide); } void Clear () { m_entries.clear(); } bool IsEmpty () const { return m_entries.empty(); } size_t GetSize () const { return m_entries.size(); } const Entry * GetEntryAtIndex (size_t i) const { return ((i < m_entries.size()) ? &m_entries[i] : nullptr); } // Clients must ensure that "i" is a valid index prior to calling this function const Entry & GetEntryRef (size_t i) const { return m_entries[i]; } Entry * Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); } const Entry * Back() const { return (m_entries.empty() ? nullptr : &m_entries.back()); } static bool BaseLessThan (const Entry& lhs, const Entry& rhs) { return lhs.GetRangeBase() < rhs.GetRangeBase(); } uint32_t FindEntryIndexThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return std::distance (begin, pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) return std::distance (begin, pos); } } return UINT32_MAX; } const Entry * FindEntryThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) { return &(*pos); } } } return nullptr; } const Entry * FindEntryThatContains (const Entry &range) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); if (pos != end && pos->Contains(range)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(range)) { return &(*pos); } } } return nullptr; } protected: Collection m_entries; }; template class RangeVector { public: typedef B BaseType; typedef S SizeType; typedef Range Entry; typedef std::vector Collection; RangeVector() = default; ~RangeVector() = default; void Append (const Entry &entry) { m_entries.push_back (entry); } bool RemoveEntrtAtIndex (uint32_t idx) { if (idx < m_entries.size()) { m_entries.erase (m_entries.begin() + idx); return true; } return false; } void Sort () { if (m_entries.size() > 1) std::stable_sort (m_entries.begin(), m_entries.end()); } #ifdef ASSERT_RANGEMAP_ARE_SORTED bool IsSorted () const { typename Collection::const_iterator pos, end, prev; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && *pos < *prev) return false; } return true; } #endif void CombineConsecutiveRanges () { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif // Can't combine if ranges if we have zero or one range if (m_entries.size() > 1) { // The list should be sorted prior to calling this function typename Collection::iterator pos; typename Collection::iterator end; typename Collection::iterator prev; bool can_combine = false; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->DoesAdjoinOrIntersect(*pos)) { can_combine = true; break; } } // We we can combine at least one entry, then we make a new collection // and populate it accordingly, and then swap it into place. if (can_combine) { Collection minimal_ranges; for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->DoesAdjoinOrIntersect(*pos)) minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); else minimal_ranges.push_back (*pos); } // Use the swap technique in case our new vector is much smaller. // We must swap when using the STL because std::vector objects never // release or reduce the memory once it has been allocated/reserved. m_entries.swap (minimal_ranges); } } } BaseType GetMinRangeBase (BaseType fail_value) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (m_entries.empty()) return fail_value; // m_entries must be sorted, so if we aren't empty, we grab the // first range's base return m_entries.front().GetRangeBase(); } BaseType GetMaxRangeEnd (BaseType fail_value) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (m_entries.empty()) return fail_value; // m_entries must be sorted, so if we aren't empty, we grab the // last range's end return m_entries.back().GetRangeEnd(); } void Slide (BaseType slide) { typename Collection::iterator pos, end; for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) pos->Slide (slide); } void Clear () { m_entries.clear(); } void Reserve (typename Collection::size_type size) { m_entries.reserve (size); } bool IsEmpty () const { return m_entries.empty(); } size_t GetSize () const { return m_entries.size(); } const Entry * GetEntryAtIndex (size_t i) const { return ((i < m_entries.size()) ? &m_entries[i] : nullptr); } // Clients must ensure that "i" is a valid index prior to calling this function const Entry & GetEntryRef (size_t i) const { return m_entries[i]; } Entry * Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); } const Entry * Back() const { return (m_entries.empty() ? nullptr : &m_entries.back()); } static bool BaseLessThan (const Entry& lhs, const Entry& rhs) { return lhs.GetRangeBase() < rhs.GetRangeBase(); } uint32_t FindEntryIndexThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return std::distance (begin, pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) return std::distance (begin, pos); } } return UINT32_MAX; } const Entry * FindEntryThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) { return &(*pos); } } } return nullptr; } const Entry * FindEntryThatContains (const Entry &range) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if (!m_entries.empty()) { typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); if (pos != end && pos->Contains(range)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(range)) { return &(*pos); } } } return nullptr; } protected: Collection m_entries; }; //---------------------------------------------------------------------- // A simple range with data class where you get to define the type of // the range base "B", the type used for the range byte size "S", and // the type for the associated data "T". //---------------------------------------------------------------------- template struct RangeData : public Range { typedef T DataType; DataType data; RangeData () : Range (), data () { } RangeData (B base, S size) : Range (base, size), data () { } RangeData (B base, S size, DataType d) : Range (base, size), data (d) { } bool operator < (const RangeData &rhs) const { if (this->base == rhs.base) { if (this->size == rhs.size) return this->data < rhs.data; else return this->size < rhs.size; } return this->base < rhs.base; } bool operator == (const RangeData &rhs) const { return this->GetRangeBase() == rhs.GetRangeBase() && this->GetByteSize() == rhs.GetByteSize() && this->data == rhs.data; } bool operator != (const RangeData &rhs) const { return this->GetRangeBase() != rhs.GetRangeBase() || this->GetByteSize() != rhs.GetByteSize() || this->data != rhs.data; } }; template class RangeDataArray { public: typedef RangeData Entry; typedef llvm::SmallVector Collection; RangeDataArray() = default; ~RangeDataArray() = default; void Append (const Entry &entry) { m_entries.push_back (entry); } void Sort () { if (m_entries.size() > 1) std::stable_sort (m_entries.begin(), m_entries.end()); } #ifdef ASSERT_RANGEMAP_ARE_SORTED bool IsSorted () const { typename Collection::const_iterator pos, end, prev; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && *pos < *prev) return false; } return true; } #endif void CombineConsecutiveEntriesWithEqualData () { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif typename Collection::iterator pos; typename Collection::iterator end; typename Collection::iterator prev; bool can_combine = false; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->data == pos->data) { can_combine = true; break; } } // We we can combine at least one entry, then we make a new collection // and populate it accordingly, and then swap it into place. if (can_combine) { Collection minimal_ranges; for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->data == pos->data) minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); else minimal_ranges.push_back (*pos); } // Use the swap technique in case our new vector is much smaller. // We must swap when using the STL because std::vector objects never // release or reduce the memory once it has been allocated/reserved. m_entries.swap (minimal_ranges); } } void Clear () { m_entries.clear(); } bool IsEmpty () const { return m_entries.empty(); } size_t GetSize () const { return m_entries.size(); } const Entry * GetEntryAtIndex (size_t i) const { return ((i < m_entries.size()) ? &m_entries[i] : nullptr); } // Clients must ensure that "i" is a valid index prior to calling this function const Entry & GetEntryRef (size_t i) const { return m_entries[i]; } static bool BaseLessThan (const Entry& lhs, const Entry& rhs) { return lhs.GetRangeBase() < rhs.GetRangeBase(); } uint32_t FindEntryIndexThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return std::distance (begin, pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) return std::distance (begin, pos); } } return UINT32_MAX; } Entry * FindEntryThatContains (B addr) { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry; entry.SetRangeBase(addr); entry.SetByteSize(1); typename Collection::iterator begin = m_entries.begin(); typename Collection::iterator end = m_entries.end(); typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) { return &(*pos); } } } return nullptr; } const Entry * FindEntryThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry; entry.SetRangeBase(addr); entry.SetByteSize(1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); if (pos != end && pos->Contains(addr)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(addr)) { return &(*pos); } } } return nullptr; } const Entry * FindEntryThatContains (const Entry &range) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); if (pos != end && pos->Contains(range)) { return &(*pos); } else if (pos != begin) { --pos; if (pos->Contains(range)) { return &(*pos); } } } return nullptr; } Entry * Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); } const Entry * Back() const { return (m_entries.empty() ? nullptr : &m_entries.back()); } protected: Collection m_entries; }; // Same as RangeDataArray, but uses std::vector as to not // require static storage of N items in the class itself template class RangeDataVector { public: typedef RangeData Entry; typedef std::vector Collection; RangeDataVector() = default; ~RangeDataVector() = default; void Append (const Entry &entry) { m_entries.push_back (entry); } void Sort () { if (m_entries.size() > 1) std::stable_sort (m_entries.begin(), m_entries.end()); } #ifdef ASSERT_RANGEMAP_ARE_SORTED bool IsSorted () const { typename Collection::const_iterator pos, end, prev; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && *pos < *prev) return false; } return true; } #endif void CombineConsecutiveEntriesWithEqualData () { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif typename Collection::iterator pos; typename Collection::iterator end; typename Collection::iterator prev; bool can_combine = false; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->data == pos->data) { can_combine = true; break; } } // We we can combine at least one entry, then we make a new collection // and populate it accordingly, and then swap it into place. if (can_combine) { Collection minimal_ranges; for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && prev->data == pos->data) minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); else minimal_ranges.push_back (*pos); } // Use the swap technique in case our new vector is much smaller. // We must swap when using the STL because std::vector objects never // release or reduce the memory once it has been allocated/reserved. m_entries.swap (minimal_ranges); } } // Calculate the byte size of ranges with zero byte sizes by finding // the next entry with a base address > the current base address void CalculateSizesOfZeroByteSizeRanges () { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif typename Collection::iterator pos; typename Collection::iterator end; typename Collection::iterator next; for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) { if (pos->GetByteSize() == 0) { // Watch out for multiple entries with same address and make sure // we find an entry that is greater than the current base address // before we use that for the size auto curr_base = pos->GetRangeBase(); for (next = pos + 1; next != end; ++next) { auto next_base = next->GetRangeBase(); if (next_base > curr_base) { pos->SetByteSize (next_base - curr_base); break; } } } } } void Clear () { m_entries.clear(); } void Reserve (typename Collection::size_type size) { m_entries.resize (size); } bool IsEmpty () const { return m_entries.empty(); } size_t GetSize () const { return m_entries.size(); } const Entry * GetEntryAtIndex (size_t i) const { return ((i < m_entries.size()) ? &m_entries[i] : nullptr); } // Clients must ensure that "i" is a valid index prior to calling this function const Entry & GetEntryRef (size_t i) const { return m_entries[i]; } static bool BaseLessThan (const Entry& lhs, const Entry& rhs) { return lhs.GetRangeBase() < rhs.GetRangeBase(); } uint32_t FindEntryIndexThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry (addr, 1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); while(pos != begin && pos[-1].Contains(addr)) --pos; if (pos != end && pos->Contains(addr)) return std::distance (begin, pos); } return UINT32_MAX; } + + uint32_t + FindEntryIndexesThatContain(B addr, std::vector &indexes) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + + if (!m_entries.empty()) + { + typename Collection::const_iterator pos; + for (const auto &entry : m_entries) + { + if (entry.Contains(addr)) + indexes.push_back(entry.data); + } + } + return indexes.size() ; + } Entry * FindEntryThatContains (B addr) { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry; entry.SetRangeBase(addr); entry.SetByteSize(1); typename Collection::iterator begin = m_entries.begin(); typename Collection::iterator end = m_entries.end(); typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); while(pos != begin && pos[-1].Contains(addr)) --pos; if (pos != end && pos->Contains(addr)) return &(*pos); } return nullptr; } const Entry * FindEntryThatContains (B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry; entry.SetRangeBase(addr); entry.SetByteSize(1); typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); while(pos != begin && pos[-1].Contains(addr)) --pos; if (pos != end && pos->Contains(addr)) return &(*pos); } return nullptr; } const Entry * FindEntryThatContains (const Entry &range) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { typename Collection::const_iterator begin = m_entries.begin(); typename Collection::const_iterator end = m_entries.end(); typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); while(pos != begin && pos[-1].Contains(range)) --pos; if (pos != end && pos->Contains(range)) return &(*pos); } return nullptr; } Entry * Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); } const Entry * Back() const { return (m_entries.empty() ? nullptr : &m_entries.back()); } protected: Collection m_entries; }; //---------------------------------------------------------------------- // A simple range with data class where you get to define the type of // the range base "B", the type used for the range byte size "S", and // the type for the associated data "T". //---------------------------------------------------------------------- template struct AddressData { typedef B BaseType; typedef T DataType; BaseType addr; DataType data; AddressData () : addr (), data () { } AddressData (B a, DataType d) : addr (a), data (d) { } bool operator < (const AddressData &rhs) const { if (this->addr == rhs.addr) return this->data < rhs.data; return this->addr < rhs.addr; } bool operator == (const AddressData &rhs) const { return this->addr == rhs.addr && this->data == rhs.data; } bool operator != (const AddressData &rhs) const { return this->addr != rhs.addr || this->data == rhs.data; } }; template class AddressDataArray { public: typedef AddressData Entry; typedef llvm::SmallVector Collection; AddressDataArray() = default; ~AddressDataArray() = default; void Append (const Entry &entry) { m_entries.push_back (entry); } void Sort () { if (m_entries.size() > 1) std::stable_sort (m_entries.begin(), m_entries.end()); } #ifdef ASSERT_RANGEMAP_ARE_SORTED bool IsSorted () const { typename Collection::const_iterator pos, end, prev; // First we determine if we can combine any of the Entry objects so we // don't end up allocating and making a new collection for no reason for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) { if (prev != end && *pos < *prev) return false; } return true; } #endif void Clear () { m_entries.clear(); } bool IsEmpty () const { return m_entries.empty(); } size_t GetSize () const { return m_entries.size(); } const Entry * GetEntryAtIndex (size_t i) const { return ((i < m_entries.size()) ? &m_entries[i] : nullptr); } // Clients must ensure that "i" is a valid index prior to calling this function const Entry & GetEntryRef (size_t i) const { return m_entries[i]; } static bool BaseLessThan (const Entry& lhs, const Entry& rhs) { return lhs.addr < rhs.addr; } Entry * FindEntry (B addr, bool exact_match_only) { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert (IsSorted()); #endif if ( !m_entries.empty() ) { Entry entry; entry.addr = addr; typename Collection::iterator begin = m_entries.begin(); typename Collection::iterator end = m_entries.end(); typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); while(pos != begin && pos[-1].addr == addr) --pos; if (pos != end) { if (pos->addr == addr || !exact_match_only) return &(*pos); } } return nullptr; } const Entry * FindNextEntry (const Entry *entry) { if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end()) return entry + 1; return nullptr; } Entry * Back() { return (m_entries.empty() ? nullptr : &m_entries.back()); } const Entry * Back() const { return (m_entries.empty() ? nullptr : &m_entries.back()); } protected: Collection m_entries; }; } // namespace lldb_private #endif // liblldb_RangeMap_h_ Index: vendor/lldb/dist/include/lldb/Symbol/Symtab.h =================================================================== --- vendor/lldb/dist/include/lldb/Symbol/Symtab.h (revision 295597) +++ vendor/lldb/dist/include/lldb/Symbol/Symtab.h (revision 295598) @@ -1,173 +1,174 @@ //===-- Symtab.h ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Symtab_h_ #define liblldb_Symtab_h_ #include #include "lldb/lldb-private.h" #include "lldb/Core/RangeMap.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Host/Mutex.h" #include "lldb/Symbol/Symbol.h" namespace lldb_private { class Symtab { public: typedef std::vector IndexCollection; typedef UniqueCStringMap NameToIndexMap; typedef enum Debug { eDebugNo, // Not a debug symbol eDebugYes, // A debug symbol eDebugAny } Debug; typedef enum Visibility { eVisibilityAny, eVisibilityExtern, eVisibilityPrivate } Visibility; Symtab(ObjectFile *objfile); ~Symtab(); void Reserve (size_t count); Symbol * Resize (size_t count); uint32_t AddSymbol(const Symbol& symbol); size_t GetNumSymbols() const; void SectionFileAddressesChanged (); void Dump(Stream *s, Target *target, SortOrder sort_type); void Dump(Stream *s, Target *target, std::vector& indexes) const; uint32_t GetIndexForSymbol (const Symbol *symbol) const; Mutex & GetMutex () { return m_mutex; } Symbol * FindSymbolByID (lldb::user_id_t uid) const; Symbol * SymbolAtIndex (size_t idx); const Symbol * SymbolAtIndex (size_t idx) const; Symbol * FindSymbolWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t &start_idx); //---------------------------------------------------------------------- /// Get the parent symbol for the given symbol. /// /// Many symbols in symbol tables are scoped by other symbols that /// contain one or more symbol. This function will look for such a /// containing symbol and return it if there is one. //---------------------------------------------------------------------- const Symbol * GetParent (Symbol *symbol) const; uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; uint32_t AppendSymbolIndexesWithTypeAndFlagsValue (lldb::SymbolType symbol_type, uint32_t flags_value, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector& matches); uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, std::vector& matches); uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, std::vector& indexes); uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes); size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, std::vector& symbol_indexes); size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); size_t FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); Symbol * FindFirstSymbolWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility); Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes); Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr); + void ForEachSymbolContainingFileAddress(lldb::addr_t file_addr, std::function const &callback); size_t FindFunctionSymbols (const ConstString &name, uint32_t name_type_mask, SymbolContextList& sc_list); void CalculateSymbolSizes (); void SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const; static void DumpSymbolHeader (Stream *s); void Finalize () { // Shrink to fit the symbols so we don't waste memory if (m_symbols.capacity() > m_symbols.size()) { collection new_symbols (m_symbols.begin(), m_symbols.end()); m_symbols.swap (new_symbols); } } void AppendSymbolNamesToMap (const IndexCollection &indexes, bool add_demangled, bool add_mangled, NameToIndexMap &name_to_index_map) const; ObjectFile * GetObjectFile() { return m_objfile; } protected: typedef std::vector collection; typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; typedef RangeDataVector FileRangeToIndexMap; void InitNameIndexes (); void InitAddressIndexes (); ObjectFile * m_objfile; collection m_symbols; FileRangeToIndexMap m_file_addr_to_index; UniqueCStringMap m_name_to_index; UniqueCStringMap m_basename_to_index; UniqueCStringMap m_method_to_index; UniqueCStringMap m_selector_to_index; mutable Mutex m_mutex; // Provide thread safety for this symbol table bool m_file_addr_to_index_computed:1, m_name_indexes_computed:1; private: bool CheckSymbolAtIndex (size_t idx, Debug symbol_debug_type, Visibility symbol_visibility) const { switch (symbol_debug_type) { case eDebugNo: if (m_symbols[idx].IsDebug() == true) return false; break; case eDebugYes: if (m_symbols[idx].IsDebug() == false) return false; break; case eDebugAny: break; } switch (symbol_visibility) { case eVisibilityAny: return true; case eVisibilityExtern: return m_symbols[idx].IsExternal(); case eVisibilityPrivate: return !m_symbols[idx].IsExternal(); } return false; } void SymbolIndicesToSymbolContextList (std::vector &symbol_indexes, SymbolContextList &sc_list); DISALLOW_COPY_AND_ASSIGN (Symtab); }; } // namespace lldb_private #endif // liblldb_Symtab_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py (revision 295597) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py (revision 295598) @@ -1,69 +1,70 @@ from __future__ import print_function import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class ExprCharTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "main.cpp" self.main_source_spec = lldb.SBFileSpec (self.main_source) self.exe = os.path.join(os.getcwd(), "a.out") def do_test(self, dictionary=None): """These basic expression commands should work as expected.""" self.build(dictionary = dictionary) target = self.dbg.CreateTarget(self.exe) self.assertTrue(target) breakpoint = target.BreakpointCreateBySourceRegex('// Break here', self.main_source_spec) self.assertTrue(breakpoint) # Launch the process, and do not stop at the entry point. process = target.LaunchSimple(None, None, self.get_process_working_directory()) self.assertTrue(process) threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) self.assertEqual(len(threads), 1) frame = threads[0].GetFrameAtIndex(0) value = frame.EvaluateExpression("foo(c)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 1) value = frame.EvaluateExpression("foo(sc)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 2) value = frame.EvaluateExpression("foo(uc)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 3) @expectedFailureWindows("llvm.org/pr21765") def test_default_char(self): self.do_test() @expectedFailureArch("arm", "llvm.org/pr23069") @expectedFailureArch("aarch64", "llvm.org/pr23069") @expectedFailureWindows("llvm.org/pr21765") def test_signed_char(self): self.do_test(dictionary={'CFLAGS_EXTRAS': '-fsigned-char'}) @expectedFailurei386("llvm.org/pr23069") @expectedFailurex86_64("llvm.org/pr23069") @expectedFailureWindows("llvm.org/pr21765") + @expectedFailureAll(bugnumber="llvm.org/pr23069", triple = 'mips*') def test_unsigned_char(self): self.do_test(dictionary={'CFLAGS_EXTRAS': '-funsigned-char'}) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/Makefile (revision 295598) @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules + Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/TestAvoidBreakpointInDelaySlot.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/TestAvoidBreakpointInDelaySlot.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/TestAvoidBreakpointInDelaySlot.py (revision 295598) @@ -0,0 +1,84 @@ +""" +Test specific to MIPS +""" + +from __future__ import print_function + +import os, time +import re +import unittest2 +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class AvoidBreakpointInDelaySlotAPITestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessArch(archs=re.compile('mips*')) + def test(self): + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.expect("file " + exe, + patterns = [ "Current executable set to .*a.out.*" ]) + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + breakpoint = target.BreakpointCreateByName('main', 'a.out') + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, self.get_process_working_directory()) + self.assertTrue(process, PROCESS_IS_VALID) + + list = target.FindFunctions('foo', lldb.eFunctionNameTypeAuto) + self.assertTrue(list.GetSize() == 1) + sc = list.GetContextAtIndex(0) + self.assertTrue(sc.GetSymbol().GetName() == "foo") + function = sc.GetFunction() + self.assertTrue(function) + self.function(function, target) + + def function (self, function, target): + """Iterate over instructions in function and place a breakpoint on delay slot instruction""" + # Get the list of all instructions in the function + insts = function.GetInstructions(target) + print(insts) + i = 0 + for inst in insts: + if (inst.HasDelaySlot()): + # Remember the address of branch instruction. + branchinstaddress = inst.GetAddress().GetLoadAddress(target) + + # Get next instruction i.e delay slot instruction. + delayinst = insts.GetInstructionAtIndex(i+1) + delayinstaddr = delayinst.GetAddress().GetLoadAddress(target) + + # Set breakpoint on delay slot instruction + breakpoint = target.BreakpointCreateByAddress(delayinstaddr) + + # Verify the breakpoint. + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + # Get the location from breakpoint + location = breakpoint.GetLocationAtIndex(0) + + # Get the address where breakpoint is actually set. + bpaddr = location.GetLoadAddress() + + # Breakpoint address should be adjusted to the address of branch instruction. + self.assertTrue(branchinstaddress == bpaddr) + i += 1 + else: + i += 1 + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/TestAvoidBreakpointInDelaySlot.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/main.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/main.c (revision 295598) @@ -0,0 +1,21 @@ +#include + +foo (int a, int b) +{ + int c; + if (a<=b) + c=b-a; + else + c=b+a; + return c; +} + +int main() +{ + int a=7, b=8, c; + + c = foo(a, b); + +return 0; +} + Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_in_delayslot/main.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 295597) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 295598) @@ -1,2776 +1,2783 @@ """ LLDB module which provides the abstract base class of lldb test case. The concrete subclass can override lldbtest.TesBase in order to inherit the common behavior for unitest.TestCase.setUp/tearDown implemented in this file. The subclass should override the attribute mydir in order for the python runtime to locate the individual test cases when running as part of a large test suite or when running each test case as a separate python invocation. ./dotest.py provides a test driver which sets up the environment to run the entire of part of the test suite . Example: # Exercises the test suite in the types directory.... /Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types ... Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' Command invoked: python ./dotest.py -A x86_64 types compilers=['clang'] Configuration: arch=x86_64 compiler=clang ---------------------------------------------------------------------- Collected 72 tests ........................................................................ ---------------------------------------------------------------------- Ran 72 tests in 135.468s OK $ """ from __future__ import print_function from __future__ import absolute_import # System modules import abc import collections from distutils.version import LooseVersion import gc import glob import inspect import os, sys, traceback import os.path import re import signal from subprocess import * import time import types # Third-party modules import unittest2 from six import add_metaclass from six import StringIO as SixStringIO from six.moves.urllib import parse as urlparse import six # LLDB modules import lldb from . import configuration from . import lldbtest_config from . import lldbutil from . import test_categories from .result_formatter import EventBuilder # dosep.py starts lots and lots of dotest instances # This option helps you find if two (or more) dotest instances are using the same # directory at the same time # Enable it to cause test failures and stderr messages if dotest instances try to run in # the same directory simultaneously # it is disabled by default because it litters the test directories with ".dirlock" files debug_confirm_directory_exclusivity = False # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables # LLDB_COMMAND_TRACE and LLDB_DO_CLEANUP are set from '-t' and '-r dir' options. # By default, traceAlways is False. if "LLDB_COMMAND_TRACE" in os.environ and os.environ["LLDB_COMMAND_TRACE"]=="YES": traceAlways = True else: traceAlways = False # By default, doCleanup is True. if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"]=="NO": doCleanup = False else: doCleanup = True # # Some commonly used assert messages. # COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" CURRENT_EXECUTABLE_SET = "Current executable set successfully" PROCESS_IS_VALID = "Process is valid" PROCESS_KILLED = "Process is killed successfully" PROCESS_EXITED = "Process exited successfully" PROCESS_STOPPED = "Process status should be stopped" RUN_SUCCEEDED = "Process is launched successfully" RUN_COMPLETED = "Process exited successfully" BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" BREAKPOINT_CREATED = "Breakpoint created successfully" BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." OBJECT_PRINTED_CORRECTLY = "Object printed correctly" SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" STEP_OUT_SUCCEEDED = "Thread step-out succeeded" STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" VALID_BREAKPOINT = "Got a valid breakpoint" VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" VALID_FILESPEC = "Got a valid filespec" VALID_MODULE = "Got a valid module" VALID_PROCESS = "Got a valid process" VALID_SYMBOL = "Got a valid symbol" VALID_TARGET = "Got a valid target" VALID_PLATFORM = "Got a valid platform" VALID_TYPE = "Got a valid type" VALID_VARIABLE = "Got a valid variable" VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" WATCHPOINT_CREATED = "Watchpoint created successfully" def CMD_MSG(str): '''A generic "Command '%s' returns successfully" message generator.''' return "Command '%s' returns successfully" % str def COMPLETION_MSG(str_before, str_after): '''A generic message generator for the completion mechanism.''' return "'%s' successfully completes to '%s'" % (str_before, str_after) def EXP_MSG(str, exe): '''A generic "'%s' returns expected result" message generator if exe. Otherwise, it generates "'%s' matches expected result" message.''' return "'%s' %s expected result" % (str, 'returns' if exe else 'matches') def SETTING_MSG(setting): '''A generic "Value of setting '%s' is correct" message generator.''' return "Value of setting '%s' is correct" % setting def EnvArray(): """Returns an env variable array from the os.environ map object.""" return list(map(lambda k,v: k+"="+v, list(os.environ.keys()), list(os.environ.values()))) def line_number(filename, string_to_match): """Helper function to return the line number of the first matched string.""" with open(filename, 'r') as f: for i, line in enumerate(f): if line.find(string_to_match) != -1: # Found our match. return i+1 raise Exception("Unable to find '%s' within file %s" % (string_to_match, filename)) def pointer_size(): """Return the pointer size of the host system.""" import ctypes a_pointer = ctypes.c_void_p(0xffff) return 8 * ctypes.sizeof(a_pointer) def is_exe(fpath): """Returns true if fpath is an executable.""" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def which(program): """Returns the full path to a program; None otherwise.""" fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None class recording(SixStringIO): """ A nice little context manager for recording the debugger interactions into our session object. If trace flag is ON, it also emits the interactions into the stderr. """ def __init__(self, test, trace): """Create a SixStringIO instance; record the session obj and trace flag.""" SixStringIO.__init__(self) # The test might not have undergone the 'setUp(self)' phase yet, so that # the attribute 'session' might not even exist yet. self.session = getattr(test, "session", None) if test else None self.trace = trace def __enter__(self): """ Context management protocol on entry to the body of the with statement. Just return the SixStringIO object. """ return self def __exit__(self, type, value, tb): """ Context management protocol on exit from the body of the with statement. If trace is ON, it emits the recordings into stderr. Always add the recordings to our session object. And close the SixStringIO object, too. """ if self.trace: print(self.getvalue(), file=sys.stderr) if self.session: print(self.getvalue(), file=self.session) self.close() @add_metaclass(abc.ABCMeta) class _BaseProcess(object): @abc.abstractproperty def pid(self): """Returns process PID if has been launched already.""" @abc.abstractmethod def launch(self, executable, args): """Launches new process with given executable and args.""" @abc.abstractmethod def terminate(self): """Terminates previously launched process..""" class _LocalProcess(_BaseProcess): def __init__(self, trace_on): self._proc = None self._trace_on = trace_on self._delayafterterminate = 0.1 @property def pid(self): return self._proc.pid def launch(self, executable, args): self._proc = Popen([executable] + args, stdout = open(os.devnull) if not self._trace_on else None, stdin = PIPE) def terminate(self): if self._proc.poll() == None: # Terminate _proc like it does the pexpect signals_to_try = [sig for sig in ['SIGHUP', 'SIGCONT', 'SIGINT'] if sig in dir(signal)] for sig in signals_to_try: try: self._proc.send_signal(getattr(signal, sig)) time.sleep(self._delayafterterminate) if self._proc.poll() != None: return except ValueError: pass # Windows says SIGINT is not a valid signal to send self._proc.terminate() time.sleep(self._delayafterterminate) if self._proc.poll() != None: return self._proc.kill() time.sleep(self._delayafterterminate) def poll(self): return self._proc.poll() class _RemoteProcess(_BaseProcess): def __init__(self, install_remote): self._pid = None self._install_remote = install_remote @property def pid(self): return self._pid def launch(self, executable, args): if self._install_remote: src_path = executable dst_path = lldbutil.append_to_process_working_directory(os.path.basename(executable)) dst_file_spec = lldb.SBFileSpec(dst_path, False) err = lldb.remote_platform.Install(lldb.SBFileSpec(src_path, True), dst_file_spec) if err.Fail(): raise Exception("remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) else: dst_path = executable dst_file_spec = lldb.SBFileSpec(executable, False) launch_info = lldb.SBLaunchInfo(args) launch_info.SetExecutableFile(dst_file_spec, True) launch_info.SetWorkingDirectory(lldb.remote_platform.GetWorkingDirectory()) # Redirect stdout and stderr to /dev/null launch_info.AddSuppressFileAction(1, False, True) launch_info.AddSuppressFileAction(2, False, True) err = lldb.remote_platform.Launch(launch_info) if err.Fail(): raise Exception("remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)) self._pid = launch_info.GetProcessID() def terminate(self): lldb.remote_platform.Kill(self._pid) # From 2.7's subprocess.check_output() convenience function. # Return a tuple (stdoutdata, stderrdata). def system(commands, **kwargs): r"""Run an os command with arguments and return its output as a byte string. If the exit code was non-zero it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute and output in the output attribute. The arguments are the same as for the Popen constructor. Example: >>> check_output(["ls", "-l", "/dev/null"]) 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' The stdout argument is not allowed as it is used internally. To capture standard error in the result, use stderr=STDOUT. >>> check_output(["/bin/sh", "-c", ... "ls -l non_existent_file ; exit 0"], ... stderr=STDOUT) 'ls: non_existent_file: No such file or directory\n' """ # Assign the sender object to variable 'test' and remove it from kwargs. test = kwargs.pop('sender', None) # [['make', 'clean', 'foo'], ['make', 'foo']] -> ['make clean foo', 'make foo'] commandList = [' '.join(x) for x in commands] output = "" error = "" for shellCommand in commandList: if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') if 'shell' in kwargs and kwargs['shell']==False: raise ValueError('shell=False not allowed') process = Popen(shellCommand, stdout=PIPE, stderr=PIPE, shell=True, universal_newlines=True, **kwargs) pid = process.pid this_output, this_error = process.communicate() retcode = process.poll() # Enable trace on failure return while tracking down FreeBSD buildbot issues trace = traceAlways if not trace and retcode and sys.platform.startswith("freebsd"): trace = True with recording(test, trace) as sbuf: print(file=sbuf) print("os command:", shellCommand, file=sbuf) print("with pid:", pid, file=sbuf) print("stdout:", this_output, file=sbuf) print("stderr:", this_error, file=sbuf) print("retcode:", retcode, file=sbuf) print(file=sbuf) if retcode: cmd = kwargs.get("args") if cmd is None: cmd = shellCommand raise CalledProcessError(retcode, cmd) output = output + this_output error = error + this_error return (output, error) def getsource_if_available(obj): """ Return the text of the source code for an object if available. Otherwise, a print representation is returned. """ import inspect try: return inspect.getsource(obj) except: return repr(obj) def builder_module(): if sys.platform.startswith("freebsd"): return __import__("builder_freebsd") if sys.platform.startswith("netbsd"): return __import__("builder_netbsd") return __import__("builder_" + sys.platform) def run_adb_command(cmd, device_id): device_id_args = [] if device_id: device_id_args = ["-s", device_id] full_cmd = ["adb"] + device_id_args + cmd p = Popen(full_cmd, stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() return p.returncode, stdout, stderr def append_android_envs(dictionary): if dictionary is None: dictionary = {} dictionary["OS"] = "Android" if android_device_api() >= 16: dictionary["PIE"] = 1 return dictionary def target_is_android(): if not hasattr(target_is_android, 'result'): triple = lldb.DBG.GetSelectedPlatform().GetTriple() match = re.match(".*-.*-.*-android", triple) target_is_android.result = match is not None return target_is_android.result def android_device_api(): if not hasattr(android_device_api, 'result'): assert configuration.lldb_platform_url is not None device_id = None parsed_url = urlparse.urlparse(configuration.lldb_platform_url) host_name = parsed_url.netloc.split(":")[0] if host_name != 'localhost': device_id = host_name if device_id.startswith('[') and device_id.endswith(']'): device_id = device_id[1:-1] retcode, stdout, stderr = run_adb_command( ["shell", "getprop", "ro.build.version.sdk"], device_id) if retcode == 0: android_device_api.result = int(stdout) else: raise LookupError( ">>> Unable to determine the API level of the Android device.\n" ">>> stdout:\n%s\n" ">>> stderr:\n%s\n" % (stdout, stderr)) return android_device_api.result def check_expected_version(comparison, expected, actual): def fn_leq(x,y): return x <= y def fn_less(x,y): return x < y def fn_geq(x,y): return x >= y def fn_greater(x,y): return x > y def fn_eq(x,y): return x == y def fn_neq(x,y): return x != y op_lookup = { "==": fn_eq, "=": fn_eq, "!=": fn_neq, "<>": fn_neq, ">": fn_greater, "<": fn_less, ">=": fn_geq, "<=": fn_leq } expected_str = '.'.join([str(x) for x in expected]) actual_str = '.'.join([str(x) for x in actual]) return op_lookup[comparison](LooseVersion(actual_str), LooseVersion(expected_str)) # # Decorators for categorizing test cases. # from functools import wraps def add_test_categories(cat): """Add test categories to a TestCase method""" cat = test_categories.validate(cat, True) def impl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@add_test_categories can only be used to decorate a test method") if hasattr(func, "categories"): cat.extend(func.categories) func.categories = cat return func return impl def benchmarks_test(func): """Decorate the item as a benchmarks test.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@benchmarks_test can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): self.skipTest("benchmarks test") return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__benchmarks_test__ = True return wrapper def no_debug_info_test(func): """Decorate the item as a test what don't use any debug info. If this annotation is specified then the test runner won't generate a separate test for each debug info format. """ if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@no_debug_info_test can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__no_debug_info_test__ = True return wrapper def debugserver_test(func): """Decorate the item as a debugserver test.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@debugserver_test can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): if configuration.dont_do_debugserver_test: self.skipTest("debugserver tests") return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__debugserver_test__ = True return wrapper def llgs_test(func): """Decorate the item as a lldb-server test.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@llgs_test can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): if configuration.dont_do_llgs_test: self.skipTest("llgs tests") return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__llgs_test__ = True return wrapper def not_remote_testsuite_ready(func): """Decorate the item as a test which is not ready yet for remote testsuite.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@not_remote_testsuite_ready can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): if lldb.remote_platform: self.skipTest("not ready for remote testsuite") return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__not_ready_for_remote_testsuite_test__ = True return wrapper def expectedFailure(expected_fn, bugnumber=None): def expectedFailure_impl(func): @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] if expected_fn(self): if configuration.results_formatter_object is not None: # Mark this test as expected to fail. configuration.results_formatter_object.handle_event( EventBuilder.event_for_mark_test_expected_failure(self)) xfail_func = unittest2.expectedFailure(func) xfail_func(*args, **kwargs) else: func(*args, **kwargs) return wrapper # if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments # return decorator in this case, so it will be used to decorating original method if six.callable(bugnumber): return expectedFailure_impl(bugnumber) else: return expectedFailure_impl # You can also pass not_in(list) to reverse the sense of the test for the arguments that # are simple lists, namely oslist, compiler, and debug_info. def not_in(iterable): return lambda x : x not in iterable def check_list_or_lambda(list_or_lambda, value): if six.callable(list_or_lambda): return list_or_lambda(value) elif isinstance(list_or_lambda, list): for item in list_or_lambda: if value in item: return True return False elif isinstance(list_or_lambda, str): return value is None or value in list_or_lambda else: return list_or_lambda is None or value is None or list_or_lambda == value +def matchArchitectures(archs, actual_arch): + retype = type(re.compile('hello, world')) + list_passes = isinstance(archs, list) and actual_arch in archs + basestring_passes = isinstance(archs, six.string_types) and actual_arch == archs + regex_passes = isinstance(archs, retype) and re.match(archs, actual_arch) + + return (list_passes or basestring_passes or regex_passes) + # provide a function to xfail on defined oslist, compiler version, and archs # if none is specified for any argument, that argument won't be checked and thus means for all # for example, # @expectedFailureAll, xfail for all platform/compiler/arch, # @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture # @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386 def expectedFailureAll(bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None): def fn(self): oslist_passes = check_list_or_lambda(oslist, self.getPlatform()) hostoslist_passes = check_list_or_lambda(hostoslist, getHostPlatform()) compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version) arch_passes = check_list_or_lambda(archs, self.getArchitecture()) triple_passes = triple is None or re.match(triple, lldb.DBG.GetSelectedPlatform().GetTriple()) debug_info_passes = check_list_or_lambda(debug_info, self.debug_info) swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version)) py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info) return (oslist_passes and hostoslist_passes and compiler_passes and arch_passes and triple_passes and debug_info_passes and swig_version_passes and py_version_passes) return expectedFailure(fn, bugnumber) def expectedFailureDwarf(bugnumber=None): return expectedFailureAll(bugnumber=bugnumber, debug_info="dwarf") def expectedFailureDwo(bugnumber=None): return expectedFailureAll(bugnumber=bugnumber, debug_info="dwo") def expectedFailureDsym(bugnumber=None): return expectedFailureAll(bugnumber=bugnumber, debug_info="dsym") def expectedFailureCompiler(compiler, compiler_version=None, bugnumber=None): if compiler_version is None: compiler_version=['=', None] return expectedFailureAll(bugnumber=bugnumber, compiler=compiler, compiler_version=compiler_version) # to XFAIL a specific clang versions, try this # @expectedFailureClang('bugnumber', ['<=', '3.4']) def expectedFailureClang(bugnumber=None, compiler_version=None): return expectedFailureCompiler('clang', compiler_version, bugnumber) def expectedFailureGcc(bugnumber=None, compiler_version=None): return expectedFailureCompiler('gcc', compiler_version, bugnumber) def expectedFailureIcc(bugnumber=None): return expectedFailureCompiler('icc', None, bugnumber) def expectedFailureArch(arch, bugnumber=None): def fn(self): return arch in self.getArchitecture() return expectedFailure(fn, bugnumber) def expectedFailurei386(bugnumber=None): return expectedFailureArch('i386', bugnumber) def expectedFailurex86_64(bugnumber=None): return expectedFailureArch('x86_64', bugnumber) def expectedFailureOS(oslist, bugnumber=None, compilers=None, debug_info=None, archs=None): def fn(self): return (self.getPlatform() in oslist and self.expectedCompiler(compilers) and (archs is None or self.getArchitecture() in archs) and (debug_info is None or self.debug_info in debug_info)) return expectedFailure(fn, bugnumber) def expectedFailureHostOS(oslist, bugnumber=None, compilers=None): def fn(self): return (getHostPlatform() in oslist and self.expectedCompiler(compilers)) return expectedFailure(fn, bugnumber) def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None): # For legacy reasons, we support both "darwin" and "macosx" as OS X triples. return expectedFailureOS(getDarwinOSTriples(), bugnumber, compilers, debug_info=debug_info) def expectedFailureFreeBSD(bugnumber=None, compilers=None, debug_info=None): return expectedFailureOS(['freebsd'], bugnumber, compilers, debug_info=debug_info) def expectedFailureLinux(bugnumber=None, compilers=None, debug_info=None, archs=None): return expectedFailureOS(['linux'], bugnumber, compilers, debug_info=debug_info, archs=archs) def expectedFailureNetBSD(bugnumber=None, compilers=None, debug_info=None): return expectedFailureOS(['netbsd'], bugnumber, compilers, debug_info=debug_info) def expectedFailureWindows(bugnumber=None, compilers=None, debug_info=None): return expectedFailureOS(['windows'], bugnumber, compilers, debug_info=debug_info) def expectedFailureHostWindows(bugnumber=None, compilers=None): return expectedFailureHostOS(['windows'], bugnumber, compilers) def matchAndroid(api_levels=None, archs=None): def match(self): if not target_is_android(): return False if archs is not None and self.getArchitecture() not in archs: return False if api_levels is not None and android_device_api() not in api_levels: return False return True return match def expectedFailureAndroid(bugnumber=None, api_levels=None, archs=None): """ Mark a test as xfail for Android. Arguments: bugnumber - The LLVM pr associated with the problem. api_levels - A sequence of numbers specifying the Android API levels for which a test is expected to fail. None means all API level. arch - A sequence of architecture names specifying the architectures for which a test is expected to fail. None means all architectures. """ return expectedFailure(matchAndroid(api_levels, archs), bugnumber) # Flakey tests get two chances to run. If they fail the first time round, the result formatter # makes sure it is run one more time. def expectedFlakey(expected_fn, bugnumber=None): def expectedFailure_impl(func): @wraps(func) def wrapper(*args, **kwargs): self = args[0] if expected_fn(self): # Send event marking test as explicitly eligible for rerunning. if configuration.results_formatter_object is not None: # Mark this test as rerunnable. configuration.results_formatter_object.handle_event( EventBuilder.event_for_mark_test_rerun_eligible(self)) func(*args, **kwargs) return wrapper # if bugnumber is not-callable(incluing None), that means decorator function is called with optional arguments # return decorator in this case, so it will be used to decorating original method if six.callable(bugnumber): return expectedFailure_impl(bugnumber) else: return expectedFailure_impl def expectedFlakeyDwarf(bugnumber=None): def fn(self): return self.debug_info == "dwarf" return expectedFlakey(fn, bugnumber) def expectedFlakeyDsym(bugnumber=None): def fn(self): return self.debug_info == "dwarf" return expectedFlakey(fn, bugnumber) def expectedFlakeyOS(oslist, bugnumber=None, compilers=None): def fn(self): return (self.getPlatform() in oslist and self.expectedCompiler(compilers)) return expectedFlakey(fn, bugnumber) def expectedFlakeyDarwin(bugnumber=None, compilers=None): # For legacy reasons, we support both "darwin" and "macosx" as OS X triples. return expectedFlakeyOS(getDarwinOSTriples(), bugnumber, compilers) def expectedFlakeyFreeBSD(bugnumber=None, compilers=None): return expectedFlakeyOS(['freebsd'], bugnumber, compilers) def expectedFlakeyLinux(bugnumber=None, compilers=None): return expectedFlakeyOS(['linux'], bugnumber, compilers) def expectedFlakeyNetBSD(bugnumber=None, compilers=None): return expectedFlakeyOS(['netbsd'], bugnumber, compilers) def expectedFlakeyCompiler(compiler, compiler_version=None, bugnumber=None): if compiler_version is None: compiler_version=['=', None] def fn(self): return compiler in self.getCompiler() and self.expectedCompilerVersion(compiler_version) return expectedFlakey(fn, bugnumber) # @expectedFlakeyClang('bugnumber', ['<=', '3.4']) def expectedFlakeyClang(bugnumber=None, compiler_version=None): return expectedFlakeyCompiler('clang', compiler_version, bugnumber) # @expectedFlakeyGcc('bugnumber', ['<=', '3.4']) def expectedFlakeyGcc(bugnumber=None, compiler_version=None): return expectedFlakeyCompiler('gcc', compiler_version, bugnumber) def expectedFlakeyAndroid(bugnumber=None, api_levels=None, archs=None): return expectedFlakey(matchAndroid(api_levels, archs), bugnumber) def skipIfRemote(func): """Decorate the item to skip tests if testing remotely.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfRemote can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case if lldb.remote_platform: self = args[0] self.skipTest("skip on remote platform") else: func(*args, **kwargs) return wrapper def skipUnlessListedRemote(remote_list=None): def myImpl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfRemote can only be used to decorate a " "test method") @wraps(func) def wrapper(*args, **kwargs): if remote_list and lldb.remote_platform: self = args[0] triple = self.dbg.GetSelectedPlatform().GetTriple() for r in remote_list: if r in triple: func(*args, **kwargs) return self.skipTest("skip on remote platform %s" % str(triple)) else: func(*args, **kwargs) return wrapper return myImpl def skipIfRemoteDueToDeadlock(func): """Decorate the item to skip tests if testing remotely due to the test deadlocking.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfRemote can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case if lldb.remote_platform: self = args[0] self.skipTest("skip on remote platform (deadlocks)") else: func(*args, **kwargs) return wrapper def skipIfNoSBHeaders(func): """Decorate the item to mark tests that should be skipped when LLDB is built with no SB API headers.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfNoSBHeaders can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] if sys.platform.startswith("darwin"): header = os.path.join(os.environ["LLDB_LIB_DIR"], 'LLDB.framework', 'Versions','Current','Headers','LLDB.h') else: header = os.path.join(os.environ["LLDB_SRC"], "include", "lldb", "API", "LLDB.h") platform = sys.platform if not os.path.exists(header): self.skipTest("skip because LLDB.h header not found") else: func(*args, **kwargs) return wrapper def skipIfiOSSimulator(func): """Decorate the item to skip tests that should be skipped on the iOS Simulator.""" return unittest2.skipIf(configuration.lldb_platform_name == 'ios-simulator', 'skip on the iOS Simulator')(func) def skipIfFreeBSD(func): """Decorate the item to skip tests that should be skipped on FreeBSD.""" return skipIfPlatform(["freebsd"])(func) def skipIfNetBSD(func): """Decorate the item to skip tests that should be skipped on NetBSD.""" return skipIfPlatform(["netbsd"])(func) def getDarwinOSTriples(): return ['darwin', 'macosx', 'ios'] def skipIfDarwin(func): """Decorate the item to skip tests that should be skipped on Darwin.""" return skipIfPlatform(getDarwinOSTriples())(func) def skipIfLinux(func): """Decorate the item to skip tests that should be skipped on Linux.""" return skipIfPlatform(["linux"])(func) def skipUnlessHostLinux(func): """Decorate the item to skip tests that should be skipped on any non Linux host.""" return skipUnlessHostPlatform(["linux"])(func) def skipIfWindows(func): """Decorate the item to skip tests that should be skipped on Windows.""" return skipIfPlatform(["windows"])(func) def skipIfHostWindows(func): """Decorate the item to skip tests that should be skipped on Windows.""" return skipIfHostPlatform(["windows"])(func) def skipUnlessWindows(func): """Decorate the item to skip tests that should be skipped on any non-Windows platform.""" return skipUnlessPlatform(["windows"])(func) def skipUnlessDarwin(func): """Decorate the item to skip tests that should be skipped on any non Darwin platform.""" return skipUnlessPlatform(getDarwinOSTriples())(func) def skipUnlessGoInstalled(func): """Decorate the item to skip tests when no Go compiler is available.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfGcc can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] compiler = self.getGoCompilerVersion() if not compiler: self.skipTest("skipping because go compiler not found") else: # Ensure the version is the minimum version supported by # the LLDB go support. match_version = re.search(r"(\d+\.\d+(\.\d+)?)", compiler) if not match_version: # Couldn't determine version. self.skipTest( "skipping because go version could not be parsed " "out of {}".format(compiler)) else: from distutils.version import StrictVersion min_strict_version = StrictVersion("1.4.0") compiler_strict_version = StrictVersion(match_version.group(1)) if compiler_strict_version < min_strict_version: self.skipTest( "skipping because available go version ({}) does " "not meet minimum required go version ({})".format( compiler_strict_version, min_strict_version)) func(*args, **kwargs) return wrapper def getPlatform(): """Returns the target platform which the tests are running on.""" platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] if platform.startswith('freebsd'): platform = 'freebsd' elif platform.startswith('netbsd'): platform = 'netbsd' return platform def getHostPlatform(): """Returns the host platform running the test suite.""" # Attempts to return a platform name matching a target Triple platform. if sys.platform.startswith('linux'): return 'linux' elif sys.platform.startswith('win32'): return 'windows' elif sys.platform.startswith('darwin'): return 'darwin' elif sys.platform.startswith('freebsd'): return 'freebsd' elif sys.platform.startswith('netbsd'): return 'netbsd' else: return sys.platform def platformIsDarwin(): """Returns true if the OS triple for the selected platform is any valid apple OS""" return getPlatform() in getDarwinOSTriples() def skipIfHostIncompatibleWithRemote(func): """Decorate the item to skip tests if binaries built on this host are incompatible.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfHostIncompatibleWithRemote can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] host_arch = self.getLldbArchitecture() host_platform = getHostPlatform() target_arch = self.getArchitecture() target_platform = 'darwin' if self.platformIsDarwin() else self.getPlatform() if not (target_arch == 'x86_64' and host_arch == 'i386') and host_arch != target_arch: self.skipTest("skipping because target %s is not compatible with host architecture %s" % (target_arch, host_arch)) elif target_platform != host_platform: self.skipTest("skipping because target is %s but host is %s" % (target_platform, host_platform)) else: func(*args, **kwargs) return wrapper def skipIfHostPlatform(oslist): """Decorate the item to skip tests if running on one of the listed host platforms.""" return unittest2.skipIf(getHostPlatform() in oslist, "skip on %s" % (", ".join(oslist))) def skipUnlessHostPlatform(oslist): """Decorate the item to skip tests unless running on one of the listed host platforms.""" return unittest2.skipUnless(getHostPlatform() in oslist, "requires on of %s" % (", ".join(oslist))) -def skipUnlessArch(archlist): +def skipUnlessArch(archs): """Decorate the item to skip tests unless running on one of the listed architectures.""" def myImpl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipUnlessArch can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): self = args[0] - if self.getArchitecture() not in archlist: - self.skipTest("skipping for architecture %s (requires one of %s)" % - (self.getArchitecture(), ", ".join(archlist))) + if not matchArchitectures(archs, self.getArchitecture()): + self.skipTest("skipping for architecture %s" % (self.getArchitecture())) else: func(*args, **kwargs) return wrapper return myImpl def skipIfPlatform(oslist): """Decorate the item to skip tests if running on one of the listed platforms.""" return unittest2.skipIf(getPlatform() in oslist, "skip on %s" % (", ".join(oslist))) def skipUnlessPlatform(oslist): """Decorate the item to skip tests unless running on one of the listed platforms.""" return unittest2.skipUnless(getPlatform() in oslist, "requires on of %s" % (", ".join(oslist))) def skipIfLinuxClang(func): """Decorate the item to skip tests that should be skipped if building on Linux with clang. """ if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfLinuxClang can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] compiler = self.getCompiler() platform = self.getPlatform() if "clang" in compiler and platform == "linux": self.skipTest("skipping because Clang is used on Linux") else: func(*args, **kwargs) return wrapper # provide a function to skip on defined oslist, compiler version, and archs # if none is specified for any argument, that argument won't be checked and thus means for all # for example, # @skipIf, skip for all platform/compiler/arch, # @skipIf(compiler='gcc'), skip for gcc on all platform/architecture # @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 # TODO: refactor current code, to make skipIfxxx functions to call this function def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None, remote=None): def fn(self): oslist_passes = check_list_or_lambda(oslist, self.getPlatform()) compiler_passes = check_list_or_lambda(self.getCompiler(), compiler) and self.expectedCompilerVersion(compiler_version) arch_passes = check_list_or_lambda(archs, self.getArchitecture()) debug_info_passes = check_list_or_lambda(debug_info, self.debug_info) swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version)) py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info) remote_passes = (remote is None) or (remote == (lldb.remote_platform is not None)) return (oslist_passes and compiler_passes and arch_passes and debug_info_passes and swig_version_passes and py_version_passes and remote_passes) local_vars = locals() args = [x for x in inspect.getargspec(skipIf).args] arg_vals = [eval(x, globals(), local_vars) for x in args] args = [x for x in zip(args, arg_vals) if x[1] is not None] reasons = ['%s=%s' % (x, str(y)) for (x,y) in args] return skipTestIfFn(fn, bugnumber, skipReason='skipping because ' + ' && '.join(reasons)) def skipIfDebugInfo(bugnumber=None, debug_info=None): return skipIf(bugnumber=bugnumber, debug_info=debug_info) def skipIfDWO(bugnumber=None): return skipIfDebugInfo(bugnumber, ["dwo"]) def skipIfDwarf(bugnumber=None): return skipIfDebugInfo(bugnumber, ["dwarf"]) def skipIfDsym(bugnumber=None): return skipIfDebugInfo(bugnumber, ["dsym"]) def skipTestIfFn(expected_fn, bugnumber=None, skipReason=None): def skipTestIfFn_impl(func): @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] if expected_fn(self): self.skipTest(skipReason) else: func(*args, **kwargs) return wrapper if six.callable(bugnumber): return skipTestIfFn_impl(bugnumber) else: return skipTestIfFn_impl def skipIfGcc(func): """Decorate the item to skip tests that should be skipped if building with gcc .""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfGcc can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] compiler = self.getCompiler() if "gcc" in compiler: self.skipTest("skipping because gcc is the test compiler") else: func(*args, **kwargs) return wrapper def skipIfIcc(func): """Decorate the item to skip tests that should be skipped if building with icc .""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfIcc can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] compiler = self.getCompiler() if "icc" in compiler: self.skipTest("skipping because icc is the test compiler") else: func(*args, **kwargs) return wrapper def skipIfi386(func): """Decorate the item to skip tests that should be skipped if building 32-bit.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfi386 can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] if "i386" == self.getArchitecture(): self.skipTest("skipping because i386 is not a supported architecture") else: func(*args, **kwargs) return wrapper def skipIfTargetAndroid(api_levels=None, archs=None): """Decorator to skip tests when the target is Android. Arguments: api_levels - The API levels for which the test should be skipped. If it is None, then the test will be skipped for all API levels. arch - A sequence of architecture names specifying the architectures for which a test is skipped. None means all architectures. """ def myImpl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipIfTargetAndroid can only be used to " "decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case self = args[0] if matchAndroid(api_levels, archs)(self): self.skipTest("skiped on Android target with API %d and architecture %s" % (android_device_api(), self.getArchitecture())) func(*args, **kwargs) return wrapper return myImpl def skipUnlessCompilerRt(func): """Decorate the item to skip tests if testing remotely.""" if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception("@skipUnless can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): from unittest2 import case import os.path compilerRtPath = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "llvm","projects","compiler-rt") print(compilerRtPath) if not os.path.exists(compilerRtPath): self = args[0] self.skipTest("skip if compiler-rt not found") else: func(*args, **kwargs) return wrapper class _PlatformContext(object): """Value object class which contains platform-specific options.""" def __init__(self, shlib_environment_var, shlib_prefix, shlib_extension): self.shlib_environment_var = shlib_environment_var self.shlib_prefix = shlib_prefix self.shlib_extension = shlib_extension class Base(unittest2.TestCase): """ Abstract base for performing lldb (see TestBase) or other generic tests (see BenchBase for one example). lldbtest.Base works with the test driver to accomplish things. """ # The concrete subclass should override this attribute. mydir = None # Keep track of the old current working directory. oldcwd = None @staticmethod def compute_mydir(test_file): '''Subclasses should call this function to correctly calculate the required "mydir" attribute as follows: mydir = TestBase.compute_mydir(__file__)''' test_dir = os.path.dirname(test_file) return test_dir[len(os.environ["LLDB_TEST"])+1:] def TraceOn(self): """Returns True if we are in trace mode (tracing detailed test execution).""" return traceAlways @classmethod def setUpClass(cls): """ Python unittest framework class setup fixture. Do current directory manipulation. """ # Fail fast if 'mydir' attribute is not overridden. if not cls.mydir or len(cls.mydir) == 0: raise Exception("Subclasses must override the 'mydir' attribute.") # Save old working directory. cls.oldcwd = os.getcwd() # Change current working directory if ${LLDB_TEST} is defined. # See also dotest.py which sets up ${LLDB_TEST}. if ("LLDB_TEST" in os.environ): full_dir = os.path.join(os.environ["LLDB_TEST"], cls.mydir) if traceAlways: print("Change dir to:", full_dir, file=sys.stderr) os.chdir(os.path.join(os.environ["LLDB_TEST"], cls.mydir)) if debug_confirm_directory_exclusivity: import lock cls.dir_lock = lock.Lock(os.path.join(full_dir, ".dirlock")) try: cls.dir_lock.try_acquire() # write the class that owns the lock into the lock file cls.dir_lock.handle.write(cls.__name__) except IOError as ioerror: # nothing else should have this directory lock # wait here until we get a lock cls.dir_lock.acquire() # read the previous owner from the lock file lock_id = cls.dir_lock.handle.read() print("LOCK ERROR: {} wants to lock '{}' but it is already locked by '{}'".format(cls.__name__, full_dir, lock_id), file=sys.stderr) raise ioerror # Set platform context. if platformIsDarwin(): cls.platformContext = _PlatformContext('DYLD_LIBRARY_PATH', 'lib', 'dylib') elif getPlatform() in ("freebsd", "linux", "netbsd"): cls.platformContext = _PlatformContext('LD_LIBRARY_PATH', 'lib', 'so') else: cls.platformContext = None @classmethod def tearDownClass(cls): """ Python unittest framework class teardown fixture. Do class-wide cleanup. """ if doCleanup: # First, let's do the platform-specific cleanup. module = builder_module() module.cleanup() # Subclass might have specific cleanup function defined. if getattr(cls, "classCleanup", None): if traceAlways: print("Call class-specific cleanup function for class:", cls, file=sys.stderr) try: cls.classCleanup() except: exc_type, exc_value, exc_tb = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_tb) if debug_confirm_directory_exclusivity: cls.dir_lock.release() del cls.dir_lock # Restore old working directory. if traceAlways: print("Restore dir to:", cls.oldcwd, file=sys.stderr) os.chdir(cls.oldcwd) @classmethod def skipLongRunningTest(cls): """ By default, we skip long running test case. This can be overridden by passing '-l' to the test driver (dotest.py). """ if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ["LLDB_SKIP_LONG_RUNNING_TEST"]: return False else: return True def enableLogChannelsForCurrentTest(self): if len(lldbtest_config.channels) == 0: return # if debug channels are specified in lldbtest_config.channels, # create a new set of log files for every test log_basename = self.getLogBasenameForCurrentTest() # confirm that the file is writeable host_log_path = "{}-host.log".format(log_basename) open(host_log_path, 'w').close() log_enable = "log enable -Tpn -f {} ".format(host_log_path) for channel_with_categories in lldbtest_config.channels: channel_then_categories = channel_with_categories.split(' ', 1) channel = channel_then_categories[0] if len(channel_then_categories) > 1: categories = channel_then_categories[1] else: categories = "default" if channel == "gdb-remote": # communicate gdb-remote categories to debugserver os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories self.ci.HandleCommand(log_enable + channel_with_categories, self.res) if not self.res.Succeeded(): raise Exception('log enable failed (check LLDB_LOG_OPTION env variable)') # Communicate log path name to debugserver & lldb-server server_log_path = "{}-server.log".format(log_basename) open(server_log_path, 'w').close() os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path # Communicate channels to lldb-server os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join(lldbtest_config.channels) if len(lldbtest_config.channels) == 0: return def disableLogChannelsForCurrentTest(self): # close all log files that we opened for channel_and_categories in lldbtest_config.channels: # channel format - [ [ ...]] channel = channel_and_categories.split(' ', 1)[0] self.ci.HandleCommand("log disable " + channel, self.res) if not self.res.Succeeded(): raise Exception('log disable failed (check LLDB_LOG_OPTION env variable)') def setUp(self): """Fixture for unittest test case setup. It works with the test driver to conditionally skip tests and does other initializations.""" #import traceback #traceback.print_stack() if "LIBCXX_PATH" in os.environ: self.libcxxPath = os.environ["LIBCXX_PATH"] else: self.libcxxPath = None if "LLDBMI_EXEC" in os.environ: self.lldbMiExec = os.environ["LLDBMI_EXEC"] else: self.lldbMiExec = None # If we spawn an lldb process for test (via pexpect), do not load the # init file unless told otherwise. if "NO_LLDBINIT" in os.environ and "NO" == os.environ["NO_LLDBINIT"]: self.lldbOption = "" else: self.lldbOption = "--no-lldbinit" # Assign the test method name to self.testMethodName. # # For an example of the use of this attribute, look at test/types dir. # There are a bunch of test cases under test/types and we don't want the # module cacheing subsystem to be confused with executable name "a.out" # used for all the test cases. self.testMethodName = self._testMethodName # This is for the case of directly spawning 'lldb'/'gdb' and interacting # with it using pexpect. self.child = None self.child_prompt = "(lldb) " # If the child is interacting with the embedded script interpreter, # there are two exits required during tear down, first to quit the # embedded script interpreter and second to quit the lldb command # interpreter. self.child_in_script_interpreter = False # These are for customized teardown cleanup. self.dict = None self.doTearDownCleanup = False # And in rare cases where there are multiple teardown cleanups. self.dicts = [] self.doTearDownCleanups = False # List of spawned subproces.Popen objects self.subprocesses = [] # List of forked process PIDs self.forkedProcessPids = [] # Create a string buffer to record the session info, to be dumped into a # test case specific file if test failure is encountered. self.log_basename = self.getLogBasenameForCurrentTest() session_file = "{}.log".format(self.log_basename) # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. self.session = open(session_file, "w") # Optimistically set __errored__, __failed__, __expected__ to False # initially. If the test errored/failed, the session info # (self.session) is then dumped into a session specific file for # diagnosis. self.__cleanup_errored__ = False self.__errored__ = False self.__failed__ = False self.__expected__ = False # We are also interested in unexpected success. self.__unexpected__ = False # And skipped tests. self.__skipped__ = False # See addTearDownHook(self, hook) which allows the client to add a hook # function to be run during tearDown() time. self.hooks = [] # See HideStdout(self). self.sys_stdout_hidden = False if self.platformContext: # set environment variable names for finding shared libraries self.dylibPath = self.platformContext.shlib_environment_var # Create the debugger instance if necessary. try: self.dbg = lldb.DBG except AttributeError: self.dbg = lldb.SBDebugger.Create() if not self.dbg: raise Exception('Invalid debugger instance') # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() self.enableLogChannelsForCurrentTest() #Initialize debug_info self.debug_info = None def setAsync(self, value): """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" old_async = self.dbg.GetAsync() self.dbg.SetAsync(value) self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) def cleanupSubprocesses(self): # Ensure any subprocesses are cleaned up for p in self.subprocesses: p.terminate() del p del self.subprocesses[:] # Ensure any forked processes are cleaned up for pid in self.forkedProcessPids: if os.path.exists("/proc/" + str(pid)): os.kill(pid, signal.SIGTERM) def spawnSubprocess(self, executable, args=[], install_remote=True): """ Creates a subprocess.Popen object with the specified executable and arguments, saves it in self.subprocesses, and returns the object. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ proc = _RemoteProcess(install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) proc.launch(executable, args) self.subprocesses.append(proc) return proc def forkSubprocess(self, executable, args=[]): """ Fork a subprocess with its own group ID. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ child_pid = os.fork() if child_pid == 0: # If more I/O support is required, this can be beefed up. fd = os.open(os.devnull, os.O_RDWR) os.dup2(fd, 1) os.dup2(fd, 2) # This call causes the child to have its of group ID os.setpgid(0,0) os.execvp(executable, [executable] + args) # Give the child time to get through the execvp() call time.sleep(0.1) self.forkedProcessPids.append(child_pid) return child_pid def HideStdout(self): """Hide output to stdout from the user. During test execution, there might be cases where we don't want to show the standard output to the user. For example, self.runCmd(r'''sc print("\n\n\tHello!\n")''') tests whether command abbreviation for 'script' works or not. There is no need to show the 'Hello' output to the user as long as the 'script' command succeeds and we are not in TraceOn() mode (see the '-t' option). In this case, the test method calls self.HideStdout(self) to redirect the sys.stdout to a null device, and restores the sys.stdout upon teardown. Note that you should only call this method at most once during a test case execution. Any subsequent call has no effect at all.""" if self.sys_stdout_hidden: return self.sys_stdout_hidden = True old_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') def restore_stdout(): sys.stdout = old_stdout self.addTearDownHook(restore_stdout) # ======================================================================= # Methods for customized teardown cleanups as well as execution of hooks. # ======================================================================= def setTearDownCleanup(self, dictionary=None): """Register a cleanup action at tearDown() time with a dictinary""" self.dict = dictionary self.doTearDownCleanup = True def addTearDownCleanup(self, dictionary): """Add a cleanup action at tearDown() time with a dictinary""" self.dicts.append(dictionary) self.doTearDownCleanups = True def addTearDownHook(self, hook): """ Add a function to be run during tearDown() time. Hooks are executed in a first come first serve manner. """ if six.callable(hook): with recording(self, traceAlways) as sbuf: print("Adding tearDown hook:", getsource_if_available(hook), file=sbuf) self.hooks.append(hook) return self def deletePexpectChild(self): # This is for the case of directly spawning 'lldb' and interacting with it # using pexpect. if self.child and self.child.isalive(): import pexpect with recording(self, traceAlways) as sbuf: print("tearing down the child process....", file=sbuf) try: if self.child_in_script_interpreter: self.child.sendline('quit()') self.child.expect_exact(self.child_prompt) self.child.sendline('settings set interpreter.prompt-on-quit false') self.child.sendline('quit') self.child.expect(pexpect.EOF) except (ValueError, pexpect.ExceptionPexpect): # child is already terminated pass except OSError as exception: import errno if exception.errno != errno.EIO: # unexpected error raise # child is already terminated pass finally: # Give it one final blow to make sure the child is terminated. self.child.close() def tearDown(self): """Fixture for unittest test case teardown.""" #import traceback #traceback.print_stack() self.deletePexpectChild() # Check and run any hook functions. for hook in reversed(self.hooks): with recording(self, traceAlways) as sbuf: print("Executing tearDown hook:", getsource_if_available(hook), file=sbuf) import inspect hook_argc = len(inspect.getargspec(hook).args) if hook_argc == 0 or getattr(hook,'im_self',None): hook() elif hook_argc == 1: hook(self) else: hook() # try the plain call and hope it works del self.hooks # Perform registered teardown cleanup. if doCleanup and self.doTearDownCleanup: self.cleanup(dictionary=self.dict) # In rare cases where there are multiple teardown cleanups added. if doCleanup and self.doTearDownCleanups: if self.dicts: for dict in reversed(self.dicts): self.cleanup(dictionary=dict) self.disableLogChannelsForCurrentTest() # ========================================================= # Various callbacks to allow introspection of test progress # ========================================================= def markError(self): """Callback invoked when an error (unexpected exception) errored.""" self.__errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("ERROR", file=sbuf) def markCleanupError(self): """Callback invoked when an error occurs while a test is cleaning up.""" self.__cleanup_errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("CLEANUP_ERROR", file=sbuf) def markFailure(self): """Callback invoked when a failure (test assertion failure) occurred.""" self.__failed__ = True with recording(self, False) as sbuf: # False because there's no need to write "FAIL" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("FAIL", file=sbuf) def markExpectedFailure(self,err,bugnumber): """Callback invoked when an expected failure/error occurred.""" self.__expected__ = True with recording(self, False) as sbuf: # False because there's no need to write "expected failure" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber == None: print("expected failure", file=sbuf) else: print("expected failure (problem id:" + str(bugnumber) + ")", file=sbuf) def markSkippedTest(self): """Callback invoked when a test is skipped.""" self.__skipped__ = True with recording(self, False) as sbuf: # False because there's no need to write "skipped test" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. print("skipped test", file=sbuf) def markUnexpectedSuccess(self, bugnumber): """Callback invoked when an unexpected success occurred.""" self.__unexpected__ = True with recording(self, False) as sbuf: # False because there's no need to write "unexpected success" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber == None: print("unexpected success", file=sbuf) else: print("unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf) def getRerunArgs(self): return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) def getLogBasenameForCurrentTest(self, prefix=None): """ returns a partial path that can be used as the beginning of the name of multiple log files pertaining to this test /--.. """ dname = os.path.join(os.environ["LLDB_TEST"], os.environ["LLDB_SESSION_DIRNAME"]) if not os.path.isdir(dname): os.mkdir(dname) compiler = self.getCompiler() if compiler[1] == ':': compiler = compiler[2:] if os.path.altsep is not None: compiler = compiler.replace(os.path.altsep, os.path.sep) fname = "{}-{}-{}".format(self.id(), self.getArchitecture(), "_".join(compiler.split(os.path.sep))) if len(fname) > 200: fname = "{}-{}-{}".format(self.id(), self.getArchitecture(), compiler.split(os.path.sep)[-1]) if prefix is not None: fname = "{}-{}".format(prefix, fname) return os.path.join(dname, fname) def dumpSessionInfo(self): """ Dump the debugger interactions leading to a test error/failure. This allows for more convenient postmortem analysis. See also LLDBTestResult (dotest.py) which is a singlton class derived from TextTestResult and overwrites addError, addFailure, and addExpectedFailure methods to allow us to to mark the test instance as such. """ # We are here because self.tearDown() detected that this test instance # either errored or failed. The lldb.test_result singleton contains # two lists (erros and failures) which get populated by the unittest # framework. Look over there for stack trace information. # # The lists contain 2-tuples of TestCase instances and strings holding # formatted tracebacks. # # See http://docs.python.org/library/unittest.html#unittest.TestResult. # output tracebacks into session pairs = [] if self.__errored__: pairs = configuration.test_result.errors prefix = 'Error' elif self.__cleanup_errored__: pairs = configuration.test_result.cleanup_errors prefix = 'CleanupError' elif self.__failed__: pairs = configuration.test_result.failures prefix = 'Failure' elif self.__expected__: pairs = configuration.test_result.expectedFailures prefix = 'ExpectedFailure' elif self.__skipped__: prefix = 'SkippedTest' elif self.__unexpected__: prefix = 'UnexpectedSuccess' else: prefix = 'Success' if not self.__unexpected__ and not self.__skipped__: for test, traceback in pairs: if test is self: print(traceback, file=self.session) # put footer (timestamp/rerun instructions) into session testMethod = getattr(self, self._testMethodName) if getattr(testMethod, "__benchmarks_test__", False): benchmarks = True else: benchmarks = False import datetime print("Session info generated @", datetime.datetime.now().ctime(), file=self.session) print("To rerun this test, issue the following command from the 'test' directory:\n", file=self.session) print("./dotest.py %s -v %s %s" % (self.getRunOptions(), ('+b' if benchmarks else '-t'), self.getRerunArgs()), file=self.session) self.session.close() del self.session # process the log files log_files_for_this_test = glob.glob(self.log_basename + "*") if prefix != 'Success' or lldbtest_config.log_success: # keep all log files, rename them to include prefix dst_log_basename = self.getLogBasenameForCurrentTest(prefix) for src in log_files_for_this_test: if os.path.isfile(src): dst = src.replace(self.log_basename, dst_log_basename) if os.name == "nt" and os.path.isfile(dst): # On Windows, renaming a -> b will throw an exception if b exists. On non-Windows platforms # it silently replaces the destination. Ultimately this means that atomic renames are not # guaranteed to be possible on Windows, but we need this to work anyway, so just remove the # destination first if it already exists. os.remove(dst) os.rename(src, dst) else: # success! (and we don't want log files) delete log files for log_file in log_files_for_this_test: try: os.unlink(log_file) except: # We've seen consistent unlink failures on Windows, perhaps because the # just-created log file is being scanned by anti-virus. Empirically, this # sleep-and-retry approach allows tests to succeed much more reliably. # Attempts to figure out exactly what process was still holding a file handle # have failed because running instrumentation like Process Monitor seems to # slow things down enough that the problem becomes much less consistent. time.sleep(0.5) os.unlink(log_file) # ==================================================== # Config. methods supported through a plugin interface # (enables reading of the current test configuration) # ==================================================== def getArchitecture(self): """Returns the architecture in effect the test suite is running with.""" module = builder_module() arch = module.getArchitecture() if arch == 'amd64': arch = 'x86_64' return arch def getLldbArchitecture(self): """Returns the architecture of the lldb binary.""" if not hasattr(self, 'lldbArchitecture'): # spawn local process command = [ lldbtest_config.lldbExec, "-o", "file " + lldbtest_config.lldbExec, "-o", "quit" ] output = check_output(command) str = output.decode("utf-8"); for line in str.splitlines(): m = re.search("Current executable set to '.*' \\((.*)\\)\\.", line) if m: self.lldbArchitecture = m.group(1) break return self.lldbArchitecture def getCompiler(self): """Returns the compiler in effect the test suite is running with.""" module = builder_module() return module.getCompiler() def getCompilerBinary(self): """Returns the compiler binary the test suite is running with.""" return self.getCompiler().split()[0] def getCompilerVersion(self): """ Returns a string that represents the compiler version. Supports: llvm, clang. """ from .lldbutil import which version = 'unknown' compiler = self.getCompilerBinary() version_output = system([[which(compiler), "-v"]])[1] for line in version_output.split(os.linesep): m = re.search('version ([0-9\.]+)', line) if m: version = m.group(1) return version def getGoCompilerVersion(self): """ Returns a string that represents the go compiler version, or None if go is not found. """ compiler = which("go") if compiler: version_output = system([[compiler, "version"]])[0] for line in version_output.split(os.linesep): m = re.search('go version (devel|go\\S+)', line) if m: return m.group(1) return None def platformIsDarwin(self): """Returns true if the OS triple for the selected platform is any valid apple OS""" return platformIsDarwin() def getPlatform(self): """Returns the target platform the test suite is running on.""" return getPlatform() def isIntelCompiler(self): """ Returns true if using an Intel (ICC) compiler, false otherwise. """ return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) def expectedCompilerVersion(self, compiler_version): """Returns True iff compiler_version[1] matches the current compiler version. Use compiler_version[0] to specify the operator used to determine if a match has occurred. Any operator other than the following defaults to an equality test: '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' """ if (compiler_version == None): return True operator = str(compiler_version[0]) version = compiler_version[1] if (version == None): return True if (operator == '>'): return self.getCompilerVersion() > version if (operator == '>=' or operator == '=>'): return self.getCompilerVersion() >= version if (operator == '<'): return self.getCompilerVersion() < version if (operator == '<=' or operator == '=<'): return self.getCompilerVersion() <= version if (operator == '!=' or operator == '!' or operator == 'not'): return str(version) not in str(self.getCompilerVersion()) return str(version) in str(self.getCompilerVersion()) def expectedCompiler(self, compilers): """Returns True iff any element of compilers is a sub-string of the current compiler.""" if (compilers == None): return True for compiler in compilers: if compiler in self.getCompiler(): return True return False def expectedArch(self, archs): """Returns True iff any element of archs is a sub-string of the current architecture.""" if (archs == None): return True for arch in archs: if arch in self.getArchitecture(): return True return False def getRunOptions(self): """Command line option for -A and -C to run this test again, called from self.dumpSessionInfo().""" arch = self.getArchitecture() comp = self.getCompiler() if arch: option_str = "-A " + arch else: option_str = "" if comp: option_str += " -C " + comp return option_str # ================================================== # Build methods supported through a plugin interface # ================================================== def getstdlibFlag(self): """ Returns the proper -stdlib flag, or empty if not required.""" if self.platformIsDarwin() or self.getPlatform() == "freebsd": stdlibflag = "-stdlib=libc++" else: # this includes NetBSD stdlibflag = "" return stdlibflag def getstdFlag(self): """ Returns the proper stdflag. """ if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): stdflag = "-std=c++0x" else: stdflag = "-std=c++11" return stdflag def buildDriver(self, sources, exe_name): """ Platform-specific way to build a program that links with LLDB (via the liblldb.so or LLDB.framework). """ stdflag = self.getstdFlag() stdlibflag = self.getstdlibFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if sys.platform.startswith("darwin"): dsym = os.path.join(lib_dir, 'LLDB.framework', 'LLDB') d = {'CXX_SOURCES' : sources, 'EXE' : exe_name, 'CFLAGS_EXTRAS' : "%s %s" % (stdflag, stdlibflag), 'FRAMEWORK_INCLUDES' : "-F%s" % lib_dir, 'LD_EXTRAS' : "%s -Wl,-rpath,%s" % (dsym, lib_dir), } elif sys.platform.rstrip('0123456789') in ('freebsd', 'linux', 'netbsd') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': d = {'CXX_SOURCES' : sources, 'EXE' : exe_name, 'CFLAGS_EXTRAS' : "%s %s -I%s" % (stdflag, stdlibflag, os.path.join(os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS' : "-L%s -llldb" % lib_dir} elif sys.platform.startswith('win'): d = {'CXX_SOURCES' : sources, 'EXE' : exe_name, 'CFLAGS_EXTRAS' : "%s %s -I%s" % (stdflag, stdlibflag, os.path.join(os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS' : "-L%s -lliblldb" % os.environ["LLDB_IMPLIB_DIR"]} if self.TraceOn(): print("Building LLDB Driver (%s) from sources %s" % (exe_name, sources)) self.buildDefault(dictionary=d) def buildLibrary(self, sources, lib_name): """Platform specific way to build a default library. """ stdflag = self.getstdFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if self.platformIsDarwin(): dsym = os.path.join(lib_dir, 'LLDB.framework', 'LLDB') d = {'DYLIB_CXX_SOURCES' : sources, 'DYLIB_NAME' : lib_name, 'CFLAGS_EXTRAS' : "%s -stdlib=libc++" % stdflag, 'FRAMEWORK_INCLUDES' : "-F%s" % lib_dir, 'LD_EXTRAS' : "%s -Wl,-rpath,%s -dynamiclib" % (dsym, lib_dir), } elif self.getPlatform() in ('freebsd', 'linux', 'netbsd') or os.environ.get('LLDB_BUILD_TYPE') == 'Makefile': d = {'DYLIB_CXX_SOURCES' : sources, 'DYLIB_NAME' : lib_name, 'CFLAGS_EXTRAS' : "%s -I%s -fPIC" % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS' : "-shared -L%s -llldb" % lib_dir} elif self.getPlatform() == 'windows': d = {'DYLIB_CXX_SOURCES' : sources, 'DYLIB_NAME' : lib_name, 'CFLAGS_EXTRAS' : "%s -I%s -fPIC" % (stdflag, os.path.join(os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS' : "-shared -l%s\liblldb.lib" % self.os.environ["LLDB_IMPLIB_DIR"]} if self.TraceOn(): print("Building LLDB Library (%s) from sources %s" % (lib_name, sources)) self.buildDefault(dictionary=d) def buildProgram(self, sources, exe_name): """ Platform specific way to build an executable from C/C++ sources. """ d = {'CXX_SOURCES' : sources, 'EXE' : exe_name} self.buildDefault(dictionary=d) def buildDefault(self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build the default binaries.""" module = builder_module() if target_is_android(): dictionary = append_android_envs(dictionary) if not module.buildDefault(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build default binary") def buildDsym(self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dsym info.""" module = builder_module() if not module.buildDsym(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dsym") def buildDwarf(self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dwarf maps.""" module = builder_module() if target_is_android(): dictionary = append_android_envs(dictionary) if not module.buildDwarf(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwarf") def buildDwo(self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build binaries with dwarf maps.""" module = builder_module() if target_is_android(): dictionary = append_android_envs(dictionary) if not module.buildDwo(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwo") def buildGo(self): """Build the default go binary. """ system([[which('go'), 'build -gcflags "-N -l" -o a.out main.go']]) def signBinary(self, binary_path): if sys.platform.startswith("darwin"): codesign_cmd = "codesign --force --sign lldb_codesign %s" % (binary_path) call(codesign_cmd, shell=True) def findBuiltClang(self): """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" paths_to_try = [ "llvm-build/Release+Asserts/x86_64/Release+Asserts/bin/clang", "llvm-build/Debug+Asserts/x86_64/Debug+Asserts/bin/clang", "llvm-build/Release/x86_64/Release/bin/clang", "llvm-build/Debug/x86_64/Debug/bin/clang", ] lldb_root_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..") for p in paths_to_try: path = os.path.join(lldb_root_path, p) if os.path.exists(path): return path # Tries to find clang at the same folder as the lldb path = os.path.join(os.path.dirname(lldbtest_config.lldbExec), "clang") if os.path.exists(path): return path return os.environ["CC"] def getBuildFlags(self, use_cpp11=True, use_libcxx=False, use_libstdcxx=False): """ Returns a dictionary (which can be provided to build* functions above) which contains OS-specific build flags. """ cflags = "" ldflags = "" # On Mac OS X, unless specifically requested to use libstdc++, use libc++ if not use_libstdcxx and self.platformIsDarwin(): use_libcxx = True if use_libcxx and self.libcxxPath: cflags += "-stdlib=libc++ " if self.libcxxPath: libcxxInclude = os.path.join(self.libcxxPath, "include") libcxxLib = os.path.join(self.libcxxPath, "lib") if os.path.isdir(libcxxInclude) and os.path.isdir(libcxxLib): cflags += "-nostdinc++ -I%s -L%s -Wl,-rpath,%s " % (libcxxInclude, libcxxLib, libcxxLib) if use_cpp11: cflags += "-std=" if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): cflags += "c++0x" else: cflags += "c++11" if self.platformIsDarwin() or self.getPlatform() == "freebsd": cflags += " -stdlib=libc++" elif self.getPlatform() == "netbsd": cflags += " -stdlib=libstdc++" elif "clang" in self.getCompiler(): cflags += " -stdlib=libstdc++" return {'CFLAGS_EXTRAS' : cflags, 'LD_EXTRAS' : ldflags, } def cleanup(self, dictionary=None): """Platform specific way to do cleanup after build.""" module = builder_module() if not module.cleanup(self, dictionary): raise Exception("Don't know how to do cleanup with dictionary: "+dictionary) def getLLDBLibraryEnvVal(self): """ Returns the path that the OS-specific library search environment variable (self.dylibPath) should be set to in order for a program to find the LLDB library. If an environment variable named self.dylibPath is already set, the new path is appended to it and returned. """ existing_library_path = os.environ[self.dylibPath] if self.dylibPath in os.environ else None lib_dir = os.environ["LLDB_LIB_DIR"] if existing_library_path: return "%s:%s" % (existing_library_path, lib_dir) elif sys.platform.startswith("darwin"): return os.path.join(lib_dir, 'LLDB.framework') else: return lib_dir def getLibcPlusPlusLibs(self): if self.getPlatform() in ('freebsd', 'linux', 'netbsd'): return ['libc++.so.1'] else: return ['libc++.1.dylib','libc++abi.dylib'] # Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. # We change the test methods to create a new test method for each test for each debug info we are # testing. The name of the new test method will be '_' and with adding # the new test method we remove the old method at the same time. class LLDBTestCaseFactory(type): def __new__(cls, name, bases, attrs): newattrs = {} for attrname, attrvalue in attrs.items(): if attrname.startswith("test") and not getattr(attrvalue, "__no_debug_info_test__", False): target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2] # If any debug info categories were explicitly tagged, assume that list to be # authoritative. If none were specified, try with all debug info formats. all_dbginfo_categories = set(test_categories.debug_info_categories) categories = set(getattr(attrvalue, "categories", [])) & all_dbginfo_categories if not categories: categories = all_dbginfo_categories supported_categories = [x for x in categories if test_categories.is_supported_on_platform(x, target_platform)] if "dsym" in supported_categories: @add_test_categories(["dsym"]) @wraps(attrvalue) def dsym_test_method(self, attrvalue=attrvalue): self.debug_info = "dsym" return attrvalue(self) dsym_method_name = attrname + "_dsym" dsym_test_method.__name__ = dsym_method_name newattrs[dsym_method_name] = dsym_test_method if "dwarf" in supported_categories: @add_test_categories(["dwarf"]) @wraps(attrvalue) def dwarf_test_method(self, attrvalue=attrvalue): self.debug_info = "dwarf" return attrvalue(self) dwarf_method_name = attrname + "_dwarf" dwarf_test_method.__name__ = dwarf_method_name newattrs[dwarf_method_name] = dwarf_test_method if "dwo" in supported_categories: @add_test_categories(["dwo"]) @wraps(attrvalue) def dwo_test_method(self, attrvalue=attrvalue): self.debug_info = "dwo" return attrvalue(self) dwo_method_name = attrname + "_dwo" dwo_test_method.__name__ = dwo_method_name newattrs[dwo_method_name] = dwo_test_method else: newattrs[attrname] = attrvalue return super(LLDBTestCaseFactory, cls).__new__(cls, name, bases, newattrs) # Setup the metaclass for this class to change the list of the test methods when a new class is loaded @add_metaclass(LLDBTestCaseFactory) class TestBase(Base): """ This abstract base class is meant to be subclassed. It provides default implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), among other things. Important things for test class writers: - Overwrite the mydir class attribute, otherwise your test class won't run. It specifies the relative directory to the top level 'test' so the test harness can change to the correct working directory before running your test. - The setUp method sets up things to facilitate subsequent interactions with the debugger as part of the test. These include: - populate the test method name - create/get a debugger set with synchronous mode (self.dbg) - get the command interpreter from with the debugger (self.ci) - create a result object for use with the command interpreter (self.res) - plus other stuffs - The tearDown method tries to perform some necessary cleanup on behalf of the test to return the debugger to a good state for the next test. These include: - execute any tearDown hooks registered by the test method with TestBase.addTearDownHook(); examples can be found in settings/TestSettings.py - kill the inferior process associated with each target, if any, and, then delete the target from the debugger's target list - perform build cleanup before running the next test method in the same test class; examples of registering for this service can be found in types/TestIntegerTypes.py with the call: - self.setTearDownCleanup(dictionary=d) - Similarly setUpClass and tearDownClass perform classwise setup and teardown fixtures. The tearDownClass method invokes a default build cleanup for the entire test class; also, subclasses can implement the classmethod classCleanup(cls) to perform special class cleanup action. - The instance methods runCmd and expect are used heavily by existing test cases to send a command to the command interpreter and to perform string/pattern matching on the output of such command execution. The expect method also provides a mode to peform string/pattern matching without running a command. - The build methods buildDefault, buildDsym, and buildDwarf are used to build the binaries used during a particular test scenario. A plugin should be provided for the sys.platform running the test suite. The Mac OS X implementation is located in plugins/darwin.py. """ # Maximum allowed attempts when launching the inferior process. # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. maxLaunchCount = 3; # Time to wait before the next launching attempt in second(s). # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. timeWaitNextLaunch = 1.0; # Returns the list of categories to which this test case belongs # by default, look for a ".categories" file, and read its contents # if no such file exists, traverse the hierarchy - we guarantee # a .categories to exist at the top level directory so we do not end up # looping endlessly - subclasses are free to define their own categories # in whatever way makes sense to them def getCategories(self): import inspect import os.path folder = inspect.getfile(self.__class__) folder = os.path.dirname(folder) while folder != '/': categories_file_name = os.path.join(folder,".categories") if os.path.exists(categories_file_name): categories_file = open(categories_file_name,'r') categories = categories_file.readline() categories_file.close() categories = str.replace(categories,'\n','') categories = str.replace(categories,'\r','') return categories.split(',') else: folder = os.path.dirname(folder) continue def setUp(self): #import traceback #traceback.print_stack() # Works with the test driver to conditionally skip tests via decorators. Base.setUp(self) if "LLDB_MAX_LAUNCH_COUNT" in os.environ: self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: self.timeWaitNextLaunch = float(os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) # We want our debugger to be synchronous. self.dbg.SetAsync(False) # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() if lldb.remote_platform and configuration.lldb_platform_working_dir: remote_test_dir = lldbutil.join_remote_paths( configuration.lldb_platform_working_dir, self.getArchitecture(), str(self.test_number), self.mydir) error = lldb.remote_platform.MakeDirectory(remote_test_dir, 448) # 448 = 0o700 if error.Success(): lldb.remote_platform.SetWorkingDirectory(remote_test_dir) # This function removes all files from the current working directory while leaving # the directories in place. The cleaup is required to reduce the disk space required # by the test suit while leaving the directories untached is neccessary because # sub-directories might belong to an other test def clean_working_directory(): # TODO: Make it working on Windows when we need it for remote debugging support # TODO: Replace the heuristic to remove the files with a logic what collects the # list of files we have to remove during test runs. shell_cmd = lldb.SBPlatformShellCommand("rm %s/*" % remote_test_dir) lldb.remote_platform.Run(shell_cmd) self.addTearDownHook(clean_working_directory) else: print("error: making remote directory '%s': %s" % (remote_test_dir, error)) def registerSharedLibrariesWithTarget(self, target, shlibs): '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing Any modules in the target that have their remote install file specification set will get uploaded to the remote host. This function registers the local copies of the shared libraries with the target and sets their remote install locations so they will be uploaded when the target is run. ''' if not shlibs or not self.platformContext: return None shlib_environment_var = self.platformContext.shlib_environment_var shlib_prefix = self.platformContext.shlib_prefix shlib_extension = '.' + self.platformContext.shlib_extension working_dir = self.get_process_working_directory() environment = ['%s=%s' % (shlib_environment_var, working_dir)] # Add any shared libraries to our target if remote so they get # uploaded into the working directory on the remote side for name in shlibs: # The path can be a full path to a shared library, or a make file name like "Foo" for # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a # basename like "libFoo.so". So figure out which one it is and resolve the local copy # of the shared library accordingly if os.path.exists(name): local_shlib_path = name # name is the full path to the local shared library else: # Check relative names local_shlib_path = os.path.join(os.getcwd(), shlib_prefix + name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join(os.getcwd(), name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join(os.getcwd(), name) # Make sure we found the local shared library in the above code self.assertTrue(os.path.exists(local_shlib_path)) # Add the shared library to our target shlib_module = target.AddModule(local_shlib_path, None, None, None) if lldb.remote_platform: # We must set the remote install location if we want the shared library # to get uploaded to the remote target remote_shlib_path = lldbutil.append_to_process_working_directory(os.path.basename(local_shlib_path)) shlib_module.SetRemoteInstallFileSpec(lldb.SBFileSpec(remote_shlib_path, False)) return environment # utility methods that tests can use to access the current objects def target(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget() def process(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess() def thread(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() def frame(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() def get_process_working_directory(self): '''Get the working directory that should be used when launching processes for local or remote processes.''' if lldb.remote_platform: # Remote tests set the platform working directory up in TestBase.setUp() return lldb.remote_platform.GetWorkingDirectory() else: # local tests change directory into each test subdirectory return os.getcwd() def tearDown(self): #import traceback #traceback.print_stack() # Ensure all the references to SB objects have gone away so that we can # be sure that all test-specific resources have been freed before we # attempt to delete the targets. gc.collect() # Delete the target(s) from the debugger as a general cleanup step. # This includes terminating the process for each target, if any. # We'd like to reuse the debugger for our next test without incurring # the initialization overhead. targets = [] for target in self.dbg: if target: targets.append(target) process = target.GetProcess() if process: rc = self.invoke(process, "Kill") self.assertTrue(rc.Success(), PROCESS_KILLED) for target in targets: self.dbg.DeleteTarget(target) # Do this last, to make sure it's in reverse order from how we setup. Base.tearDown(self) # This must be the last statement, otherwise teardown hooks or other # lines might depend on this still being active. del self.dbg def switch_to_thread_with_stop_reason(self, stop_reason): """ Run the 'thread list' command, and select the thread with stop reason as 'stop_reason'. If no such thread exists, no select action is done. """ from .lldbutil import stop_reason_to_str self.runCmd('thread list') output = self.res.GetOutput() thread_line_pattern = re.compile("^[ *] thread #([0-9]+):.*stop reason = %s" % stop_reason_to_str(stop_reason)) for line in output.splitlines(): matched = thread_line_pattern.match(line) if matched: self.runCmd('thread select %s' % matched.group(1)) def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): """ Ask the command interpreter to handle the command and then check its return status. """ # Fail fast if 'cmd' is not meaningful. if not cmd or len(cmd) == 0: raise Exception("Bad 'cmd' parameter encountered") trace = (True if traceAlways else trace) if cmd.startswith("target create "): cmd = cmd.replace("target create ", "file ") running = (cmd.startswith("run") or cmd.startswith("process launch")) for i in range(self.maxLaunchCount if running else 1): self.ci.HandleCommand(cmd, self.res, inHistory) with recording(self, trace) as sbuf: print("runCmd:", cmd, file=sbuf) if not check: print("check of return status not required", file=sbuf) if self.res.Succeeded(): print("output:", self.res.GetOutput(), file=sbuf) else: print("runCmd failed!", file=sbuf) print(self.res.GetError(), file=sbuf) if self.res.Succeeded(): break elif running: # For process launch, wait some time before possible next try. time.sleep(self.timeWaitNextLaunch) with recording(self, trace) as sbuf: print("Command '" + cmd + "' failed!", file=sbuf) if check: self.assertTrue(self.res.Succeeded(), msg if msg else CMD_MSG(cmd)) def match (self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True): """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern Otherwise, all the arguments have the same meanings as for the expect function""" trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic info. self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" for pattern in patterns: # Match Objects always have a boolean value of True. match_object = re.search(pattern, output) matched = bool(match_object) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) if matched: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, exe)) return match_object def expect(self, str, msg=None, patterns=None, startstr=None, endstr=None, substrs=None, trace=False, error=False, matching=True, exe=True, inHistory=False): """ Similar to runCmd; with additional expect style output matching ability. Ask the command interpreter to handle the command and then check its return status. The 'msg' parameter specifies an informational assert message. We expect the output from running the command to start with 'startstr', matches the substrings contained in 'substrs', and regexp matches the patterns contained in 'patterns'. If the keyword argument error is set to True, it signifies that the API client is expecting the command to fail. In this case, the error stream from running the command is retrieved and compared against the golden input, instead. If the keyword argument matching is set to False, it signifies that the API client is expecting the output of the command not to match the golden input. Finally, the required argument 'str' represents the lldb command to be sent to the command interpreter. In case the keyword argument 'exe' is set to False, the 'str' is treated as a string to be matched/not-matched against the golden input. """ trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic info. self.runCmd(str, msg=msg, trace = (True if trace else False), check = not error, inHistory=inHistory) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. if isinstance(str,lldb.SBCommandReturnObject): output = str.GetOutput() else: output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" # Start from the startstr, if specified. # If there's no startstr, set the initial state appropriately. matched = output.startswith(startstr) if startstr else (True if matching else False) if startstr: with recording(self, trace) as sbuf: print("%s start string: %s" % (heading, startstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for endstr, if specified. keepgoing = matched if matching else not matched if endstr: matched = output.endswith(endstr) with recording(self, trace) as sbuf: print("%s end string: %s" % (heading, endstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for sub strings, if specified. keepgoing = matched if matching else not matched if substrs and keepgoing: for str in substrs: matched = output.find(str) != -1 with recording(self, trace) as sbuf: print("%s sub string: %s" % (heading, str), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break # Search for regular expression patterns, if specified. keepgoing = matched if matching else not matched if patterns and keepgoing: for pattern in patterns: # Match Objects always have a boolean value of True. matched = bool(re.search(pattern, output)) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, exe)) def invoke(self, obj, name, trace=False): """Use reflection to call a method dynamically with no argument.""" trace = (True if traceAlways else trace) method = getattr(obj, name) import inspect self.assertTrue(inspect.ismethod(method), name + "is a method name of object: " + str(obj)) result = method() with recording(self, trace) as sbuf: print(str(method) + ":", result, file=sbuf) return result def build(self, architecture=None, compiler=None, dictionary=None, clean=True): """Platform specific way to build the default binaries.""" module = builder_module() if target_is_android(): dictionary = append_android_envs(dictionary) if self.debug_info is None: return self.buildDefault(architecture, compiler, dictionary, clean) elif self.debug_info == "dsym": return self.buildDsym(architecture, compiler, dictionary, clean) elif self.debug_info == "dwarf": return self.buildDwarf(architecture, compiler, dictionary, clean) elif self.debug_info == "dwo": return self.buildDwo(architecture, compiler, dictionary, clean) else: self.fail("Can't build for debug info: %s" % self.debug_info) # ================================================= # Misc. helper methods for debugging test execution # ================================================= def DebugSBValue(self, val): """Debug print a SBValue object, if traceAlways is True.""" from .lldbutil import value_type_to_str if not traceAlways: return err = sys.stderr err.write(val.GetName() + ":\n") err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') err.write('\t' + "Value -> " + str(val.GetValue()) + '\n') err.write('\t' + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned())+ '\n') err.write('\t' + "ValueType -> " + value_type_to_str(val.GetValueType()) + '\n') err.write('\t' + "Summary -> " + str(val.GetSummary()) + '\n') err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') err.write('\t' + "Location -> " + val.GetLocation() + '\n') def DebugSBType(self, type): """Debug print a SBType object, if traceAlways is True.""" if not traceAlways: return err = sys.stderr err.write(type.GetName() + ":\n") err.write('\t' + "ByteSize -> " + str(type.GetByteSize()) + '\n') err.write('\t' + "IsPointerType -> " + str(type.IsPointerType()) + '\n') err.write('\t' + "IsReferenceType -> " + str(type.IsReferenceType()) + '\n') def DebugPExpect(self, child): """Debug the spwaned pexpect object.""" if not traceAlways: return print(child) @classmethod def RemoveTempFile(cls, file): if os.path.exists(file): os.remove(file) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py (revision 295597) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/function_symbol/TestSymbolAPI.py (revision 295598) @@ -1,81 +1,80 @@ """ Test newly added SBSymbol and SBAddress APIs. """ from __future__ import print_function import os, time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class SymbolAPITestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to of function 'c'. self.line1 = line_number('main.c', '// Find the line number for breakpoint 1 here.') self.line2 = line_number('main.c', '// Find the line number for breakpoint 2 here.') @add_test_categories(['pyapi']) - @expectedFailureWindows("llvm.org/pr24778") def test(self): """Exercise some SBSymbol and SBAddress APIs.""" self.build() exe = os.path.join(os.getcwd(), "a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) # Now create the two breakpoints inside function 'a'. breakpoint1 = target.BreakpointCreateByLocation('main.c', self.line1) breakpoint2 = target.BreakpointCreateByLocation('main.c', self.line2) #print("breakpoint1:", breakpoint1) #print("breakpoint2:", breakpoint2) self.assertTrue(breakpoint1 and breakpoint1.GetNumLocations() == 1, VALID_BREAKPOINT) self.assertTrue(breakpoint2 and breakpoint2.GetNumLocations() == 1, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. process = target.LaunchSimple (None, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) # Frame #0 should be on self.line1. self.assertTrue(process.GetState() == lldb.eStateStopped) thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") frame0 = thread.GetFrameAtIndex(0) symbol_line1 = frame0.GetSymbol() # We should have a symbol type of code. self.assertTrue(symbol_line1.GetType() == lldb.eSymbolTypeCode) addr_line1 = symbol_line1.GetStartAddress() # And a section type of code, too. self.assertTrue(addr_line1.GetSection().GetSectionType() == lldb.eSectionTypeCode) # Continue the inferior, the breakpoint 2 should be hit. process.Continue() self.assertTrue(process.GetState() == lldb.eStateStopped) thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") frame0 = thread.GetFrameAtIndex(0) symbol_line2 = frame0.GetSymbol() # We should have a symbol type of code. self.assertTrue(symbol_line2.GetType() == lldb.eSymbolTypeCode) addr_line2 = symbol_line2.GetStartAddress() # And a section type of code, too. self.assertTrue(addr_line2.GetSection().GetSectionType() == lldb.eSectionTypeCode) # Now verify that both addresses point to the same module. if self.TraceOn(): print("UUID:", addr_line1.GetModule().GetUUIDString()) self.assertTrue(addr_line1.GetModule().GetUUIDString() == addr_line2.GetModule().GetUUIDString()) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py (revision 295597) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/lldbutil/process/TestPrintStackTraces.py (revision 295598) @@ -1,52 +1,56 @@ """ Test SBprocess and SBThread APIs with printing of the stack traces using lldbutil. """ from __future__ import print_function import os, time import re import lldb from lldbsuite.test.lldbtest import * class ThreadsStackTracesTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break inside main(). self.line = line_number('main.cpp', '// Set break point at this line.') @expectedFailureAll("llvm.org/pr23043", ["linux"], archs=["i386"]) # We are unable to produce a backtrace of the main thread when the thread is blocked in fgets + + #The __thread_start function in libc doesn't contain any epilogue and prologue instructions + #hence unwinding fail when we are stopped in __thread_start + @expectedFailureAll(triple = 'mips*') @expectedFailureWindows("llvm.org/pr24778") @add_test_categories(['pyapi']) def test_stack_traces(self): """Test SBprocess and SBThread APIs with printing of the stack traces.""" self.build() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) breakpoint = target.BreakpointCreateByLocation("main.cpp", self.line) self.assertTrue(breakpoint, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. process = target.LaunchSimple (["abc", "xyz"], None, self.get_process_working_directory()) if not process: self.fail("SBTarget.LaunchProcess() failed") import lldbsuite.test.lldbutil as lldbutil if process.GetState() != lldb.eStateStopped: self.fail("Process should be in the 'stopped' state, " "instead the actual state is: '%s'" % lldbutil.state_type_to_str(process.GetState())) stacktraces = lldbutil.print_stacktraces(process, string_buffer=True) self.expect(stacktraces, exe=False, substrs = ['(int)argc=3']) Index: vendor/lldb/dist/scripts/interface/SBInstruction.i =================================================================== --- vendor/lldb/dist/scripts/interface/SBInstruction.i (revision 295597) +++ vendor/lldb/dist/scripts/interface/SBInstruction.i (revision 295598) @@ -1,103 +1,106 @@ //===-- SWIG Interface for SBInstruction ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include // There's a lot to be fixed here, but need to wait for underlying insn implementation // to be revised & settle down first. namespace lldb { class SBInstruction { public: SBInstruction (); SBInstruction (const SBInstruction &rhs); ~SBInstruction (); bool IsValid(); lldb::SBAddress GetAddress(); lldb::AddressClass GetAddressClass (); const char * GetMnemonic (lldb::SBTarget target); const char * GetOperands (lldb::SBTarget target); const char * GetComment (lldb::SBTarget target); lldb::SBData GetData (lldb::SBTarget target); size_t GetByteSize (); bool DoesBranch (); + bool + HasDelaySlot (); + void Print (FILE *out); bool GetDescription (lldb::SBStream &description); bool EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); bool DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' bool TestEmulation (lldb::SBStream &output_stream, const char *test_file); %pythoncode %{ def __mnemonic_property__ (self): return self.GetMnemonic (target) def __operands_property__ (self): return self.GetOperands (target) def __comment_property__ (self): return self.GetComment (target) def __file_addr_property__ (self): return self.GetAddress ().GetFileAddress() def __load_adrr_property__ (self): return self.GetComment (target) __swig_getmethods__["mnemonic"] = __mnemonic_property__ if _newclass: mnemonic = property(__mnemonic_property__, None, doc='''A read only property that returns the mnemonic for this instruction as a string.''') __swig_getmethods__["operands"] = __operands_property__ if _newclass: operands = property(__operands_property__, None, doc='''A read only property that returns the operands for this instruction as a string.''') __swig_getmethods__["comment"] = __comment_property__ if _newclass: comment = property(__comment_property__, None, doc='''A read only property that returns the comment for this instruction as a string.''') __swig_getmethods__["addr"] = GetAddress if _newclass: addr = property(GetAddress, None, doc='''A read only property that returns an lldb object that represents the address (lldb.SBAddress) for this instruction.''') __swig_getmethods__["size"] = GetByteSize if _newclass: size = property(GetByteSize, None, doc='''A read only property that returns the size in bytes for this instruction as an integer.''') __swig_getmethods__["is_branch"] = DoesBranch if _newclass: is_branch = property(DoesBranch, None, doc='''A read only property that returns a boolean value that indicates if this instruction is a branch instruction.''') %} }; } // namespace lldb Index: vendor/lldb/dist/source/API/SBInstruction.cpp =================================================================== --- vendor/lldb/dist/source/API/SBInstruction.cpp (revision 295597) +++ vendor/lldb/dist/source/API/SBInstruction.cpp (revision 295598) @@ -1,263 +1,271 @@ //===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/API/SBInstruction.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; SBInstruction::SBInstruction () { } SBInstruction::SBInstruction (const lldb::InstructionSP& inst_sp) : m_opaque_sp (inst_sp) { } SBInstruction::SBInstruction(const SBInstruction &rhs) : m_opaque_sp (rhs.m_opaque_sp) { } const SBInstruction & SBInstruction::operator = (const SBInstruction &rhs) { if (this != &rhs) m_opaque_sp = rhs.m_opaque_sp; return *this; } SBInstruction::~SBInstruction () { } bool SBInstruction::IsValid() { return (m_opaque_sp.get() != NULL); } SBAddress SBInstruction::GetAddress() { SBAddress sb_addr; if (m_opaque_sp && m_opaque_sp->GetAddress().IsValid()) sb_addr.SetAddress(&m_opaque_sp->GetAddress()); return sb_addr; } const char * SBInstruction::GetMnemonic(SBTarget target) { if (m_opaque_sp) { Mutex::Locker api_locker; ExecutionContext exe_ctx; TargetSP target_sp (target.GetSP()); if (target_sp) { api_locker.Lock (target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext (exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return m_opaque_sp->GetMnemonic(&exe_ctx); } return NULL; } const char * SBInstruction::GetOperands(SBTarget target) { if (m_opaque_sp) { Mutex::Locker api_locker; ExecutionContext exe_ctx; TargetSP target_sp (target.GetSP()); if (target_sp) { api_locker.Lock (target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext (exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return m_opaque_sp->GetOperands(&exe_ctx); } return NULL; } const char * SBInstruction::GetComment(SBTarget target) { if (m_opaque_sp) { Mutex::Locker api_locker; ExecutionContext exe_ctx; TargetSP target_sp (target.GetSP()); if (target_sp) { api_locker.Lock (target_sp->GetAPIMutex()); target_sp->CalculateExecutionContext (exe_ctx); exe_ctx.SetProcessSP(target_sp->GetProcessSP()); } return m_opaque_sp->GetComment(&exe_ctx); } return NULL; } size_t SBInstruction::GetByteSize () { if (m_opaque_sp) return m_opaque_sp->GetOpcode().GetByteSize(); return 0; } SBData SBInstruction::GetData (SBTarget target) { lldb::SBData sb_data; if (m_opaque_sp) { DataExtractorSP data_extractor_sp (new DataExtractor()); if (m_opaque_sp->GetData (*data_extractor_sp)) { sb_data.SetOpaque (data_extractor_sp); } } return sb_data; } bool SBInstruction::DoesBranch () { if (m_opaque_sp) return m_opaque_sp->DoesBranch (); return false; } +bool +SBInstruction::HasDelaySlot () +{ + if (m_opaque_sp) + return m_opaque_sp->HasDelaySlot (); + return false; +} + void SBInstruction::SetOpaque (const lldb::InstructionSP &inst_sp) { m_opaque_sp = inst_sp; } bool SBInstruction::GetDescription (lldb::SBStream &s) { if (m_opaque_sp) { SymbolContext sc; const Address &addr = m_opaque_sp->GetAddress(); ModuleSP module_sp (addr.GetModule()); if (module_sp) module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); // Use the "ref()" instead of the "get()" accessor in case the SBStream // didn't have a stream already created, one will get created... FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL, &sc, NULL, &format, 0); return true; } return false; } void SBInstruction::Print (FILE *out) { if (out == NULL) return; if (m_opaque_sp) { SymbolContext sc; const Address &addr = m_opaque_sp->GetAddress(); ModuleSP module_sp (addr.GetModule()); if (module_sp) module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); StreamFile out_stream (out, false); FormatEntity::Entry format; FormatEntity::Parse("${addr}: ", format); m_opaque_sp->Dump (&out_stream, 0, true, false, NULL, &sc, NULL, &format, 0); } } bool SBInstruction::EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options) { if (m_opaque_sp) { lldb::StackFrameSP frame_sp (frame.GetFrameSP()); if (frame_sp) { lldb_private::ExecutionContext exe_ctx; frame_sp->CalculateExecutionContext (exe_ctx); lldb_private::Target *target = exe_ctx.GetTargetPtr(); lldb_private::ArchSpec arch = target->GetArchitecture(); return m_opaque_sp->Emulate (arch, evaluate_options, (void *) frame_sp.get(), &lldb_private::EmulateInstruction::ReadMemoryFrame, &lldb_private::EmulateInstruction::WriteMemoryFrame, &lldb_private::EmulateInstruction::ReadRegisterFrame, &lldb_private::EmulateInstruction::WriteRegisterFrame); } } return false; } bool SBInstruction::DumpEmulation (const char *triple) { if (m_opaque_sp && triple) { lldb_private::ArchSpec arch (triple, NULL); return m_opaque_sp->DumpEmulation (arch); } return false; } bool SBInstruction::TestEmulation (lldb::SBStream &output_stream, const char *test_file) { if (!m_opaque_sp.get()) m_opaque_sp.reset (new PseudoInstruction()); return m_opaque_sp->TestEmulation (output_stream.get(), test_file); } lldb::AddressClass SBInstruction::GetAddressClass () { if (m_opaque_sp.get()) return m_opaque_sp->GetAddressClass(); return eAddressClassInvalid; } Index: vendor/lldb/dist/source/Core/Module.cpp =================================================================== --- vendor/lldb/dist/source/Core/Module.cpp (revision 295597) +++ vendor/lldb/dist/source/Core/Module.cpp (revision 295598) @@ -1,1869 +1,1880 @@ //===-- Module.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/AddressResolverFileLine.h" #include "lldb/Core/Error.h" #include "lldb/Core/Module.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" #include "lldb/Host/Symbols.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Symbol/TypeMap.h" #include "Plugins/ObjectFile/JIT/ObjectFileJIT.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/Signals.h" using namespace lldb; using namespace lldb_private; // Shared pointers to modules track module lifetimes in // targets and in the global module, but this collection // will track all module objects that are still alive typedef std::vector ModuleCollection; static ModuleCollection & GetModuleCollection() { // This module collection needs to live past any module, so we could either make it a // shared pointer in each module or just leak is. Since it is only an empty vector by // the time all the modules have gone away, we just leak it for now. If we decide this // is a big problem we can introduce a Finalize method that will tear everything down in // a predictable order. static ModuleCollection *g_module_collection = NULL; if (g_module_collection == NULL) g_module_collection = new ModuleCollection(); return *g_module_collection; } Mutex * Module::GetAllocationModuleCollectionMutex() { // NOTE: The mutex below must be leaked since the global module list in // the ModuleList class will get torn at some point, and we can't know // if it will tear itself down before the "g_module_collection_mutex" below // will. So we leak a Mutex object below to safeguard against that static Mutex *g_module_collection_mutex = NULL; if (g_module_collection_mutex == NULL) g_module_collection_mutex = new Mutex (Mutex::eMutexTypeRecursive); // NOTE: known leak return g_module_collection_mutex; } size_t Module::GetNumberAllocatedModules () { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); return GetModuleCollection().size(); } Module * Module::GetAllocatedModuleAtIndex (size_t idx) { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); if (idx < modules.size()) return modules[idx]; return NULL; } #if 0 // These functions help us to determine if modules are still loaded, yet don't require that // you have a command interpreter and can easily be called from an external debugger. namespace lldb { void ClearModuleInfo (void) { const bool mandatory = true; ModuleList::RemoveOrphanSharedModules(mandatory); } void DumpModuleInfo (void) { Mutex::Locker locker (Module::GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); const size_t count = modules.size(); printf ("%s: %" PRIu64 " modules:\n", __PRETTY_FUNCTION__, (uint64_t)count); for (size_t i=0; iGetDescription(&strm, eDescriptionLevelFull); printf ("%p: shared = %i, ref_count = %3u, module = %s\n", module, in_shared_module_list, (uint32_t)module->use_count(), strm.GetString().c_str()); } } } #endif Module::Module (const ModuleSpec &module_spec) : m_mutex (Mutex::eMutexTypeRecursive), m_mod_time (), m_arch (), m_uuid (), m_file (), m_platform_file(), m_remote_install_file(), m_symfile_spec (), m_object_name (), m_object_offset (), m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), m_type_system_map(), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_file_has_changed (false), m_first_file_changed_log (false) { // Scope for locker below... { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); if (log) log->Printf ("%p Module::Module((%s) '%s%s%s%s')", static_cast(this), module_spec.GetArchitecture().GetArchitectureName(), module_spec.GetFileSpec().GetPath().c_str(), module_spec.GetObjectName().IsEmpty() ? "" : "(", module_spec.GetObjectName().IsEmpty() ? "" : module_spec.GetObjectName().AsCString(""), module_spec.GetObjectName().IsEmpty() ? "" : ")"); // First extract all module specifications from the file using the local // file path. If there are no specifications, then don't fill anything in ModuleSpecList modules_specs; if (ObjectFile::GetModuleSpecifications(module_spec.GetFileSpec(), 0, 0, modules_specs) == 0) return; // Now make sure that one of the module specifications matches what we just // extract. We might have a module specification that specifies a file "/usr/lib/dyld" // with UUID XXX, but we might have a local version of "/usr/lib/dyld" that has // UUID YYY and we don't want those to match. If they don't match, just don't // fill any ivars in so we don't accidentally grab the wrong file later since // they don't match... ModuleSpec matching_module_spec; if (modules_specs.FindMatchingModuleSpec(module_spec, matching_module_spec) == 0) return; if (module_spec.GetFileSpec()) m_mod_time = module_spec.GetFileSpec().GetModificationTime(); else if (matching_module_spec.GetFileSpec()) m_mod_time = matching_module_spec.GetFileSpec().GetModificationTime(); // Copy the architecture from the actual spec if we got one back, else use the one that was specified if (matching_module_spec.GetArchitecture().IsValid()) m_arch = matching_module_spec.GetArchitecture(); else if (module_spec.GetArchitecture().IsValid()) m_arch = module_spec.GetArchitecture(); // Copy the file spec over and use the specified one (if there was one) so we // don't use a path that might have gotten resolved a path in 'matching_module_spec' if (module_spec.GetFileSpec()) m_file = module_spec.GetFileSpec(); else if (matching_module_spec.GetFileSpec()) m_file = matching_module_spec.GetFileSpec(); // Copy the platform file spec over if (module_spec.GetPlatformFileSpec()) m_platform_file = module_spec.GetPlatformFileSpec(); else if (matching_module_spec.GetPlatformFileSpec()) m_platform_file = matching_module_spec.GetPlatformFileSpec(); // Copy the symbol file spec over if (module_spec.GetSymbolFileSpec()) m_symfile_spec = module_spec.GetSymbolFileSpec(); else if (matching_module_spec.GetSymbolFileSpec()) m_symfile_spec = matching_module_spec.GetSymbolFileSpec(); // Copy the object name over if (matching_module_spec.GetObjectName()) m_object_name = matching_module_spec.GetObjectName(); else m_object_name = module_spec.GetObjectName(); // Always trust the object offset (file offset) and object modification // time (for mod time in a BSD static archive) of from the matching // module specification m_object_offset = matching_module_spec.GetObjectOffset(); m_object_mod_time = matching_module_spec.GetObjectModificationTime(); } Module::Module(const FileSpec& file_spec, const ArchSpec& arch, const ConstString *object_name, lldb::offset_t object_offset, const TimeValue *object_mod_time_ptr) : m_mutex (Mutex::eMutexTypeRecursive), m_mod_time (file_spec.GetModificationTime()), m_arch (arch), m_uuid (), m_file (file_spec), m_platform_file(), m_remote_install_file (), m_symfile_spec (), m_object_name (), m_object_offset (object_offset), m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), m_type_system_map(), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_file_has_changed (false), m_first_file_changed_log (false) { // Scope for locker below... { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } if (object_name) m_object_name = *object_name; if (object_mod_time_ptr) m_object_mod_time = *object_mod_time_ptr; Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); if (log) log->Printf ("%p Module::Module((%s) '%s%s%s%s')", static_cast(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); } Module::Module () : m_mutex (Mutex::eMutexTypeRecursive), m_mod_time (), m_arch (), m_uuid (), m_file (), m_platform_file(), m_remote_install_file (), m_symfile_spec (), m_object_name (), m_object_offset (0), m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), m_type_system_map(), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), m_did_load_symbol_vendor (false), m_did_parse_uuid (false), m_file_has_changed (false), m_first_file_changed_log (false) { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } Module::~Module() { // Lock our module down while we tear everything down to make sure // we don't get any access to the module while it is being destroyed Mutex::Locker locker (m_mutex); // Scope for locker below... { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); ModuleCollection::iterator end = modules.end(); ModuleCollection::iterator pos = std::find(modules.begin(), end, this); assert (pos != end); modules.erase(pos); } Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); if (log) log->Printf ("%p Module::~Module((%s) '%s%s%s%s')", static_cast(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); // Release any auto pointers before we start tearing down our member // variables since the object file and symbol files might need to make // function calls back into this module object. The ordering is important // here because symbol files can require the module object file. So we tear // down the symbol file first, then the object file. m_sections_ap.reset(); m_symfile_ap.reset(); m_objfile_sp.reset(); } ObjectFile * Module::GetMemoryObjectFile (const lldb::ProcessSP &process_sp, lldb::addr_t header_addr, Error &error, size_t size_to_read) { if (m_objfile_sp) { error.SetErrorString ("object file already exists"); } else { Mutex::Locker locker (m_mutex); if (process_sp) { m_did_load_objfile = true; std::unique_ptr data_ap (new DataBufferHeap (size_to_read, 0)); Error readmem_error; const size_t bytes_read = process_sp->ReadMemory (header_addr, data_ap->GetBytes(), data_ap->GetByteSize(), readmem_error); if (bytes_read == size_to_read) { DataBufferSP data_sp(data_ap.release()); m_objfile_sp = ObjectFile::FindPlugin(shared_from_this(), process_sp, header_addr, data_sp); if (m_objfile_sp) { StreamString s; s.Printf("0x%16.16" PRIx64, header_addr); m_object_name.SetCString (s.GetData()); // Once we get the object file, update our module with the object file's // architecture since it might differ in vendor/os if some parts were // unknown. m_objfile_sp->GetArchitecture (m_arch); } else { error.SetErrorString ("unable to find suitable object file plug-in"); } } else { error.SetErrorStringWithFormat ("unable to read header from memory: %s", readmem_error.AsCString()); } } else { error.SetErrorString ("invalid process"); } } return m_objfile_sp.get(); } const lldb_private::UUID& Module::GetUUID() { if (m_did_parse_uuid.load() == false) { Mutex::Locker locker (m_mutex); if (m_did_parse_uuid.load() == false) { ObjectFile * obj_file = GetObjectFile (); if (obj_file != NULL) { obj_file->GetUUID(&m_uuid); m_did_parse_uuid = true; } } } return m_uuid; } TypeSystem * Module::GetTypeSystemForLanguage (LanguageType language) { return m_type_system_map.GetTypeSystemForLanguage(language, this, true); } void Module::ParseAllDebugSymbols() { Mutex::Locker locker (m_mutex); size_t num_comp_units = GetNumCompileUnits(); if (num_comp_units == 0) return; SymbolContext sc; sc.module_sp = shared_from_this(); SymbolVendor *symbols = GetSymbolVendor (); for (size_t cu_idx = 0; cu_idx < num_comp_units; cu_idx++) { sc.comp_unit = symbols->GetCompileUnitAtIndex(cu_idx).get(); if (sc.comp_unit) { sc.function = NULL; symbols->ParseVariablesForContext(sc); symbols->ParseCompileUnitFunctions(sc); for (size_t func_idx = 0; (sc.function = sc.comp_unit->GetFunctionAtIndex(func_idx).get()) != NULL; ++func_idx) { symbols->ParseFunctionBlocks(sc); // Parse the variables for this function and all its blocks symbols->ParseVariablesForContext(sc); } // Parse all types for this compile unit sc.function = NULL; symbols->ParseTypes(sc); } } } void Module::CalculateSymbolContext(SymbolContext* sc) { sc->module_sp = shared_from_this(); } ModuleSP Module::CalculateSymbolContextModule () { return shared_from_this(); } void Module::DumpSymbolContext(Stream *s) { s->Printf(", Module{%p}", static_cast(this)); } size_t Module::GetNumCompileUnits() { Mutex::Locker locker (m_mutex); Timer scoped_timer(__PRETTY_FUNCTION__, "Module::GetNumCompileUnits (module = %p)", static_cast(this)); SymbolVendor *symbols = GetSymbolVendor (); if (symbols) return symbols->GetNumCompileUnits(); return 0; } CompUnitSP Module::GetCompileUnitAtIndex (size_t index) { Mutex::Locker locker (m_mutex); size_t num_comp_units = GetNumCompileUnits (); CompUnitSP cu_sp; if (index < num_comp_units) { SymbolVendor *symbols = GetSymbolVendor (); if (symbols) cu_sp = symbols->GetCompileUnitAtIndex(index); } return cu_sp; } bool Module::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) { Mutex::Locker locker (m_mutex); Timer scoped_timer(__PRETTY_FUNCTION__, "Module::ResolveFileAddress (vm_addr = 0x%" PRIx64 ")", vm_addr); SectionList *section_list = GetSectionList(); if (section_list) return so_addr.ResolveAddressUsingFileSections(vm_addr, section_list); return false; } uint32_t Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc, bool resolve_tail_call_address) { Mutex::Locker locker (m_mutex); uint32_t resolved_flags = 0; // Clear the result symbol context in case we don't find anything, but don't clear the target sc.Clear(false); // Get the section from the section/offset address. SectionSP section_sp (so_addr.GetSection()); // Make sure the section matches this module before we try and match anything if (section_sp && section_sp->GetModule().get() == this) { // If the section offset based address resolved itself, then this // is the right module. sc.module_sp = shared_from_this(); resolved_flags |= eSymbolContextModule; SymbolVendor* sym_vendor = GetSymbolVendor(); if (!sym_vendor) return resolved_flags; // Resolve the compile unit, function, block, line table or line // entry if requested. if (resolve_scope & eSymbolContextCompUnit || resolve_scope & eSymbolContextFunction || resolve_scope & eSymbolContextBlock || resolve_scope & eSymbolContextLineEntry ) { resolved_flags |= sym_vendor->ResolveSymbolContext (so_addr, resolve_scope, sc); } // Resolve the symbol if requested, but don't re-look it up if we've already found it. if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol)) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab && so_addr.IsSectionOffset()) { - sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + Symbol *matching_symbol = nullptr; + + symtab->ForEachSymbolContainingFileAddress(so_addr.GetFileAddress(), + [&matching_symbol](Symbol *symbol) -> bool { + if (symbol->GetType() != eSymbolTypeInvalid) + { + matching_symbol = symbol; + return false; // Stop iterating + } + return true; // Keep iterating + }); + sc.symbol = matching_symbol; if (!sc.symbol && resolve_scope & eSymbolContextFunction && !(resolved_flags & eSymbolContextFunction)) { bool verify_unique = false; // No need to check again since ResolveSymbolContext failed to find a symbol at this address. if (ObjectFile *obj_file = sc.module_sp->GetObjectFile()) sc.symbol = obj_file->ResolveSymbolForAddress(so_addr, verify_unique); } if (sc.symbol) { if (sc.symbol->IsSynthetic()) { // We have a synthetic symbol so lets check if the object file // from the symbol file in the symbol vendor is different than // the object file for the module, and if so search its symbol // table to see if we can come up with a better symbol. For example // dSYM files on MacOSX have an unstripped symbol table inside of // them. ObjectFile *symtab_objfile = symtab->GetObjectFile(); if (symtab_objfile && symtab_objfile->IsStripped()) { SymbolFile *symfile = sym_vendor->GetSymbolFile(); if (symfile) { ObjectFile *symfile_objfile = symfile->GetObjectFile(); if (symfile_objfile != symtab_objfile) { Symtab *symfile_symtab = symfile_objfile->GetSymtab(); if (symfile_symtab) { Symbol *symbol = symfile_symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); if (symbol && !symbol->IsSynthetic()) { sc.symbol = symbol; } } } } } } resolved_flags |= eSymbolContextSymbol; } } } // For function symbols, so_addr may be off by one. This is a convention consistent // with FDE row indices in eh_frame sections, but requires extra logic here to permit // symbol lookup for disassembly and unwind. if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol) && resolve_tail_call_address && so_addr.IsSectionOffset()) { Address previous_addr = so_addr; previous_addr.Slide(-1); bool do_resolve_tail_call_address = false; // prevent recursion const uint32_t flags = ResolveSymbolContextForAddress(previous_addr, resolve_scope, sc, do_resolve_tail_call_address); if (flags & eSymbolContextSymbol) { AddressRange addr_range; if (sc.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range)) { if (addr_range.GetBaseAddress().GetSection() == so_addr.GetSection()) { // If the requested address is one past the address range of a function (i.e. a tail call), // or the decremented address is the start of a function (i.e. some forms of trampoline), // indicate that the symbol has been resolved. if (so_addr.GetOffset() == addr_range.GetBaseAddress().GetOffset() || so_addr.GetOffset() == addr_range.GetBaseAddress().GetOffset() + addr_range.GetByteSize()) { resolved_flags |= flags; } } else { sc.symbol = nullptr; // Don't trust the symbol if the sections didn't match. } } } } } return resolved_flags; } uint32_t Module::ResolveSymbolContextForFilePath ( const char *file_path, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list ) { FileSpec file_spec(file_path, false); return ResolveSymbolContextsForFileSpec (file_spec, line, check_inlines, resolve_scope, sc_list); } uint32_t Module::ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) { Mutex::Locker locker (m_mutex); Timer scoped_timer(__PRETTY_FUNCTION__, "Module::ResolveSymbolContextForFilePath (%s:%u, check_inlines = %s, resolve_scope = 0x%8.8x)", file_spec.GetPath().c_str(), line, check_inlines ? "yes" : "no", resolve_scope); const uint32_t initial_count = sc_list.GetSize(); SymbolVendor *symbols = GetSymbolVendor (); if (symbols) symbols->ResolveSymbolContext (file_spec, line, check_inlines, resolve_scope, sc_list); return sc_list.GetSize() - initial_count; } size_t Module::FindGlobalVariables (const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, size_t max_matches, VariableList& variables) { SymbolVendor *symbols = GetSymbolVendor (); if (symbols) return symbols->FindGlobalVariables(name, parent_decl_ctx, append, max_matches, variables); return 0; } size_t Module::FindGlobalVariables (const RegularExpression& regex, bool append, size_t max_matches, VariableList& variables) { SymbolVendor *symbols = GetSymbolVendor (); if (symbols) return symbols->FindGlobalVariables(regex, append, max_matches, variables); return 0; } size_t Module::FindCompileUnits (const FileSpec &path, bool append, SymbolContextList &sc_list) { if (!append) sc_list.Clear(); const size_t start_size = sc_list.GetSize(); const size_t num_compile_units = GetNumCompileUnits(); SymbolContext sc; sc.module_sp = shared_from_this(); const bool compare_directory = (bool)path.GetDirectory(); for (size_t i=0; iFindFunctions(lookup_name, parent_decl_ctx, lookup_name_type_mask, include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) symtab->FindFunctionSymbols(lookup_name, lookup_name_type_mask, sc_list); } } if (match_name_after_lookup) { SymbolContext sc; size_t i = old_size; while (iFindFunctions(name, parent_decl_ctx, name_type_mask, include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) symtab->FindFunctionSymbols(name, name_type_mask, sc_list); } } } return sc_list.GetSize() - old_size; } size_t Module::FindFunctions (const RegularExpression& regex, bool include_symbols, bool include_inlines, bool append, SymbolContextList& sc_list) { if (!append) sc_list.Clear(); const size_t start_size = sc_list.GetSize(); SymbolVendor *symbols = GetSymbolVendor (); if (symbols) { symbols->FindFunctions(regex, include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->AppendSymbolIndexesMatchingRegExAndType (regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); const size_t num_matches = symbol_indexes.size(); if (num_matches) { SymbolContext sc(this); const size_t end_functions_added_index = sc_list.GetSize(); size_t num_functions_added_to_sc_list = end_functions_added_index - start_size; if (num_functions_added_to_sc_list == 0) { // No functions were added, just symbols, so we can just append them for (size_t i=0; iSymbolAtIndex(symbol_indexes[i]); SymbolType sym_type = sc.symbol->GetType(); if (sc.symbol && (sym_type == eSymbolTypeCode || sym_type == eSymbolTypeResolver)) sc_list.Append(sc); } } else { typedef std::map FileAddrToIndexMap; FileAddrToIndexMap file_addr_to_index; for (size_t i=start_size; iGetAddressRange().GetBaseAddress().GetFileAddress()] = i; } FileAddrToIndexMap::const_iterator end = file_addr_to_index.end(); // Functions were added so we need to merge symbols into any // existing function symbol contexts for (size_t i=start_size; iSymbolAtIndex(symbol_indexes[i]); SymbolType sym_type = sc.symbol->GetType(); if (sc.symbol && sc.symbol->ValueIsAddress() && (sym_type == eSymbolTypeCode || sym_type == eSymbolTypeResolver)) { FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddressRef().GetFileAddress()); if (pos == end) sc_list.Append(sc); else sc_list[pos->second].symbol = sc.symbol; } } } } } } } return sc_list.GetSize() - start_size; } void Module::FindAddressesForLine (const lldb::TargetSP target_sp, const FileSpec &file, uint32_t line, Function *function, std::vector
&output_local, std::vector
&output_extern) { SearchFilterByModule filter(target_sp, m_file); AddressResolverFileLine resolver(file, line, true); resolver.ResolveAddress (filter); for (size_t n=0;nFindTypes(sc, name, parent_decl_ctx, append, max_matches, types); } return 0; } size_t Module::FindTypesInNamespace (const SymbolContext& sc, const ConstString &type_name, const CompilerDeclContext *parent_decl_ctx, size_t max_matches, TypeList& type_list) { const bool append = true; TypeMap types_map; size_t num_types = FindTypes_Impl(sc, type_name, parent_decl_ctx, append, max_matches, types_map); if (num_types > 0) sc.SortTypeList(types_map, type_list); return num_types; } lldb::TypeSP Module::FindFirstType (const SymbolContext& sc, const ConstString &name, bool exact_match) { TypeList type_list; const size_t num_matches = FindTypes (sc, name, exact_match, 1, type_list); if (num_matches) return type_list.GetTypeAtIndex(0); return TypeSP(); } size_t Module::FindTypes (const SymbolContext& sc, const ConstString &name, bool exact_match, size_t max_matches, TypeList& types) { size_t num_matches = 0; const char *type_name_cstr = name.GetCString(); std::string type_scope; std::string type_basename; const bool append = true; TypeClass type_class = eTypeClassAny; TypeMap typesmap; if (Type::GetTypeScopeAndBasename (type_name_cstr, type_scope, type_basename, type_class)) { // Check if "name" starts with "::" which means the qualified type starts // from the root namespace and implies and exact match. The typenames we // get back from clang do not start with "::" so we need to strip this off // in order to get the qualified names to match if (type_scope.size() >= 2 && type_scope[0] == ':' && type_scope[1] == ':') { type_scope.erase(0,2); exact_match = true; } ConstString type_basename_const_str (type_basename.c_str()); if (FindTypes_Impl(sc, type_basename_const_str, NULL, append, max_matches, typesmap)) { typesmap.RemoveMismatchedTypes (type_scope, type_basename, type_class, exact_match); num_matches = typesmap.GetSize(); } } else { // The type is not in a namespace/class scope, just search for it by basename if (type_class != eTypeClassAny) { // The "type_name_cstr" will have been modified if we have a valid type class // prefix (like "struct", "class", "union", "typedef" etc). FindTypes_Impl(sc, ConstString(type_name_cstr), NULL, append, max_matches, typesmap); typesmap.RemoveMismatchedTypes (type_class); num_matches = typesmap.GetSize(); } else { num_matches = FindTypes_Impl(sc, name, NULL, append, max_matches, typesmap); } } if (num_matches > 0) sc.SortTypeList(typesmap, types); return num_matches; } SymbolVendor* Module::GetSymbolVendor (bool can_create, lldb_private::Stream *feedback_strm) { if (m_did_load_symbol_vendor.load() == false) { Mutex::Locker locker (m_mutex); if (m_did_load_symbol_vendor.load() == false && can_create) { ObjectFile *obj_file = GetObjectFile (); if (obj_file != NULL) { Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); m_symfile_ap.reset(SymbolVendor::FindPlugin(shared_from_this(), feedback_strm)); m_did_load_symbol_vendor = true; } } } return m_symfile_ap.get(); } void Module::SetFileSpecAndObjectName (const FileSpec &file, const ConstString &object_name) { // Container objects whose paths do not specify a file directly can call // this function to correct the file and object names. m_file = file; m_mod_time = file.GetModificationTime(); m_object_name = object_name; } const ArchSpec& Module::GetArchitecture () const { return m_arch; } std::string Module::GetSpecificationDescription () const { std::string spec(GetFileSpec().GetPath()); if (m_object_name) { spec += '('; spec += m_object_name.GetCString(); spec += ')'; } return spec; } void Module::GetDescription (Stream *s, lldb::DescriptionLevel level) { Mutex::Locker locker (m_mutex); if (level >= eDescriptionLevelFull) { if (m_arch.IsValid()) s->Printf("(%s) ", m_arch.GetArchitectureName()); } if (level == eDescriptionLevelBrief) { const char *filename = m_file.GetFilename().GetCString(); if (filename) s->PutCString (filename); } else { char path[PATH_MAX]; if (m_file.GetPath(path, sizeof(path))) s->PutCString(path); } const char *object_name = m_object_name.GetCString(); if (object_name) s->Printf("(%s)", object_name); } void Module::ReportError (const char *format, ...) { if (format && format[0]) { StreamString strm; strm.PutCString("error: "); GetDescription(&strm, lldb::eDescriptionLevelBrief); strm.PutChar (' '); va_list args; va_start (args, format); strm.PrintfVarArg(format, args); va_end (args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len-1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } Host::SystemLog (Host::eSystemLogError, "%s", strm.GetString().c_str()); } } bool Module::FileHasChanged () const { if (m_file_has_changed == false) m_file_has_changed = (m_file.GetModificationTime() != m_mod_time); return m_file_has_changed; } void Module::ReportErrorIfModifyDetected (const char *format, ...) { if (m_first_file_changed_log == false) { if (FileHasChanged ()) { m_first_file_changed_log = true; if (format) { StreamString strm; strm.PutCString("error: the object file "); GetDescription(&strm, lldb::eDescriptionLevelFull); strm.PutCString (" has been modified\n"); va_list args; va_start (args, format); strm.PrintfVarArg(format, args); va_end (args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len-1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } strm.PutCString("The debug session should be aborted as the original debug information has been overwritten.\n"); Host::SystemLog (Host::eSystemLogError, "%s", strm.GetString().c_str()); } } } } void Module::ReportWarning (const char *format, ...) { if (format && format[0]) { StreamString strm; strm.PutCString("warning: "); GetDescription(&strm, lldb::eDescriptionLevelFull); strm.PutChar (' '); va_list args; va_start (args, format); strm.PrintfVarArg(format, args); va_end (args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len-1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } Host::SystemLog (Host::eSystemLogWarning, "%s", strm.GetString().c_str()); } } void Module::LogMessage (Log *log, const char *format, ...) { if (log) { StreamString log_message; GetDescription(&log_message, lldb::eDescriptionLevelFull); log_message.PutCString (": "); va_list args; va_start (args, format); log_message.PrintfVarArg (format, args); va_end (args); log->PutCString(log_message.GetString().c_str()); } } void Module::LogMessageVerboseBacktrace (Log *log, const char *format, ...) { if (log) { StreamString log_message; GetDescription(&log_message, lldb::eDescriptionLevelFull); log_message.PutCString (": "); va_list args; va_start (args, format); log_message.PrintfVarArg (format, args); va_end (args); if (log->GetVerbose()) { std::string back_trace; llvm::raw_string_ostream stream(back_trace); llvm::sys::PrintStackTrace(stream); log_message.PutCString(back_trace.c_str()); } log->PutCString(log_message.GetString().c_str()); } } void Module::Dump(Stream *s) { Mutex::Locker locker (m_mutex); //s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); s->Indent(); s->Printf("Module %s%s%s%s\n", m_file.GetPath().c_str(), m_object_name ? "(" : "", m_object_name ? m_object_name.GetCString() : "", m_object_name ? ")" : ""); s->IndentMore(); ObjectFile *objfile = GetObjectFile (); if (objfile) objfile->Dump(s); SymbolVendor *symbols = GetSymbolVendor (); if (symbols) symbols->Dump(s); s->IndentLess(); } TypeList* Module::GetTypeList () { SymbolVendor *symbols = GetSymbolVendor (); if (symbols) return &symbols->GetTypeList(); return NULL; } const ConstString & Module::GetObjectName() const { return m_object_name; } ObjectFile * Module::GetObjectFile() { if (m_did_load_objfile.load() == false) { Mutex::Locker locker (m_mutex); if (m_did_load_objfile.load() == false) { Timer scoped_timer(__PRETTY_FUNCTION__, "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); DataBufferSP data_sp; lldb::offset_t data_offset = 0; const lldb::offset_t file_size = m_file.GetByteSize(); if (file_size > m_object_offset) { m_did_load_objfile = true; m_objfile_sp = ObjectFile::FindPlugin (shared_from_this(), &m_file, m_object_offset, file_size - m_object_offset, data_sp, data_offset); if (m_objfile_sp) { // Once we get the object file, update our module with the object file's // architecture since it might differ in vendor/os if some parts were // unknown. But since the matching arch might already be more specific // than the generic COFF architecture, only merge in those values that // overwrite unspecified unknown values. ArchSpec new_arch; m_objfile_sp->GetArchitecture(new_arch); m_arch.MergeFrom(new_arch); } else { ReportError ("failed to load objfile for %s", GetFileSpec().GetPath().c_str()); } } } } return m_objfile_sp.get(); } SectionList * Module::GetSectionList() { // Populate m_unified_sections_ap with sections from objfile. if (m_sections_ap.get() == NULL) { ObjectFile *obj_file = GetObjectFile(); if (obj_file) obj_file->CreateSections(*GetUnifiedSectionList()); } return m_sections_ap.get(); } void Module::SectionFileAddressesChanged () { ObjectFile *obj_file = GetObjectFile (); if (obj_file) obj_file->SectionFileAddressesChanged (); SymbolVendor* sym_vendor = GetSymbolVendor(); if (sym_vendor) sym_vendor->SectionFileAddressesChanged (); } SectionList * Module::GetUnifiedSectionList() { // Populate m_unified_sections_ap with sections from objfile. if (m_sections_ap.get() == NULL) m_sections_ap.reset(new SectionList()); return m_sections_ap.get(); } const Symbol * Module::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type) { Timer scoped_timer(__PRETTY_FUNCTION__, "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)", name.AsCString(), symbol_type); SymbolVendor* sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) return symtab->FindFirstSymbolWithNameAndType (name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny); } return NULL; } void Module::SymbolIndicesToSymbolContextList (Symtab *symtab, std::vector &symbol_indexes, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. size_t num_indices = symbol_indexes.size(); if (num_indices > 0) { SymbolContext sc; CalculateSymbolContext (&sc); for (size_t i = 0; i < num_indices; i++) { sc.symbol = symtab->SymbolAtIndex (symbol_indexes[i]); if (sc.symbol) sc_list.Append (sc); } } } size_t Module::FindFunctionSymbols (const ConstString &name, uint32_t name_type_mask, SymbolContextList& sc_list) { Timer scoped_timer(__PRETTY_FUNCTION__, "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)", name.AsCString(), name_type_mask); SymbolVendor* sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) return symtab->FindFunctionSymbols (name, name_type_mask, sc_list); } return 0; } size_t Module::FindSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. Timer scoped_timer(__PRETTY_FUNCTION__, "Module::FindSymbolsWithNameAndType (name = %s, type = %i)", name.AsCString(), symbol_type); const size_t initial_size = sc_list.GetSize(); SymbolVendor* sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->FindAllSymbolsWithNameAndType (name, symbol_type, symbol_indexes); SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); } } return sc_list.GetSize() - initial_size; } size_t Module::FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, SymbolType symbol_type, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. Timer scoped_timer(__PRETTY_FUNCTION__, "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)", regex.GetText(), symbol_type); const size_t initial_size = sc_list.GetSize(); SymbolVendor* sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->FindAllSymbolsMatchingRexExAndType (regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); SymbolIndicesToSymbolContextList (symtab, symbol_indexes, sc_list); } } return sc_list.GetSize() - initial_size; } void Module::SetSymbolFileFileSpec (const FileSpec &file) { if (!file.Exists()) return; if (m_symfile_ap) { // Remove any sections in the unified section list that come from the current symbol vendor. SectionList *section_list = GetSectionList(); SymbolFile *symbol_file = m_symfile_ap->GetSymbolFile(); if (section_list && symbol_file) { ObjectFile *obj_file = symbol_file->GetObjectFile(); // Make sure we have an object file and that the symbol vendor's objfile isn't // the same as the module's objfile before we remove any sections for it... if (obj_file) { // Check to make sure we aren't trying to specify the file we already have if (obj_file->GetFileSpec() == file) { // We are being told to add the exact same file that we already have // we don't have to do anything. return; } // Cleare the current symtab as we are going to replace it with a new one obj_file->ClearSymtab(); // The symbol file might be a directory bundle ("/tmp/a.out.dSYM") instead // of a full path to the symbol file within the bundle // ("/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out"). So we need to check this if (file.IsDirectory()) { std::string new_path(file.GetPath()); std::string old_path(obj_file->GetFileSpec().GetPath()); if (old_path.find(new_path) == 0) { // We specified the same bundle as the symbol file that we already have return; } } if (obj_file != m_objfile_sp.get()) { size_t num_sections = section_list->GetNumSections (0); for (size_t idx = num_sections; idx > 0; --idx) { lldb::SectionSP section_sp (section_list->GetSectionAtIndex (idx - 1)); if (section_sp->GetObjectFile() == obj_file) { section_list->DeleteSection (idx - 1); } } } } } // Keep all old symbol files around in case there are any lingering type references in // any SBValue objects that might have been handed out. m_old_symfiles.push_back(std::move(m_symfile_ap)); } m_symfile_spec = file; m_symfile_ap.reset(); m_did_load_symbol_vendor = false; } bool Module::IsExecutable () { if (GetObjectFile() == NULL) return false; else return GetObjectFile()->IsExecutable(); } bool Module::IsLoadedInTarget (Target *target) { ObjectFile *obj_file = GetObjectFile(); if (obj_file) { SectionList *sections = GetSectionList(); if (sections != NULL) { size_t num_sections = sections->GetSize(); for (size_t sect_idx = 0; sect_idx < num_sections; sect_idx++) { SectionSP section_sp = sections->GetSectionAtIndex(sect_idx); if (section_sp->GetLoadBaseAddress(target) != LLDB_INVALID_ADDRESS) { return true; } } } } return false; } bool Module::LoadScriptingResourceInTarget (Target *target, Error& error, Stream* feedback_stream) { if (!target) { error.SetErrorString("invalid destination Target"); return false; } LoadScriptFromSymFile should_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); if (should_load == eLoadScriptFromSymFileFalse) return false; Debugger &debugger = target->GetDebugger(); const ScriptLanguage script_language = debugger.GetScriptLanguage(); if (script_language != eScriptLanguageNone) { PlatformSP platform_sp(target->GetPlatform()); if (!platform_sp) { error.SetErrorString("invalid Platform"); return false; } FileSpecList file_specs = platform_sp->LocateExecutableScriptingResources (target, *this, feedback_stream); const uint32_t num_specs = file_specs.GetSize(); if (num_specs) { ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); if (script_interpreter) { for (uint32_t i=0; iPrintf("warning: '%s' contains a debug script. To run this script in " "this debug session:\n\n command script import \"%s\"\n\n" "To run all discovered debug scripts in this session:\n\n" " settings set target.load-script-from-symbol-file true\n", GetFileSpec().GetFileNameStrippingExtension().GetCString(), scripting_fspec.GetPath().c_str()); return false; } StreamString scripting_stream; scripting_fspec.Dump(&scripting_stream); const bool can_reload = true; const bool init_lldb_globals = false; bool did_load = script_interpreter->LoadScriptingModule(scripting_stream.GetData(), can_reload, init_lldb_globals, error); if (!did_load) return false; } } } else { error.SetErrorString("invalid ScriptInterpreter"); return false; } } } return true; } bool Module::SetArchitecture (const ArchSpec &new_arch) { if (!m_arch.IsValid()) { m_arch = new_arch; return true; } return m_arch.IsCompatibleMatch(new_arch); } bool Module::SetLoadAddress (Target &target, lldb::addr_t value, bool value_is_offset, bool &changed) { ObjectFile *object_file = GetObjectFile(); if (object_file) { changed = object_file->SetLoadAddress(target, value, value_is_offset); return true; } else { changed = false; } return false; } bool Module::MatchesModuleSpec (const ModuleSpec &module_ref) { const UUID &uuid = module_ref.GetUUID(); if (uuid.IsValid()) { // If the UUID matches, then nothing more needs to match... if (uuid == GetUUID()) return true; else return false; } const FileSpec &file_spec = module_ref.GetFileSpec(); if (file_spec) { if (!FileSpec::Equal (file_spec, m_file, (bool)file_spec.GetDirectory()) && !FileSpec::Equal (file_spec, m_platform_file, (bool)file_spec.GetDirectory())) return false; } const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec(); if (platform_file_spec) { if (!FileSpec::Equal (platform_file_spec, GetPlatformFileSpec (), (bool)platform_file_spec.GetDirectory())) return false; } const ArchSpec &arch = module_ref.GetArchitecture(); if (arch.IsValid()) { if (!m_arch.IsCompatibleMatch(arch)) return false; } const ConstString &object_name = module_ref.GetObjectName(); if (object_name) { if (object_name != GetObjectName()) return false; } return true; } bool Module::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const { Mutex::Locker locker (m_mutex); return m_source_mappings.FindFile (orig_spec, new_spec); } bool Module::RemapSourceFile (const char *path, std::string &new_path) const { Mutex::Locker locker (m_mutex); return m_source_mappings.RemapPath(path, new_path); } uint32_t Module::GetVersion (uint32_t *versions, uint32_t num_versions) { ObjectFile *obj_file = GetObjectFile(); if (obj_file) return obj_file->GetVersion (versions, num_versions); if (versions && num_versions) { for (uint32_t i=0; im_objfile_sp.reset (new ObjectFileJIT (module_sp, delegate_sp)); if (module_sp->m_objfile_sp) { // Once we get the object file, update our module with the object file's // architecture since it might differ in vendor/os if some parts were // unknown. module_sp->m_objfile_sp->GetArchitecture (module_sp->m_arch); } return module_sp; } return ModuleSP(); } bool Module::GetIsDynamicLinkEditor() { ObjectFile * obj_file = GetObjectFile (); if (obj_file) return obj_file->GetIsDynamicLinkEditor(); return false; } Index: vendor/lldb/dist/source/Expression/IRDynamicChecks.cpp =================================================================== --- vendor/lldb/dist/source/Expression/IRDynamicChecks.cpp (revision 295597) +++ vendor/lldb/dist/source/Expression/IRDynamicChecks.cpp (revision 295598) @@ -1,675 +1,675 @@ //===-- IRDynamicChecks.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/Support/raw_ostream.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/Value.h" // Project includes #include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/Log.h" #include "lldb/Expression/UtilityFunction.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" using namespace llvm; using namespace lldb_private; static char ID; -#define VALID_POINTER_CHECK_NAME "$__lldb_valid_pointer_check" +#define VALID_POINTER_CHECK_NAME "_$__lldb_valid_pointer_check" #define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check" static const char g_valid_pointer_check_text[] = "extern \"C\" void\n" -"$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n" +"_$__lldb_valid_pointer_check (unsigned char *$__lldb_arg_ptr)\n" "{\n" " unsigned char $__lldb_local_val = *$__lldb_arg_ptr;\n" "}"; DynamicCheckerFunctions::DynamicCheckerFunctions() = default; DynamicCheckerFunctions::~DynamicCheckerFunctions() = default; bool DynamicCheckerFunctions::Install(Stream &error_stream, ExecutionContext &exe_ctx) { Error error; m_valid_pointer_check.reset(exe_ctx.GetTargetRef().GetUtilityFunctionForLanguage(g_valid_pointer_check_text, lldb::eLanguageTypeC, VALID_POINTER_CHECK_NAME, error)); if (error.Fail()) return false; if (!m_valid_pointer_check->Install(error_stream, exe_ctx)) return false; Process *process = exe_ctx.GetProcessPtr(); if (process) { ObjCLanguageRuntime *objc_language_runtime = process->GetObjCLanguageRuntime(); if (objc_language_runtime) { m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(VALID_OBJC_OBJECT_CHECK_NAME)); if (!m_objc_object_check->Install(error_stream, exe_ctx)) return false; } } return true; } bool DynamicCheckerFunctions::DoCheckersExplainStop (lldb::addr_t addr, Stream &message) { // FIXME: We have to get the checkers to know why they scotched the call in more detail, // so we can print a better message here. if (m_valid_pointer_check && m_valid_pointer_check->ContainsAddress(addr)) { message.Printf ("Attempted to dereference an invalid pointer."); return true; } else if (m_objc_object_check && m_objc_object_check->ContainsAddress(addr)) { message.Printf ("Attempted to dereference an invalid ObjC Object or send it an unrecognized selector"); return true; } return false; } static std::string PrintValue(llvm::Value *V, bool truncate = false) { std::string s; raw_string_ostream rso(s); V->print(rso); rso.flush(); if (truncate) s.resize(s.length() - 1); return s; } //---------------------------------------------------------------------- /// @class Instrumenter IRDynamicChecks.cpp /// @brief Finds and instruments individual LLVM IR instructions /// /// When instrumenting LLVM IR, it is frequently desirable to first search /// for instructions, and then later modify them. This way iterators /// remain intact, and multiple passes can look at the same code base without /// treading on each other's toes. /// /// The Instrumenter class implements this functionality. A client first /// calls Inspect on a function, which populates a list of instructions to /// be instrumented. Then, later, when all passes' Inspect functions have /// been called, the client calls Instrument, which adds the desired /// instrumentation. /// /// A subclass of Instrumenter must override InstrumentInstruction, which /// is responsible for adding whatever instrumentation is necessary. /// /// A subclass of Instrumenter may override: /// /// - InspectInstruction [default: does nothing] /// /// - InspectBasicBlock [default: iterates through the instructions in a /// basic block calling InspectInstruction] /// /// - InspectFunction [default: iterates through the basic blocks in a /// function calling InspectBasicBlock] //---------------------------------------------------------------------- class Instrumenter { public: //------------------------------------------------------------------ /// Constructor /// /// @param[in] module /// The module being instrumented. //------------------------------------------------------------------ Instrumenter (llvm::Module &module, DynamicCheckerFunctions &checker_functions) : m_module(module), m_checker_functions(checker_functions), m_i8ptr_ty(nullptr), m_intptr_ty(nullptr) { } virtual ~Instrumenter() = default; //------------------------------------------------------------------ /// Inspect a function to find instructions to instrument /// /// @param[in] function /// The function to inspect. /// /// @return /// True on success; false on error. //------------------------------------------------------------------ bool Inspect (llvm::Function &function) { return InspectFunction(function); } //------------------------------------------------------------------ /// Instrument all the instructions found by Inspect() /// /// @return /// True on success; false on error. //------------------------------------------------------------------ bool Instrument () { for (InstIterator ii = m_to_instrument.begin(), last_ii = m_to_instrument.end(); ii != last_ii; ++ii) { if (!InstrumentInstruction(*ii)) return false; } return true; } protected: //------------------------------------------------------------------ /// Add instrumentation to a single instruction /// /// @param[in] inst /// The instruction to be instrumented. /// /// @return /// True on success; false otherwise. //------------------------------------------------------------------ virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0; //------------------------------------------------------------------ /// Register a single instruction to be instrumented /// /// @param[in] inst /// The instruction to be instrumented. //------------------------------------------------------------------ void RegisterInstruction(llvm::Instruction &i) { m_to_instrument.push_back(&i); } //------------------------------------------------------------------ /// Determine whether a single instruction is interesting to /// instrument, and, if so, call RegisterInstruction /// /// @param[in] i /// The instruction to be inspected. /// /// @return /// False if there was an error scanning; true otherwise. //------------------------------------------------------------------ virtual bool InspectInstruction(llvm::Instruction &i) { return true; } //------------------------------------------------------------------ /// Scan a basic block to see if any instructions are interesting /// /// @param[in] bb /// The basic block to be inspected. /// /// @return /// False if there was an error scanning; true otherwise. //------------------------------------------------------------------ virtual bool InspectBasicBlock(llvm::BasicBlock &bb) { for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end(); ii != last_ii; ++ii) { if (!InspectInstruction(*ii)) return false; } return true; } //------------------------------------------------------------------ /// Scan a function to see if any instructions are interesting /// /// @param[in] f /// The function to be inspected. /// /// @return /// False if there was an error scanning; true otherwise. //------------------------------------------------------------------ virtual bool InspectFunction(llvm::Function &f) { for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); bbi != last_bbi; ++bbi) { if (!InspectBasicBlock(*bbi)) return false; } return true; } //------------------------------------------------------------------ /// Build a function pointer for a function with signature /// void (*)(uint8_t*) with a given address /// /// @param[in] start_address /// The address of the function. /// /// @return /// The function pointer, for use in a CallInst. //------------------------------------------------------------------ llvm::Value *BuildPointerValidatorFunc(lldb::addr_t start_address) { llvm::Type *param_array[1]; param_array[0] = const_cast(GetI8PtrTy()); ArrayRef params(param_array, 1); FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); Constant *fun_addr_int = ConstantInt::get(GetIntptrTy(), start_address, false); return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); } //------------------------------------------------------------------ /// Build a function pointer for a function with signature /// void (*)(uint8_t*, uint8_t*) with a given address /// /// @param[in] start_address /// The address of the function. /// /// @return /// The function pointer, for use in a CallInst. //------------------------------------------------------------------ llvm::Value *BuildObjectCheckerFunc(lldb::addr_t start_address) { llvm::Type *param_array[2]; param_array[0] = const_cast(GetI8PtrTy()); param_array[1] = const_cast(GetI8PtrTy()); ArrayRef params(param_array, 2); FunctionType *fun_ty = FunctionType::get(llvm::Type::getVoidTy(m_module.getContext()), params, true); PointerType *fun_ptr_ty = PointerType::getUnqual(fun_ty); Constant *fun_addr_int = ConstantInt::get(GetIntptrTy(), start_address, false); return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); } PointerType *GetI8PtrTy() { if (!m_i8ptr_ty) m_i8ptr_ty = llvm::Type::getInt8PtrTy(m_module.getContext()); return m_i8ptr_ty; } IntegerType *GetIntptrTy() { if (!m_intptr_ty) { llvm::DataLayout data_layout(&m_module); m_intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), data_layout.getPointerSizeInBits()); } return m_intptr_ty; } typedef std::vector InstVector; typedef InstVector::iterator InstIterator; InstVector m_to_instrument; ///< List of instructions the inspector found llvm::Module &m_module; ///< The module which is being instrumented DynamicCheckerFunctions &m_checker_functions; ///< The dynamic checker functions for the process private: PointerType *m_i8ptr_ty; IntegerType *m_intptr_ty; }; class ValidPointerChecker : public Instrumenter { public: ValidPointerChecker (llvm::Module &module, DynamicCheckerFunctions &checker_functions) : Instrumenter(module, checker_functions), m_valid_pointer_check_func(nullptr) { } ~ValidPointerChecker() override = default; protected: bool InstrumentInstruction(llvm::Instruction *inst) override { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log) log->Printf("Instrumenting load/store instruction: %s\n", PrintValue(inst).c_str()); if (!m_valid_pointer_check_func) m_valid_pointer_check_func = BuildPointerValidatorFunc(m_checker_functions.m_valid_pointer_check->StartAddress()); llvm::Value *dereferenced_ptr = nullptr; if (llvm::LoadInst *li = dyn_cast (inst)) dereferenced_ptr = li->getPointerOperand(); else if (llvm::StoreInst *si = dyn_cast (inst)) dereferenced_ptr = si->getPointerOperand(); else return false; // Insert an instruction to cast the loaded value to int8_t* BitCastInst *bit_cast = new BitCastInst(dereferenced_ptr, GetI8PtrTy(), "", inst); // Insert an instruction to call the helper with the result llvm::Value *arg_array[1]; arg_array[0] = bit_cast; llvm::ArrayRef args(arg_array, 1); CallInst::Create(m_valid_pointer_check_func, args, "", inst); return true; } bool InspectInstruction(llvm::Instruction &i) override { if (dyn_cast (&i) || dyn_cast (&i)) RegisterInstruction(i); return true; } private: llvm::Value *m_valid_pointer_check_func; }; class ObjcObjectChecker : public Instrumenter { public: ObjcObjectChecker(llvm::Module &module, DynamicCheckerFunctions &checker_functions) : Instrumenter(module, checker_functions), m_objc_object_check_func(nullptr) { } ~ObjcObjectChecker() override = default; enum msgSend_type { eMsgSend = 0, eMsgSendSuper, eMsgSendSuper_stret, eMsgSend_fpret, eMsgSend_stret }; std::map msgSend_types; protected: bool InstrumentInstruction(llvm::Instruction *inst) override { CallInst *call_inst = dyn_cast(inst); if (!call_inst) return false; // call_inst really shouldn't be nullptr, because otherwise InspectInstruction wouldn't have registered it if (!m_objc_object_check_func) m_objc_object_check_func = BuildObjectCheckerFunc(m_checker_functions.m_objc_object_check->StartAddress()); // id objc_msgSend(id theReceiver, SEL theSelector, ...) llvm::Value *target_object; llvm::Value *selector; switch (msgSend_types[inst]) { case eMsgSend: case eMsgSend_fpret: target_object = call_inst->getArgOperand(0); selector = call_inst->getArgOperand(1); break; case eMsgSend_stret: target_object = call_inst->getArgOperand(1); selector = call_inst->getArgOperand(2); break; case eMsgSendSuper: case eMsgSendSuper_stret: return true; } // These objects should always be valid according to Sean Calannan assert (target_object); assert (selector); // Insert an instruction to cast the receiver id to int8_t* BitCastInst *bit_cast = new BitCastInst(target_object, GetI8PtrTy(), "", inst); // Insert an instruction to call the helper with the result llvm::Value *arg_array[2]; arg_array[0] = bit_cast; arg_array[1] = selector; ArrayRef args(arg_array, 2); CallInst::Create(m_objc_object_check_func, args, "", inst); return true; } bool InspectInstruction(llvm::Instruction &i) override { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); CallInst *call_inst = dyn_cast(&i); if (call_inst) { // This metadata is set by IRForTarget::MaybeHandleCall(). MDNode *metadata = call_inst->getMetadata("lldb.call.realName"); if (!metadata) return true; if (metadata->getNumOperands() != 1) { if (log) log->Printf("Function call metadata has %d operands for [%p] %s", metadata->getNumOperands(), static_cast(call_inst), PrintValue(call_inst).c_str()); return false; } MDString *real_name = dyn_cast(metadata->getOperand(0)); if (!real_name) { if (log) log->Printf("Function call metadata is not an MDString for [%p] %s", static_cast(call_inst), PrintValue(call_inst).c_str()); return false; } std::string name_str = real_name->getString(); const char* name_cstr = name_str.c_str(); if (log) log->Printf("Found call to %s: %s\n", name_cstr, PrintValue(call_inst).c_str()); if (name_str.find("objc_msgSend") == std::string::npos) return true; if (!strcmp(name_cstr, "objc_msgSend")) { RegisterInstruction(i); msgSend_types[&i] = eMsgSend; return true; } if (!strcmp(name_cstr, "objc_msgSend_stret")) { RegisterInstruction(i); msgSend_types[&i] = eMsgSend_stret; return true; } if (!strcmp(name_cstr, "objc_msgSend_fpret")) { RegisterInstruction(i); msgSend_types[&i] = eMsgSend_fpret; return true; } if (!strcmp(name_cstr, "objc_msgSendSuper")) { RegisterInstruction(i); msgSend_types[&i] = eMsgSendSuper; return true; } if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) { RegisterInstruction(i); msgSend_types[&i] = eMsgSendSuper_stret; return true; } if (log) log->Printf("Function name '%s' contains 'objc_msgSend' but is not handled", name_str.c_str()); return true; } return true; } private: llvm::Value *m_objc_object_check_func; }; IRDynamicChecks::IRDynamicChecks(DynamicCheckerFunctions &checker_functions, const char *func_name) : ModulePass(ID), m_func_name(func_name), m_checker_functions(checker_functions) { } IRDynamicChecks::~IRDynamicChecks() = default; bool IRDynamicChecks::runOnModule(llvm::Module &M) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); llvm::Function* function = M.getFunction(StringRef(m_func_name.c_str())); if (!function) { if (log) log->Printf("Couldn't find %s() in the module", m_func_name.c_str()); return false; } if (m_checker_functions.m_valid_pointer_check) { ValidPointerChecker vpc(M, m_checker_functions); if (!vpc.Inspect(*function)) return false; if (!vpc.Instrument()) return false; } if (m_checker_functions.m_objc_object_check) { ObjcObjectChecker ooc(M, m_checker_functions); if (!ooc.Inspect(*function)) return false; if (!ooc.Instrument()) return false; } if (log && log->GetVerbose()) { std::string s; raw_string_ostream oss(s); M.print(oss, nullptr); oss.flush(); log->Printf ("Module after dynamic checks: \n%s", s.c_str()); } return true; } void IRDynamicChecks::assignPassManager(PMStack &PMS, PassManagerType T) { } PassManagerType IRDynamicChecks::getPotentialPassManagerType() const { return PMT_ModulePassManager; } Index: vendor/lldb/dist/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp (revision 295597) +++ vendor/lldb/dist/source/Plugins/ABI/SysV-mips/ABISysV_mips.cpp (revision 295598) @@ -1,625 +1,636 @@ //===-- ABISysV_mips.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ABISysV_mips.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectRegister.h" #include "lldb/Core/ValueObjectMemory.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Target/Target.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" using namespace lldb; using namespace lldb_private; enum dwarf_regnums { dwarf_r0 = 0, dwarf_r1, dwarf_r2, dwarf_r3, dwarf_r4, dwarf_r5, dwarf_r6, dwarf_r7, dwarf_r8, dwarf_r9, dwarf_r10, dwarf_r11, dwarf_r12, dwarf_r13, dwarf_r14, dwarf_r15, dwarf_r16, dwarf_r17, dwarf_r18, dwarf_r19, dwarf_r20, dwarf_r21, dwarf_r22, dwarf_r23, dwarf_r24, dwarf_r25, dwarf_r26, dwarf_r27, dwarf_r28, dwarf_r29, dwarf_r30, dwarf_r31, dwarf_sr, dwarf_lo, dwarf_hi, dwarf_bad, dwarf_cause, dwarf_pc }; static const RegisterInfo g_register_infos[] = { // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGINS LLDB NATIVE VALUE REGS INVALIDATE REGS // ======== ====== == === ============= =========== ============ ============== ============ ================= =================== ========== ================= { "r0" , "zero", 4, 0, eEncodingUint, eFormatHex, { dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r1" , "AT", 4, 0, eEncodingUint, eFormatHex, { dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r2" , "v0", 4, 0, eEncodingUint, eFormatHex, { dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r3" , "v1", 4, 0, eEncodingUint, eFormatHex, { dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r4" , "arg1", 4, 0, eEncodingUint, eFormatHex, { dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r5" , "arg2", 4, 0, eEncodingUint, eFormatHex, { dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r6" , "arg3", 4, 0, eEncodingUint, eFormatHex, { dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r7" , "arg4", 4, 0, eEncodingUint, eFormatHex, { dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r8" , "arg5", 4, 0, eEncodingUint, eFormatHex, { dwarf_r8, dwarf_r8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r9" , "arg6", 4, 0, eEncodingUint, eFormatHex, { dwarf_r9, dwarf_r9, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r10" , "arg7", 4, 0, eEncodingUint, eFormatHex, { dwarf_r10, dwarf_r10, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r11" , "arg8", 4, 0, eEncodingUint, eFormatHex, { dwarf_r11, dwarf_r11, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r12" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r13" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r14" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r15" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r16" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r17" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r18" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r19" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r20" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r21" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r22" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r23" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r24" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r25" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r26" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r27" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r28" , "gp", 4, 0, eEncodingUint, eFormatHex, { dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r29" , "sp", 4, 0, eEncodingUint, eFormatHex, { dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r30" , "fp", 4, 0, eEncodingUint, eFormatHex, { dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r31" , "ra", 4, 0, eEncodingUint, eFormatHex, { dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "sr" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "lo" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "hi" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "bad" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "cause" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "pc" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, }; static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); const lldb_private::RegisterInfo * ABISysV_mips::GetRegisterInfoArray (uint32_t &count) { count = k_num_register_infos; return g_register_infos; } size_t ABISysV_mips::GetRedZoneSize () const { return 0; } //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ ABISP ABISysV_mips::CreateInstance (const ArchSpec &arch) { static ABISP g_abi_sp; const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch(); if ((arch_type == llvm::Triple::mips) || (arch_type == llvm::Triple::mipsel)) { if (!g_abi_sp) g_abi_sp.reset (new ABISysV_mips); return g_abi_sp; } return ABISP(); } bool ABISysV_mips::PrepareTrivialCall (Thread &thread, addr_t sp, addr_t func_addr, addr_t return_addr, llvm::ArrayRef args) const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log) { StreamString s; s.Printf("ABISysV_mips::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, (uint64_t)return_addr); for (size_t i = 0; i < args.size(); ++i) s.Printf (", arg%zd = 0x%" PRIx64, i + 1, args[i]); s.PutCString (")"); log->PutCString(s.GetString().c_str()); } RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return false; const RegisterInfo *reg_info = NULL; RegisterValue reg_value; // Argument registers const char *reg_names[] = { "r4", "r5", "r6", "r7" }; llvm::ArrayRef::iterator ai = args.begin(), ae = args.end(); // Write arguments to registers for (size_t i = 0; i < llvm::array_lengthof(reg_names); ++i) { if (ai == ae) break; reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); if (log) log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1, args[i], reg_info->name); if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) return false; ++ai; } // If we have more than 4 arguments --Spill onto the stack if (ai != ae) { // No of arguments to go on stack size_t num_stack_regs = args.size(); // Allocate needed space for args on the stack sp -= (num_stack_regs * 4); // Keep the stack 8 byte aligned sp &= ~(8ull-1ull); // just using arg1 to get the right size const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); addr_t arg_pos = sp+16; size_t i = 4; for (; ai != ae; ++ai) { reg_value.SetUInt32(*ai); if (log) log->Printf("About to write arg%zd (0x%" PRIx64 ") at 0x%" PRIx64 "", i+1, args[i], arg_pos); if (reg_ctx->WriteRegisterValueToMemory(reg_info, arg_pos, reg_info->byte_size, reg_value).Fail()) return false; arg_pos += reg_info->byte_size; i++; } } Error error; const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *sp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0); + const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0); if (log) - log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0); + /* Write r0 with 0, in case we are stopped in syscall, + * such setting prevents automatic decrement of the PC. + * This clears the bug 23659 for MIPS. + */ + if (!reg_ctx->WriteRegisterFromUnsigned (r0_info, (uint64_t)0)) + return false; + + if (log) + log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + // Set "sp" to the requested value if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_info, sp)) return false; if (log) - log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr); + log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr); // Set "ra" to the return address if (!reg_ctx->WriteRegisterFromUnsigned (ra_reg_info, return_addr)) return false; if (log) log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr); // Set pc to the address of the called function. if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) return false; if (log) log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr); // All callers of position independent functions must place the address of the called function in t9 (r25) if (!reg_ctx->WriteRegisterFromUnsigned (r25_info, func_addr)) return false; return true; } bool ABISysV_mips::GetArgumentValues (Thread &thread, ValueList &values) const { return false; } Error ABISysV_mips::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) { Error error; if (!new_value_sp) { error.SetErrorString("Empty value object for return value."); return error; } CompilerType compiler_type = new_value_sp->GetCompilerType(); if (!compiler_type) { error.SetErrorString ("Null clang type for return value."); return error; } Thread *thread = frame_sp->GetThread().get(); bool is_signed; uint32_t count; bool is_complex; RegisterContext *reg_ctx = thread->GetRegisterContext().get(); bool set_it_simple = false; if (compiler_type.IsIntegerType (is_signed) || compiler_type.IsPointerType()) { DataExtractor data; Error data_error; size_t num_bytes = new_value_sp->GetData(data, data_error); if (data_error.Fail()) { error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); return error; } lldb::offset_t offset = 0; if (num_bytes <= 8) { const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0); if (num_bytes <= 4) { uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); if (reg_ctx->WriteRegisterFromUnsigned (r2_info, raw_value)) set_it_simple = true; } else { uint32_t raw_value = data.GetMaxU32(&offset, 4); if (reg_ctx->WriteRegisterFromUnsigned (r2_info, raw_value)) { const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0); uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); if (reg_ctx->WriteRegisterFromUnsigned (r3_info, raw_value)) set_it_simple = true; } } } else { error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); } } else if (compiler_type.IsFloatingPointType (count, is_complex)) { if (is_complex) error.SetErrorString ("We don't support returning complex values at present"); else error.SetErrorString ("We don't support returning float values at present"); } if (!set_it_simple) error.SetErrorString ("We only support setting simple integer return types at present."); return error; } ValueObjectSP ABISysV_mips::GetReturnValueObjectSimple (Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; return return_valobj_sp; } ValueObjectSP ABISysV_mips::GetReturnValueObjectImpl (Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; Value value; if (!return_compiler_type) return return_valobj_sp; ExecutionContext exe_ctx (thread.shared_from_this()); if (exe_ctx.GetTargetPtr() == NULL || exe_ctx.GetProcessPtr() == NULL) return return_valobj_sp; value.SetCompilerType(return_compiler_type); RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return return_valobj_sp; bool is_signed = false; bool is_complex = false; uint32_t count = 0; // In MIPS register "r2" (v0) holds the integer function return values const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoByName("r2", 0); size_t bit_width = return_compiler_type.GetBitSize(&thread); if (return_compiler_type.IsIntegerType (is_signed)) { switch (bit_width) { default: return return_valobj_sp; case 64: { const RegisterInfo *r3_reg_info = reg_ctx->GetRegisterInfoByName("r3", 0); uint64_t raw_value; raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX; raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r3_reg_info, 0) & UINT32_MAX)) << 32; if (is_signed) value.GetScalar() = (int64_t)raw_value; else value.GetScalar() = (uint64_t)raw_value; } break; case 32: if (is_signed) value.GetScalar() = (int32_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX); else value.GetScalar() = (uint32_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX); break; case 16: if (is_signed) value.GetScalar() = (int16_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX); else value.GetScalar() = (uint16_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT16_MAX); break; case 8: if (is_signed) value.GetScalar() = (int8_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX); else value.GetScalar() = (uint8_t)(reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT8_MAX); break; } } else if (return_compiler_type.IsPointerType ()) { uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_reg_info, 0) & UINT32_MAX; value.GetScalar() = ptr; } else if (return_compiler_type.IsAggregateType ()) { // Structure/Vector is always passed in memory and pointer to that memory is passed in r2. uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r2", 0), 0); // We have got the address. Create a memory object out of it return_valobj_sp = ValueObjectMemory::Create (&thread, "", Address (mem_address, NULL), return_compiler_type); return return_valobj_sp; } else if (return_compiler_type.IsFloatingPointType (count, is_complex)) { const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0); if (count == 1 && !is_complex) { switch (bit_width) { default: return return_valobj_sp; case 64: { static_assert(sizeof(double) == sizeof(uint64_t), ""); uint64_t raw_value; raw_value = reg_ctx->ReadRegisterAsUnsigned(f0_info, 0) & UINT32_MAX; raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(f1_info, 0) & UINT32_MAX)) << 32; value.GetScalar() = *reinterpret_cast(&raw_value); break; } case 32: { static_assert(sizeof(float) == sizeof(uint32_t), ""); uint32_t raw_value = reg_ctx->ReadRegisterAsUnsigned(f0_info, 0) & UINT32_MAX; value.GetScalar() = *reinterpret_cast(&raw_value); break; } } } else { // not handled yet return return_valobj_sp; } } else { // not handled yet return return_valobj_sp; } // If we get here, we have a valid Value, so make our ValueObject out of it: return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); return return_valobj_sp; } bool ABISysV_mips::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0); // The previous PC is in the RA row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("mips at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetReturnAddressRegister(dwarf_r31); return true; } bool ABISysV_mips::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0); row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("mips default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; } bool ABISysV_mips::RegisterIsVolatile (const RegisterInfo *reg_info) { return !RegisterIsCalleeSaved (reg_info); } bool ABISysV_mips::RegisterIsCalleeSaved (const RegisterInfo *reg_info) { if (reg_info) { // Preserved registers are : // r16-r23, r28, r29, r30, r31 const char *name = reg_info->name; if (name[0] == 'r') { switch (name[1]) { case '1': if (name[2] == '6' || name[2] == '7' || name[2] == '8' || name[2] == '9') // r16-r19 return name[3] == '\0'; break; case '2': if (name[2] == '0' || name[2] == '1' || name[2] == '2' || name[2] == '3' // r20-r23 || name[2] == '8' || name[2] == '9') // r28 and r29 return name[3] == '\0'; break; case '3': if (name[2] == '0' || name[2] == '1') // r30 and r31 return name[3] == '\0'; break; } if (name[0] == 'g' && name[1] == 'p' && name[2] == '\0') // gp (r28) return true; if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp (r29) return true; if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp (r30) return true; if (name[0] == 'r' && name[1] == 'a' && name[2] == '\0') // ra (r31) return true; } } return false; } void ABISysV_mips::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), "System V ABI for mips targets", CreateInstance); } void ABISysV_mips::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } lldb_private::ConstString ABISysV_mips::GetPluginNameStatic() { static ConstString g_name("sysv-mips"); return g_name; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString ABISysV_mips::GetPluginName() { return GetPluginNameStatic(); } uint32_t ABISysV_mips::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp (revision 295597) +++ vendor/lldb/dist/source/Plugins/ABI/SysV-mips64/ABISysV_mips64.cpp (revision 295598) @@ -1,820 +1,831 @@ //===-- ABISysV_mips64.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ABISysV_mips64.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectRegister.h" #include "lldb/Core/ValueObjectMemory.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Target/Target.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" using namespace lldb; using namespace lldb_private; enum dwarf_regnums { dwarf_r0 = 0, dwarf_r1, dwarf_r2, dwarf_r3, dwarf_r4, dwarf_r5, dwarf_r6, dwarf_r7, dwarf_r8, dwarf_r9, dwarf_r10, dwarf_r11, dwarf_r12, dwarf_r13, dwarf_r14, dwarf_r15, dwarf_r16, dwarf_r17, dwarf_r18, dwarf_r19, dwarf_r20, dwarf_r21, dwarf_r22, dwarf_r23, dwarf_r24, dwarf_r25, dwarf_r26, dwarf_r27, dwarf_r28, dwarf_r29, dwarf_r30, dwarf_r31, dwarf_sr, dwarf_lo, dwarf_hi, dwarf_bad, dwarf_cause, dwarf_pc }; static const RegisterInfo g_register_infos_mips64[] = { // NAME ALT SZ OFF ENCODING FORMAT EH_FRAME DWARF GENERIC PROCESS PLUGIN LLDB NATIVE VALUE REGS INVALIDATE REGS // ======== ====== == === ============= ========== ============= ================= ==================== ================= ==================== ========== =============== { "r0" , "zero", 8, 0, eEncodingUint, eFormatHex, { dwarf_r0, dwarf_r0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r1" , "AT", 8, 0, eEncodingUint, eFormatHex, { dwarf_r1, dwarf_r1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r2" , "v0", 8, 0, eEncodingUint, eFormatHex, { dwarf_r2, dwarf_r2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r3" , "v1", 8, 0, eEncodingUint, eFormatHex, { dwarf_r3, dwarf_r3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r4" , "arg1", 8, 0, eEncodingUint, eFormatHex, { dwarf_r4, dwarf_r4, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r5" , "arg2", 8, 0, eEncodingUint, eFormatHex, { dwarf_r5, dwarf_r5, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r6" , "arg3", 8, 0, eEncodingUint, eFormatHex, { dwarf_r6, dwarf_r6, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r7" , "arg4", 8, 0, eEncodingUint, eFormatHex, { dwarf_r7, dwarf_r7, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r8" , "arg5", 8, 0, eEncodingUint, eFormatHex, { dwarf_r8, dwarf_r8, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r9" , "arg6", 8, 0, eEncodingUint, eFormatHex, { dwarf_r9, dwarf_r9, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r10" , "arg7", 8, 0, eEncodingUint, eFormatHex, { dwarf_r10, dwarf_r10, LLDB_REGNUM_GENERIC_ARG7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r11" , "arg8", 8, 0, eEncodingUint, eFormatHex, { dwarf_r11, dwarf_r11, LLDB_REGNUM_GENERIC_ARG8, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r12" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r12, dwarf_r12, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r13" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r13, dwarf_r13, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r14" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r14, dwarf_r14, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r15" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r15, dwarf_r15, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r16" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r16, dwarf_r16, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r17" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r17, dwarf_r17, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r18" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r18, dwarf_r18, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r19" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r19, dwarf_r19, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r20" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r20, dwarf_r20, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r21" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r21, dwarf_r21, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r22" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r22, dwarf_r22, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r23" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r23, dwarf_r23, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r24" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r24, dwarf_r24, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r25" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r25, dwarf_r25, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r26" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r26, dwarf_r26, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r27" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_r27, dwarf_r27, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r28" , "gp", 8, 0, eEncodingUint, eFormatHex, { dwarf_r28, dwarf_r28, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r29" , "sp", 8, 0, eEncodingUint, eFormatHex, { dwarf_r29, dwarf_r29, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r30" , "fp", 8, 0, eEncodingUint, eFormatHex, { dwarf_r30, dwarf_r30, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "r31" , "ra", 8, 0, eEncodingUint, eFormatHex, { dwarf_r31, dwarf_r31, LLDB_REGNUM_GENERIC_RA, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "sr" , NULL, 4, 0, eEncodingUint, eFormatHex, { dwarf_sr, dwarf_sr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "lo" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_lo, dwarf_lo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "hi" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_hi, dwarf_hi, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "bad" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_bad, dwarf_bad, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "cause" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_cause, dwarf_cause, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, { "pc" , NULL, 8, 0, eEncodingUint, eFormatHex, { dwarf_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, }; static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos_mips64); const lldb_private::RegisterInfo * ABISysV_mips64::GetRegisterInfoArray (uint32_t &count) { count = k_num_register_infos; return g_register_infos_mips64; } size_t ABISysV_mips64::GetRedZoneSize () const { return 0; } //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ ABISP ABISysV_mips64::CreateInstance (const ArchSpec &arch) { static ABISP g_abi_sp; const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch(); if ((arch_type == llvm::Triple::mips64) || (arch_type == llvm::Triple::mips64el)) { if (!g_abi_sp) g_abi_sp.reset (new ABISysV_mips64); return g_abi_sp; } return ABISP(); } bool ABISysV_mips64::PrepareTrivialCall (Thread &thread, addr_t sp, addr_t func_addr, addr_t return_addr, llvm::ArrayRef args) const { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log) { StreamString s; s.Printf("ABISysV_mips64::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, (uint64_t)return_addr); for (size_t i = 0; i < args.size(); ++i) s.Printf (", arg%zd = 0x%" PRIx64, i + 1, args[i]); s.PutCString (")"); log->PutCString(s.GetString().c_str()); } RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return false; const RegisterInfo *reg_info = NULL; if (args.size() > 8) // TODO handle more than 8 arguments return false; for (size_t i = 0; i < args.size(); ++i) { reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); if (log) log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1, args[i], reg_info->name); if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) return false; } // First, align the SP if (log) log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, (uint64_t)sp, (uint64_t)(sp & ~0xfull)); sp &= ~(0xfull); // 16-byte alignment Error error; const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *sp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); const RegisterInfo *r25_info = reg_ctx->GetRegisterInfoByName("r25", 0); + const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("zero", 0); if (log) - log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + log->Printf("Writing R0: 0x%" PRIx64, (uint64_t)0); + /* Write r0 with 0, in case we are stopped in syscall, + * such setting prevents automatic decrement of the PC. + * This clears the bug 23659 for MIPS. + */ + if (!reg_ctx->WriteRegisterFromUnsigned (r0_info, (uint64_t)0)) + return false; + + if (log) + log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + // Set "sp" to the requested value if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_info, sp)) return false; if (log) - log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr); + log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr); // Set "ra" to the return address if (!reg_ctx->WriteRegisterFromUnsigned (ra_reg_info, return_addr)) return false; if (log) log->Printf("Writing PC: 0x%" PRIx64, (uint64_t)func_addr); // Set pc to the address of the called function. if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) return false; if (log) log->Printf("Writing r25: 0x%" PRIx64, (uint64_t)func_addr); // All callers of position independent functions must place the address of the called function in t9 (r25) if (!reg_ctx->WriteRegisterFromUnsigned (r25_info, func_addr)) return false; return true; } bool ABISysV_mips64::GetArgumentValues (Thread &thread, ValueList &values) const { return false; } Error ABISysV_mips64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) { Error error; if (!new_value_sp) { error.SetErrorString("Empty value object for return value."); return error; } CompilerType compiler_type = new_value_sp->GetCompilerType(); if (!compiler_type) { error.SetErrorString ("Null clang type for return value."); return error; } Thread *thread = frame_sp->GetThread().get(); RegisterContext *reg_ctx = thread->GetRegisterContext().get(); if (!reg_ctx) error.SetErrorString("no registers are available"); DataExtractor data; Error data_error; size_t num_bytes = new_value_sp->GetData(data, data_error); if (data_error.Fail()) { error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); return error; } const uint32_t type_flags = compiler_type.GetTypeInfo (NULL); if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) { if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer ) { lldb::offset_t offset = 0; if (num_bytes <= 16) { const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0); if (num_bytes <= 8) { uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); if (!reg_ctx->WriteRegisterFromUnsigned (r2_info, raw_value)) error.SetErrorString ("failed to write register r2"); } else { uint64_t raw_value = data.GetMaxU64(&offset, 8); if (reg_ctx->WriteRegisterFromUnsigned (r2_info, raw_value)) { const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0); raw_value = data.GetMaxU64(&offset, num_bytes - offset); if (!reg_ctx->WriteRegisterFromUnsigned (r3_info, raw_value)) error.SetErrorString ("failed to write register r3"); } else error.SetErrorString ("failed to write register r2"); } } else { error.SetErrorString("We don't support returning longer than 128 bit integer values at present."); } } else if (type_flags & eTypeIsFloat) { error.SetErrorString("TODO: Handle Float Types."); } } else if (type_flags & eTypeIsVector) { error.SetErrorString("returning vector values are not supported"); } return error; } ValueObjectSP ABISysV_mips64::GetReturnValueObjectSimple (Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; return return_valobj_sp; } ValueObjectSP ABISysV_mips64::GetReturnValueObjectImpl (Thread &thread, CompilerType &return_compiler_type) const { ValueObjectSP return_valobj_sp; Value value; Error error; ExecutionContext exe_ctx (thread.shared_from_this()); if (exe_ctx.GetTargetPtr() == NULL || exe_ctx.GetProcessPtr() == NULL) return return_valobj_sp; value.SetCompilerType(return_compiler_type); RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return return_valobj_sp; Target *target = exe_ctx.GetTargetPtr(); ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); const size_t byte_size = return_compiler_type.GetByteSize(nullptr); const uint32_t type_flags = return_compiler_type.GetTypeInfo (NULL); const RegisterInfo *r2_info = reg_ctx->GetRegisterInfoByName("r2", 0); const RegisterInfo *r3_info = reg_ctx->GetRegisterInfoByName("r3", 0); if (type_flags & eTypeIsScalar || type_flags & eTypeIsPointer) { value.SetValueType(Value::eValueTypeScalar); bool success = false; if (type_flags & eTypeIsInteger || type_flags & eTypeIsPointer) { // Extract the register context so we can read arguments from registers // In MIPS register "r2" (v0) holds the integer function return values uint64_t raw_value = reg_ctx->ReadRegisterAsUnsigned(r2_info, 0); const bool is_signed = (type_flags & eTypeIsSigned) != 0; switch (byte_size) { default: break; case sizeof(uint64_t): if (is_signed) value.GetScalar() = (int64_t)(raw_value); else value.GetScalar() = (uint64_t)(raw_value); success = true; break; case sizeof(uint32_t): if (is_signed) value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); else value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); success = true; break; case sizeof(uint16_t): if (is_signed) value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); else value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); success = true; break; case sizeof(uint8_t): if (is_signed) value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); else value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); success = true; break; } } else if (type_flags & eTypeIsFloat) { if (type_flags & eTypeIsComplex) { // Don't handle complex yet. } else { if (byte_size <= sizeof(long double)) { const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); const RegisterInfo *f2_info = reg_ctx->GetRegisterInfoByName("f2", 0); RegisterValue f0_value, f2_value; DataExtractor f0_data, f2_data; reg_ctx->ReadRegister (f0_info, f0_value); reg_ctx->ReadRegister (f2_info, f2_value); f0_value.GetData(f0_data); f2_value.GetData(f2_data); lldb::offset_t offset = 0; if (byte_size == sizeof(float)) { value.GetScalar() = (float) f0_data.GetFloat(&offset); success = true; } else if (byte_size == sizeof(double)) { value.GetScalar() = (double) f0_data.GetDouble(&offset); success = true; } else if (byte_size == sizeof(long double)) { DataExtractor *copy_from_extractor = NULL; DataBufferSP data_sp (new DataBufferHeap(16, 0)); DataExtractor return_ext (data_sp, target_byte_order, target->GetArchitecture().GetAddressByteSize()); if (target_byte_order == eByteOrderLittle) { f0_data.Append(f2_data); copy_from_extractor = &f0_data; } else { f2_data.Append(f0_data); copy_from_extractor = &f2_data; } copy_from_extractor->CopyByteOrderedData (0, byte_size, data_sp->GetBytes(), byte_size, target_byte_order); return_valobj_sp = ValueObjectConstResult::Create (&thread, return_compiler_type, ConstString(""), return_ext); return return_valobj_sp; } } } } if (success) return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass || type_flags & eTypeIsVector) { // Any structure of up to 16 bytes in size is returned in the registers. if (byte_size <= 16) { DataBufferSP data_sp (new DataBufferHeap(16, 0)); DataExtractor return_ext (data_sp, target_byte_order, target->GetArchitecture().GetAddressByteSize()); RegisterValue r2_value, r3_value, f0_value, f1_value, f2_value; uint32_t integer_bytes = 0; // Tracks how much bytes of r2 and r3 registers we've consumed so far bool use_fp_regs = 0; // True if return values are in FP return registers. bool found_non_fp_field = 0; // True if we found any non floating point field in structure. bool use_r2 = 0; // True if return values are in r2 register. bool use_r3 = 0; // True if return values are in r3 register. bool sucess = 0; // True if the result is copied into our data buffer std::string name; bool is_complex; uint32_t count; const uint32_t num_children = return_compiler_type.GetNumFields (); // A structure consisting of one or two FP values (and nothing else) will be // returned in the two FP return-value registers i.e fp0 and fp2. if (num_children <= 2) { uint64_t field_bit_offset = 0; // Check if this structure contains only floating point fields for (uint32_t idx = 0; idx < num_children; idx++) { CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); if (field_compiler_type.IsFloatingPointType (count, is_complex)) use_fp_regs = 1; else found_non_fp_field = 1; } if (use_fp_regs && !found_non_fp_field) { // We have one or two FP-only values in this structure. Get it from f0/f2 registers. DataExtractor f0_data, f1_data, f2_data; const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0); const RegisterInfo *f2_info = reg_ctx->GetRegisterInfoByName("f2", 0); reg_ctx->ReadRegister (f0_info, f0_value); reg_ctx->ReadRegister (f2_info, f2_value); f0_value.GetData(f0_data); f2_value.GetData(f2_data); for (uint32_t idx = 0; idx < num_children; idx++) { CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); const size_t field_byte_width = field_compiler_type.GetByteSize(nullptr); DataExtractor *copy_from_extractor = NULL; if (idx == 0) { if (field_byte_width == 16) // This case is for long double type. { // If structure contains long double type, then it is returned in fp0/fp1 registers. reg_ctx->ReadRegister (f1_info, f1_value); f1_value.GetData(f1_data); if (target_byte_order == eByteOrderLittle) { f0_data.Append(f1_data); copy_from_extractor = &f0_data; } else { f1_data.Append(f0_data); copy_from_extractor = &f1_data; } } else copy_from_extractor = &f0_data; // This is in f0, copy from register to our result structure } else copy_from_extractor = &f2_data; // This is in f2, copy from register to our result structure // Sanity check to avoid crash if (!copy_from_extractor || field_byte_width > copy_from_extractor->GetByteSize()) return return_valobj_sp; // copy the register contents into our data buffer copy_from_extractor->CopyByteOrderedData (0, field_byte_width, data_sp->GetBytes() + (field_bit_offset/8), field_byte_width, target_byte_order); } // The result is in our data buffer. Create a variable object out of it return_valobj_sp = ValueObjectConstResult::Create (&thread, return_compiler_type, ConstString(""), return_ext); return return_valobj_sp; } } // If we reach here, it means this structure either contains more than two fields or // it contains at least one non floating point type. // In that case, all fields are returned in GP return registers. for (uint32_t idx = 0; idx < num_children; idx++) { uint64_t field_bit_offset = 0; bool is_signed; uint32_t padding; CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); const size_t field_byte_width = field_compiler_type.GetByteSize(nullptr); // if we don't know the size of the field (e.g. invalid type), just bail out if (field_byte_width == 0) break; uint32_t field_byte_offset = field_bit_offset/8; if (field_compiler_type.IsIntegerType (is_signed) || field_compiler_type.IsPointerType () || field_compiler_type.IsFloatingPointType (count, is_complex)) { padding = field_byte_offset - integer_bytes; if (integer_bytes < 8) { // We have not yet consumed r2 completely. if (integer_bytes + field_byte_width + padding <= 8) { // This field fits in r2, copy its value from r2 to our result structure integer_bytes = integer_bytes + field_byte_width + padding; // Increase the consumed bytes. use_r2 = 1; } else { // There isn't enough space left in r2 for this field, so this will be in r3. integer_bytes = integer_bytes + field_byte_width + padding; // Increase the consumed bytes. use_r3 = 1; } } // We already have consumed at-least 8 bytes that means r2 is done, and this field will be in r3. // Check if this field can fit in r3. else if (integer_bytes + field_byte_width + padding <= 16) { integer_bytes = integer_bytes + field_byte_width + padding; use_r3 = 1; } else { // There isn't any space left for this field, this should not happen as we have already checked // the overall size is not greater than 16 bytes. For now, return a NULL return value object. return return_valobj_sp; } } } // Vector types upto 16 bytes are returned in GP return registers if (type_flags & eTypeIsVector) { if (byte_size <= 8) use_r2 = 1; else { use_r2 = 1; use_r3 = 1; } } if (use_r2) { reg_ctx->ReadRegister (r2_info, r2_value); const size_t bytes_copied = r2_value.GetAsMemoryData (r2_info, data_sp->GetBytes(), r2_info->byte_size, target_byte_order, error); if (bytes_copied != r2_info->byte_size) return return_valobj_sp; sucess = 1; } if (use_r3) { reg_ctx->ReadRegister (r3_info, r3_value); const size_t bytes_copied = r3_value.GetAsMemoryData (r3_info, data_sp->GetBytes() + r2_info->byte_size, r3_info->byte_size, target_byte_order, error); if (bytes_copied != r3_info->byte_size) return return_valobj_sp; sucess = 1; } if (sucess) { // The result is in our data buffer. Create a variable object out of it return_valobj_sp = ValueObjectConstResult::Create (&thread, return_compiler_type, ConstString(""), return_ext); } return return_valobj_sp; } // Any structure/vector greater than 16 bytes in size is returned in memory. // The pointer to that memory is returned in r2. uint64_t mem_address = reg_ctx->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r2", 0), 0); // We have got the address. Create a memory object out of it return_valobj_sp = ValueObjectMemory::Create (&thread, "", Address (mem_address, NULL), return_compiler_type); } return return_valobj_sp; } bool ABISysV_mips64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); // Our Call Frame Address is the stack pointer value row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0); // The previous PC is in the RA row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("mips64 at-func-entry default"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetReturnAddressRegister(dwarf_r31); return true; } bool ABISysV_mips64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r29, 0); row->SetRegisterLocationToRegister(dwarf_pc, dwarf_r31, true); unwind_plan.AppendRow (row); unwind_plan.SetSourceName ("mips64 default unwind plan"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); return true; } bool ABISysV_mips64::RegisterIsVolatile (const RegisterInfo *reg_info) { return !RegisterIsCalleeSaved (reg_info); } bool ABISysV_mips64::RegisterIsCalleeSaved (const RegisterInfo *reg_info) { if (reg_info) { // Preserved registers are : // r16-r23, r28, r29, r30, r31 int reg = ((reg_info->byte_offset) / 8); bool save = (reg >= 16) && (reg <= 23); save |= (reg >= 28) && (reg <= 31); return save; } return false; } void ABISysV_mips64::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), "System V ABI for mips64 targets", CreateInstance); } void ABISysV_mips64::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } lldb_private::ConstString ABISysV_mips64::GetPluginNameStatic() { static ConstString g_name("sysv-mips64"); return g_name; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString ABISysV_mips64::GetPluginName() { return GetPluginNameStatic(); } uint32_t ABISysV_mips64::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp (revision 295597) +++ vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp (revision 295598) @@ -1,1056 +1,1069 @@ //===-- ObjectFilePECOFF.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ObjectFilePECOFF.h" #include "WindowsMiniDump.h" #include "llvm/Support/COFF.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/UUID.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #define OPT_HEADER_MAGIC_PE32 0x010b #define OPT_HEADER_MAGIC_PE32_PLUS 0x020b using namespace lldb; using namespace lldb_private; void ObjectFilePECOFF::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications, SaveCore); } void ObjectFilePECOFF::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } lldb_private::ConstString ObjectFilePECOFF::GetPluginNameStatic() { static ConstString g_name("pe-coff"); return g_name; } const char * ObjectFilePECOFF::GetPluginDescriptionStatic() { return "Portable Executable and Common Object File Format object file reader (32 and 64 bit)"; } ObjectFile * ObjectFilePECOFF::CreateInstance (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length) { if (!data_sp) { data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); data_offset = 0; } if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) { // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); std::unique_ptr objfile_ap(new ObjectFilePECOFF (module_sp, data_sp, data_offset, file, file_offset, length)); if (objfile_ap.get() && objfile_ap->ParseHeader()) return objfile_ap.release(); } return NULL; } ObjectFile * ObjectFilePECOFF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { return NULL; } size_t ObjectFilePECOFF::GetModuleSpecifications (const lldb_private::FileSpec& file, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { const size_t initial_count = specs.GetSize(); if (ObjectFilePECOFF::MagicBytesMatch(data_sp)) { DataExtractor data; data.SetData(data_sp, data_offset, length); data.SetByteOrder(eByteOrderLittle); dos_header_t dos_header; coff_header_t coff_header; if (ParseDOSHeader(data, dos_header)) { lldb::offset_t offset = dos_header.e_lfanew; uint32_t pe_signature = data.GetU32(&offset); if (pe_signature != IMAGE_NT_SIGNATURE) return false; if (ParseCOFFHeader(data, &offset, coff_header)) { ArchSpec spec; if (coff_header.machine == MachineAmd64) { spec.SetTriple("x86_64-pc-windows"); specs.Append(ModuleSpec(file, spec)); } else if (coff_header.machine == MachineX86) { spec.SetTriple("i386-pc-windows"); specs.Append(ModuleSpec(file, spec)); spec.SetTriple("i686-pc-windows"); specs.Append(ModuleSpec(file, spec)); } } } } return specs.GetSize() - initial_count; } bool ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp, const lldb_private::FileSpec &outfile, lldb_private::Error &error) { return SaveMiniDump(process_sp, outfile, error); } bool ObjectFilePECOFF::MagicBytesMatch (DataBufferSP& data_sp) { DataExtractor data(data_sp, eByteOrderLittle, 4); lldb::offset_t offset = 0; uint16_t magic = data.GetU16 (&offset); return magic == IMAGE_DOS_SIGNATURE; } +lldb::SymbolType +ObjectFilePECOFF::MapSymbolType(uint16_t coff_symbol_type) +{ + // TODO: We need to complete this mapping of COFF symbol types to LLDB ones. + // For now, here's a hack to make sure our function have types. + const auto complex_type = coff_symbol_type >> llvm::COFF::SCT_COMPLEX_TYPE_SHIFT; + if (complex_type == llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) + { + return lldb::eSymbolTypeCode; + } + return lldb::eSymbolTypeInvalid; +} ObjectFilePECOFF::ObjectFilePECOFF (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, lldb::offset_t data_offset, const FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length) : ObjectFile (module_sp, file, file_offset, length, data_sp, data_offset), m_dos_header (), m_coff_header (), m_coff_header_opt (), m_sect_headers () { ::memset (&m_dos_header, 0, sizeof(m_dos_header)); ::memset (&m_coff_header, 0, sizeof(m_coff_header)); ::memset (&m_coff_header_opt, 0, sizeof(m_coff_header_opt)); } ObjectFilePECOFF::~ObjectFilePECOFF() { } bool ObjectFilePECOFF::ParseHeader () { ModuleSP module_sp(GetModule()); if (module_sp) { lldb_private::Mutex::Locker locker(module_sp->GetMutex()); m_sect_headers.clear(); m_data.SetByteOrder (eByteOrderLittle); lldb::offset_t offset = 0; if (ParseDOSHeader(m_data, m_dos_header)) { offset = m_dos_header.e_lfanew; uint32_t pe_signature = m_data.GetU32 (&offset); if (pe_signature != IMAGE_NT_SIGNATURE) return false; if (ParseCOFFHeader(m_data, &offset, m_coff_header)) { if (m_coff_header.hdrsize > 0) ParseCOFFOptionalHeader(&offset); ParseSectionHeaders (offset); } return true; } } return false; } bool ObjectFilePECOFF::SetLoadAddress(Target &target, addr_t value, bool value_is_offset) { bool changed = false; ModuleSP module_sp = GetModule(); if (module_sp) { size_t num_loaded_sections = 0; SectionList *section_list = GetSectionList (); if (section_list) { if (!value_is_offset) { value -= m_image_base; } const size_t num_sections = section_list->GetSize(); size_t sect_idx = 0; for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) { // Iterate through the object file sections to find all // of the sections that have SHF_ALLOC in their flag bits. SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); if (section_sp && !section_sp->IsThreadSpecific()) { if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + value)) ++num_loaded_sections; } } changed = num_loaded_sections > 0; } } return changed; } ByteOrder ObjectFilePECOFF::GetByteOrder () const { return eByteOrderLittle; } bool ObjectFilePECOFF::IsExecutable() const { return (m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0; } uint32_t ObjectFilePECOFF::GetAddressByteSize () const { if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32_PLUS) return 8; else if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) return 4; return 4; } //---------------------------------------------------------------------- // NeedsEndianSwap // // Return true if an endian swap needs to occur when extracting data // from this file. //---------------------------------------------------------------------- bool ObjectFilePECOFF::NeedsEndianSwap() const { #if defined(__LITTLE_ENDIAN__) return false; #else return true; #endif } //---------------------------------------------------------------------- // ParseDOSHeader //---------------------------------------------------------------------- bool ObjectFilePECOFF::ParseDOSHeader (DataExtractor &data, dos_header_t &dos_header) { bool success = false; lldb::offset_t offset = 0; success = data.ValidOffsetForDataOfSize(0, sizeof(dos_header)); if (success) { dos_header.e_magic = data.GetU16(&offset); // Magic number success = dos_header.e_magic == IMAGE_DOS_SIGNATURE; if (success) { dos_header.e_cblp = data.GetU16(&offset); // Bytes on last page of file dos_header.e_cp = data.GetU16(&offset); // Pages in file dos_header.e_crlc = data.GetU16(&offset); // Relocations dos_header.e_cparhdr = data.GetU16(&offset); // Size of header in paragraphs dos_header.e_minalloc = data.GetU16(&offset); // Minimum extra paragraphs needed dos_header.e_maxalloc = data.GetU16(&offset); // Maximum extra paragraphs needed dos_header.e_ss = data.GetU16(&offset); // Initial (relative) SS value dos_header.e_sp = data.GetU16(&offset); // Initial SP value dos_header.e_csum = data.GetU16(&offset); // Checksum dos_header.e_ip = data.GetU16(&offset); // Initial IP value dos_header.e_cs = data.GetU16(&offset); // Initial (relative) CS value dos_header.e_lfarlc = data.GetU16(&offset); // File address of relocation table dos_header.e_ovno = data.GetU16(&offset); // Overlay number dos_header.e_res[0] = data.GetU16(&offset); // Reserved words dos_header.e_res[1] = data.GetU16(&offset); // Reserved words dos_header.e_res[2] = data.GetU16(&offset); // Reserved words dos_header.e_res[3] = data.GetU16(&offset); // Reserved words dos_header.e_oemid = data.GetU16(&offset); // OEM identifier (for e_oeminfo) dos_header.e_oeminfo = data.GetU16(&offset); // OEM information; e_oemid specific dos_header.e_res2[0] = data.GetU16(&offset); // Reserved words dos_header.e_res2[1] = data.GetU16(&offset); // Reserved words dos_header.e_res2[2] = data.GetU16(&offset); // Reserved words dos_header.e_res2[3] = data.GetU16(&offset); // Reserved words dos_header.e_res2[4] = data.GetU16(&offset); // Reserved words dos_header.e_res2[5] = data.GetU16(&offset); // Reserved words dos_header.e_res2[6] = data.GetU16(&offset); // Reserved words dos_header.e_res2[7] = data.GetU16(&offset); // Reserved words dos_header.e_res2[8] = data.GetU16(&offset); // Reserved words dos_header.e_res2[9] = data.GetU16(&offset); // Reserved words dos_header.e_lfanew = data.GetU32(&offset); // File address of new exe header } } if (!success) memset(&dos_header, 0, sizeof(dos_header)); return success; } //---------------------------------------------------------------------- // ParserCOFFHeader //---------------------------------------------------------------------- bool ObjectFilePECOFF::ParseCOFFHeader(DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header) { bool success = data.ValidOffsetForDataOfSize (*offset_ptr, sizeof(coff_header)); if (success) { coff_header.machine = data.GetU16(offset_ptr); coff_header.nsects = data.GetU16(offset_ptr); coff_header.modtime = data.GetU32(offset_ptr); coff_header.symoff = data.GetU32(offset_ptr); coff_header.nsyms = data.GetU32(offset_ptr); coff_header.hdrsize = data.GetU16(offset_ptr); coff_header.flags = data.GetU16(offset_ptr); } if (!success) memset(&coff_header, 0, sizeof(coff_header)); return success; } bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) { bool success = false; const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize; if (*offset_ptr < end_offset) { success = true; m_coff_header_opt.magic = m_data.GetU16(offset_ptr); m_coff_header_opt.major_linker_version = m_data.GetU8 (offset_ptr); m_coff_header_opt.minor_linker_version = m_data.GetU8 (offset_ptr); m_coff_header_opt.code_size = m_data.GetU32(offset_ptr); m_coff_header_opt.data_size = m_data.GetU32(offset_ptr); m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr); m_coff_header_opt.entry = m_data.GetU32(offset_ptr); m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr); const uint32_t addr_byte_size = GetAddressByteSize (); if (*offset_ptr < end_offset) { if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) { // PE32 only m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr); } else m_coff_header_opt.data_offset = 0; if (*offset_ptr < end_offset) { m_coff_header_opt.image_base = m_data.GetMaxU64 (offset_ptr, addr_byte_size); m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr); m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr); m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr); m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr); m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr); m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr); m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr); m_coff_header_opt.image_size = m_data.GetU32(offset_ptr); m_coff_header_opt.header_size = m_data.GetU32(offset_ptr); m_coff_header_opt.checksum = m_data.GetU32(offset_ptr); m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr); m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr); m_coff_header_opt.stack_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); m_coff_header_opt.stack_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); m_coff_header_opt.heap_reserve_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); m_coff_header_opt.heap_commit_size = m_data.GetMaxU64 (offset_ptr, addr_byte_size); m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr); uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr); m_coff_header_opt.data_dirs.clear(); m_coff_header_opt.data_dirs.resize(num_data_dir_entries); uint32_t i; for (i=0; i 0) { const uint32_t addr_byte_size = GetAddressByteSize (); const size_t section_header_byte_size = nsects * sizeof(section_header_t); DataBufferSP section_header_data_sp(m_file.ReadFileContents (section_header_data_offset, section_header_byte_size)); DataExtractor section_header_data (section_header_data_sp, GetByteOrder(), addr_byte_size); lldb::offset_t offset = 0; if (section_header_data.ValidOffsetForDataOfSize (offset, section_header_byte_size)) { m_sect_headers.resize(nsects); for (uint32_t idx = 0; idxGetMutex()); if (m_symtab_ap.get() == NULL) { SectionList *sect_list = GetSectionList(); m_symtab_ap.reset(new Symtab(this)); Mutex::Locker symtab_locker (m_symtab_ap->GetMutex()); const uint32_t num_syms = m_coff_header.nsyms; if (num_syms > 0 && m_coff_header.symoff > 0) { const uint32_t symbol_size = 18; const uint32_t addr_byte_size = GetAddressByteSize (); - const size_t symbol_data_size = num_syms * symbol_size; - // Include the 4 bytes string table size at the end of the symbols + const size_t symbol_data_size = num_syms * symbol_size; + // Include the 4-byte string table size at the end of the symbols DataBufferSP symtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff, symbol_data_size + 4)); DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), addr_byte_size); lldb::offset_t offset = symbol_data_size; const uint32_t strtab_size = symtab_data.GetU32 (&offset); DataBufferSP strtab_data_sp(m_file.ReadFileContents (m_coff_header.symoff + symbol_data_size, strtab_size)); DataExtractor strtab_data (strtab_data_sp, GetByteOrder(), addr_byte_size); // First 4 bytes should be zeroed after strtab_size has been read, // because it is used as offset 0 to encode a NULL string. uint32_t* strtab_data_start = (uint32_t*)strtab_data_sp->GetBytes(); strtab_data_start[0] = 0; offset = 0; std::string symbol_name; Symbol *symbols = m_symtab_ap->Resize (num_syms); for (uint32_t i=0; i= 1) { Address symbol_addr(sect_list->GetSectionAtIndex(symbol.sect-1), symbol.value); symbols[i].GetAddressRef() = symbol_addr; + symbols[i].SetType(MapSymbolType(symbol.type)); } if (symbol.naux > 0) { i += symbol.naux; offset += symbol_size; } } } // Read export header if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 && m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) { export_directory_entry export_table; uint32_t data_start = m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; Address address(m_coff_header_opt.image_base + data_start, sect_list); DataBufferSP symtab_data_sp(m_file.ReadFileContents(address.GetSection()->GetFileOffset() + address.GetOffset(), m_coff_header_opt.data_dirs[0].vmsize)); DataExtractor symtab_data (symtab_data_sp, GetByteOrder(), GetAddressByteSize()); lldb::offset_t offset = 0; // Read export_table header export_table.characteristics = symtab_data.GetU32(&offset); export_table.time_date_stamp = symtab_data.GetU32(&offset); export_table.major_version = symtab_data.GetU16(&offset); export_table.minor_version = symtab_data.GetU16(&offset); export_table.name = symtab_data.GetU32(&offset); export_table.base = symtab_data.GetU32(&offset); export_table.number_of_functions = symtab_data.GetU32(&offset); export_table.number_of_names = symtab_data.GetU32(&offset); export_table.address_of_functions = symtab_data.GetU32(&offset); export_table.address_of_names = symtab_data.GetU32(&offset); export_table.address_of_name_ordinals = symtab_data.GetU32(&offset); bool has_ordinal = export_table.address_of_name_ordinals != 0; lldb::offset_t name_offset = export_table.address_of_names - data_start; lldb::offset_t name_ordinal_offset = export_table.address_of_name_ordinals - data_start; Symbol *symbols = m_symtab_ap->Resize(export_table.number_of_names); std::string symbol_name; // Read each export table entry for (size_t i = 0; i < export_table.number_of_names; ++i) { uint32_t name_ordinal = has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; uint32_t name_address = symtab_data.GetU32(&name_offset); const char* symbol_name_cstr = symtab_data.PeekCStr(name_address - data_start); symbol_name.assign(symbol_name_cstr); lldb::offset_t function_offset = export_table.address_of_functions - data_start + sizeof(uint32_t) * name_ordinal; uint32_t function_rva = symtab_data.GetU32(&function_offset); Address symbol_addr(m_coff_header_opt.image_base + function_rva, sect_list); symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); symbols[i].GetAddressRef() = symbol_addr; symbols[i].SetType(lldb::eSymbolTypeCode); symbols[i].SetDebug(true); } } } } return m_symtab_ap.get(); } bool ObjectFilePECOFF::IsStripped () { // TODO: determine this for COFF return false; } void ObjectFilePECOFF::CreateSections (SectionList &unified_section_list) { if (!m_sections_ap.get()) { m_sections_ap.reset(new SectionList()); ModuleSP module_sp(GetModule()); if (module_sp) { lldb_private::Mutex::Locker locker(module_sp->GetMutex()); const uint32_t nsects = m_sect_headers.size(); ModuleSP module_sp (GetModule()); for (uint32_t idx = 0; idxSetIsEncrypted (segment_is_encrypted); unified_section_list.AddSection(section_sp); m_sections_ap->AddSection (section_sp); } } } } bool ObjectFilePECOFF::GetUUID (UUID* uuid) { return false; } uint32_t ObjectFilePECOFF::GetDependentModules (FileSpecList& files) { return 0; } //---------------------------------------------------------------------- // Dump // // Dump the specifics of the runtime file container (such as any headers // segments, sections, etc). //---------------------------------------------------------------------- void ObjectFilePECOFF::Dump(Stream *s) { ModuleSP module_sp(GetModule()); if (module_sp) { lldb_private::Mutex::Locker locker(module_sp->GetMutex()); s->Printf("%p: ", static_cast(this)); s->Indent(); s->PutCString("ObjectFilePECOFF"); ArchSpec header_arch; GetArchitecture (header_arch); *s << ", file = '" << m_file << "', arch = " << header_arch.GetArchitectureName() << "\n"; SectionList *sections = GetSectionList(); if (sections) sections->Dump(s, NULL, true, UINT32_MAX); if (m_symtab_ap.get()) m_symtab_ap->Dump(s, NULL, eSortOrderNone); if (m_dos_header.e_magic) DumpDOSHeader (s, m_dos_header); if (m_coff_header.machine) { DumpCOFFHeader (s, m_coff_header); if (m_coff_header.hdrsize) DumpOptCOFFHeader (s, m_coff_header_opt); } s->EOL(); DumpSectionHeaders(s); s->EOL(); } } //---------------------------------------------------------------------- // DumpDOSHeader // // Dump the MS-DOS header to the specified output stream //---------------------------------------------------------------------- void ObjectFilePECOFF::DumpDOSHeader(Stream *s, const dos_header_t& header) { s->PutCString ("MSDOS Header\n"); s->Printf (" e_magic = 0x%4.4x\n", header.e_magic); s->Printf (" e_cblp = 0x%4.4x\n", header.e_cblp); s->Printf (" e_cp = 0x%4.4x\n", header.e_cp); s->Printf (" e_crlc = 0x%4.4x\n", header.e_crlc); s->Printf (" e_cparhdr = 0x%4.4x\n", header.e_cparhdr); s->Printf (" e_minalloc = 0x%4.4x\n", header.e_minalloc); s->Printf (" e_maxalloc = 0x%4.4x\n", header.e_maxalloc); s->Printf (" e_ss = 0x%4.4x\n", header.e_ss); s->Printf (" e_sp = 0x%4.4x\n", header.e_sp); s->Printf (" e_csum = 0x%4.4x\n", header.e_csum); s->Printf (" e_ip = 0x%4.4x\n", header.e_ip); s->Printf (" e_cs = 0x%4.4x\n", header.e_cs); s->Printf (" e_lfarlc = 0x%4.4x\n", header.e_lfarlc); s->Printf (" e_ovno = 0x%4.4x\n", header.e_ovno); s->Printf (" e_res[4] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", header.e_res[0], header.e_res[1], header.e_res[2], header.e_res[3]); s->Printf (" e_oemid = 0x%4.4x\n", header.e_oemid); s->Printf (" e_oeminfo = 0x%4.4x\n", header.e_oeminfo); s->Printf (" e_res2[10] = { 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x, 0x%4.4x }\n", header.e_res2[0], header.e_res2[1], header.e_res2[2], header.e_res2[3], header.e_res2[4], header.e_res2[5], header.e_res2[6], header.e_res2[7], header.e_res2[8], header.e_res2[9]); s->Printf (" e_lfanew = 0x%8.8x\n", header.e_lfanew); } //---------------------------------------------------------------------- // DumpCOFFHeader // // Dump the COFF header to the specified output stream //---------------------------------------------------------------------- void ObjectFilePECOFF::DumpCOFFHeader(Stream *s, const coff_header_t& header) { s->PutCString ("COFF Header\n"); s->Printf (" machine = 0x%4.4x\n", header.machine); s->Printf (" nsects = 0x%4.4x\n", header.nsects); s->Printf (" modtime = 0x%8.8x\n", header.modtime); s->Printf (" symoff = 0x%8.8x\n", header.symoff); s->Printf (" nsyms = 0x%8.8x\n", header.nsyms); s->Printf (" hdrsize = 0x%4.4x\n", header.hdrsize); } //---------------------------------------------------------------------- // DumpOptCOFFHeader // // Dump the optional COFF header to the specified output stream //---------------------------------------------------------------------- void ObjectFilePECOFF::DumpOptCOFFHeader(Stream *s, const coff_opt_header_t& header) { s->PutCString ("Optional COFF Header\n"); s->Printf (" magic = 0x%4.4x\n", header.magic); s->Printf (" major_linker_version = 0x%2.2x\n", header.major_linker_version); s->Printf (" minor_linker_version = 0x%2.2x\n", header.minor_linker_version); s->Printf (" code_size = 0x%8.8x\n", header.code_size); s->Printf (" data_size = 0x%8.8x\n", header.data_size); s->Printf (" bss_size = 0x%8.8x\n", header.bss_size); s->Printf (" entry = 0x%8.8x\n", header.entry); s->Printf (" code_offset = 0x%8.8x\n", header.code_offset); s->Printf (" data_offset = 0x%8.8x\n", header.data_offset); s->Printf (" image_base = 0x%16.16" PRIx64 "\n", header.image_base); s->Printf (" sect_alignment = 0x%8.8x\n", header.sect_alignment); s->Printf (" file_alignment = 0x%8.8x\n", header.file_alignment); s->Printf (" major_os_system_version = 0x%4.4x\n", header.major_os_system_version); s->Printf (" minor_os_system_version = 0x%4.4x\n", header.minor_os_system_version); s->Printf (" major_image_version = 0x%4.4x\n", header.major_image_version); s->Printf (" minor_image_version = 0x%4.4x\n", header.minor_image_version); s->Printf (" major_subsystem_version = 0x%4.4x\n", header.major_subsystem_version); s->Printf (" minor_subsystem_version = 0x%4.4x\n", header.minor_subsystem_version); s->Printf (" reserved1 = 0x%8.8x\n", header.reserved1); s->Printf (" image_size = 0x%8.8x\n", header.image_size); s->Printf (" header_size = 0x%8.8x\n", header.header_size); s->Printf (" checksum = 0x%8.8x\n", header.checksum); s->Printf (" subsystem = 0x%4.4x\n", header.subsystem); s->Printf (" dll_flags = 0x%4.4x\n", header.dll_flags); s->Printf (" stack_reserve_size = 0x%16.16" PRIx64 "\n", header.stack_reserve_size); s->Printf (" stack_commit_size = 0x%16.16" PRIx64 "\n", header.stack_commit_size); s->Printf (" heap_reserve_size = 0x%16.16" PRIx64 "\n", header.heap_reserve_size); s->Printf (" heap_commit_size = 0x%16.16" PRIx64 "\n", header.heap_commit_size); s->Printf (" loader_flags = 0x%8.8x\n", header.loader_flags); s->Printf (" num_data_dir_entries = 0x%8.8x\n", (uint32_t)header.data_dirs.size()); uint32_t i; for (i=0; iPrintf (" data_dirs[%2u] vmaddr = 0x%8.8x, vmsize = 0x%8.8x\n", i, header.data_dirs[i].vmaddr, header.data_dirs[i].vmsize); } } //---------------------------------------------------------------------- // DumpSectionHeader // // Dump a single ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFilePECOFF::DumpSectionHeader(Stream *s, const section_header_t& sh) { std::string name; GetSectionName(name, sh); s->Printf ("%-16s 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%4.4x 0x%4.4x 0x%8.8x\n", name.c_str(), sh.vmaddr, sh.vmsize, sh.offset, sh.size, sh.reloff, sh.lineoff, sh.nreloc, sh.nline, sh.flags); } //---------------------------------------------------------------------- // DumpSectionHeaders // // Dump all of the ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFilePECOFF::DumpSectionHeaders(Stream *s) { s->PutCString ("Section Headers\n"); s->PutCString ("IDX name vm addr vm size file off file size reloc off line off nreloc nline flags\n"); s->PutCString ("==== ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ------ ------ ----------\n"); uint32_t idx = 0; SectionHeaderCollIter pos, end = m_sect_headers.end(); for (pos = m_sect_headers.begin(); pos != end; ++pos, ++idx) { s->Printf ("[%2u] ", idx); ObjectFilePECOFF::DumpSectionHeader(s, *pos); } } bool ObjectFilePECOFF::GetArchitecture (ArchSpec &arch) { uint16_t machine = m_coff_header.machine; switch (machine) { case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: case llvm::COFF::IMAGE_FILE_MACHINE_I386: case llvm::COFF::IMAGE_FILE_MACHINE_POWERPC: case llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP: case llvm::COFF::IMAGE_FILE_MACHINE_ARM: case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: case llvm::COFF::IMAGE_FILE_MACHINE_THUMB: arch.SetArchitecture (eArchTypeCOFF, machine, LLDB_INVALID_CPUTYPE); return true; default: break; } return false; } ObjectFile::Type ObjectFilePECOFF::CalculateType() { if (m_coff_header.machine != 0) { if ((m_coff_header.flags & llvm::COFF::IMAGE_FILE_DLL) == 0) return eTypeExecutable; else return eTypeSharedLibrary; } return eTypeExecutable; } ObjectFile::Strata ObjectFilePECOFF::CalculateStrata() { return eStrataUser; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString ObjectFilePECOFF::GetPluginName() { return GetPluginNameStatic(); } uint32_t ObjectFilePECOFF::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h (revision 295597) +++ vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h (revision 295598) @@ -1,303 +1,306 @@ //===-- ObjectFilePECOFF.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_ObjectFilePECOFF_h_ #define liblldb_ObjectFilePECOFF_h_ // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/Symbol/ObjectFile.h" class ObjectFilePECOFF : public lldb_private::ObjectFile { public: typedef enum MachineType { MachineUnknown = 0x0, MachineAm33 = 0x1d3, MachineAmd64 = 0x8664, MachineArm = 0x1c0, MachineArmNt = 0x1c4, MachineArm64 = 0xaa64, MachineEbc = 0xebc, MachineX86 = 0x14c, MachineIA64 = 0x200, MachineM32R = 0x9041, MachineMips16 = 0x266, MachineMipsFpu = 0x366, MachineMipsFpu16 = 0x466, MachinePowerPc = 0x1f0, MachinePowerPcfp = 0x1f1, MachineR4000 = 0x166, MachineSh3 = 0x1a2, MachineSh3dsp = 0x1a3, MachineSh4 = 0x1a6, MachineSh5 = 0x1a8, MachineThumb = 0x1c2, MachineWcemIpsv2 = 0x169 } MachineType; ObjectFilePECOFF(const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length); ~ObjectFilePECOFF() override; //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ static void Initialize(); static void Terminate(); static lldb_private::ConstString GetPluginNameStatic(); static const char * GetPluginDescriptionStatic(); static ObjectFile * CreateInstance (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec* file, lldb::offset_t offset, lldb::offset_t length); static lldb_private::ObjectFile * CreateMemoryInstance (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); static size_t GetModuleSpecifications (const lldb_private::FileSpec& file, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs); static bool SaveCore (const lldb::ProcessSP &process_sp, const lldb_private::FileSpec &outfile, lldb_private::Error &error); static bool MagicBytesMatch (lldb::DataBufferSP& data_sp); - + + static lldb::SymbolType + MapSymbolType(uint16_t coff_symbol_type); + bool ParseHeader() override; bool SetLoadAddress(lldb_private::Target &target, lldb::addr_t value, bool value_is_offset) override; lldb::ByteOrder GetByteOrder() const override; bool IsExecutable() const override; uint32_t GetAddressByteSize() const override; - + // virtual lldb_private::AddressClass // GetAddressClass (lldb::addr_t file_addr); lldb_private::Symtab * GetSymtab() override; bool IsStripped() override; void CreateSections(lldb_private::SectionList &unified_section_list) override; void Dump(lldb_private::Stream *s) override; bool GetArchitecture(lldb_private::ArchSpec &arch) override; bool GetUUID(lldb_private::UUID* uuid) override; uint32_t GetDependentModules(lldb_private::FileSpecList& files) override; // virtual lldb_private::Address // GetEntryPointAddress (); ObjectFile::Type CalculateType() override; ObjectFile::Strata CalculateStrata() override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString GetPluginName() override; uint32_t GetPluginVersion() override; protected: bool NeedsEndianSwap() const; typedef struct dos_header { // DOS .EXE header uint16_t e_magic; // Magic number uint16_t e_cblp; // Bytes on last page of file uint16_t e_cp; // Pages in file uint16_t e_crlc; // Relocations uint16_t e_cparhdr; // Size of header in paragraphs uint16_t e_minalloc; // Minimum extra paragraphs needed uint16_t e_maxalloc; // Maximum extra paragraphs needed uint16_t e_ss; // Initial (relative) SS value uint16_t e_sp; // Initial SP value uint16_t e_csum; // Checksum uint16_t e_ip; // Initial IP value uint16_t e_cs; // Initial (relative) CS value uint16_t e_lfarlc; // File address of relocation table uint16_t e_ovno; // Overlay number uint16_t e_res[4]; // Reserved words uint16_t e_oemid; // OEM identifier (for e_oeminfo) uint16_t e_oeminfo; // OEM information; e_oemid specific uint16_t e_res2[10]; // Reserved words uint32_t e_lfanew; // File address of new exe header } dos_header_t; typedef struct coff_header { uint16_t machine; uint16_t nsects; uint32_t modtime; uint32_t symoff; uint32_t nsyms; uint16_t hdrsize; uint16_t flags; } coff_header_t; typedef struct data_directory { uint32_t vmaddr; uint32_t vmsize; } data_directory_t; typedef struct coff_opt_header { uint16_t magic; uint8_t major_linker_version; uint8_t minor_linker_version; uint32_t code_size; uint32_t data_size; uint32_t bss_size; uint32_t entry; uint32_t code_offset; uint32_t data_offset; uint64_t image_base; uint32_t sect_alignment; uint32_t file_alignment; uint16_t major_os_system_version; uint16_t minor_os_system_version; uint16_t major_image_version; uint16_t minor_image_version; uint16_t major_subsystem_version; uint16_t minor_subsystem_version; uint32_t reserved1; uint32_t image_size; uint32_t header_size; uint32_t checksum; uint16_t subsystem; uint16_t dll_flags; uint64_t stack_reserve_size; uint64_t stack_commit_size; uint64_t heap_reserve_size; uint64_t heap_commit_size; uint32_t loader_flags; // uint32_t num_data_dir_entries; std::vector data_dirs; // will contain num_data_dir_entries entries } coff_opt_header_t; typedef enum coff_data_dir_type { coff_data_dir_export_table = 0, coff_data_dir_import_table = 1, } coff_data_dir_type; typedef struct section_header { char name[8]; uint32_t vmsize; // Virtual Size uint32_t vmaddr; // Virtual Addr uint32_t size; // File size uint32_t offset; // File offset uint32_t reloff; // Offset to relocations uint32_t lineoff;// Offset to line table entries uint16_t nreloc; // Number of relocation entries uint16_t nline; // Number of line table entries uint32_t flags; } section_header_t; typedef struct coff_symbol { char name[8]; uint32_t value; uint16_t sect; uint16_t type; uint8_t storage; uint8_t naux; } coff_symbol_t; typedef struct export_directory_entry { uint32_t characteristics; uint32_t time_date_stamp; uint16_t major_version; uint16_t minor_version; uint32_t name; uint32_t base; uint32_t number_of_functions; uint32_t number_of_names; uint32_t address_of_functions; uint32_t address_of_names; uint32_t address_of_name_ordinals; } export_directory_entry; static bool ParseDOSHeader (lldb_private::DataExtractor &data, dos_header_t &dos_header); static bool ParseCOFFHeader (lldb_private::DataExtractor &data, lldb::offset_t *offset_ptr, coff_header_t &coff_header); bool ParseCOFFOptionalHeader (lldb::offset_t *offset_ptr); bool ParseSectionHeaders (uint32_t offset); static void DumpDOSHeader(lldb_private::Stream *s, const dos_header_t& header); static void DumpCOFFHeader(lldb_private::Stream *s, const coff_header_t& header); static void DumpOptCOFFHeader(lldb_private::Stream *s, const coff_opt_header_t& header); void DumpSectionHeaders(lldb_private::Stream *s); void DumpSectionHeader(lldb_private::Stream *s, const section_header_t& sh); bool GetSectionName(std::string& sect_name, const section_header_t& sect); typedef std::vector SectionHeaderColl; typedef SectionHeaderColl::iterator SectionHeaderCollIter; typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; private: dos_header_t m_dos_header; coff_header_t m_coff_header; coff_opt_header_t m_coff_header_opt; SectionHeaderColl m_sect_headers; lldb::addr_t m_image_base; }; #endif // liblldb_ObjectFilePECOFF_h_ Index: vendor/lldb/dist/source/Plugins/Platform/MacOSX/Makefile =================================================================== --- vendor/lldb/dist/source/Plugins/Platform/MacOSX/Makefile (revision 295597) +++ vendor/lldb/dist/source/Plugins/Platform/MacOSX/Makefile (revision 295598) @@ -1,15 +1,34 @@ ##===- source/Plugins/Platform/MacOSX/Makefile -------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## LLDB_LEVEL := ../../../.. +LEVEL := $(LLDB_LEVEL)/../.. + +include $(LEVEL)/Makefile.config + +SOURCES += PlatformDarwin.cpp \ + PlatformDarwinKernel.cpp \ + PlatformMacOSX.cpp \ + PlatformRemoteiOS.cpp \ + PlatformRemoteAppleTV.cpp \ + PlatformRemoteAppleWatch.cpp + +ifeq ($(HOST_OS),Darwin) +SOURCES += PlatformAppleSimulator.cpp \ + PlatformiOSSimulator.cpp \ + PlatformiOSSimulatorCoreSimulatorSupport.mm \ + PlatformAppleTVSimulator.cpp \ + PlatformAppleWatchSimulator.cpp +endif + LIBRARYNAME := lldbPluginPlatformMacOSX BUILD_ARCHIVE = 1 include $(LLDB_LEVEL)/Makefile Index: vendor/lldb/dist/source/Symbol/GoASTContext.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/GoASTContext.cpp (revision 295597) +++ vendor/lldb/dist/source/Symbol/GoASTContext.cpp (revision 295598) @@ -1,1519 +1,1658 @@ //===-- GoASTContext.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Core/ValueObject.h" #include "lldb/DataFormatters/StringPrinter.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/GoASTContext.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Target.h" #include "Plugins/ExpressionParser/Go/GoUserExpression.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserGo.h" using namespace lldb; namespace lldb_private { class GoArray; class GoFunction; class GoStruct; class GoType { public: enum { KIND_BOOL = 1, KIND_INT = 2, KIND_INT8 = 3, KIND_INT16 = 4, KIND_INT32 = 5, KIND_INT64 = 6, KIND_UINT = 7, KIND_UINT8 = 8, KIND_UINT16 = 9, KIND_UINT32 = 10, KIND_UINT64 = 11, KIND_UINTPTR = 12, KIND_FLOAT32 = 13, KIND_FLOAT64 = 14, KIND_COMPLEX64 = 15, KIND_COMPLEX128 = 16, KIND_ARRAY = 17, KIND_CHAN = 18, KIND_FUNC = 19, KIND_INTERFACE = 20, KIND_MAP = 21, KIND_PTR = 22, KIND_SLICE = 23, KIND_STRING = 24, KIND_STRUCT = 25, KIND_UNSAFEPOINTER = 26, KIND_LLDB_VOID, // Extension for LLDB, not used by go runtime. KIND_MASK = (1 << 5) - 1, KIND_DIRECT_IFACE = 1 << 5 }; GoType(int kind, const ConstString &name) : m_kind(kind & KIND_MASK) , m_name(name) { if (m_kind == KIND_FUNC) m_kind = KIND_FUNC; } virtual ~GoType() {} int GetGoKind() const { return m_kind; } const ConstString & GetName() const { return m_name; } virtual CompilerType GetElementType() const { return CompilerType(); } bool IsTypedef() const { switch (m_kind) { case KIND_CHAN: case KIND_MAP: case KIND_INTERFACE: return true; default: return false; } } GoArray *GetArray(); GoFunction *GetFunction(); GoStruct *GetStruct(); private: int m_kind; ConstString m_name; GoType(const GoType &) = delete; const GoType &operator=(const GoType &) = delete; }; class GoElem : public GoType { public: GoElem(int kind, const ConstString &name, const CompilerType &elem) : GoType(kind, name) , m_elem(elem) { } virtual CompilerType GetElementType() const { return m_elem; } private: // TODO: should we store this differently? CompilerType m_elem; GoElem(const GoElem &) = delete; const GoElem &operator=(const GoElem &) = delete; }; class GoArray : public GoElem { public: GoArray(const ConstString &name, uint64_t length, const CompilerType &elem) : GoElem(KIND_ARRAY, name, elem) , m_length(length) { } uint64_t GetLength() const { return m_length; } private: uint64_t m_length; GoArray(const GoArray &) = delete; const GoArray &operator=(const GoArray &) = delete; }; class GoFunction : public GoType { public: GoFunction(const ConstString &name, bool is_variadic) : GoType(KIND_FUNC, name) , m_is_variadic(is_variadic) { } bool IsVariadic() const { return m_is_variadic; } private: bool m_is_variadic; GoFunction(const GoFunction &) = delete; const GoFunction &operator=(const GoFunction &) = delete; }; class GoStruct : public GoType { public: struct Field { Field(const ConstString &name, const CompilerType &type, uint64_t offset) : m_name(name) , m_type(type) , m_byte_offset(offset) { } ConstString m_name; CompilerType m_type; uint64_t m_byte_offset; }; GoStruct(int kind, const ConstString &name, int64_t byte_size) : GoType(kind == 0 ? KIND_STRUCT : kind, name), m_is_complete(false), m_byte_size(byte_size) { } uint32_t GetNumFields() const { return m_fields.size(); } const Field * GetField(uint32_t i) const { if (i < m_fields.size()) return &m_fields[i]; return nullptr; } void AddField(const ConstString &name, const CompilerType &type, uint64_t offset) { m_fields.push_back(Field(name, type, offset)); } bool IsComplete() const { return m_is_complete; } void SetComplete() { m_is_complete = true; } int64_t GetByteSize() const { return m_byte_size; } private: bool m_is_complete; int64_t m_byte_size; std::vector m_fields; GoStruct(const GoStruct &) = delete; const GoStruct &operator=(const GoStruct &) = delete; }; GoArray * GoType::GetArray() { if (m_kind == KIND_ARRAY) { return static_cast(this); } return nullptr; } GoFunction * GoType::GetFunction() { if (m_kind == KIND_FUNC) { return static_cast(this); } return nullptr; } GoStruct * GoType::GetStruct() { switch (m_kind) { case KIND_STRING: case KIND_STRUCT: case KIND_SLICE: return static_cast(this); } return nullptr; } } // namespace lldb_private using namespace lldb_private; GoASTContext::GoASTContext() : TypeSystem(eKindGo) , m_pointer_byte_size(0) , m_int_byte_size(0) , m_types(new TypeMap) { } GoASTContext::~GoASTContext() { } //------------------------------------------------------------------ // PluginInterface functions //------------------------------------------------------------------ ConstString GoASTContext::GetPluginNameStatic() { return ConstString("go"); } ConstString GoASTContext::GetPluginName() { return GoASTContext::GetPluginNameStatic(); } uint32_t GoASTContext::GetPluginVersion() { return 1; } lldb::TypeSystemSP GoASTContext::CreateInstance (lldb::LanguageType language, Module *module, Target *target) { if (language == eLanguageTypeGo) { ArchSpec arch; std::shared_ptr go_ast_sp; if (module) { arch = module->GetArchitecture(); go_ast_sp = std::shared_ptr(new GoASTContext); } else if (target) { arch = target->GetArchitecture(); go_ast_sp = std::shared_ptr(new GoASTContextForExpr(target->shared_from_this())); } if (arch.IsValid()) { go_ast_sp->SetAddressByteSize(arch.GetAddressByteSize()); return go_ast_sp; } } return lldb::TypeSystemSP(); } void GoASTContext::EnumerateSupportedLanguages(std::set &languages_for_types, std::set &languages_for_expressions) { static std::vector s_supported_languages_for_types({ lldb::eLanguageTypeGo}); static std::vector s_supported_languages_for_expressions({}); languages_for_types.insert(s_supported_languages_for_types.begin(), s_supported_languages_for_types.end()); languages_for_expressions.insert(s_supported_languages_for_expressions.begin(), s_supported_languages_for_expressions.end()); } void GoASTContext::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), "AST context plug-in", CreateInstance, EnumerateSupportedLanguages); } void GoASTContext::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } //---------------------------------------------------------------------- // Tests //---------------------------------------------------------------------- bool GoASTContext::IsArrayType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size, bool *is_incomplete) { if (element_type) element_type->Clear(); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; GoArray *array = static_cast(type)->GetArray(); if (array) { if (size) *size = array->GetLength(); if (element_type) *element_type = array->GetElementType(); return true; } return false; } bool GoASTContext::IsVectorType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size) { if (element_type) element_type->Clear(); if (size) *size = 0; return false; } bool GoASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) { int kind = static_cast(type)->GetGoKind(); if (kind < GoType::KIND_ARRAY) return false; if (kind == GoType::KIND_PTR) return false; if (kind == GoType::KIND_CHAN) return false; if (kind == GoType::KIND_MAP) return false; if (kind == GoType::KIND_STRING) return false; if (kind == GoType::KIND_UNSAFEPOINTER) return false; return true; } bool GoASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsCharType(lldb::opaque_compiler_type_t type) { // Go's DWARF doesn't distinguish between rune and int32. return false; } bool GoASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; GoType *t = static_cast(type); if (GoStruct *s = t->GetStruct()) return s->IsComplete(); if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR) return t->GetElementType().IsCompleteType(); return true; } bool GoASTContext::IsConst(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsCStringType(lldb::opaque_compiler_type_t type, uint32_t &length) { return false; } bool GoASTContext::IsDefined(lldb::opaque_compiler_type_t type) { return type != nullptr; } bool GoASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { int kind = static_cast(type)->GetGoKind(); if (kind >= GoType::KIND_FLOAT32 && kind <= GoType::KIND_COMPLEX128) { if (kind >= GoType::KIND_COMPLEX64) { is_complex = true; count = 2; } else { is_complex = false; count = 1; } return true; } count = 0; is_complex = false; return false; } bool GoASTContext::IsFunctionType(lldb::opaque_compiler_type_t type, bool *is_variadic_ptr) { GoFunction *func = static_cast(type)->GetFunction(); if (func) { if (is_variadic_ptr) *is_variadic_ptr = func->IsVariadic(); return true; } if (is_variadic_ptr) *is_variadic_ptr = false; return false; } uint32_t GoASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, CompilerType *base_type_ptr) { return false; } size_t GoASTContext::GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) { return 0; } CompilerType GoASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, const size_t index) { return CompilerType(); } bool GoASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) { return IsFunctionType(type); } bool GoASTContext::IsIntegerType(lldb::opaque_compiler_type_t type, bool &is_signed) { is_signed = false; // TODO: Is bool an integer? if (type) { int kind = static_cast(type)->GetGoKind(); if (kind <= GoType::KIND_UINTPTR) { is_signed = (kind != GoType::KIND_BOOL) & (kind <= GoType::KIND_INT64); return true; } } return false; } bool GoASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsPossibleDynamicType(lldb::opaque_compiler_type_t type, CompilerType *target_type, // Can pass NULL bool check_cplusplus, bool check_objc) { if (target_type) target_type->Clear(); if (type) return static_cast(type)->GetGoKind() == GoType::KIND_INTERFACE; return false; } bool GoASTContext::IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsPointerType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (!type) return false; GoType *t = static_cast(type); if (pointee_type) { *pointee_type = t->GetElementType(); } switch (t->GetGoKind()) { case GoType::KIND_PTR: case GoType::KIND_UNSAFEPOINTER: case GoType::KIND_CHAN: case GoType::KIND_MAP: // TODO: is function a pointer? return true; default: return false; } } bool GoASTContext::IsPointerOrReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { return IsPointerType(type, pointee_type); } bool GoASTContext::IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) { return false; } bool GoASTContext::IsScalarType(lldb::opaque_compiler_type_t type) { return !IsAggregateType(type); } bool GoASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) { if (type) return static_cast(type)->IsTypedef(); return false; } bool GoASTContext::IsVoidType(lldb::opaque_compiler_type_t type) { if (!type) return false; return static_cast(type)->GetGoKind() == GoType::KIND_LLDB_VOID; } bool GoASTContext::SupportsLanguage (lldb::LanguageType language) { return language == eLanguageTypeGo; } //---------------------------------------------------------------------- // Type Completion //---------------------------------------------------------------------- bool GoASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; GoType *t = static_cast(type); if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR || t->GetArray()) return t->GetElementType().GetCompleteType(); if (GoStruct *s = t->GetStruct()) { if (s->IsComplete()) return true; CompilerType compiler_type(this, s); SymbolFile *symbols = GetSymbolFile(); return symbols && symbols->CompleteType(compiler_type); } return true; } //---------------------------------------------------------------------- // AST related queries //---------------------------------------------------------------------- uint32_t GoASTContext::GetPointerByteSize() { return m_pointer_byte_size; } //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- ConstString GoASTContext::GetTypeName(lldb::opaque_compiler_type_t type) { if (type) return static_cast(type)->GetName(); return ConstString(); } uint32_t GoASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type, CompilerType *pointee_or_element_compiler_type) { if (pointee_or_element_compiler_type) pointee_or_element_compiler_type->Clear(); if (!type) return 0; GoType *t = static_cast(type); if (pointee_or_element_compiler_type) *pointee_or_element_compiler_type = t->GetElementType(); int kind = t->GetGoKind(); if (kind == GoType::KIND_ARRAY) return eTypeHasChildren | eTypeIsArray; if (kind < GoType::KIND_ARRAY) { uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue; if (kind < GoType::KIND_FLOAT32) { builtin_type_flags |= eTypeIsInteger | eTypeIsScalar; if (kind >= GoType::KIND_INT && kind <= GoType::KIND_INT64) builtin_type_flags |= eTypeIsSigned; } else { builtin_type_flags |= eTypeIsFloat; if (kind < GoType::KIND_COMPLEX64) builtin_type_flags |= eTypeIsComplex; else builtin_type_flags |= eTypeIsScalar; } return builtin_type_flags; } if (kind == GoType::KIND_STRING) return eTypeHasValue | eTypeIsBuiltIn; if (kind == GoType::KIND_FUNC) return eTypeIsFuncPrototype | eTypeHasValue; if (IsPointerType(type)) return eTypeIsPointer | eTypeHasValue | eTypeHasChildren; if (kind == GoType::KIND_LLDB_VOID) return 0; return eTypeHasChildren | eTypeIsStructUnion; } lldb::TypeClass GoASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) { if (!type) return eTypeClassInvalid; int kind = static_cast(type)->GetGoKind(); if (kind == GoType::KIND_FUNC) return eTypeClassFunction; if (IsPointerType(type)) return eTypeClassPointer; if (kind < GoType::KIND_COMPLEX64) return eTypeClassBuiltin; if (kind <= GoType::KIND_COMPLEX128) return eTypeClassComplexFloat; if (kind == GoType::KIND_LLDB_VOID) return eTypeClassInvalid; return eTypeClassStruct; } lldb::BasicType GoASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { ConstString name = GetTypeName(type); if (name) { typedef UniqueCStringMap TypeNameToBasicTypeMap; static TypeNameToBasicTypeMap g_type_map; static std::once_flag g_once_flag; std::call_once(g_once_flag, [](){ // "void" g_type_map.Append(ConstString("void").GetCString(), eBasicTypeVoid); // "int" g_type_map.Append(ConstString("int").GetCString(), eBasicTypeInt); g_type_map.Append(ConstString("uint").GetCString(), eBasicTypeUnsignedInt); // Miscellaneous g_type_map.Append(ConstString("bool").GetCString(), eBasicTypeBool); // Others. Should these map to C types? g_type_map.Append(ConstString("byte").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("uint8").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("uint16").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("uint32").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("uint64").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("int8").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("int16").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("int32").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("int64").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("float32").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("float64").GetCString(), eBasicTypeOther); g_type_map.Append(ConstString("uintptr").GetCString(), eBasicTypeOther); g_type_map.Sort(); }); return g_type_map.Find(name.GetCString(), eBasicTypeInvalid); } return eBasicTypeInvalid; } lldb::LanguageType GoASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) { return lldb::eLanguageTypeGo; } unsigned GoASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) { return 0; } //---------------------------------------------------------------------- // Creating related types //---------------------------------------------------------------------- CompilerType GoASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type, uint64_t *stride) { GoArray *array = static_cast(type)->GetArray(); if (array) { if (stride) { *stride = array->GetElementType().GetByteSize(nullptr); } return array->GetElementType(); } return CompilerType(); } CompilerType GoASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) { GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType(); return CompilerType(this, type); } CompilerType GoASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) { return CompilerType(this, type); } // Returns -1 if this isn't a function of if the function doesn't have a prototype // Returns a value >= 0 if there is a prototype. int GoASTContext::GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) { return GetNumberOfFunctionArguments(type); } CompilerType GoASTContext::GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { return GetFunctionArgumentAtIndex(type, idx); } CompilerType GoASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) { CompilerType result; if (type) { GoType *t = static_cast(type); if (t->GetGoKind() == GoType::KIND_FUNC) result = t->GetElementType(); } return result; } size_t GoASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { return 0; } TypeMemberFunctionImpl GoASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { return TypeMemberFunctionImpl(); } CompilerType GoASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) { return CompilerType(this, type); } CompilerType GoASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) { if (!type) return CompilerType(); return static_cast(type)->GetElementType(); } CompilerType GoASTContext::GetPointerType(lldb::opaque_compiler_type_t type) { if (!type) return CompilerType(); ConstString type_name = GetTypeName(type); ConstString pointer_name(std::string("*") + type_name.GetCString()); GoType *pointer = (*m_types)[pointer_name].get(); if (pointer == nullptr) { pointer = new GoElem(GoType::KIND_PTR, pointer_name, CompilerType(this, type)); (*m_types)[pointer_name].reset(pointer); } return CompilerType(this, pointer); } // If the current object represents a typedef type, get the underlying type CompilerType GoASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) { if (IsTypedefType(type)) return static_cast(type)->GetElementType(); return CompilerType(); } //---------------------------------------------------------------------- // Create related types using the current type's AST //---------------------------------------------------------------------- CompilerType GoASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { return CompilerType(); } CompilerType GoASTContext::GetBuiltinTypeForEncodingAndBitSize (lldb::Encoding encoding, size_t bit_size) { return CompilerType(); } //---------------------------------------------------------------------- // Exploring the type //---------------------------------------------------------------------- uint64_t GoASTContext::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { if (!type) return 0; if (!GetCompleteType(type)) return 0; GoType *t = static_cast(type); GoArray *array = nullptr; switch (t->GetGoKind()) { case GoType::KIND_BOOL: case GoType::KIND_INT8: case GoType::KIND_UINT8: return 8; case GoType::KIND_INT16: case GoType::KIND_UINT16: return 16; case GoType::KIND_INT32: case GoType::KIND_UINT32: case GoType::KIND_FLOAT32: return 32; case GoType::KIND_INT64: case GoType::KIND_UINT64: case GoType::KIND_FLOAT64: case GoType::KIND_COMPLEX64: return 64; case GoType::KIND_COMPLEX128: return 128; case GoType::KIND_INT: case GoType::KIND_UINT: return m_int_byte_size * 8; case GoType::KIND_UINTPTR: case GoType::KIND_FUNC: // I assume this is a pointer? case GoType::KIND_CHAN: case GoType::KIND_PTR: case GoType::KIND_UNSAFEPOINTER: case GoType::KIND_MAP: return m_pointer_byte_size * 8; case GoType::KIND_ARRAY: array = t->GetArray(); return array->GetLength() * array->GetElementType().GetBitSize(exe_scope); case GoType::KIND_INTERFACE: return t->GetElementType().GetBitSize(exe_scope); case GoType::KIND_SLICE: case GoType::KIND_STRING: case GoType::KIND_STRUCT: return t->GetStruct()->GetByteSize() * 8; default: assert(false); } return 0; } lldb::Encoding GoASTContext::GetEncoding(lldb::opaque_compiler_type_t type, uint64_t &count) { count = 1; bool is_signed; if (IsIntegerType(type, is_signed)) return is_signed ? lldb::eEncodingSint : eEncodingUint; bool is_complex; uint32_t complex_count; if (IsFloatingPointType(type, complex_count, is_complex)) { count = complex_count; return eEncodingIEEE754; } if (IsPointerType(type)) return eEncodingUint; return eEncodingInvalid; } lldb::Format GoASTContext::GetFormat(lldb::opaque_compiler_type_t type) { if (!type) return eFormatDefault; switch (static_cast(type)->GetGoKind()) { case GoType::KIND_BOOL: return eFormatBoolean; case GoType::KIND_INT: case GoType::KIND_INT8: case GoType::KIND_INT16: case GoType::KIND_INT32: case GoType::KIND_INT64: return eFormatDecimal; case GoType::KIND_UINT: case GoType::KIND_UINT8: case GoType::KIND_UINT16: case GoType::KIND_UINT32: case GoType::KIND_UINT64: return eFormatUnsigned; case GoType::KIND_FLOAT32: case GoType::KIND_FLOAT64: return eFormatFloat; case GoType::KIND_COMPLEX64: case GoType::KIND_COMPLEX128: return eFormatComplexFloat; case GoType::KIND_UINTPTR: case GoType::KIND_CHAN: case GoType::KIND_PTR: case GoType::KIND_MAP: case GoType::KIND_UNSAFEPOINTER: return eFormatHex; case GoType::KIND_STRING: return eFormatCString; case GoType::KIND_ARRAY: case GoType::KIND_INTERFACE: case GoType::KIND_SLICE: case GoType::KIND_STRUCT: default: // Don't know how to display this. return eFormatBytes; } } size_t GoASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { return 0; } uint32_t GoASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_base_classes) { if (!type || !GetCompleteType(type)) return 0; GoType *t = static_cast(type); if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType elem = t->GetElementType(); if (elem.IsAggregateType()) return elem.GetNumChildren(omit_empty_base_classes); return 1; } else if (GoArray *array = t->GetArray()) { return array->GetLength(); } else if (t->IsTypedef()) { return t->GetElementType().GetNumChildren(omit_empty_base_classes); } return GetNumFields(type); } uint32_t GoASTContext::GetNumFields(lldb::opaque_compiler_type_t type) { if (!type || !GetCompleteType(type)) return 0; GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType().GetNumFields(); GoStruct *s = t->GetStruct(); if (s) return s->GetNumFields(); return 0; } CompilerType GoASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { if (bit_offset_ptr) *bit_offset_ptr = 0; if (bitfield_bit_size_ptr) *bitfield_bit_size_ptr = 0; if (is_bitfield_ptr) *is_bitfield_ptr = false; if (!type || !GetCompleteType(type)) return CompilerType(); GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType().GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); GoStruct *s = t->GetStruct(); if (s) { const auto *field = s->GetField(idx); if (field) { name = field->m_name.GetStringRef(); if (bit_offset_ptr) *bit_offset_ptr = field->m_byte_offset * 8; return field->m_type; } } return CompilerType(); } CompilerType GoASTContext::GetChildCompilerTypeAtIndex(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, bool &child_is_deref_of_parent, ValueObject *valobj, uint64_t &language_flags) { child_name.clear(); child_byte_size = 0; child_byte_offset = 0; child_bitfield_bit_size = 0; child_bitfield_bit_offset = 0; child_is_base_class = false; child_is_deref_of_parent = false; language_flags = 0; if (!type || !GetCompleteType(type)) return CompilerType(); GoType *t = static_cast(type); if (t->GetStruct()) { uint64_t bit_offset; CompilerType ret = GetFieldAtIndex(type, idx, child_name, &bit_offset, nullptr, nullptr); child_byte_size = ret.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); child_byte_offset = bit_offset / 8; return ret; } else if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType pointee = t->GetElementType(); if (!pointee.IsValid() || pointee.IsVoidType()) return CompilerType(); if (transparent_pointers && pointee.IsAggregateType()) { bool tmp_child_is_deref_of_parent = false; return pointee.GetChildCompilerTypeAtIndex(exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { child_is_deref_of_parent = true; const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '*'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0 && pointee.GetCompleteType()) { child_byte_size = pointee.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee; } } } else if (GoArray *a = t->GetArray()) { if (ignore_array_bounds || idx < a->GetLength()) { CompilerType element_type = a->GetElementType(); if (element_type.GetCompleteType()) { char element_name[64]; ::snprintf(element_name, sizeof(element_name), "[%zu]", idx); child_name.assign(element_name); child_byte_size = element_type.GetByteSize(exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; return element_type; } } } else if (t->IsTypedef()) { return t->GetElementType().GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } return CompilerType(); } // Lookup a child given a name. This function will match base class names // and member member names in "clang_type" only, not descendants. uint32_t GoASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes) { if (!type || !GetCompleteType(type)) return UINT_MAX; GoType *t = static_cast(type); GoStruct *s = t->GetStruct(); if (s) { for (uint32_t i = 0; i < s->GetNumFields(); ++i) { const GoStruct::Field *f = s->GetField(i); if (f->m_name.GetStringRef() == name) return i; } } else if (t->GetGoKind() == GoType::KIND_PTR || t->IsTypedef()) { return t->GetElementType().GetIndexOfChildWithName(name, omit_empty_base_classes); } return UINT_MAX; } // Lookup a child member given a name. This function will match member names // only and will descend into "clang_type" children in search for the first // member in this class, or any base class that matches "name". // TODO: Return all matches for a given name by returning a vector> // so we catch all names that match a given child name, not just the first. size_t GoASTContext::GetIndexOfChildMemberWithName(lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes, std::vector &child_indexes) { uint32_t index = GetIndexOfChildWithName(type, name, omit_empty_base_classes); if (index == UINT_MAX) return 0; child_indexes.push_back(index); return 1; } // Converts "s" to a floating point value and place resulting floating // point bytes in the "dst" buffer. size_t GoASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, const char *s, uint8_t *dst, size_t dst_size) { assert(false); return 0; } //---------------------------------------------------------------------- // Dumping types //---------------------------------------------------------------------- +#define DEPTH_INCREMENT 2 + void GoASTContext::DumpValue(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, - const DataExtractor &data, lldb::offset_t data_offset, size_t data_byte_size, + const DataExtractor &data, lldb::offset_t data_byte_offset, size_t data_byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, bool show_types, bool show_summary, bool verbose, uint32_t depth) { - assert(false); + if (IsTypedefType(type)) + type = GetTypedefedType(type).GetOpaqueQualType(); + if (!type) + return; + GoType *t = static_cast(type); + + if (GoStruct *st = t->GetStruct()) + { + if (GetCompleteType(type)) + { + uint32_t field_idx = 0; + for (auto* field = st->GetField(field_idx); field != nullptr; field_idx++) + { + // Print the starting squiggly bracket (if this is the + // first member) or comma (for member 2 and beyond) for + // the struct/union/class member. + if (field_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent + s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); + + // Print the member type if requested + if (show_types) + { + ConstString field_type_name = field->m_type.GetTypeName(); + s->Printf("(%s) ", field_type_name.AsCString()); + } + // Print the member name and equal sign + s->Printf("%s = ", field->m_name.AsCString()); + + + // Dump the value of the member + CompilerType field_type = field->m_type; + field_type.DumpValue (exe_ctx, + s, // Stream to dump to + field_type.GetFormat(), // The format with which to display the member + data, // Data buffer containing all bytes for this type + data_byte_offset + field->m_byte_offset,// Offset into "data" where to grab value from + field->m_type.GetByteSize(exe_ctx->GetBestExecutionContextScope()), // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (field_idx > 0) + s->Printf("\n%*s}", depth, ""); + + } + } + + if (GoArray *a = t->GetArray()) { + CompilerType element_clang_type = a->GetElementType(); + lldb::Format element_format = element_clang_type.GetFormat(); + uint32_t element_byte_size = element_clang_type.GetByteSize(exe_ctx->GetBestExecutionContextScope()); + + uint64_t element_idx; + for (element_idx = 0; element_idx < a->GetLength(); ++element_idx) + { + // Print the starting squiggly bracket (if this is the + // first member) or comman (for member 2 and beyong) for + // the struct/union/class member. + if (element_idx == 0) + s->PutChar('{'); + else + s->PutChar(','); + + // Indent and print the index + s->Printf("\n%*s[%" PRIu64 "] ", depth + DEPTH_INCREMENT, "", element_idx); + + // Figure out the field offset within the current struct/union/class type + uint64_t element_offset = element_idx * element_byte_size; + + // Dump the value of the member + element_clang_type.DumpValue (exe_ctx, + s, // Stream to dump to + element_format, // The format with which to display the element + data, // Data buffer containing all bytes for this type + data_byte_offset + element_offset,// Offset into "data" where to grab value from + element_byte_size, // Size of this type in bytes + 0, // Bitfield bit size + 0, // Bitfield bit offset + show_types, // Boolean indicating if we should show the variable types + show_summary, // Boolean indicating if we should show a summary for the current type + verbose, // Verbose output? + depth + DEPTH_INCREMENT); // Scope depth for any types that have children + } + + // Indent the trailing squiggly bracket + if (element_idx > 0) + s->Printf("\n%*s}", depth, ""); + } + + if (show_summary) + DumpSummary (type, exe_ctx, s, data, data_byte_offset, data_byte_size); } bool GoASTContext::DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format, const DataExtractor &data, lldb::offset_t byte_offset, size_t byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, ExecutionContextScope *exe_scope) { if (!type) return false; if (IsAggregateType(type)) { return false; } else { GoType *t = static_cast(type); if (t->IsTypedef()) { CompilerType typedef_compiler_type = t->GetElementType(); if (format == eFormatDefault) format = typedef_compiler_type.GetFormat(); uint64_t typedef_byte_size = typedef_compiler_type.GetByteSize(exe_scope); return typedef_compiler_type.DumpTypeValue( s, format, // The format with which to display the element data, // Data buffer containing all bytes for this type byte_offset, // Offset into "data" where to grab value from typedef_byte_size, // Size of this type in bytes bitfield_bit_size, // Size in bits of a bitfield value, if zero don't treat as a bitfield bitfield_bit_offset, // Offset in bits of a bitfield value if bitfield_bit_size != 0 exe_scope); } uint32_t item_count = 1; // A few formats, we might need to modify our size and count for depending // on how we are trying to display the value... switch (format) { default: case eFormatBoolean: case eFormatBinary: case eFormatComplex: case eFormatCString: // NULL terminated C strings case eFormatDecimal: case eFormatEnum: case eFormatHex: case eFormatHexUppercase: case eFormatFloat: case eFormatOctal: case eFormatOSType: case eFormatUnsigned: case eFormatPointer: case eFormatVectorOfChar: case eFormatVectorOfSInt8: case eFormatVectorOfUInt8: case eFormatVectorOfSInt16: case eFormatVectorOfUInt16: case eFormatVectorOfSInt32: case eFormatVectorOfUInt32: case eFormatVectorOfSInt64: case eFormatVectorOfUInt64: case eFormatVectorOfFloat32: case eFormatVectorOfFloat64: case eFormatVectorOfUInt128: break; case eFormatChar: case eFormatCharPrintable: case eFormatCharArray: case eFormatBytes: case eFormatBytesWithASCII: item_count = byte_size; byte_size = 1; break; case eFormatUnicode16: item_count = byte_size / 2; byte_size = 2; break; case eFormatUnicode32: item_count = byte_size / 4; byte_size = 4; break; } return data.Dump(s, byte_offset, format, byte_size, item_count, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset, exe_scope); } return 0; } void GoASTContext::DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, lldb::offset_t data_offset, size_t data_byte_size) { - assert(false); + if (type && GoType::KIND_STRING == static_cast(type)->GetGoKind()) + { + // TODO(ribrdb): read length and data + } } void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) { - assert(false); -} // Dump to stdout + // Dump to stdout + StreamFile s (stdout, false); + DumpTypeDescription (type, &s); +} void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stream *s) { - assert(false); + if (!type) + return; + ConstString name = GetTypeName(type); + GoType *t = static_cast(type); + + if (GoStruct *st = t->GetStruct()) + { + if (GetCompleteType(type)) + { + if (NULL == strchr(name.AsCString(), '{')) + s->Printf("type %s ", name.AsCString()); + s->PutCString("struct {"); + if (st->GetNumFields() == 0) { + s->PutChar('}'); + return; + } + s->IndentMore(); + uint32_t field_idx = 0; + for (auto* field = st->GetField(field_idx); field != nullptr; field_idx++) + { + s->PutChar('\n'); + s->Indent(); + s->Printf("%s %s", field->m_name.AsCString(), field->m_type.GetTypeName().AsCString()); + } + s->IndentLess(); + s->PutChar('\n'); + s->Indent("}"); + return; + } + } + + s->PutCString(name.AsCString()); } CompilerType GoASTContext::CreateArrayType(const ConstString &name, const CompilerType &element_type, uint64_t length) { GoType *type = new GoArray(name, length, element_type); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateBaseType(int go_kind, const lldb_private::ConstString &name, uint64_t byte_size) { if (go_kind == GoType::KIND_UINT || go_kind == GoType::KIND_INT) m_int_byte_size = byte_size; GoType *type = new GoType(go_kind, name); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateTypedefType(int kind, const ConstString &name, CompilerType impl) { GoType *type = new GoElem(kind, name, impl); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateVoidType(const lldb_private::ConstString &name) { GoType *type = new GoType(GoType::KIND_LLDB_VOID, name); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateStructType(int kind, const lldb_private::ConstString &name, uint32_t byte_size) { GoType *type = new GoStruct(kind, name, byte_size); (*m_types)[name].reset(type); return CompilerType(this, type); } void GoASTContext::AddFieldToStruct(const lldb_private::CompilerType &struct_type, const lldb_private::ConstString &name, const lldb_private::CompilerType &field_type, uint32_t byte_offset) { if (!struct_type) return; GoASTContext *ast = llvm::dyn_cast_or_null(struct_type.GetTypeSystem()); if (!ast) return; GoType *type = static_cast(struct_type.GetOpaqueQualType()); if (GoStruct *s = type->GetStruct()) s->AddField(name, field_type, byte_offset); } void GoASTContext::CompleteStructType(const lldb_private::CompilerType &struct_type) { if (!struct_type) return; GoASTContext *ast = llvm::dyn_cast_or_null(struct_type.GetTypeSystem()); if (!ast) return; GoType *type = static_cast(struct_type.GetOpaqueQualType()); if (GoStruct *s = type->GetStruct()) s->SetComplete(); } CompilerType GoASTContext::CreateFunctionType(const lldb_private::ConstString &name, CompilerType *params, size_t params_count, bool is_variadic) { GoType *type = new GoFunction(name, is_variadic); (*m_types)[name].reset(type); return CompilerType(this, type); } bool GoASTContext::IsGoString(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_STRING == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsGoSlice(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_SLICE == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsGoInterface(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_INTERFACE == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsPointerKind(uint8_t kind) { return (kind & GoType::KIND_MASK) == GoType::KIND_PTR; } bool GoASTContext::IsDirectIface(uint8_t kind) { return (kind & GoType::KIND_DIRECT_IFACE) == GoType::KIND_DIRECT_IFACE; } DWARFASTParser * GoASTContext::GetDWARFParser() { if (!m_dwarf_ast_parser_ap) m_dwarf_ast_parser_ap.reset(new DWARFASTParserGo(*this)); return m_dwarf_ast_parser_ap.get(); } UserExpression * GoASTContextForExpr::GetUserExpression(const char *expr, const char *expr_prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options) { TargetSP target = m_target_wp.lock(); if (target) return new GoUserExpression(*target, expr, expr_prefix, language, desired_type, options); return nullptr; } Index: vendor/lldb/dist/source/Symbol/Symtab.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/Symtab.cpp (revision 295597) +++ vendor/lldb/dist/source/Symbol/Symtab.cpp (revision 295598) @@ -1,1218 +1,1238 @@ //===-- Symtab.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include "lldb/Core/Module.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Section.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Symtab.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" using namespace lldb; using namespace lldb_private; Symtab::Symtab(ObjectFile *objfile) : m_objfile (objfile), m_symbols (), m_file_addr_to_index (), m_name_to_index (), m_mutex (Mutex::eMutexTypeRecursive), m_file_addr_to_index_computed (false), m_name_indexes_computed (false) { } Symtab::~Symtab() { } void Symtab::Reserve(size_t count) { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. m_symbols.reserve (count); } Symbol * Symtab::Resize(size_t count) { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. m_symbols.resize (count); return &m_symbols[0]; } uint32_t Symtab::AddSymbol(const Symbol& symbol) { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. uint32_t symbol_idx = m_symbols.size(); m_name_to_index.Clear(); m_file_addr_to_index.Clear(); m_symbols.push_back(symbol); m_file_addr_to_index_computed = false; m_name_indexes_computed = false; return symbol_idx; } size_t Symtab::GetNumSymbols() const { Mutex::Locker locker (m_mutex); return m_symbols.size(); } void Symtab::SectionFileAddressesChanged () { m_name_to_index.Clear(); m_file_addr_to_index_computed = false; } void Symtab::Dump (Stream *s, Target *target, SortOrder sort_order) { Mutex::Locker locker (m_mutex); // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); s->Indent(); const FileSpec &file_spec = m_objfile->GetFileSpec(); const char * object_name = nullptr; if (m_objfile->GetModule()) object_name = m_objfile->GetModule()->GetObjectName().GetCString(); if (file_spec) s->Printf("Symtab, file = %s%s%s%s, num_symbols = %" PRIu64, file_spec.GetPath().c_str(), object_name ? "(" : "", object_name ? object_name : "", object_name ? ")" : "", (uint64_t)m_symbols.size()); else s->Printf("Symtab, num_symbols = %" PRIu64 "", (uint64_t)m_symbols.size()); if (!m_symbols.empty()) { switch (sort_order) { case eSortOrderNone: { s->PutCString (":\n"); DumpSymbolHeader (s); const_iterator begin = m_symbols.begin(); const_iterator end = m_symbols.end(); for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) { s->Indent(); pos->Dump(s, target, std::distance(begin, pos)); } } break; case eSortOrderByName: { // Although we maintain a lookup by exact name map, the table // isn't sorted by name. So we must make the ordered symbol list // up ourselves. s->PutCString (" (sorted by name):\n"); DumpSymbolHeader (s); typedef std::multimap CStringToSymbol; CStringToSymbol name_map; for (const_iterator pos = m_symbols.begin(), end = m_symbols.end(); pos != end; ++pos) { const char *name = pos->GetName().AsCString(); if (name && name[0]) name_map.insert (std::make_pair(name, &(*pos))); } for (CStringToSymbol::const_iterator pos = name_map.begin(), end = name_map.end(); pos != end; ++pos) { s->Indent(); pos->second->Dump (s, target, pos->second - &m_symbols[0]); } } break; case eSortOrderByAddress: s->PutCString (" (sorted by address):\n"); DumpSymbolHeader (s); if (!m_file_addr_to_index_computed) InitAddressIndexes(); const size_t num_entries = m_file_addr_to_index.GetSize(); for (size_t i=0; iIndent(); const uint32_t symbol_idx = m_file_addr_to_index.GetEntryRef(i).data; m_symbols[symbol_idx].Dump(s, target, symbol_idx); } break; } } } void Symtab::Dump(Stream *s, Target *target, std::vector& indexes) const { Mutex::Locker locker (m_mutex); const size_t num_symbols = GetNumSymbols(); //s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); s->Indent(); s->Printf("Symtab %" PRIu64 " symbol indexes (%" PRIu64 " symbols total):\n", (uint64_t)indexes.size(), (uint64_t)m_symbols.size()); s->IndentMore(); if (!indexes.empty()) { std::vector::const_iterator pos; std::vector::const_iterator end = indexes.end(); DumpSymbolHeader (s); for (pos = indexes.begin(); pos != end; ++pos) { size_t idx = *pos; if (idx < num_symbols) { s->Indent(); m_symbols[idx].Dump(s, target, idx); } } } s->IndentLess (); } void Symtab::DumpSymbolHeader (Stream *s) { s->Indent(" Debug symbol\n"); s->Indent(" |Synthetic symbol\n"); s->Indent(" ||Externally Visible\n"); s->Indent(" |||\n"); s->Indent("Index UserID DSX Type File Address/Value Load Address Size Flags Name\n"); s->Indent("------- ------ --- --------------- ------------------ ------------------ ------------------ ---------- ----------------------------------\n"); } static int CompareSymbolID (const void *key, const void *p) { const user_id_t match_uid = *(const user_id_t*) key; const user_id_t symbol_uid = ((const Symbol *)p)->GetID(); if (match_uid < symbol_uid) return -1; if (match_uid > symbol_uid) return 1; return 0; } Symbol * Symtab::FindSymbolByID (lldb::user_id_t symbol_uid) const { Mutex::Locker locker (m_mutex); Symbol *symbol = (Symbol*)::bsearch (&symbol_uid, &m_symbols[0], m_symbols.size(), sizeof(m_symbols[0]), CompareSymbolID); return symbol; } Symbol * Symtab::SymbolAtIndex(size_t idx) { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. if (idx < m_symbols.size()) return &m_symbols[idx]; return nullptr; } const Symbol * Symtab::SymbolAtIndex(size_t idx) const { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. if (idx < m_symbols.size()) return &m_symbols[idx]; return nullptr; } //---------------------------------------------------------------------- // InitNameIndexes //---------------------------------------------------------------------- void Symtab::InitNameIndexes() { // Protected function, no need to lock mutex... if (!m_name_indexes_computed) { m_name_indexes_computed = true; Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); // Create the name index vector to be able to quickly search by name const size_t num_symbols = m_symbols.size(); #if 1 m_name_to_index.Reserve (num_symbols); #else // TODO: benchmark this to see if we save any memory. Otherwise we // will always keep the memory reserved in the vector unless we pull // some STL swap magic and then recopy... uint32_t actual_count = 0; for (const_iterator pos = m_symbols.begin(), end = m_symbols.end(); pos != end; ++pos) { const Mangled &mangled = pos->GetMangled(); if (mangled.GetMangledName()) ++actual_count; if (mangled.GetDemangledName()) ++actual_count; } m_name_to_index.Reserve (actual_count); #endif NameToIndexMap::Entry entry; // The "const char *" in "class_contexts" must come from a ConstString::GetCString() std::set class_contexts; UniqueCStringMap mangled_name_to_index; std::vector symbol_contexts(num_symbols, nullptr); for (entry.value = 0; entry.valueIsTrampoline()) continue; const Mangled &mangled = symbol->GetMangled(); entry.cstring = mangled.GetMangledName().GetCString(); if (entry.cstring && entry.cstring[0]) { m_name_to_index.Append (entry); if (symbol->ContainsLinkerAnnotations()) { // If the symbol has linker annotations, also add the version without the // annotations. entry.cstring = ConstString(m_objfile->StripLinkerSymbolAnnotations(entry.cstring)).GetCString(); m_name_to_index.Append (entry); } const SymbolType symbol_type = symbol->GetType(); if (symbol_type == eSymbolTypeCode || symbol_type == eSymbolTypeResolver) { if (entry.cstring[0] == '_' && entry.cstring[1] == 'Z' && (entry.cstring[2] != 'T' && // avoid virtual table, VTT structure, typeinfo structure, and typeinfo name entry.cstring[2] != 'G' && // avoid guard variables entry.cstring[2] != 'Z')) // named local entities (if we eventually handle eSymbolTypeData, we will want this back) { CPlusPlusLanguage::MethodName cxx_method (mangled.GetDemangledName(lldb::eLanguageTypeC_plus_plus)); entry.cstring = ConstString(cxx_method.GetBasename()).GetCString(); if (entry.cstring && entry.cstring[0]) { // ConstString objects permanently store the string in the pool so calling // GetCString() on the value gets us a const char * that will never go away const char *const_context = ConstString(cxx_method.GetContext()).GetCString(); if (entry.cstring[0] == '~' || !cxx_method.GetQualifiers().empty()) { // The first character of the demangled basename is '~' which // means we have a class destructor. We can use this information // to help us know what is a class and what isn't. if (class_contexts.find(const_context) == class_contexts.end()) class_contexts.insert(const_context); m_method_to_index.Append (entry); } else { if (const_context && const_context[0]) { if (class_contexts.find(const_context) != class_contexts.end()) { // The current decl context is in our "class_contexts" which means // this is a method on a class m_method_to_index.Append (entry); } else { // We don't know if this is a function basename or a method, // so put it into a temporary collection so once we are done // we can look in class_contexts to see if each entry is a class // or just a function and will put any remaining items into // m_method_to_index or m_basename_to_index as needed mangled_name_to_index.Append (entry); symbol_contexts[entry.value] = const_context; } } else { // No context for this function so this has to be a basename m_basename_to_index.Append(entry); } } } } } } entry.cstring = mangled.GetDemangledName(symbol->GetLanguage()).GetCString(); if (entry.cstring && entry.cstring[0]) { m_name_to_index.Append (entry); if (symbol->ContainsLinkerAnnotations()) { // If the symbol has linker annotations, also add the version without the // annotations. entry.cstring = ConstString(m_objfile->StripLinkerSymbolAnnotations(entry.cstring)).GetCString(); m_name_to_index.Append (entry); } } // If the demangled name turns out to be an ObjC name, and // is a category name, add the version without categories to the index too. ObjCLanguage::MethodName objc_method (entry.cstring, true); if (objc_method.IsValid(true)) { entry.cstring = objc_method.GetSelector().GetCString(); m_selector_to_index.Append (entry); ConstString objc_method_no_category (objc_method.GetFullNameWithoutCategory(true)); if (objc_method_no_category) { entry.cstring = objc_method_no_category.GetCString(); m_name_to_index.Append (entry); } } } size_t count; if (!mangled_name_to_index.IsEmpty()) { count = mangled_name_to_index.GetSize(); for (size_t i=0; iGetMangled(); if (add_demangled) { entry.cstring = mangled.GetDemangledName(symbol->GetLanguage()).GetCString(); if (entry.cstring && entry.cstring[0]) name_to_index_map.Append (entry); } if (add_mangled) { entry.cstring = mangled.GetMangledName().GetCString(); if (entry.cstring && entry.cstring[0]) name_to_index_map.Append (entry); } } } } uint32_t Symtab::AppendSymbolIndexesWithType (SymbolType symbol_type, std::vector& indexes, uint32_t start_idx, uint32_t end_index) const { Mutex::Locker locker (m_mutex); uint32_t prev_size = indexes.size(); const uint32_t count = std::min (m_symbols.size(), end_index); for (uint32_t i = start_idx; i < count; ++i) { if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) indexes.push_back(i); } return indexes.size() - prev_size; } uint32_t Symtab::AppendSymbolIndexesWithTypeAndFlagsValue (SymbolType symbol_type, uint32_t flags_value, std::vector& indexes, uint32_t start_idx, uint32_t end_index) const { Mutex::Locker locker (m_mutex); uint32_t prev_size = indexes.size(); const uint32_t count = std::min (m_symbols.size(), end_index); for (uint32_t i = start_idx; i < count; ++i) { if ((symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) && m_symbols[i].GetFlags() == flags_value) indexes.push_back(i); } return indexes.size() - prev_size; } uint32_t Symtab::AppendSymbolIndexesWithType (SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes, uint32_t start_idx, uint32_t end_index) const { Mutex::Locker locker (m_mutex); uint32_t prev_size = indexes.size(); const uint32_t count = std::min (m_symbols.size(), end_index); for (uint32_t i = start_idx; i < count; ++i) { if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) { if (CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility)) indexes.push_back(i); } } return indexes.size() - prev_size; } uint32_t Symtab::GetIndexForSymbol (const Symbol *symbol) const { if (!m_symbols.empty()) { const Symbol *first_symbol = &m_symbols[0]; if (symbol >= first_symbol && symbol < first_symbol + m_symbols.size()) return symbol - first_symbol; } return UINT32_MAX; } struct SymbolSortInfo { const bool sort_by_load_addr; const Symbol *symbols; }; namespace { struct SymbolIndexComparator { const std::vector& symbols; std::vector &addr_cache; // Getting from the symbol to the Address to the File Address involves some work. // Since there are potentially many symbols here, and we're using this for sorting so // we're going to be computing the address many times, cache that in addr_cache. // The array passed in has to be the same size as the symbols array passed into the // member variable symbols, and should be initialized with LLDB_INVALID_ADDRESS. // NOTE: You have to make addr_cache externally and pass it in because std::stable_sort // makes copies of the comparator it is initially passed in, and you end up spending // huge amounts of time copying this array... SymbolIndexComparator(const std::vector& s, std::vector &a) : symbols(s), addr_cache(a) { assert (symbols.size() == addr_cache.size()); } bool operator()(uint32_t index_a, uint32_t index_b) { addr_t value_a = addr_cache[index_a]; if (value_a == LLDB_INVALID_ADDRESS) { value_a = symbols[index_a].GetAddressRef().GetFileAddress(); addr_cache[index_a] = value_a; } addr_t value_b = addr_cache[index_b]; if (value_b == LLDB_INVALID_ADDRESS) { value_b = symbols[index_b].GetAddressRef().GetFileAddress(); addr_cache[index_b] = value_b; } if (value_a == value_b) { // The if the values are equal, use the original symbol user ID lldb::user_id_t uid_a = symbols[index_a].GetID(); lldb::user_id_t uid_b = symbols[index_b].GetID(); if (uid_a < uid_b) return true; if (uid_a > uid_b) return false; return false; } else if (value_a < value_b) return true; return false; } }; } void Symtab::SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__,__PRETTY_FUNCTION__); // No need to sort if we have zero or one items... if (indexes.size() <= 1) return; // Sort the indexes in place using std::stable_sort. // NOTE: The use of std::stable_sort instead of std::sort here is strictly for performance, // not correctness. The indexes vector tends to be "close" to sorted, which the // stable sort handles better. std::vector addr_cache(m_symbols.size(), LLDB_INVALID_ADDRESS); SymbolIndexComparator comparator(m_symbols, addr_cache); std::stable_sort(indexes.begin(), indexes.end(), comparator); // Remove any duplicates if requested if (remove_duplicates) std::unique(indexes.begin(), indexes.end()); } uint32_t Symtab::AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector& indexes) { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); if (symbol_name) { const char *symbol_cstr = symbol_name.GetCString(); if (!m_name_indexes_computed) InitNameIndexes(); return m_name_to_index.GetValues (symbol_cstr, indexes); } return 0; } uint32_t Symtab::AppendSymbolIndexesWithName (const ConstString& symbol_name, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes) { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); if (symbol_name) { const size_t old_size = indexes.size(); if (!m_name_indexes_computed) InitNameIndexes(); const char *symbol_cstr = symbol_name.GetCString(); std::vector all_name_indexes; const size_t name_match_count = m_name_to_index.GetValues (symbol_cstr, all_name_indexes); for (size_t i=0; i& indexes) { Mutex::Locker locker (m_mutex); if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0) { std::vector::iterator pos = indexes.begin(); while (pos != indexes.end()) { if (symbol_type == eSymbolTypeAny || m_symbols[*pos].GetType() == symbol_type) ++pos; else pos = indexes.erase(pos); } } return indexes.size(); } uint32_t Symtab::AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes) { Mutex::Locker locker (m_mutex); if (AppendSymbolIndexesWithName(symbol_name, symbol_debug_type, symbol_visibility, indexes) > 0) { std::vector::iterator pos = indexes.begin(); while (pos != indexes.end()) { if (symbol_type == eSymbolTypeAny || m_symbols[*pos].GetType() == symbol_type) ++pos; else pos = indexes.erase(pos); } } return indexes.size(); } uint32_t Symtab::AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®exp, SymbolType symbol_type, std::vector& indexes) { Mutex::Locker locker (m_mutex); uint32_t prev_size = indexes.size(); uint32_t sym_end = m_symbols.size(); for (uint32_t i = 0; i < sym_end; i++) { if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) { const char *name = m_symbols[i].GetName().AsCString(); if (name) { if (regexp.Execute (name)) indexes.push_back(i); } } } return indexes.size() - prev_size; } uint32_t Symtab::AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®exp, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes) { Mutex::Locker locker (m_mutex); uint32_t prev_size = indexes.size(); uint32_t sym_end = m_symbols.size(); for (uint32_t i = 0; i < sym_end; i++) { if (symbol_type == eSymbolTypeAny || m_symbols[i].GetType() == symbol_type) { if (CheckSymbolAtIndex(i, symbol_debug_type, symbol_visibility) == false) continue; const char *name = m_symbols[i].GetName().AsCString(); if (name) { if (regexp.Execute (name)) indexes.push_back(i); } } } return indexes.size() - prev_size; } Symbol * Symtab::FindSymbolWithType (SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t& start_idx) { Mutex::Locker locker (m_mutex); const size_t count = m_symbols.size(); for (size_t idx = start_idx; idx < count; ++idx) { if (symbol_type == eSymbolTypeAny || m_symbols[idx].GetType() == symbol_type) { if (CheckSymbolAtIndex(idx, symbol_debug_type, symbol_visibility)) { start_idx = idx; return &m_symbols[idx]; } } } return nullptr; } size_t Symtab::FindAllSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, std::vector& symbol_indexes) { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); // Initialize all of the lookup by name indexes before converting NAME // to a uniqued string NAME_STR below. if (!m_name_indexes_computed) InitNameIndexes(); if (name) { // The string table did have a string that matched, but we need // to check the symbols and match the symbol_type if any was given. AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_indexes); } return symbol_indexes.size(); } size_t Symtab::FindAllSymbolsWithNameAndType (const ConstString &name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes) { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); // Initialize all of the lookup by name indexes before converting NAME // to a uniqued string NAME_STR below. if (!m_name_indexes_computed) InitNameIndexes(); if (name) { // The string table did have a string that matched, but we need // to check the symbols and match the symbol_type if any was given. AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_debug_type, symbol_visibility, symbol_indexes); } return symbol_indexes.size(); } size_t Symtab::FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes) { Mutex::Locker locker (m_mutex); AppendSymbolIndexesMatchingRegExAndType(regex, symbol_type, symbol_debug_type, symbol_visibility, symbol_indexes); return symbol_indexes.size(); } Symbol * Symtab::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility) { Mutex::Locker locker (m_mutex); Timer scoped_timer (__PRETTY_FUNCTION__, "%s", __PRETTY_FUNCTION__); if (!m_name_indexes_computed) InitNameIndexes(); if (name) { std::vector matching_indexes; // The string table did have a string that matched, but we need // to check the symbols and match the symbol_type if any was given. if (AppendSymbolIndexesWithNameAndType (name, symbol_type, symbol_debug_type, symbol_visibility, matching_indexes)) { std::vector::const_iterator pos, end = matching_indexes.end(); for (pos = matching_indexes.begin(); pos != end; ++pos) { Symbol *symbol = SymbolAtIndex(*pos); if (symbol->Compare(name, symbol_type)) return symbol; } } } return nullptr; } typedef struct { const Symtab *symtab; const addr_t file_addr; Symbol *match_symbol; const uint32_t *match_index_ptr; addr_t match_offset; } SymbolSearchInfo; static int SymbolWithClosestFileAddress (SymbolSearchInfo *info, const uint32_t *index_ptr) { const Symbol *symbol = info->symtab->SymbolAtIndex (index_ptr[0]); if (symbol == nullptr) return -1; const addr_t info_file_addr = info->file_addr; if (symbol->ValueIsAddress()) { const addr_t curr_file_addr = symbol->GetAddressRef().GetFileAddress(); if (info_file_addr < curr_file_addr) return -1; // Since we are finding the closest symbol that is greater than or equal // to 'info->file_addr' we set the symbol here. This will get set // multiple times, but after the search is done it will contain the best // symbol match info->match_symbol = const_cast(symbol); info->match_index_ptr = index_ptr; info->match_offset = info_file_addr - curr_file_addr; if (info_file_addr > curr_file_addr) return +1; return 0; } return -1; } void Symtab::InitAddressIndexes() { // Protected function, no need to lock mutex... if (!m_file_addr_to_index_computed && !m_symbols.empty()) { m_file_addr_to_index_computed = true; FileRangeToIndexMap::Entry entry; const_iterator begin = m_symbols.begin(); const_iterator end = m_symbols.end(); for (const_iterator pos = m_symbols.begin(); pos != end; ++pos) { if (pos->ValueIsAddress()) { entry.SetRangeBase(pos->GetAddressRef().GetFileAddress()); entry.SetByteSize(pos->GetByteSize()); entry.data = std::distance(begin, pos); m_file_addr_to_index.Append(entry); } } const size_t num_entries = m_file_addr_to_index.GetSize(); if (num_entries > 0) { m_file_addr_to_index.Sort(); m_file_addr_to_index.CalculateSizesOfZeroByteSizeRanges(); // Now our last symbols might not have had sizes because there // was no subsequent symbol to calculate the size from. If this is // the case, then calculate the size by capping it at the end of the // section in which the symbol resides for (int i = num_entries - 1; i >= 0; --i) { const FileRangeToIndexMap::Entry &entry = m_file_addr_to_index.GetEntryRef(i); // As we iterate backwards, as soon as we find a symbol with a valid // byte size, we are done if (entry.GetByteSize() > 0) break; // Cap the size to the end of the section in which the symbol resides SectionSP section_sp (m_objfile->GetSectionList()->FindSectionContainingFileAddress (entry.GetRangeBase())); if (section_sp) { const lldb::addr_t end_section_file_addr = section_sp->GetFileAddress() + section_sp->GetByteSize(); const lldb::addr_t symbol_file_addr = entry.GetRangeBase(); if (end_section_file_addr > symbol_file_addr) { Symbol &symbol = m_symbols[entry.data]; symbol.SetByteSize(end_section_file_addr - symbol_file_addr); symbol.SetSizeIsSynthesized(true); } } } // Sort again in case the range size changes the ordering m_file_addr_to_index.Sort(); } } } void Symtab::CalculateSymbolSizes () { Mutex::Locker locker (m_mutex); if (!m_symbols.empty()) { if (!m_file_addr_to_index_computed) InitAddressIndexes(); const size_t num_entries = m_file_addr_to_index.GetSize(); for (size_t i = 0; i < num_entries; ++i) { // The entries in the m_file_addr_to_index have calculated the sizes already // so we will use this size if we need to. const FileRangeToIndexMap::Entry &entry = m_file_addr_to_index.GetEntryRef(i); Symbol &symbol = m_symbols[entry.data]; // If the symbol size is already valid, no need to do anything if (symbol.GetByteSizeIsValid()) continue; const addr_t range_size = entry.GetByteSize(); if (range_size > 0) { symbol.SetByteSize(range_size); symbol.SetSizeIsSynthesized(true); } } } } Symbol * Symtab::FindSymbolContainingFileAddress (addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes) { Mutex::Locker locker (m_mutex); SymbolSearchInfo info = { this, file_addr, nullptr, nullptr, 0 }; ::bsearch (&info, indexes, num_indexes, sizeof(uint32_t), (ComparisonFunction)SymbolWithClosestFileAddress); if (info.match_symbol) { if (info.match_offset == 0) { // We found an exact match! return info.match_symbol; } const size_t symbol_byte_size = info.match_symbol->GetByteSize(); if (symbol_byte_size == 0) { // We weren't able to find the size of the symbol so lets just go // with that match we found in our search... return info.match_symbol; } // We were able to figure out a symbol size so lets make sure our // offset puts "file_addr" in the symbol's address range. if (info.match_offset < symbol_byte_size) return info.match_symbol; } return nullptr; } Symbol * Symtab::FindSymbolContainingFileAddress (addr_t file_addr) { Mutex::Locker locker (m_mutex); if (!m_file_addr_to_index_computed) InitAddressIndexes(); const FileRangeToIndexMap::Entry *entry = m_file_addr_to_index.FindEntryThatContains(file_addr); if (entry) return SymbolAtIndex(entry->data); return nullptr; } void +Symtab::ForEachSymbolContainingFileAddress(addr_t file_addr, std::function const &callback) +{ + Mutex::Locker locker (m_mutex); + + if (!m_file_addr_to_index_computed) + InitAddressIndexes(); + + std::vector all_addr_indexes; + + // Get all symbols with file_addr + const size_t addr_match_count = m_file_addr_to_index.FindEntryIndexesThatContain(file_addr, all_addr_indexes); + + for (size_t i = 0; i < addr_match_count; ++i) + { + if (!callback(SymbolAtIndex(all_addr_indexes[i]))) + break; + } +} + +void Symtab::SymbolIndicesToSymbolContextList (std::vector &symbol_indexes, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. const bool merge_symbol_into_function = true; size_t num_indices = symbol_indexes.size(); if (num_indices > 0) { SymbolContext sc; sc.module_sp = m_objfile->GetModule(); for (size_t i = 0; i < num_indices; i++) { sc.symbol = SymbolAtIndex (symbol_indexes[i]); if (sc.symbol) sc_list.AppendIfUnique(sc, merge_symbol_into_function); } } } size_t Symtab::FindFunctionSymbols (const ConstString &name, uint32_t name_type_mask, SymbolContextList& sc_list) { size_t count = 0; std::vector symbol_indexes; const char *name_cstr = name.GetCString(); // eFunctionNameTypeAuto should be pre-resolved by a call to Module::PrepareForFunctionNameLookup() assert ((name_type_mask & eFunctionNameTypeAuto) == 0); if (name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeFull)) { std::vector temp_symbol_indexes; FindAllSymbolsWithNameAndType (name, eSymbolTypeAny, temp_symbol_indexes); unsigned temp_symbol_indexes_size = temp_symbol_indexes.size(); if (temp_symbol_indexes_size > 0) { Mutex::Locker locker (m_mutex); for (unsigned i = 0; i < temp_symbol_indexes_size; i++) { SymbolContext sym_ctx; sym_ctx.symbol = SymbolAtIndex (temp_symbol_indexes[i]); if (sym_ctx.symbol) { switch (sym_ctx.symbol->GetType()) { case eSymbolTypeCode: case eSymbolTypeResolver: case eSymbolTypeReExported: symbol_indexes.push_back(temp_symbol_indexes[i]); break; default: break; } } } } } if (name_type_mask & eFunctionNameTypeBase) { // From mangled names we can't tell what is a basename and what // is a method name, so we just treat them the same if (!m_name_indexes_computed) InitNameIndexes(); if (!m_basename_to_index.IsEmpty()) { const UniqueCStringMap::Entry *match; for (match = m_basename_to_index.FindFirstValueForName(name_cstr); match != nullptr; match = m_basename_to_index.FindNextValueForName(match)) { symbol_indexes.push_back(match->value); } } } if (name_type_mask & eFunctionNameTypeMethod) { if (!m_name_indexes_computed) InitNameIndexes(); if (!m_method_to_index.IsEmpty()) { const UniqueCStringMap::Entry *match; for (match = m_method_to_index.FindFirstValueForName(name_cstr); match != nullptr; match = m_method_to_index.FindNextValueForName(match)) { symbol_indexes.push_back(match->value); } } } if (name_type_mask & eFunctionNameTypeSelector) { if (!m_name_indexes_computed) InitNameIndexes(); if (!m_selector_to_index.IsEmpty()) { const UniqueCStringMap::Entry *match; for (match = m_selector_to_index.FindFirstValueForName(name_cstr); match != nullptr; match = m_selector_to_index.FindNextValueForName(match)) { symbol_indexes.push_back(match->value); } } } if (!symbol_indexes.empty()) { std::sort(symbol_indexes.begin(), symbol_indexes.end()); symbol_indexes.erase(std::unique(symbol_indexes.begin(), symbol_indexes.end()), symbol_indexes.end()); count = symbol_indexes.size(); SymbolIndicesToSymbolContextList (symbol_indexes, sc_list); } return count; } const Symbol * Symtab::GetParent (Symbol *child_symbol) const { uint32_t child_idx = GetIndexForSymbol(child_symbol); if (child_idx != UINT32_MAX && child_idx > 0) { for (uint32_t idx = child_idx - 1; idx != UINT32_MAX; --idx) { const Symbol *symbol = SymbolAtIndex (idx); const uint32_t sibling_idx = symbol->GetSiblingIndex(); if (sibling_idx != UINT32_MAX && sibling_idx > child_idx) return symbol; } } return NULL; } Index: vendor/lldb/dist/source/Target/Target.cpp =================================================================== --- vendor/lldb/dist/source/Target/Target.cpp (revision 295597) +++ vendor/lldb/dist/source/Target/Target.cpp (revision 295598) @@ -1,4182 +1,4182 @@ //===-- Target.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/Target.h" #include "lldb/Breakpoint/BreakpointResolver.h" #include "lldb/Breakpoint/BreakpointResolverAddress.h" #include "lldb/Breakpoint/BreakpointResolverFileLine.h" #include "lldb/Breakpoint/BreakpointResolverFileRegex.h" #include "lldb/Breakpoint/BreakpointResolverName.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Event.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/REPL.h" #include "lldb/Expression/UserExpression.h" #include "Plugins/ExpressionParser/Clang/ClangASTSource.h" #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupWatchpoint.h" #include "lldb/Interpreter/OptionValues.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Language.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/LLDBAssert.h" using namespace lldb; using namespace lldb_private; ConstString & Target::GetStaticBroadcasterClass () { static ConstString class_name ("lldb.target"); return class_name; } Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp, bool is_dummy_target) : TargetProperties (this), Broadcaster (&debugger, Target::GetStaticBroadcasterClass().AsCString()), ExecutionContextScope (), m_debugger (debugger), m_platform_sp (platform_sp), m_mutex (Mutex::eMutexTypeRecursive), m_arch (target_arch), m_images (this), m_section_load_history (), m_breakpoint_list (false), m_internal_breakpoint_list (true), m_watchpoint_list (), m_process_sp (), m_search_filter_sp (), m_image_search_paths (ImageSearchPathsChanged, this), m_ast_importer_sp (), m_source_manager_ap(), m_stop_hooks (), m_stop_hook_next_id (0), m_valid (true), m_suppress_stop_hooks (false), m_is_dummy_target(is_dummy_target) { SetEventName (eBroadcastBitBreakpointChanged, "breakpoint-changed"); SetEventName (eBroadcastBitModulesLoaded, "modules-loaded"); SetEventName (eBroadcastBitModulesUnloaded, "modules-unloaded"); SetEventName (eBroadcastBitWatchpointChanged, "watchpoint-changed"); SetEventName (eBroadcastBitSymbolsLoaded, "symbols-loaded"); CheckInWithManager(); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) log->Printf ("%p Target::Target()", static_cast(this)); if (m_arch.IsValid()) { LogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET, "Target::Target created with architecture %s (%s)", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); } } Target::~Target() { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) log->Printf ("%p Target::~Target()", static_cast(this)); DeleteCurrentProcess (); } void Target::PrimeFromDummyTarget(Target *target) { if (!target) return; m_stop_hooks = target->m_stop_hooks; for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) { if (breakpoint_sp->IsInternal()) continue; BreakpointSP new_bp (new Breakpoint (*this, *breakpoint_sp.get())); AddBreakpoint (new_bp, false); } } void Target::Dump (Stream *s, lldb::DescriptionLevel description_level) { // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); if (description_level != lldb::eDescriptionLevelBrief) { s->Indent(); s->PutCString("Target\n"); s->IndentMore(); m_images.Dump(s); m_breakpoint_list.Dump(s); m_internal_breakpoint_list.Dump(s); s->IndentLess(); } else { Module *exe_module = GetExecutableModulePointer(); if (exe_module) s->PutCString (exe_module->GetFileSpec().GetFilename().GetCString()); else s->PutCString ("No executable module."); } } void Target::CleanupProcess () { // Do any cleanup of the target we need to do between process instances. // NB It is better to do this before destroying the process in case the // clean up needs some help from the process. m_breakpoint_list.ClearAllBreakpointSites(); m_internal_breakpoint_list.ClearAllBreakpointSites(); // Disable watchpoints just on the debugger side. Mutex::Locker locker; this->GetWatchpointList().GetListMutex(locker); DisableAllWatchpoints(false); ClearAllWatchpointHitCounts(); ClearAllWatchpointHistoricValues(); } void Target::DeleteCurrentProcess () { if (m_process_sp) { m_section_load_history.Clear(); if (m_process_sp->IsAlive()) m_process_sp->Destroy(false); m_process_sp->Finalize(); CleanupProcess (); m_process_sp.reset(); } } const lldb::ProcessSP & Target::CreateProcess (Listener &listener, const char *plugin_name, const FileSpec *crash_file) { DeleteCurrentProcess (); m_process_sp = Process::FindPlugin(shared_from_this(), plugin_name, listener, crash_file); return m_process_sp; } const lldb::ProcessSP & Target::GetProcessSP () const { return m_process_sp; } lldb::REPLSP Target::GetREPL (Error &err, lldb::LanguageType language, const char *repl_options, bool can_create) { if (language == eLanguageTypeUnknown) { std::set repl_languages; Language::GetLanguagesSupportingREPLs(repl_languages); if (repl_languages.size() == 1) { language = *repl_languages.begin(); } else if (repl_languages.size() == 0) { err.SetErrorStringWithFormat("LLDB isn't configured with support support for any REPLs."); return REPLSP(); } else { err.SetErrorStringWithFormat("Multiple possible REPL languages. Please specify a language."); return REPLSP(); } } REPLMap::iterator pos = m_repl_map.find(language); if (pos != m_repl_map.end()) { return pos->second; } if (!can_create) { err.SetErrorStringWithFormat("Couldn't find an existing REPL for %s, and can't create a new one", Language::GetNameForLanguageType(language)); return lldb::REPLSP(); } Debugger *const debugger = nullptr; lldb::REPLSP ret = REPL::Create(err, language, debugger, this, repl_options); if (ret) { m_repl_map[language] = ret; return m_repl_map[language]; } if (err.Success()) { err.SetErrorStringWithFormat("Couldn't create a REPL for %s", Language::GetNameForLanguageType(language)); } return lldb::REPLSP(); } void Target::SetREPL (lldb::LanguageType language, lldb::REPLSP repl_sp) { lldbassert(!m_repl_map.count(language)); m_repl_map[language] = repl_sp; } void Target::Destroy() { Mutex::Locker locker (m_mutex); m_valid = false; DeleteCurrentProcess (); m_platform_sp.reset(); m_arch.Clear(); ClearModules(true); m_section_load_history.Clear(); const bool notify = false; m_breakpoint_list.RemoveAll(notify); m_internal_breakpoint_list.RemoveAll(notify); m_last_created_breakpoint.reset(); m_last_created_watchpoint.reset(); m_search_filter_sp.reset(); m_image_search_paths.Clear(notify); m_stop_hooks.clear(); m_stop_hook_next_id = 0; m_suppress_stop_hooks = false; } BreakpointList & Target::GetBreakpointList(bool internal) { if (internal) return m_internal_breakpoint_list; else return m_breakpoint_list; } const BreakpointList & Target::GetBreakpointList(bool internal) const { if (internal) return m_internal_breakpoint_list; else return m_breakpoint_list; } BreakpointSP Target::GetBreakpointByID (break_id_t break_id) { BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); return bp_sp; } BreakpointSP Target::CreateSourceRegexBreakpoint (const FileSpecList *containingModules, const FileSpecList *source_file_spec_list, RegularExpression &source_regex, bool internal, bool hardware, LazyBool move_to_nearest_code) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, source_file_spec_list)); if (move_to_nearest_code == eLazyBoolCalculate) move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex(nullptr, source_regex, !static_cast(move_to_nearest_code))); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } BreakpointSP Target::CreateBreakpoint (const FileSpecList *containingModules, const FileSpec &file, uint32_t line_no, LazyBool check_inlines, LazyBool skip_prologue, bool internal, bool hardware, LazyBool move_to_nearest_code) { if (check_inlines == eLazyBoolCalculate) { const InlineStrategy inline_strategy = GetInlineStrategy(); switch (inline_strategy) { case eInlineBreakpointsNever: check_inlines = eLazyBoolNo; break; case eInlineBreakpointsHeaders: if (file.IsSourceImplementationFile()) check_inlines = eLazyBoolNo; else check_inlines = eLazyBoolYes; break; case eInlineBreakpointsAlways: check_inlines = eLazyBoolYes; break; } } SearchFilterSP filter_sp; if (check_inlines == eLazyBoolNo) { // Not checking for inlines, we are looking only for matching compile units FileSpecList compile_unit_list; compile_unit_list.Append (file); filter_sp = GetSearchFilterForModuleAndCUList (containingModules, &compile_unit_list); } else { filter_sp = GetSearchFilterForModuleList (containingModules); } if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (move_to_nearest_code == eLazyBoolCalculate) move_to_nearest_code = GetMoveToNearestCode() ? eLazyBoolYes : eLazyBoolNo; BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine(nullptr, file, line_no, check_inlines, skip_prologue, !static_cast(move_to_nearest_code))); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } BreakpointSP Target::CreateBreakpoint (lldb::addr_t addr, bool internal, bool hardware) { Address so_addr; // Check for any reason we want to move this breakpoint to other address. addr = GetBreakableLoadAddress(addr); // Attempt to resolve our load address if possible, though it is ok if // it doesn't resolve to section/offset. // Try and resolve as a load address if possible GetSectionLoadList().ResolveLoadAddress(addr, so_addr); if (!so_addr.IsValid()) { // The address didn't resolve, so just set this as an absolute address so_addr.SetOffset (addr); } BreakpointSP bp_sp (CreateBreakpoint(so_addr, internal, hardware)); return bp_sp; } BreakpointSP Target::CreateBreakpoint (const Address &addr, bool internal, bool hardware) { SearchFilterSP filter_sp(new SearchFilterForUnconstrainedSearches (shared_from_this())); BreakpointResolverSP resolver_sp(new BreakpointResolverAddress(nullptr, addr)); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, false); } lldb::BreakpointSP Target::CreateAddressInModuleBreakpoint (lldb::addr_t file_addr, bool internal, const FileSpec *file_spec, bool request_hardware) { SearchFilterSP filter_sp(new SearchFilterForUnconstrainedSearches (shared_from_this())); BreakpointResolverSP resolver_sp(new BreakpointResolverAddress(nullptr, file_addr, file_spec)); return CreateBreakpoint (filter_sp, resolver_sp, internal, request_hardware, false); } BreakpointSP Target::CreateBreakpoint (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const char *func_name, uint32_t func_name_type_mask, LanguageType language, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (func_name) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp(new BreakpointResolverName(nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, skip_prologue)); bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } lldb::BreakpointSP Target::CreateBreakpoint (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const std::vector &func_names, uint32_t func_name_type_mask, LanguageType language, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; size_t num_names = func_names.size(); if (num_names > 0) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp(new BreakpointResolverName(nullptr, func_names, func_name_type_mask, language, skip_prologue)); bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } BreakpointSP Target::CreateBreakpoint (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const char *func_names[], size_t num_names, uint32_t func_name_type_mask, LanguageType language, LazyBool skip_prologue, bool internal, bool hardware) { BreakpointSP bp_sp; if (num_names > 0) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles)); if (skip_prologue == eLazyBoolCalculate) skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo; if (language == lldb::eLanguageTypeUnknown) language = GetLanguage(); BreakpointResolverSP resolver_sp(new BreakpointResolverName(nullptr, func_names, num_names, func_name_type_mask, language, skip_prologue)); bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; } SearchFilterSP Target::GetSearchFilterForModule (const FileSpec *containingModule) { SearchFilterSP filter_sp; if (containingModule != nullptr) { // TODO: We should look into sharing module based search filters // across many breakpoints like we do for the simple target based one filter_sp.reset (new SearchFilterByModule (shared_from_this(), *containingModule)); } else { if (!m_search_filter_sp) m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; } SearchFilterSP Target::GetSearchFilterForModuleList (const FileSpecList *containingModules) { SearchFilterSP filter_sp; if (containingModules && containingModules->GetSize() != 0) { // TODO: We should look into sharing module based search filters // across many breakpoints like we do for the simple target based one filter_sp.reset (new SearchFilterByModuleList (shared_from_this(), *containingModules)); } else { if (!m_search_filter_sp) m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; } SearchFilterSP Target::GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles) { if (containingSourceFiles == nullptr || containingSourceFiles->GetSize() == 0) return GetSearchFilterForModuleList(containingModules); SearchFilterSP filter_sp; if (containingModules == nullptr) { // We could make a special "CU List only SearchFilter". Better yet was if these could be composable, // but that will take a little reworking. filter_sp.reset (new SearchFilterByModuleListAndCU (shared_from_this(), FileSpecList(), *containingSourceFiles)); } else { filter_sp.reset (new SearchFilterByModuleListAndCU (shared_from_this(), *containingModules, *containingSourceFiles)); } return filter_sp; } BreakpointSP Target::CreateFuncRegexBreakpoint (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, RegularExpression &func_regex, lldb::LanguageType requested_language, LazyBool skip_prologue, bool internal, bool hardware) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles)); bool skip = (skip_prologue == eLazyBoolCalculate) ? GetSkipPrologue() : static_cast(skip_prologue); BreakpointResolverSP resolver_sp(new BreakpointResolverName(nullptr, func_regex, requested_language, skip)); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, true); } lldb::BreakpointSP Target::CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal, Args *additional_args, Error *error) { BreakpointSP exc_bkpt_sp = LanguageRuntime::CreateExceptionBreakpoint (*this, language, catch_bp, throw_bp, internal); if (exc_bkpt_sp && additional_args) { Breakpoint::BreakpointPreconditionSP precondition_sp = exc_bkpt_sp->GetPrecondition(); if (precondition_sp && additional_args) { if (error) *error = precondition_sp->ConfigurePrecondition(*additional_args); else precondition_sp->ConfigurePrecondition(*additional_args); } } return exc_bkpt_sp; } BreakpointSP Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal, bool request_hardware, bool resolve_indirect_symbols) { BreakpointSP bp_sp; if (filter_sp && resolver_sp) { bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp, request_hardware, resolve_indirect_symbols)); resolver_sp->SetBreakpoint (bp_sp.get()); AddBreakpoint (bp_sp, internal); } return bp_sp; } void Target::AddBreakpoint (lldb::BreakpointSP bp_sp, bool internal) { if (!bp_sp) return; if (internal) m_internal_breakpoint_list.Add (bp_sp, false); else m_breakpoint_list.Add (bp_sp, true); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) { StreamString s; bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); } bp_sp->ResolveBreakpoint(); if (!internal) { m_last_created_breakpoint = bp_sp; } } bool Target::ProcessIsValid() { return (m_process_sp && m_process_sp->IsAlive()); } static bool CheckIfWatchpointsExhausted(Target *target, Error &error) { uint32_t num_supported_hardware_watchpoints; Error rc = target->GetProcessSP()->GetWatchpointSupportInfo(num_supported_hardware_watchpoints); if (rc.Success()) { uint32_t num_current_watchpoints = target->GetWatchpointList().GetSize(); if (num_current_watchpoints >= num_supported_hardware_watchpoints) error.SetErrorStringWithFormat("number of supported hardware watchpoints (%u) has been reached", num_supported_hardware_watchpoints); } return false; } // See also Watchpoint::SetWatchpointType(uint32_t type) and // the OptionGroupWatchpoint::WatchType enum type. WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size, const CompilerType *type, uint32_t kind, Error &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf("Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64 " type = %u)\n", __FUNCTION__, addr, (uint64_t)size, kind); WatchpointSP wp_sp; if (!ProcessIsValid()) { error.SetErrorString("process is not alive"); return wp_sp; } if (addr == LLDB_INVALID_ADDRESS || size == 0) { if (size == 0) error.SetErrorString("cannot set a watchpoint with watch_size of 0"); else error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr); return wp_sp; } if (!LLDB_WATCH_TYPE_IS_VALID(kind)) { error.SetErrorStringWithFormat ("invalid watchpoint type: %d", kind); } // Currently we only support one watchpoint per address, with total number // of watchpoints limited by the hardware which the inferior is running on. // Grab the list mutex while doing operations. const bool notify = false; // Don't notify about all the state changes we do on creating the watchpoint. Mutex::Locker locker; this->GetWatchpointList().GetListMutex(locker); WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr); if (matched_sp) { size_t old_size = matched_sp->GetByteSize(); uint32_t old_type = (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) | (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0); // Return the existing watchpoint if both size and type match. if (size == old_size && kind == old_type) { wp_sp = matched_sp; wp_sp->SetEnabled(false, notify); } else { // Nil the matched watchpoint; we will be creating a new one. m_process_sp->DisableWatchpoint(matched_sp.get(), notify); m_watchpoint_list.Remove(matched_sp->GetID(), true); } } if (!wp_sp) { wp_sp.reset(new Watchpoint(*this, addr, size, type)); wp_sp->SetWatchpointType(kind, notify); m_watchpoint_list.Add (wp_sp, true); } error = m_process_sp->EnableWatchpoint(wp_sp.get(), notify); if (log) log->Printf("Target::%s (creation of watchpoint %s with id = %u)\n", __FUNCTION__, error.Success() ? "succeeded" : "failed", wp_sp->GetID()); if (error.Fail()) { // Enabling the watchpoint on the device side failed. // Remove the said watchpoint from the list maintained by the target instance. m_watchpoint_list.Remove (wp_sp->GetID(), true); // See if we could provide more helpful error message. if (!CheckIfWatchpointsExhausted(this, error)) { if (!OptionGroupWatchpoint::IsWatchSizeSupported(size)) error.SetErrorStringWithFormat("watch size of %" PRIu64 " is not supported", (uint64_t)size); } wp_sp.reset(); } else m_last_created_watchpoint = wp_sp; return wp_sp; } void Target::RemoveAllBreakpoints (bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.RemoveAll (true); if (internal_also) m_internal_breakpoint_list.RemoveAll (false); m_last_created_breakpoint.reset(); } void Target::DisableAllBreakpoints (bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.SetEnabledAll (false); if (internal_also) m_internal_breakpoint_list.SetEnabledAll (false); } void Target::EnableAllBreakpoints (bool internal_also) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no"); m_breakpoint_list.SetEnabledAll (true); if (internal_also) m_internal_breakpoint_list.SetEnabledAll (true); } bool Target::RemoveBreakpointByID (break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); if (DisableBreakpointByID (break_id)) { if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) m_internal_breakpoint_list.Remove(break_id, false); else { if (m_last_created_breakpoint) { if (m_last_created_breakpoint->GetID() == break_id) m_last_created_breakpoint.reset(); } m_breakpoint_list.Remove(break_id, true); } return true; } return false; } bool Target::DisableBreakpointByID (break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); if (bp_sp) { bp_sp->SetEnabled (false); return true; } return false; } bool Target::EnableBreakpointByID (break_id_t break_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no"); BreakpointSP bp_sp; if (LLDB_BREAK_ID_IS_INTERNAL (break_id)) bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id); else bp_sp = m_breakpoint_list.FindBreakpointByID (break_id); if (bp_sp) { bp_sp->SetEnabled (true); return true; } return false; } // The flag 'end_to_end', default to true, signifies that the operation is // performed end to end, for both the debugger and the debuggee. // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end // to end operations. bool Target::RemoveAllWatchpoints (bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.RemoveAll(true); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Error rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } m_watchpoint_list.RemoveAll (true); m_last_created_watchpoint.reset(); return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to // end operations. bool Target::DisableAllWatchpoints (bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.SetEnabledAll(false); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Error rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to // end operations. bool Target::EnableAllWatchpoints (bool end_to_end) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); if (!end_to_end) { m_watchpoint_list.SetEnabledAll(true); return true; } // Otherwise, it's an end to end operation. if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; Error rc = m_process_sp->EnableWatchpoint(wp_sp.get()); if (rc.Fail()) return false; } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::ClearAllWatchpointHitCounts () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->ResetHitCount(); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::ClearAllWatchpointHistoricValues () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->ResetHistoricValues(); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list // during these operations. bool Target::IgnoreAllWatchpoints (uint32_t ignore_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s\n", __FUNCTION__); if (!ProcessIsValid()) return false; size_t num_watchpoints = m_watchpoint_list.GetSize(); for (size_t i = 0; i < num_watchpoints; ++i) { WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i); if (!wp_sp) return false; wp_sp->SetIgnoreCount(ignore_count); } return true; // Success! } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::DisableWatchpointByID (lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id); if (wp_sp) { Error rc = m_process_sp->DisableWatchpoint(wp_sp.get()); if (rc.Success()) return true; // Else, fallthrough. } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::EnableWatchpointByID (lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id); if (wp_sp) { Error rc = m_process_sp->EnableWatchpoint(wp_sp.get()); if (rc.Success()) return true; // Else, fallthrough. } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::RemoveWatchpointByID (lldb::watch_id_t watch_id) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id); if (watch_to_remove_sp == m_last_created_watchpoint) m_last_created_watchpoint.reset(); if (DisableWatchpointByID (watch_id)) { m_watchpoint_list.Remove(watch_id, true); return true; } return false; } // Assumption: Caller holds the list mutex lock for m_watchpoint_list. bool Target::IgnoreWatchpointByID (lldb::watch_id_t watch_id, uint32_t ignore_count) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS)); if (log) log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id); if (!ProcessIsValid()) return false; WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id); if (wp_sp) { wp_sp->SetIgnoreCount(ignore_count); return true; } return false; } ModuleSP Target::GetExecutableModule () { // search for the first executable in the module list for (size_t i = 0; i < m_images.GetSize(); ++i) { ModuleSP module_sp = m_images.GetModuleAtIndex (i); lldb_private::ObjectFile * obj = module_sp->GetObjectFile(); if (obj == nullptr) continue; if (obj->GetType() == ObjectFile::Type::eTypeExecutable) return module_sp; } // as fall back return the first module loaded return m_images.GetModuleAtIndex (0); } Module* Target::GetExecutableModulePointer () { return GetExecutableModule().get(); } static void LoadScriptingResourceForModule (const ModuleSP &module_sp, Target *target) { Error error; StreamString feedback_stream; if (module_sp && !module_sp->LoadScriptingResourceInTarget(target, error, &feedback_stream)) { if (error.AsCString()) target->GetDebugger().GetErrorFile()->Printf("unable to load scripting data for module %s - error reported was %s\n", module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), error.AsCString()); } if (feedback_stream.GetSize()) target->GetDebugger().GetErrorFile()->Printf("%s\n", feedback_stream.GetData()); } void Target::ClearModules(bool delete_locations) { ModulesDidUnload (m_images, delete_locations); m_section_load_history.Clear(); m_images.Clear(); m_scratch_type_system_map.Clear(); m_ast_importer_sp.reset(); } void Target::DidExec () { // When a process exec's we need to know about it so we can do some cleanup. m_breakpoint_list.RemoveInvalidLocations(m_arch); m_internal_breakpoint_list.RemoveInvalidLocations(m_arch); } void Target::SetExecutableModule (ModuleSP& executable_sp, bool get_dependent_files) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); ClearModules(false); if (executable_sp) { Timer scoped_timer (__PRETTY_FUNCTION__, "Target::SetExecutableModule (executable = '%s')", executable_sp->GetFileSpec().GetPath().c_str()); m_images.Append(executable_sp); // The first image is our executable file // If we haven't set an architecture yet, reset our architecture based on what we found in the executable module. if (!m_arch.IsValid()) { m_arch = executable_sp->GetArchitecture(); if (log) log->Printf ("Target::SetExecutableModule setting architecture to %s (%s) based on executable file", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); } FileSpecList dependent_files; ObjectFile *executable_objfile = executable_sp->GetObjectFile(); if (executable_objfile && get_dependent_files) { executable_objfile->GetDependentModules(dependent_files); for (uint32_t i=0; iGetFileWithUUID(dependent_file_spec, nullptr, platform_dependent_file_spec); else platform_dependent_file_spec = dependent_file_spec; ModuleSpec module_spec (platform_dependent_file_spec, m_arch); ModuleSP image_module_sp(GetSharedModule (module_spec)); if (image_module_sp) { ObjectFile *objfile = image_module_sp->GetObjectFile(); if (objfile) objfile->GetDependentModules(dependent_files); } } } } } bool Target::SetArchitecture (const ArchSpec &arch_spec) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); bool missing_local_arch = !m_arch.IsValid(); bool replace_local_arch = true; bool compatible_local_arch = false; ArchSpec other(arch_spec); if (!missing_local_arch) { if (m_arch.IsCompatibleMatch(arch_spec)) { other.MergeFrom(m_arch); if (m_arch.IsCompatibleMatch(other)) { compatible_local_arch = true; bool arch_changed, vendor_changed, os_changed, os_ver_changed, env_changed; m_arch.PiecewiseTripleCompare(other, arch_changed, vendor_changed, os_changed, os_ver_changed, env_changed); if (!arch_changed && !vendor_changed && !os_changed) replace_local_arch = false; } } } if (compatible_local_arch || missing_local_arch) { // If we haven't got a valid arch spec, or the architectures are compatible // update the architecture, unless the one we already have is more specified if (replace_local_arch) m_arch = other; if (log) log->Printf ("Target::SetArchitecture set architecture to %s (%s)", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str()); return true; } // If we have an executable file, try to reset the executable to the desired architecture if (log) log->Printf ("Target::SetArchitecture changing architecture to %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str()); m_arch = other; ModuleSP executable_sp = GetExecutableModule (); ClearModules(true); // Need to do something about unsetting breakpoints. if (executable_sp) { if (log) log->Printf("Target::SetArchitecture Trying to select executable file architecture %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str()); ModuleSpec module_spec (executable_sp->GetFileSpec(), other); Error error = ModuleList::GetSharedModule(module_spec, executable_sp, &GetExecutableSearchPaths(), nullptr, nullptr); if (!error.Fail() && executable_sp) { SetExecutableModule (executable_sp, true); return true; } } return false; } bool Target::MergeArchitecture (const ArchSpec &arch_spec) { if (arch_spec.IsValid()) { if (m_arch.IsCompatibleMatch(arch_spec)) { // The current target arch is compatible with "arch_spec", see if we // can improve our current architecture using bits from "arch_spec" // Merge bits from arch_spec into "merged_arch" and set our architecture ArchSpec merged_arch (m_arch); merged_arch.MergeFrom (arch_spec); return SetArchitecture(merged_arch); } else { // The new architecture is different, we just need to replace it return SetArchitecture(arch_spec); } } return false; } void Target::WillClearList (const ModuleList& module_list) { } void Target::ModuleAdded (const ModuleList& module_list, const ModuleSP &module_sp) { // A module is being added to this target for the first time if (m_valid) { ModuleList my_module_list; my_module_list.Append(module_sp); LoadScriptingResourceForModule(module_sp, this); ModulesDidLoad (my_module_list); } } void Target::ModuleRemoved (const ModuleList& module_list, const ModuleSP &module_sp) { // A module is being removed from this target. if (m_valid) { ModuleList my_module_list; my_module_list.Append(module_sp); ModulesDidUnload (my_module_list, false); } } void Target::ModuleUpdated (const ModuleList& module_list, const ModuleSP &old_module_sp, const ModuleSP &new_module_sp) { // A module is replacing an already added module if (m_valid) { m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, new_module_sp); m_internal_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, new_module_sp); } } void Target::ModulesDidLoad (ModuleList &module_list) { if (m_valid && module_list.GetSize()) { m_breakpoint_list.UpdateBreakpoints (module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints (module_list, true, false); if (m_process_sp) { m_process_sp->ModulesDidLoad (module_list); } BroadcastEvent (eBroadcastBitModulesLoaded, new TargetEventData (this->shared_from_this(), module_list)); } } void Target::SymbolsDidLoad (ModuleList &module_list) { if (m_valid && module_list.GetSize()) { if (m_process_sp) { LanguageRuntime* runtime = m_process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); if (runtime) { ObjCLanguageRuntime *objc_runtime = (ObjCLanguageRuntime*)runtime; objc_runtime->SymbolsDidLoad(module_list); } } m_breakpoint_list.UpdateBreakpoints (module_list, true, false); m_internal_breakpoint_list.UpdateBreakpoints (module_list, true, false); BroadcastEvent (eBroadcastBitSymbolsLoaded, new TargetEventData (this->shared_from_this(), module_list)); } } void Target::ModulesDidUnload (ModuleList &module_list, bool delete_locations) { if (m_valid && module_list.GetSize()) { UnloadModuleSections (module_list); m_breakpoint_list.UpdateBreakpoints (module_list, false, delete_locations); m_internal_breakpoint_list.UpdateBreakpoints (module_list, false, delete_locations); BroadcastEvent (eBroadcastBitModulesUnloaded, new TargetEventData (this->shared_from_this(), module_list)); } } bool Target::ModuleIsExcludedForUnconstrainedSearches (const FileSpec &module_file_spec) { if (GetBreakpointsConsultPlatformAvoidList()) { ModuleList matchingModules; ModuleSpec module_spec (module_file_spec); size_t num_modules = GetImages().FindModules(module_spec, matchingModules); // If there is more than one module for this file spec, only return true if ALL the modules are on the // black list. if (num_modules > 0) { for (size_t i = 0; i < num_modules; i++) { if (!ModuleIsExcludedForUnconstrainedSearches (matchingModules.GetModuleAtIndex(i))) return false; } return true; } } return false; } bool Target::ModuleIsExcludedForUnconstrainedSearches (const lldb::ModuleSP &module_sp) { if (GetBreakpointsConsultPlatformAvoidList()) { if (m_platform_sp) return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches (*this, module_sp); } return false; } size_t Target::ReadMemoryFromFileCache (const Address& addr, void *dst, size_t dst_len, Error &error) { SectionSP section_sp (addr.GetSection()); if (section_sp) { // If the contents of this section are encrypted, the on-disk file is unusable. Read only from live memory. if (section_sp->IsEncrypted()) { error.SetErrorString("section is encrypted"); return 0; } ModuleSP module_sp (section_sp->GetModule()); if (module_sp) { ObjectFile *objfile = section_sp->GetModule()->GetObjectFile(); if (objfile) { size_t bytes_read = objfile->ReadSectionData (section_sp.get(), addr.GetOffset(), dst, dst_len); if (bytes_read > 0) return bytes_read; else error.SetErrorStringWithFormat("error reading data from section %s", section_sp->GetName().GetCString()); } else error.SetErrorString("address isn't from a object file"); } else error.SetErrorString("address isn't in a module"); } else error.SetErrorString("address doesn't contain a section that points to a section in a object file"); return 0; } size_t Target::ReadMemory (const Address& addr, bool prefer_file_cache, void *dst, size_t dst_len, Error &error, lldb::addr_t *load_addr_ptr) { error.Clear(); // if we end up reading this from process memory, we will fill this // with the actual load address if (load_addr_ptr) *load_addr_ptr = LLDB_INVALID_ADDRESS; size_t bytes_read = 0; addr_t load_addr = LLDB_INVALID_ADDRESS; addr_t file_addr = LLDB_INVALID_ADDRESS; Address resolved_addr; if (!addr.IsSectionOffset()) { SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) { // No sections are loaded, so we must assume we are not running // yet and anything we are given is a file address. file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its offset is the file address m_images.ResolveFileAddress (file_addr, resolved_addr); } else { // We have at least one section loaded. This can be because // we have manually loaded some sections with "target modules load ..." // or because we have have a live process that has sections loaded // through the dynamic loader load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its offset is the load address section_load_list.ResolveLoadAddress (load_addr, resolved_addr); } } if (!resolved_addr.IsValid()) resolved_addr = addr; if (prefer_file_cache) { bytes_read = ReadMemoryFromFileCache (resolved_addr, dst, dst_len, error); if (bytes_read > 0) return bytes_read; } if (ProcessIsValid()) { if (load_addr == LLDB_INVALID_ADDRESS) load_addr = resolved_addr.GetLoadAddress (this); if (load_addr == LLDB_INVALID_ADDRESS) { ModuleSP addr_module_sp (resolved_addr.GetModule()); if (addr_module_sp && addr_module_sp->GetFileSpec()) error.SetErrorStringWithFormat("%s[0x%" PRIx64 "] can't be resolved, %s in not currently loaded", addr_module_sp->GetFileSpec().GetFilename().AsCString(""), resolved_addr.GetFileAddress(), addr_module_sp->GetFileSpec().GetFilename().AsCString("")); else error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress()); } else { bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error); if (bytes_read != dst_len) { if (error.Success()) { if (bytes_read == 0) error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed", load_addr); else error.SetErrorStringWithFormat("only %" PRIu64 " of %" PRIu64 " bytes were read from memory at 0x%" PRIx64, (uint64_t)bytes_read, (uint64_t)dst_len, load_addr); } } if (bytes_read) { if (load_addr_ptr) *load_addr_ptr = load_addr; return bytes_read; } // If the address is not section offset we have an address that // doesn't resolve to any address in any currently loaded shared // libraries and we failed to read memory so there isn't anything // more we can do. If it is section offset, we might be able to // read cached memory from the object file. if (!resolved_addr.IsSectionOffset()) return 0; } } if (!prefer_file_cache && resolved_addr.IsSectionOffset()) { // If we didn't already try and read from the object file cache, then // try it after failing to read from the process. return ReadMemoryFromFileCache (resolved_addr, dst, dst_len, error); } return 0; } size_t Target::ReadCStringFromMemory (const Address& addr, std::string &out_str, Error &error) { char buf[256]; out_str.clear(); addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); while (1) { size_t length = ReadCStringFromMemory (address, buf, sizeof(buf), error); if (length == 0) break; out_str.append(buf, length); // If we got "length - 1" bytes, we didn't get the whole C string, we // need to read some more characters if (length == sizeof(buf) - 1) curr_addr += length; else break; address = Address(curr_addr); } return out_str.size(); } size_t Target::ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_len, Error &result_error) { size_t total_cstr_len = 0; if (dst && dst_max_len) { result_error.Clear(); // NULL out everything just to be safe memset (dst, 0, dst_max_len); Error error; addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); // We could call m_process_sp->GetMemoryCacheLineSize() but I don't // think this really needs to be tied to the memory cache subsystem's // cache line size, so leave this as a fixed constant. const size_t cache_line_size = 512; size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; while (bytes_left > 0) { addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size); addr_t bytes_to_read = std::min(bytes_left, cache_line_bytes_left); size_t bytes_read = ReadMemory (address, false, curr_dst, bytes_to_read, error); if (bytes_read == 0) { result_error = error; dst[total_cstr_len] = '\0'; break; } const size_t len = strlen(curr_dst); total_cstr_len += len; if (len < bytes_to_read) break; curr_dst += bytes_read; curr_addr += bytes_read; bytes_left -= bytes_read; address = Address(curr_addr); } } else { if (dst == nullptr) result_error.SetErrorString("invalid arguments"); else result_error.Clear(); } return total_cstr_len; } size_t Target::ReadScalarIntegerFromMemory (const Address& addr, bool prefer_file_cache, uint32_t byte_size, bool is_signed, Scalar &scalar, Error &error) { uint64_t uval; if (byte_size <= sizeof(uval)) { size_t bytes_read = ReadMemory (addr, prefer_file_cache, &uval, byte_size, error); if (bytes_read == byte_size) { DataExtractor data (&uval, sizeof(uval), m_arch.GetByteOrder(), m_arch.GetAddressByteSize()); lldb::offset_t offset = 0; if (byte_size <= 4) scalar = data.GetMaxU32 (&offset, byte_size); else scalar = data.GetMaxU64 (&offset, byte_size); if (is_signed) scalar.SignExtend(byte_size * 8); return bytes_read; } } else { error.SetErrorStringWithFormat ("byte size of %u is too large for integer scalar type", byte_size); } return 0; } uint64_t Target::ReadUnsignedIntegerFromMemory (const Address& addr, bool prefer_file_cache, size_t integer_byte_size, uint64_t fail_value, Error &error) { Scalar scalar; if (ReadScalarIntegerFromMemory (addr, prefer_file_cache, integer_byte_size, false, scalar, error)) return scalar.ULongLong(fail_value); return fail_value; } bool Target::ReadPointerFromMemory (const Address& addr, bool prefer_file_cache, Error &error, Address &pointer_addr) { Scalar scalar; if (ReadScalarIntegerFromMemory (addr, prefer_file_cache, m_arch.GetAddressByteSize(), false, scalar, error)) { addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS); if (pointer_vm_addr != LLDB_INVALID_ADDRESS) { SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) { // No sections are loaded, so we must assume we are not running // yet and anything we are given is a file address. m_images.ResolveFileAddress (pointer_vm_addr, pointer_addr); } else { // We have at least one section loaded. This can be because // we have manually loaded some sections with "target modules load ..." // or because we have have a live process that has sections loaded // through the dynamic loader section_load_list.ResolveLoadAddress (pointer_vm_addr, pointer_addr); } // We weren't able to resolve the pointer value, so just return // an address with no section if (!pointer_addr.IsValid()) pointer_addr.SetOffset (pointer_vm_addr); return true; } } return false; } ModuleSP Target::GetSharedModule (const ModuleSpec &module_spec, Error *error_ptr) { ModuleSP module_sp; Error error; // First see if we already have this module in our module list. If we do, then we're done, we don't need // to consult the shared modules list. But only do this if we are passed a UUID. if (module_spec.GetUUID().IsValid()) module_sp = m_images.FindFirstModule(module_spec); if (!module_sp) { ModuleSP old_module_sp; // This will get filled in if we have a new version of the library bool did_create_module = false; // If there are image search path entries, try to use them first to acquire a suitable image. if (m_image_search_paths.GetSize()) { ModuleSpec transformed_spec (module_spec); if (m_image_search_paths.RemapPath (module_spec.GetFileSpec().GetDirectory(), transformed_spec.GetFileSpec().GetDirectory())) { transformed_spec.GetFileSpec().GetFilename() = module_spec.GetFileSpec().GetFilename(); error = ModuleList::GetSharedModule (transformed_spec, module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } } if (!module_sp) { // If we have a UUID, we can check our global shared module list in case // we already have it. If we don't have a valid UUID, then we can't since // the path in "module_spec" will be a platform path, and we will need to // let the platform find that file. For example, we could be asking for // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick // the local copy of "/usr/lib/dyld" since our platform could be a remote // platform that has its own "/usr/lib/dyld" in an SDK or in a local file // cache. if (module_spec.GetUUID().IsValid()) { // We have a UUID, it is OK to check the global module list... error = ModuleList::GetSharedModule (module_spec, module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } if (!module_sp) { // The platform is responsible for finding and caching an appropriate // module in the shared module cache. if (m_platform_sp) { error = m_platform_sp->GetSharedModule (module_spec, m_process_sp.get(), module_sp, &GetExecutableSearchPaths(), &old_module_sp, &did_create_module); } else { error.SetErrorString("no platform is currently set"); } } } // We found a module that wasn't in our target list. Let's make sure that there wasn't an equivalent // module in the list already, and if there was, let's remove it. if (module_sp) { ObjectFile *objfile = module_sp->GetObjectFile(); if (objfile) { switch (objfile->GetType()) { case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of a program's execution state case ObjectFile::eTypeExecutable: /// A normal executable case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker executable case ObjectFile::eTypeObjectFile: /// An intermediate object file case ObjectFile::eTypeSharedLibrary: /// A shared library that can be used during execution break; case ObjectFile::eTypeDebugInfo: /// An object file that contains only debug information if (error_ptr) error_ptr->SetErrorString("debug info files aren't valid target modules, please specify an executable"); return ModuleSP(); case ObjectFile::eTypeStubLibrary: /// A library that can be linked against but not used for execution if (error_ptr) error_ptr->SetErrorString("stub libraries aren't valid target modules, please specify an executable"); return ModuleSP(); default: if (error_ptr) error_ptr->SetErrorString("unsupported file type, please specify an executable"); return ModuleSP(); } // GetSharedModule is not guaranteed to find the old shared module, for instance // in the common case where you pass in the UUID, it is only going to find the one // module matching the UUID. In fact, it has no good way to know what the "old module" // relevant to this target is, since there might be many copies of a module with this file spec // in various running debug sessions, but only one of them will belong to this target. // So let's remove the UUID from the module list, and look in the target's module list. // Only do this if there is SOMETHING else in the module spec... if (!old_module_sp) { if (module_spec.GetUUID().IsValid() && !module_spec.GetFileSpec().GetFilename().IsEmpty() && !module_spec.GetFileSpec().GetDirectory().IsEmpty()) { ModuleSpec module_spec_copy(module_spec.GetFileSpec()); module_spec_copy.GetUUID().Clear(); ModuleList found_modules; size_t num_found = m_images.FindModules (module_spec_copy, found_modules); if (num_found == 1) { old_module_sp = found_modules.GetModuleAtIndex(0); } } } if (old_module_sp && m_images.GetIndexForModule (old_module_sp.get()) != LLDB_INVALID_INDEX32) { m_images.ReplaceModule(old_module_sp, module_sp); Module *old_module_ptr = old_module_sp.get(); old_module_sp.reset(); ModuleList::RemoveSharedModuleIfOrphaned (old_module_ptr); } else m_images.Append(module_sp); } else module_sp.reset(); } } if (error_ptr) *error_ptr = error; return module_sp; } TargetSP Target::CalculateTarget () { return shared_from_this(); } ProcessSP Target::CalculateProcess () { return ProcessSP(); } ThreadSP Target::CalculateThread () { return ThreadSP(); } StackFrameSP Target::CalculateStackFrame () { return StackFrameSP(); } void Target::CalculateExecutionContext (ExecutionContext &exe_ctx) { exe_ctx.Clear(); exe_ctx.SetTargetPtr(this); } PathMappingList & Target::GetImageSearchPathList () { return m_image_search_paths; } void Target::ImageSearchPathsChanged(const PathMappingList &path_list, void *baton) { Target *target = (Target *)baton; ModuleSP exe_module_sp (target->GetExecutableModule()); if (exe_module_sp) target->SetExecutableModule (exe_module_sp, true); } TypeSystem * Target::GetScratchTypeSystemForLanguage (Error *error, lldb::LanguageType language, bool create_on_demand) { if (!m_valid) return nullptr; if (error) { error->Clear(); } if (language == eLanguageTypeMipsAssembler // GNU AS and LLVM use it for all assembly code || language == eLanguageTypeUnknown) { std::set languages_for_types; std::set languages_for_expressions; Language::GetLanguagesSupportingTypeSystems(languages_for_types, languages_for_expressions); if (languages_for_expressions.count(eLanguageTypeC)) { language = eLanguageTypeC; // LLDB's default. Override by setting the target language. } else { if (languages_for_expressions.empty()) { return nullptr; } else { language = *languages_for_expressions.begin(); } } } return m_scratch_type_system_map.GetTypeSystemForLanguage(language, this, create_on_demand); } PersistentExpressionState * Target::GetPersistentExpressionStateForLanguage (lldb::LanguageType language) { TypeSystem *type_system = GetScratchTypeSystemForLanguage(nullptr, language, true); if (type_system) { return type_system->GetPersistentExpressionState(); } else { return nullptr; } } UserExpression * Target::GetUserExpressionForLanguage(const char *expr, const char *expr_prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options, Error &error) { Error type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage (&type_system_error, language); UserExpression *user_expr = nullptr; if (!type_system) { error.SetErrorStringWithFormat("Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return nullptr; } user_expr = type_system->GetUserExpression(expr, expr_prefix, language, desired_type, options); if (!user_expr) error.SetErrorStringWithFormat("Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return user_expr; } FunctionCaller * Target::GetFunctionCallerForLanguage (lldb::LanguageType language, const CompilerType &return_type, const Address& function_address, const ValueList &arg_value_list, const char *name, Error &error) { Error type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage (&type_system_error, language); FunctionCaller *persistent_fn = nullptr; if (!type_system) { error.SetErrorStringWithFormat("Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return persistent_fn; } persistent_fn = type_system->GetFunctionCaller (return_type, function_address, arg_value_list, name); if (!persistent_fn) error.SetErrorStringWithFormat("Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return persistent_fn; } UtilityFunction * Target::GetUtilityFunctionForLanguage (const char *text, lldb::LanguageType language, const char *name, Error &error) { Error type_system_error; TypeSystem *type_system = GetScratchTypeSystemForLanguage (&type_system_error, language); UtilityFunction *utility_fn = nullptr; if (!type_system) { error.SetErrorStringWithFormat("Could not find type system for language %s: %s", Language::GetNameForLanguageType(language), type_system_error.AsCString()); return utility_fn; } utility_fn = type_system->GetUtilityFunction (text, name); if (!utility_fn) error.SetErrorStringWithFormat("Could not create an expression for language %s", Language::GetNameForLanguageType(language)); return utility_fn; } ClangASTContext * Target::GetScratchClangASTContext(bool create_on_demand) { if (m_valid) { if (TypeSystem* type_system = GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC, create_on_demand)) return llvm::dyn_cast(type_system); } return nullptr; } ClangASTImporterSP Target::GetClangASTImporter() { if (m_valid) { if (!m_ast_importer_sp) { m_ast_importer_sp.reset(new ClangASTImporter()); } return m_ast_importer_sp; } return ClangASTImporterSP(); } void Target::SettingsInitialize () { Process::SettingsInitialize (); } void Target::SettingsTerminate () { Process::SettingsTerminate (); } FileSpecList Target::GetDefaultExecutableSearchPaths () { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetExecutableSearchPaths(); return FileSpecList(); } FileSpecList Target::GetDefaultDebugFileSearchPaths () { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetDebugFileSearchPaths(); return FileSpecList(); } FileSpecList Target::GetDefaultClangModuleSearchPaths () { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetClangModuleSearchPaths(); return FileSpecList(); } ArchSpec Target::GetDefaultArchitecture () { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) return properties_sp->GetDefaultArchitecture(); return ArchSpec(); } void Target::SetDefaultArchitecture (const ArchSpec &arch) { TargetPropertiesSP properties_sp(Target::GetGlobalProperties()); if (properties_sp) { LogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET, "Target::SetDefaultArchitecture setting target's default architecture to %s (%s)", arch.GetArchitectureName(), arch.GetTriple().getTriple().c_str()); return properties_sp->SetDefaultArchitecture(arch); } } Target * Target::GetTargetFromContexts (const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) { // The target can either exist in the "process" of ExecutionContext, or in // the "target_sp" member of SymbolContext. This accessor helper function // will get the target from one of these locations. Target *target = nullptr; if (sc_ptr != nullptr) target = sc_ptr->target_sp.get(); if (target == nullptr && exe_ctx_ptr) target = exe_ctx_ptr->GetTargetPtr(); return target; } ExpressionResults Target::EvaluateExpression(const char *expr_cstr, ExecutionContextScope *exe_scope, lldb::ValueObjectSP &result_valobj_sp, const EvaluateExpressionOptions& options) { result_valobj_sp.reset(); ExpressionResults execution_results = eExpressionSetupError; if (expr_cstr == nullptr || expr_cstr[0] == '\0') return execution_results; // We shouldn't run stop hooks in expressions. // Be sure to reset this if you return anywhere within this function. bool old_suppress_value = m_suppress_stop_hooks; m_suppress_stop_hooks = true; ExecutionContext exe_ctx; if (exe_scope) { exe_scope->CalculateExecutionContext(exe_ctx); } else if (m_process_sp) { m_process_sp->CalculateExecutionContext(exe_ctx); } else { CalculateExecutionContext(exe_ctx); } // Make sure we aren't just trying to see the value of a persistent // variable (something like "$0") lldb::ExpressionVariableSP persistent_var_sp; // Only check for persistent variables the expression starts with a '$' if (expr_cstr[0] == '$') persistent_var_sp = GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC)->GetPersistentExpressionState()->GetVariable (expr_cstr); if (persistent_var_sp) { result_valobj_sp = persistent_var_sp->GetValueObject (); execution_results = eExpressionCompleted; } else { const char *prefix = GetExpressionPrefixContentsAsCString(); Error error; execution_results = UserExpression::Evaluate (exe_ctx, options, expr_cstr, prefix, result_valobj_sp, error); } m_suppress_stop_hooks = old_suppress_value; return execution_results; } lldb::ExpressionVariableSP Target::GetPersistentVariable(const ConstString &name) { lldb::ExpressionVariableSP variable_sp; m_scratch_type_system_map.ForEach([this, name, &variable_sp](TypeSystem *type_system) -> bool { if (PersistentExpressionState *persistent_state = type_system->GetPersistentExpressionState()) { variable_sp = persistent_state->GetVariable(name); if (variable_sp) return false; // Stop iterating the ForEach } return true; // Keep iterating the ForEach }); return variable_sp; } lldb::addr_t Target::GetPersistentSymbol(const ConstString &name) { lldb::addr_t address = LLDB_INVALID_ADDRESS; m_scratch_type_system_map.ForEach([this, name, &address](TypeSystem *type_system) -> bool { if (PersistentExpressionState *persistent_state = type_system->GetPersistentExpressionState()) { address = persistent_state->LookupSymbol(name); if (address != LLDB_INVALID_ADDRESS) return false; // Stop iterating the ForEach } return true; // Keep iterating the ForEach }); return address; } lldb::addr_t Target::GetCallableLoadAddress (lldb::addr_t load_addr, AddressClass addr_class) const { addr_t code_addr = load_addr; switch (m_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassUnknown: case eAddressClassInvalid: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: if ((code_addr & 2ull) || (addr_class == eAddressClassCodeAlternateISA)) code_addr |= 1ull; break; } break; case llvm::Triple::arm: case llvm::Triple::thumb: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassUnknown: case eAddressClassInvalid: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: // Check if bit zero it no set? if ((code_addr & 1ull) == 0) { // Bit zero isn't set, check if the address is a multiple of 2? if (code_addr & 2ull) { // The address is a multiple of 2 so it must be thumb, set bit zero code_addr |= 1ull; } else if (addr_class == eAddressClassCodeAlternateISA) { // We checked the address and the address claims to be the alternate ISA // which means thumb, so set bit zero. code_addr |= 1ull; } } break; } break; default: break; } return code_addr; } lldb::addr_t Target::GetOpcodeLoadAddress (lldb::addr_t load_addr, AddressClass addr_class) const { addr_t opcode_addr = load_addr; switch (m_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::arm: case llvm::Triple::thumb: switch (addr_class) { case eAddressClassData: case eAddressClassDebug: return LLDB_INVALID_ADDRESS; case eAddressClassInvalid: case eAddressClassUnknown: case eAddressClassCode: case eAddressClassCodeAlternateISA: case eAddressClassRuntime: opcode_addr &= ~(1ull); break; } break; default: break; } return opcode_addr; } lldb::addr_t Target::GetBreakableLoadAddress (lldb::addr_t addr) { addr_t breakable_addr = addr; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); switch (m_arch.GetMachine()) { default: break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: { addr_t function_start = 0; addr_t current_offset = 0; uint32_t loop_count = 0; Address resolved_addr; uint32_t arch_flags = m_arch.GetFlags (); bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16; bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips; SectionLoadList §ion_load_list = GetSectionLoadList(); if (section_load_list.IsEmpty()) // No sections are loaded, so we must assume we are not running yet // and need to operate only on file address. m_images.ResolveFileAddress (addr, resolved_addr); else section_load_list.ResolveLoadAddress(addr, resolved_addr); // Get the function boundaries to make sure we don't scan back before the beginning of the current function. ModuleSP temp_addr_module_sp (resolved_addr.GetModule()); if (temp_addr_module_sp) { SymbolContext sc; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, resolve_scope, sc); + Address sym_addr; if (sc.function) - { - function_start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(this); - if (function_start == LLDB_INVALID_ADDRESS) - function_start = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); - } + sym_addr = sc.function->GetAddressRange().GetBaseAddress(); else if (sc.symbol) - { - Address sym_addr = sc.symbol->GetAddress(); + sym_addr = sc.symbol->GetAddress(); + + function_start = sym_addr.GetLoadAddress(this); + if (function_start == LLDB_INVALID_ADDRESS) function_start = sym_addr.GetFileAddress(); - } - current_offset = addr - function_start; + + if (function_start) + current_offset = addr - function_start; } // If breakpoint address is start of function then we dont have to do anything. if (current_offset == 0) return breakable_addr; else loop_count = current_offset / 2; if (loop_count > 3) { // Scan previous 6 bytes if (IsMips16 | IsMicromips) loop_count = 3; // For mips-only, instructions are always 4 bytes, so scan previous 4 bytes only. else loop_count = 2; } // Create Disassembler Instance lldb::DisassemblerSP disasm_sp(Disassembler::FindPlugin(m_arch, nullptr, nullptr)); ExecutionContext exe_ctx; CalculateExecutionContext(exe_ctx); InstructionList instruction_list; InstructionSP prev_insn; bool prefer_file_cache = true; // Read from file uint32_t inst_to_choose = 0; for (uint32_t i = 1; i <= loop_count; i++) { // Adjust the address to read from. resolved_addr.Slide (-2); AddressRange range(resolved_addr, i*2); uint32_t insn_size = 0; disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache); uint32_t num_insns = disasm_sp->GetInstructionList().GetSize(); if (num_insns) { prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0); insn_size = prev_insn->GetOpcode().GetByteSize(); if (i == 1 && insn_size == 2) { // This looks like a valid 2-byte instruction (but it could be a part of upper 4 byte instruction). instruction_list.Append(prev_insn); inst_to_choose = 1; } else if (i == 2) { // Here we may get one 4-byte instruction or two 2-byte instructions. if (num_insns == 2) { // Looks like there are two 2-byte instructions above our breakpoint target address. // Now the upper 2-byte instruction is either a valid 2-byte instruction or could be a part of it's upper 4-byte instruction. // In both cases we don't care because in this case lower 2-byte instruction is definitely a valid instruction // and whatever i=1 iteration has found out is true. inst_to_choose = 1; break; } else if (insn_size == 4) { // This instruction claims its a valid 4-byte instruction. But it could be a part of it's upper 4-byte instruction. // Lets try scanning upper 2 bytes to verify this. instruction_list.Append(prev_insn); inst_to_choose = 2; } } else if (i == 3) { if (insn_size == 4) // FIXME: We reached here that means instruction at [target - 4] has already claimed to be a 4-byte instruction, // and now instruction at [target - 6] is also claiming that it's a 4-byte instruction. This can not be true. // In this case we can not decide the valid previous instruction so we let lldb set the breakpoint at the address given by user. inst_to_choose = 0; else // This is straight-forward inst_to_choose = 2; break; } } else { // Decode failed, bytes do not form a valid instruction. So whatever previous iteration has found out is true. if (i > 1) { inst_to_choose = i - 1; break; } } } // Check if we are able to find any valid instruction. if (inst_to_choose) { if (inst_to_choose > instruction_list.GetSize()) inst_to_choose--; prev_insn = instruction_list.GetInstructionAtIndex(inst_to_choose - 1); if (prev_insn->HasDelaySlot()) { uint32_t shift_size = prev_insn->GetOpcode().GetByteSize(); // Adjust the breakable address breakable_addr = addr - shift_size; if (log) log->Printf ("Target::%s Breakpoint at 0x%8.8" PRIx64 " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n", __FUNCTION__, addr, breakable_addr); } } break; } } return breakable_addr; } SourceManager & Target::GetSourceManager () { if (!m_source_manager_ap) m_source_manager_ap.reset (new SourceManager(shared_from_this())); return *m_source_manager_ap; } ClangModulesDeclVendor * Target::GetClangModulesDeclVendor () { static Mutex s_clang_modules_decl_vendor_mutex; // If this is contended we can make it per-target { Mutex::Locker clang_modules_decl_vendor_locker(s_clang_modules_decl_vendor_mutex); if (!m_clang_modules_decl_vendor_ap) { m_clang_modules_decl_vendor_ap.reset(ClangModulesDeclVendor::Create(*this)); } } return m_clang_modules_decl_vendor_ap.get(); } Target::StopHookSP Target::CreateStopHook () { lldb::user_id_t new_uid = ++m_stop_hook_next_id; Target::StopHookSP stop_hook_sp (new StopHook(shared_from_this(), new_uid)); m_stop_hooks[new_uid] = stop_hook_sp; return stop_hook_sp; } bool Target::RemoveStopHookByID (lldb::user_id_t user_id) { size_t num_removed = m_stop_hooks.erase(user_id); return (num_removed != 0); } void Target::RemoveAllStopHooks () { m_stop_hooks.clear(); } Target::StopHookSP Target::GetStopHookByID (lldb::user_id_t user_id) { StopHookSP found_hook; StopHookCollection::iterator specified_hook_iter; specified_hook_iter = m_stop_hooks.find (user_id); if (specified_hook_iter != m_stop_hooks.end()) found_hook = (*specified_hook_iter).second; return found_hook; } bool Target::SetStopHookActiveStateByID (lldb::user_id_t user_id, bool active_state) { StopHookCollection::iterator specified_hook_iter; specified_hook_iter = m_stop_hooks.find (user_id); if (specified_hook_iter == m_stop_hooks.end()) return false; (*specified_hook_iter).second->SetIsActive (active_state); return true; } void Target::SetAllStopHooksActiveState (bool active_state) { StopHookCollection::iterator pos, end = m_stop_hooks.end(); for (pos = m_stop_hooks.begin(); pos != end; pos++) { (*pos).second->SetIsActive (active_state); } } void Target::RunStopHooks () { if (m_suppress_stop_hooks) return; if (!m_process_sp) return; // make sure we check that we are not stopped because of us running a user expression // since in that case we do not want to run the stop-hooks if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression()) return; if (m_stop_hooks.empty()) return; StopHookCollection::iterator pos, end = m_stop_hooks.end(); // If there aren't any active stop hooks, don't bother either: bool any_active_hooks = false; for (pos = m_stop_hooks.begin(); pos != end; pos++) { if ((*pos).second->IsActive()) { any_active_hooks = true; break; } } if (!any_active_hooks) return; CommandReturnObject result; std::vector exc_ctx_with_reasons; std::vector sym_ctx_with_reasons; ThreadList &cur_threadlist = m_process_sp->GetThreadList(); size_t num_threads = cur_threadlist.GetSize(); for (size_t i = 0; i < num_threads; i++) { lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex (i); if (cur_thread_sp->ThreadStoppedForAReason()) { lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0); exc_ctx_with_reasons.push_back(ExecutionContext(m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get())); sym_ctx_with_reasons.push_back(cur_frame_sp->GetSymbolContext(eSymbolContextEverything)); } } // If no threads stopped for a reason, don't run the stop-hooks. size_t num_exe_ctx = exc_ctx_with_reasons.size(); if (num_exe_ctx == 0) return; result.SetImmediateOutputStream (m_debugger.GetAsyncOutputStream()); result.SetImmediateErrorStream (m_debugger.GetAsyncErrorStream()); bool keep_going = true; bool hooks_ran = false; bool print_hook_header = (m_stop_hooks.size() != 1); bool print_thread_header = (num_exe_ctx != 1); for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++) { // result.Clear(); StopHookSP cur_hook_sp = (*pos).second; if (!cur_hook_sp->IsActive()) continue; bool any_thread_matched = false; for (size_t i = 0; keep_going && i < num_exe_ctx; i++) { if ((cur_hook_sp->GetSpecifier() == nullptr || cur_hook_sp->GetSpecifier()->SymbolContextMatches(sym_ctx_with_reasons[i])) && (cur_hook_sp->GetThreadSpecifier() == nullptr || cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx_with_reasons[i].GetThreadRef()))) { if (!hooks_ran) { hooks_ran = true; } if (print_hook_header && !any_thread_matched) { const char *cmd = (cur_hook_sp->GetCommands().GetSize() == 1 ? cur_hook_sp->GetCommands().GetStringAtIndex(0) : nullptr); if (cmd) result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(), cmd); else result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID()); any_thread_matched = true; } if (print_thread_header) result.AppendMessageWithFormat("-- Thread %d\n", exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); CommandInterpreterRunOptions options; options.SetStopOnContinue (true); options.SetStopOnError (true); options.SetEchoCommands (false); options.SetPrintResults (true); options.SetAddToHistory (false); GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(), &exc_ctx_with_reasons[i], options, result); // If the command started the target going again, we should bag out of // running the stop hooks. if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || (result.GetStatus() == eReturnStatusSuccessContinuingResult)) { result.AppendMessageWithFormat ("Aborting stop hooks, hook %" PRIu64 " set the program running.", cur_hook_sp->GetID()); keep_going = false; } } } } result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); } const TargetPropertiesSP & Target::GetGlobalProperties() { static TargetPropertiesSP g_settings_sp; if (!g_settings_sp) { g_settings_sp.reset(new TargetProperties(nullptr)); } return g_settings_sp; } Error Target::Install (ProcessLaunchInfo *launch_info) { Error error; PlatformSP platform_sp (GetPlatform()); if (platform_sp) { if (platform_sp->IsRemote()) { if (platform_sp->IsConnected()) { // Install all files that have an install path, and always install the // main executable when connected to a remote platform const ModuleList& modules = GetImages(); const size_t num_images = modules.GetSize(); for (size_t idx = 0; idx < num_images; ++idx) { const bool is_main_executable = idx == 0; ModuleSP module_sp(modules.GetModuleAtIndex(idx)); if (module_sp) { FileSpec local_file (module_sp->GetFileSpec()); if (local_file) { FileSpec remote_file (module_sp->GetRemoteInstallFileSpec()); if (!remote_file) { if (is_main_executable) // TODO: add setting for always installing main executable??? { // Always install the main executable remote_file = platform_sp->GetRemoteWorkingDirectory(); remote_file.AppendPathComponent(module_sp->GetFileSpec().GetFilename().GetCString()); } } if (remote_file) { error = platform_sp->Install(local_file, remote_file); if (error.Success()) { module_sp->SetPlatformFileSpec(remote_file); if (is_main_executable) { platform_sp->SetFilePermissions(remote_file, 0700); if (launch_info) launch_info->SetExecutableFile(remote_file, false); } } else break; } } } } } } } return error; } bool Target::ResolveLoadAddress (addr_t load_addr, Address &so_addr, uint32_t stop_id) { return m_section_load_history.ResolveLoadAddress(stop_id, load_addr, so_addr); } bool Target::ResolveFileAddress (lldb::addr_t file_addr, Address &resolved_addr) { return m_images.ResolveFileAddress(file_addr, resolved_addr); } bool Target::SetSectionLoadAddress (const SectionSP §ion_sp, addr_t new_section_load_addr, bool warn_multiple) { const addr_t old_section_load_addr = m_section_load_history.GetSectionLoadAddress (SectionLoadHistory::eStopIDNow, section_sp); if (old_section_load_addr != new_section_load_addr) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); if (m_section_load_history.SetSectionLoadAddress (stop_id, section_sp, new_section_load_addr, warn_multiple)) return true; // Return true if the section load address was changed... } return false; // Return false to indicate nothing changed } size_t Target::UnloadModuleSections (const ModuleList &module_list) { size_t section_unload_count = 0; size_t num_modules = module_list.GetSize(); for (size_t i=0; iGetStopID(); else stop_id = m_section_load_history.GetLastStopID(); SectionList *sections = module_sp->GetSectionList(); size_t section_unload_count = 0; if (sections) { const uint32_t num_sections = sections->GetNumSections(0); for (uint32_t i = 0; i < num_sections; ++i) { section_unload_count += m_section_load_history.SetSectionUnloaded(stop_id, sections->GetSectionAtIndex(i)); } } return section_unload_count; } bool Target::SetSectionUnloaded (const lldb::SectionSP §ion_sp) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); return m_section_load_history.SetSectionUnloaded (stop_id, section_sp); } bool Target::SetSectionUnloaded (const lldb::SectionSP §ion_sp, addr_t load_addr) { uint32_t stop_id = 0; ProcessSP process_sp(GetProcessSP()); if (process_sp) stop_id = process_sp->GetStopID(); else stop_id = m_section_load_history.GetLastStopID(); return m_section_load_history.SetSectionUnloaded (stop_id, section_sp, load_addr); } void Target::ClearAllLoadedSections () { m_section_load_history.Clear(); } Error Target::Launch (ProcessLaunchInfo &launch_info, Stream *stream) { Error error; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); if (log) log->Printf ("Target::%s() called for %s", __FUNCTION__, launch_info.GetExecutableFile().GetPath().c_str ()); StateType state = eStateInvalid; // Scope to temporarily get the process state in case someone has manually // remotely connected already to a process and we can skip the platform // launching. { ProcessSP process_sp (GetProcessSP()); if (process_sp) { state = process_sp->GetState(); if (log) log->Printf ("Target::%s the process exists, and its current state is %s", __FUNCTION__, StateAsCString (state)); } else { if (log) log->Printf ("Target::%s the process instance doesn't currently exist.", __FUNCTION__); } } launch_info.GetFlags().Set (eLaunchFlagDebug); // Get the value of synchronous execution here. If you wait till after you have started to // run, then you could have hit a breakpoint, whose command might switch the value, and // then you'll pick up that incorrect value. Debugger &debugger = GetDebugger(); const bool synchronous_execution = debugger.GetCommandInterpreter().GetSynchronous (); PlatformSP platform_sp (GetPlatform()); // Finalize the file actions, and if none were given, default to opening // up a pseudo terminal const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; if (log) log->Printf ("Target::%s have platform=%s, platform_sp->IsHost()=%s, default_to_use_pty=%s", __FUNCTION__, platform_sp ? "true" : "false", platform_sp ? (platform_sp->IsHost () ? "true" : "false") : "n/a", default_to_use_pty ? "true" : "false"); launch_info.FinalizeFileActions (this, default_to_use_pty); if (state == eStateConnected) { if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) { error.SetErrorString("can't launch in tty when launching through a remote connection"); return error; } } if (!launch_info.GetArchitecture().IsValid()) launch_info.GetArchitecture() = GetArchitecture(); // If we're not already connected to the process, and if we have a platform that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && platform_sp->CanDebugProcess ()) { if (log) log->Printf ("Target::%s asking the platform to debug the process", __FUNCTION__); // Get a weak pointer to the previous process if we have one ProcessWP process_wp; if (m_process_sp) process_wp = m_process_sp; m_process_sp = GetPlatform()->DebugProcess (launch_info, debugger, this, error); // Cleanup the old process since someone might still have a strong // reference to this process and we would like to allow it to cleanup // as much as it can without the object being destroyed. We try to // lock the shared pointer and if that works, then someone else still // has a strong reference to the process. ProcessSP old_process_sp(process_wp.lock()); if (old_process_sp) old_process_sp->Finalize(); } else { if (log) log->Printf ("Target::%s the platform doesn't know how to debug a process, getting a process plugin to do this for us.", __FUNCTION__); if (state == eStateConnected) { assert(m_process_sp); } else { // Use a Process plugin to construct the process. const char *plugin_name = launch_info.GetProcessPluginName(); CreateProcess(launch_info.GetListenerForProcess(debugger), plugin_name, nullptr); } // Since we didn't have a platform launch the process, launch it here. if (m_process_sp) error = m_process_sp->Launch (launch_info); } if (!m_process_sp) { if (error.Success()) error.SetErrorString("failed to launch or debug process"); return error; } if (error.Success()) { if (synchronous_execution || !launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { ListenerSP hijack_listener_sp (launch_info.GetHijackListener()); if (!hijack_listener_sp) { hijack_listener_sp.reset(new Listener("lldb.Target.Launch.hijack")); launch_info.SetHijackListener(hijack_listener_sp); m_process_sp->HijackProcessEvents(hijack_listener_sp.get()); } StateType state = m_process_sp->WaitForProcessToStop(nullptr, nullptr, false, hijack_listener_sp.get(), nullptr); if (state == eStateStopped) { if (!launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) { if (synchronous_execution) { error = m_process_sp->PrivateResume(); if (error.Success()) { state = m_process_sp->WaitForProcessToStop(nullptr, nullptr, true, hijack_listener_sp.get(), stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) { error.SetErrorStringWithFormat("process isn't stopped: %s", StateAsCString(state)); } } } else { m_process_sp->RestoreProcessEvents(); error = m_process_sp->PrivateResume(); } if (!error.Success()) { Error error2; error2.SetErrorStringWithFormat("process resume at entry point failed: %s", error.AsCString()); error = error2; } } } else if (state == eStateExited) { bool with_shell = !!launch_info.GetShell(); const int exit_status = m_process_sp->GetExitStatus(); const char *exit_desc = m_process_sp->GetExitDescription(); #define LAUNCH_SHELL_MESSAGE "\n'r' and 'run' are aliases that default to launching through a shell.\nTry launching without going through a shell by using 'process launch'." if (exit_desc && exit_desc[0]) { if (with_shell) error.SetErrorStringWithFormat ("process exited with status %i (%s)" LAUNCH_SHELL_MESSAGE, exit_status, exit_desc); else error.SetErrorStringWithFormat ("process exited with status %i (%s)", exit_status, exit_desc); } else { if (with_shell) error.SetErrorStringWithFormat ("process exited with status %i" LAUNCH_SHELL_MESSAGE, exit_status); else error.SetErrorStringWithFormat ("process exited with status %i", exit_status); } } else { error.SetErrorStringWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state)); } } m_process_sp->RestoreProcessEvents (); } else { Error error2; error2.SetErrorStringWithFormat ("process launch failed: %s", error.AsCString()); error = error2; } return error; } Error Target::Attach (ProcessAttachInfo &attach_info, Stream *stream) { auto state = eStateInvalid; auto process_sp = GetProcessSP (); if (process_sp) { state = process_sp->GetState (); if (process_sp->IsAlive () && state != eStateConnected) { if (state == eStateAttaching) return Error ("process attach is in progress"); return Error ("a process is already being debugged"); } } const ModuleSP old_exec_module_sp = GetExecutableModule (); // If no process info was specified, then use the target executable // name as the process to attach to by default if (!attach_info.ProcessInfoSpecified ()) { if (old_exec_module_sp) attach_info.GetExecutableFile ().GetFilename () = old_exec_module_sp->GetPlatformFileSpec ().GetFilename (); if (!attach_info.ProcessInfoSpecified ()) { return Error ("no process specified, create a target with a file, or specify the --pid or --name"); } } const auto platform_sp = GetDebugger ().GetPlatformList ().GetSelectedPlatform (); ListenerSP hijack_listener_sp; const bool async = attach_info.GetAsync(); if (!async) { hijack_listener_sp.reset (new Listener ("lldb.Target.Attach.attach.hijack")); attach_info.SetHijackListener (hijack_listener_sp); } Error error; if (state != eStateConnected && platform_sp != nullptr && platform_sp->CanDebugProcess ()) { SetPlatform (platform_sp); process_sp = platform_sp->Attach (attach_info, GetDebugger (), this, error); } else { if (state != eStateConnected) { const char *plugin_name = attach_info.GetProcessPluginName (); process_sp = CreateProcess (attach_info.GetListenerForProcess (GetDebugger ()), plugin_name, nullptr); if (process_sp == nullptr) { error.SetErrorStringWithFormat ("failed to create process using plugin %s", (plugin_name) ? plugin_name : "null"); return error; } } if (hijack_listener_sp) process_sp->HijackProcessEvents (hijack_listener_sp.get ()); error = process_sp->Attach (attach_info); } if (error.Success () && process_sp) { if (async) { process_sp->RestoreProcessEvents (); } else { state = process_sp->WaitForProcessToStop (nullptr, nullptr, false, attach_info.GetHijackListener ().get (), stream); process_sp->RestoreProcessEvents (); if (state != eStateStopped) { const char *exit_desc = process_sp->GetExitDescription (); if (exit_desc) error.SetErrorStringWithFormat ("%s", exit_desc); else error.SetErrorString ("process did not stop (no such process or permission problem?)"); process_sp->Destroy (false); } } } return error; } //-------------------------------------------------------------- // Target::StopHook //-------------------------------------------------------------- Target::StopHook::StopHook (lldb::TargetSP target_sp, lldb::user_id_t uid) : UserID (uid), m_target_sp (target_sp), m_commands (), m_specifier_sp (), m_thread_spec_ap(), m_active (true) { } Target::StopHook::StopHook (const StopHook &rhs) : UserID (rhs.GetID()), m_target_sp (rhs.m_target_sp), m_commands (rhs.m_commands), m_specifier_sp (rhs.m_specifier_sp), m_thread_spec_ap (), m_active (rhs.m_active) { if (rhs.m_thread_spec_ap) m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); } Target::StopHook::~StopHook() = default; void Target::StopHook::SetSpecifier(SymbolContextSpecifier *specifier) { m_specifier_sp.reset(specifier); } void Target::StopHook::SetThreadSpecifier (ThreadSpec *specifier) { m_thread_spec_ap.reset (specifier); } void Target::StopHook::GetDescription (Stream *s, lldb::DescriptionLevel level) const { int indent_level = s->GetIndentLevel(); s->SetIndentLevel(indent_level + 2); s->Printf ("Hook: %" PRIu64 "\n", GetID()); if (m_active) s->Indent ("State: enabled\n"); else s->Indent ("State: disabled\n"); if (m_specifier_sp) { s->Indent(); s->PutCString ("Specifier:\n"); s->SetIndentLevel (indent_level + 4); m_specifier_sp->GetDescription (s, level); s->SetIndentLevel (indent_level + 2); } if (m_thread_spec_ap) { StreamString tmp; s->Indent("Thread:\n"); m_thread_spec_ap->GetDescription (&tmp, level); s->SetIndentLevel (indent_level + 4); s->Indent (tmp.GetData()); s->PutCString ("\n"); s->SetIndentLevel (indent_level + 2); } s->Indent ("Commands: \n"); s->SetIndentLevel (indent_level + 4); uint32_t num_commands = m_commands.GetSize(); for (uint32_t i = 0; i < num_commands; i++) { s->Indent(m_commands.GetStringAtIndex(i)); s->PutCString ("\n"); } s->SetIndentLevel (indent_level); } //-------------------------------------------------------------- // class TargetProperties //-------------------------------------------------------------- OptionEnumValueElement lldb_private::g_dynamic_value_types[] = { { eNoDynamicValues, "no-dynamic-values", "Don't calculate the dynamic type of values"}, { eDynamicCanRunTarget, "run-target", "Calculate the dynamic type of values even if you have to run the target."}, { eDynamicDontRunTarget, "no-run-target", "Calculate the dynamic type of values, but don't run the target."}, { 0, nullptr, nullptr } }; static OptionEnumValueElement g_inline_breakpoint_enums[] = { { eInlineBreakpointsNever, "never", "Never look for inline breakpoint locations (fastest). This setting should only be used if you know that no inlining occurs in your programs."}, { eInlineBreakpointsHeaders, "headers", "Only check for inline breakpoint locations when setting breakpoints in header files, but not when setting breakpoint in implementation source files (default)."}, { eInlineBreakpointsAlways, "always", "Always look for inline breakpoint locations when setting file and line breakpoints (slower but most accurate)."}, { 0, nullptr, nullptr } }; typedef enum x86DisassemblyFlavor { eX86DisFlavorDefault, eX86DisFlavorIntel, eX86DisFlavorATT } x86DisassemblyFlavor; static OptionEnumValueElement g_x86_dis_flavor_value_types[] = { { eX86DisFlavorDefault, "default", "Disassembler default (currently att)."}, { eX86DisFlavorIntel, "intel", "Intel disassembler flavor."}, { eX86DisFlavorATT, "att", "AT&T disassembler flavor."}, { 0, nullptr, nullptr } }; static OptionEnumValueElement g_hex_immediate_style_values[] = { { Disassembler::eHexStyleC, "c", "C-style (0xffff)."}, { Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."}, { 0, nullptr, nullptr } }; static OptionEnumValueElement g_load_script_from_sym_file_values[] = { { eLoadScriptFromSymFileTrue, "true", "Load debug scripts inside symbol files"}, { eLoadScriptFromSymFileFalse, "false", "Do not load debug scripts inside symbol files."}, { eLoadScriptFromSymFileWarn, "warn", "Warn about debug scripts inside symbol files but do not load them."}, { 0, nullptr, nullptr } }; static OptionEnumValueElement g_memory_module_load_level_values[] = { { eMemoryModuleLoadLevelMinimal, "minimal" , "Load minimal information when loading modules from memory. Currently this setting loads sections only."}, { eMemoryModuleLoadLevelPartial, "partial" , "Load partial information when loading modules from memory. Currently this setting loads sections and function bounds."}, { eMemoryModuleLoadLevelComplete, "complete", "Load complete information when loading modules from memory. Currently this setting loads sections and all symbols."}, { 0, nullptr, nullptr } }; static PropertyDefinition g_properties[] = { { "default-arch" , OptionValue::eTypeArch , true , 0 , nullptr, nullptr, "Default architecture to choose, when there's a choice." }, { "move-to-nearest-code" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Move breakpoints to nearest code." }, { "language" , OptionValue::eTypeLanguage , false, eLanguageTypeUnknown , nullptr, nullptr, "The language to use when interpreting expressions entered in commands." }, { "expr-prefix" , OptionValue::eTypeFileSpec , false, 0 , nullptr, nullptr, "Path to a file containing expressions to be prepended to all expressions." }, { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eDynamicDontRunTarget , nullptr, g_dynamic_value_types, "Should printed values be shown as their dynamic value." }, { "enable-synthetic-value" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Should synthetic values be used by default whenever available." }, { "skip-prologue" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Skip function prologues when setting breakpoints by name." }, { "source-map" , OptionValue::eTypePathMap , false, 0 , nullptr, nullptr, "Source path remappings are used to track the change of location between a source file when built, and " "where it exists on the current system. It consists of an array of duples, the first element of each duple is " "some part (starting at the root) of the path to the file when it was built, " "and the second is where the remainder of the original build hierarchy is rooted on the local system. " "Each element of the array is checked in order and the first one that results in a match wins." }, { "exec-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , nullptr, nullptr, "Executable search paths to use when locating executable files whose paths don't match the local file system." }, { "debug-file-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , nullptr, nullptr, "List of directories to be searched when locating debug symbol files." }, { "clang-module-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , nullptr, nullptr, "List of directories to be searched when locating modules for Clang." }, { "auto-import-clang-modules" , OptionValue::eTypeBoolean , false, false , nullptr, nullptr, "Automatically load Clang modules referred to by the program." }, { "max-children-count" , OptionValue::eTypeSInt64 , false, 256 , nullptr, nullptr, "Maximum number of children to expand in any level of depth." }, { "max-string-summary-length" , OptionValue::eTypeSInt64 , false, 1024 , nullptr, nullptr, "Maximum number of characters to show when using %s in summary strings." }, { "max-memory-read-size" , OptionValue::eTypeSInt64 , false, 1024 , nullptr, nullptr, "Maximum number of bytes that 'memory read' will fetch before --force must be specified." }, { "breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Consult the platform module avoid list when setting non-module specific breakpoints." }, { "arg0" , OptionValue::eTypeString , false, 0 , nullptr, nullptr, "The first argument passed to the program in the argument array which can be different from the executable itself." }, { "run-args" , OptionValue::eTypeArgs , false, 0 , nullptr, nullptr, "A list containing all the arguments to be passed to the executable when it is run. Note that this does NOT include the argv[0] which is in target.arg0." }, { "env-vars" , OptionValue::eTypeDictionary, false, OptionValue::eTypeString , nullptr, nullptr, "A list of all the environment variables to be passed to the executable's environment, and their values." }, { "inherit-env" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Inherit the environment from the process that is running LLDB." }, { "input-path" , OptionValue::eTypeFileSpec , false, 0 , nullptr, nullptr, "The file/path to be used by the executable program for reading its standard input." }, { "output-path" , OptionValue::eTypeFileSpec , false, 0 , nullptr, nullptr, "The file/path to be used by the executable program for writing its standard output." }, { "error-path" , OptionValue::eTypeFileSpec , false, 0 , nullptr, nullptr, "The file/path to be used by the executable program for writing its standard error." }, { "detach-on-error" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "debugserver will detach (rather than killing) a process if it loses connection with lldb." }, { "disable-aslr" , OptionValue::eTypeBoolean , false, true , nullptr, nullptr, "Disable Address Space Layout Randomization (ASLR)" }, { "disable-stdio" , OptionValue::eTypeBoolean , false, false , nullptr, nullptr, "Disable stdin/stdout for process (e.g. for a GUI application)" }, { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsAlways , nullptr, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " "Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. " "Usually this is limited to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. " "Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. " "Always checking for inlined breakpoint locations can be expensive (memory and time), so if you have a project with many headers " "and find that setting breakpoints is slow, then you can change this setting to headers. " "This setting allows you to control exactly which strategy is used when setting " "file and line breakpoints." }, // FIXME: This is the wrong way to do per-architecture settings, but we don't have a general per architecture settings system in place yet. { "x86-disassembly-flavor" , OptionValue::eTypeEnum , false, eX86DisFlavorDefault, nullptr, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets." }, { "use-hex-immediates" , OptionValue::eTypeBoolean , false, true, nullptr, nullptr, "Show immediates in disassembly as hexadecimal." }, { "hex-immediate-style" , OptionValue::eTypeEnum , false, Disassembler::eHexStyleC, nullptr, g_hex_immediate_style_values, "Which style to use for printing hexadecimal disassembly values." }, { "use-fast-stepping" , OptionValue::eTypeBoolean , false, true, nullptr, nullptr, "Use a fast stepping algorithm based on running from branch to branch rather than instruction single-stepping." }, { "load-script-from-symbol-file" , OptionValue::eTypeEnum , false, eLoadScriptFromSymFileWarn, nullptr, g_load_script_from_sym_file_values, "Allow LLDB to load scripting resources embedded in symbol files when available." }, { "memory-module-load-level" , OptionValue::eTypeEnum , false, eMemoryModuleLoadLevelComplete, nullptr, g_memory_module_load_level_values, "Loading modules from memory can be slow as reading the symbol tables and other data can take a long time depending on your connection to the debug target. " "This setting helps users control how much information gets loaded when loading modules from memory." "'complete' is the default value for this setting which will load all sections and symbols by reading them from memory (slowest, most accurate). " "'partial' will load sections and attempt to find function bounds without downloading the symbol table (faster, still accurate, missing symbol names). " "'minimal' is the fastest setting and will load section data with no symbols, but should rarely be used as stack frames in these memory regions will be inaccurate and not provide any context (fastest). " }, { "display-expression-in-crashlogs" , OptionValue::eTypeBoolean , false, false, nullptr, nullptr, "Expressions that crash will show up in crash logs if the host system supports executable specific crash log strings and this setting is set to true." }, { "trap-handler-names" , OptionValue::eTypeArray , true, OptionValue::eTypeString, nullptr, nullptr, "A list of trap handler function names, e.g. a common Unix user process one is _sigtramp." }, { "display-runtime-support-values" , OptionValue::eTypeBoolean , false, false, nullptr, nullptr, "If true, LLDB will show variables that are meant to support the operation of a language's runtime support." }, { "non-stop-mode" , OptionValue::eTypeBoolean , false, 0, nullptr, nullptr, "Disable lock-step debugging, instead control threads independently." }, { nullptr , OptionValue::eTypeInvalid , false, 0 , nullptr, nullptr, nullptr } }; enum { ePropertyDefaultArch, ePropertyMoveToNearestCode, ePropertyLanguage, ePropertyExprPrefix, ePropertyPreferDynamic, ePropertyEnableSynthetic, ePropertySkipPrologue, ePropertySourceMap, ePropertyExecutableSearchPaths, ePropertyDebugFileSearchPaths, ePropertyClangModuleSearchPaths, ePropertyAutoImportClangModules, ePropertyMaxChildrenCount, ePropertyMaxSummaryLength, ePropertyMaxMemReadSize, ePropertyBreakpointUseAvoidList, ePropertyArg0, ePropertyRunArgs, ePropertyEnvVars, ePropertyInheritEnv, ePropertyInputPath, ePropertyOutputPath, ePropertyErrorPath, ePropertyDetachOnError, ePropertyDisableASLR, ePropertyDisableSTDIO, ePropertyInlineStrategy, ePropertyDisassemblyFlavor, ePropertyUseHexImmediates, ePropertyHexImmediateStyle, ePropertyUseFastStepping, ePropertyLoadScriptFromSymbolFile, ePropertyMemoryModuleLoadLevel, ePropertyDisplayExpressionsInCrashlogs, ePropertyTrapHandlerNames, ePropertyDisplayRuntimeSupportValues, ePropertyNonStopModeEnabled }; class TargetOptionValueProperties : public OptionValueProperties { public: TargetOptionValueProperties (const ConstString &name) : OptionValueProperties (name), m_target(nullptr), m_got_host_env (false) { } // This constructor is used when creating TargetOptionValueProperties when it // is part of a new lldb_private::Target instance. It will copy all current // global property values as needed TargetOptionValueProperties (Target *target, const TargetPropertiesSP &target_properties_sp) : OptionValueProperties(*target_properties_sp->GetValueProperties()), m_target (target), m_got_host_env (false) { } const Property * GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the target options, we will always // try and grab the setting from the current target if there is one. Else we just // use the one from this instance. if (idx == ePropertyEnvVars) GetHostEnvironmentIfNeeded (); if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { TargetOptionValueProperties *target_properties = static_cast(target->GetValueProperties().get()); if (this != target_properties) return target_properties->ProtectedGetPropertyAtIndex (idx); } } return ProtectedGetPropertyAtIndex (idx); } lldb::TargetSP GetTargetSP () { return m_target->shared_from_this(); } protected: void GetHostEnvironmentIfNeeded () const { if (!m_got_host_env) { if (m_target) { m_got_host_env = true; const uint32_t idx = ePropertyInheritEnv; if (GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0)) { PlatformSP platform_sp (m_target->GetPlatform()); if (platform_sp) { StringList env; if (platform_sp->GetEnvironment(env)) { OptionValueDictionary *env_dict = GetPropertyAtIndexAsOptionValueDictionary(nullptr, ePropertyEnvVars); if (env_dict) { const bool can_replace = false; const size_t envc = env.GetSize(); for (size_t idx=0; idxSetValueForKey(key, OptionValueSP(new OptionValueString(value)), can_replace); } } } } } } } } } Target *m_target; mutable bool m_got_host_env; }; //---------------------------------------------------------------------- // TargetProperties //---------------------------------------------------------------------- TargetProperties::TargetProperties (Target *target) : Properties (), m_launch_info () { if (target) { m_collection_sp.reset (new TargetOptionValueProperties(target, Target::GetGlobalProperties())); // Set callbacks to update launch_info whenever "settins set" updated any of these properties m_collection_sp->SetValueChangedCallback(ePropertyArg0, TargetProperties::Arg0ValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyRunArgs, TargetProperties::RunArgsValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyEnvVars, TargetProperties::EnvVarsValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyInputPath, TargetProperties::InputPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyOutputPath, TargetProperties::OutputPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyErrorPath, TargetProperties::ErrorPathValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyDetachOnError, TargetProperties::DetachOnErrorValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyDisableASLR, TargetProperties::DisableASLRValueChangedCallback, this); m_collection_sp->SetValueChangedCallback(ePropertyDisableSTDIO, TargetProperties::DisableSTDIOValueChangedCallback, this); // Update m_launch_info once it was created Arg0ValueChangedCallback(this, nullptr); RunArgsValueChangedCallback(this, nullptr); //EnvVarsValueChangedCallback(this, nullptr); // FIXME: cause segfault in Target::GetPlatform() InputPathValueChangedCallback(this, nullptr); OutputPathValueChangedCallback(this, nullptr); ErrorPathValueChangedCallback(this, nullptr); DetachOnErrorValueChangedCallback(this, nullptr); DisableASLRValueChangedCallback(this, nullptr); DisableSTDIOValueChangedCallback(this, nullptr); } else { m_collection_sp.reset (new TargetOptionValueProperties(ConstString("target"))); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty(ConstString("process"), ConstString("Settings specify to processes."), true, Process::GetGlobalProperties()->GetValueProperties()); } } TargetProperties::~TargetProperties() = default; ArchSpec TargetProperties::GetDefaultArchitecture () const { OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch(nullptr, ePropertyDefaultArch); if (value) return value->GetCurrentValue(); return ArchSpec(); } void TargetProperties::SetDefaultArchitecture (const ArchSpec& arch) { OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch(nullptr, ePropertyDefaultArch); if (value) return value->SetCurrentValue(arch, true); } bool TargetProperties::GetMoveToNearestCode() const { const uint32_t idx = ePropertyMoveToNearestCode; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } lldb::DynamicValueType TargetProperties::GetPreferDynamicValue() const { const uint32_t idx = ePropertyPreferDynamic; return (lldb::DynamicValueType)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); } bool TargetProperties::SetPreferDynamicValue (lldb::DynamicValueType d) { const uint32_t idx = ePropertyPreferDynamic; return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, d); } bool TargetProperties::GetDisableASLR () const { const uint32_t idx = ePropertyDisableASLR; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDisableASLR (bool b) { const uint32_t idx = ePropertyDisableASLR; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetDetachOnError () const { const uint32_t idx = ePropertyDetachOnError; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDetachOnError (bool b) { const uint32_t idx = ePropertyDetachOnError; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetDisableSTDIO () const { const uint32_t idx = ePropertyDisableSTDIO; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } void TargetProperties::SetDisableSTDIO (bool b) { const uint32_t idx = ePropertyDisableSTDIO; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } const char * TargetProperties::GetDisassemblyFlavor () const { const uint32_t idx = ePropertyDisassemblyFlavor; const char *return_value; x86DisassemblyFlavor flavor_value = (x86DisassemblyFlavor) m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); return_value = g_x86_dis_flavor_value_types[flavor_value].string_value; return return_value; } InlineStrategy TargetProperties::GetInlineStrategy () const { const uint32_t idx = ePropertyInlineStrategy; return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); } const char * TargetProperties::GetArg0 () const { const uint32_t idx = ePropertyArg0; return m_collection_sp->GetPropertyAtIndexAsString(nullptr, idx, nullptr); } void TargetProperties::SetArg0 (const char *arg) { const uint32_t idx = ePropertyArg0; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, arg); m_launch_info.SetArg0(arg); } bool TargetProperties::GetRunArguments (Args &args) const { const uint32_t idx = ePropertyRunArgs; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); } void TargetProperties::SetRunArguments (const Args &args) { const uint32_t idx = ePropertyRunArgs; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); m_launch_info.GetArguments() = args; } size_t TargetProperties::GetEnvironmentAsArgs (Args &env) const { const uint32_t idx = ePropertyEnvVars; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env); } void TargetProperties::SetEnvironmentFromArgs (const Args &env) { const uint32_t idx = ePropertyEnvVars; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, env); m_launch_info.GetEnvironmentEntries() = env; } bool TargetProperties::GetSkipPrologue() const { const uint32_t idx = ePropertySkipPrologue; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } PathMappingList & TargetProperties::GetSourcePathMap () const { const uint32_t idx = ePropertySourceMap; OptionValuePathMappings *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList & TargetProperties::GetExecutableSearchPaths () { const uint32_t idx = ePropertyExecutableSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList & TargetProperties::GetDebugFileSearchPaths () { const uint32_t idx = ePropertyDebugFileSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } FileSpecList & TargetProperties::GetClangModuleSearchPaths () { const uint32_t idx = ePropertyClangModuleSearchPaths; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } bool TargetProperties::GetEnableAutoImportClangModules() const { const uint32_t idx = ePropertyAutoImportClangModules; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetEnableSyntheticValue () const { const uint32_t idx = ePropertyEnableSynthetic; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } uint32_t TargetProperties::GetMaximumNumberOfChildrenToDisplay() const { const uint32_t idx = ePropertyMaxChildrenCount; return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value); } uint32_t TargetProperties::GetMaximumSizeOfStringSummary() const { const uint32_t idx = ePropertyMaxSummaryLength; return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value); } uint32_t TargetProperties::GetMaximumMemReadSize () const { const uint32_t idx = ePropertyMaxMemReadSize; return m_collection_sp->GetPropertyAtIndexAsSInt64(nullptr, idx, g_properties[idx].default_uint_value); } FileSpec TargetProperties::GetStandardInputPath () const { const uint32_t idx = ePropertyInputPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void TargetProperties::SetStandardInputPath (const char *p) { const uint32_t idx = ePropertyInputPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p); } FileSpec TargetProperties::GetStandardOutputPath () const { const uint32_t idx = ePropertyOutputPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } void TargetProperties::SetStandardOutputPath (const char *p) { const uint32_t idx = ePropertyOutputPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p); } FileSpec TargetProperties::GetStandardErrorPath () const { const uint32_t idx = ePropertyErrorPath; return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx); } LanguageType TargetProperties::GetLanguage () const { OptionValueLanguage *value = m_collection_sp->GetPropertyAtIndexAsOptionValueLanguage(nullptr, ePropertyLanguage); if (value) return value->GetCurrentValue(); return LanguageType(); } const char * TargetProperties::GetExpressionPrefixContentsAsCString () { const uint32_t idx = ePropertyExprPrefix; OptionValueFileSpec *file = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec(nullptr, false, idx); if (file) { const bool null_terminate = true; DataBufferSP data_sp(file->GetFileContents(null_terminate)); if (data_sp) return (const char *) data_sp->GetBytes(); } return nullptr; } void TargetProperties::SetStandardErrorPath (const char *p) { const uint32_t idx = ePropertyErrorPath; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p); } bool TargetProperties::GetBreakpointsConsultPlatformAvoidList () { const uint32_t idx = ePropertyBreakpointUseAvoidList; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetUseHexImmediates () const { const uint32_t idx = ePropertyUseHexImmediates; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetUseFastStepping () const { const uint32_t idx = ePropertyUseFastStepping; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } bool TargetProperties::GetDisplayExpressionsInCrashlogs () const { const uint32_t idx = ePropertyDisplayExpressionsInCrashlogs; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, g_properties[idx].default_uint_value != 0); } LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile () const { const uint32_t idx = ePropertyLoadScriptFromSymbolFile; return (LoadScriptFromSymFile)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); } Disassembler::HexImmediateStyle TargetProperties::GetHexImmediateStyle () const { const uint32_t idx = ePropertyHexImmediateStyle; return (Disassembler::HexImmediateStyle)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); } MemoryModuleLoadLevel TargetProperties::GetMemoryModuleLoadLevel() const { const uint32_t idx = ePropertyMemoryModuleLoadLevel; return (MemoryModuleLoadLevel)m_collection_sp->GetPropertyAtIndexAsEnumeration(nullptr, idx, g_properties[idx].default_uint_value); } bool TargetProperties::GetUserSpecifiedTrapHandlerNames (Args &args) const { const uint32_t idx = ePropertyTrapHandlerNames; return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args); } void TargetProperties::SetUserSpecifiedTrapHandlerNames (const Args &args) { const uint32_t idx = ePropertyTrapHandlerNames; m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args); } bool TargetProperties::GetDisplayRuntimeSupportValues () const { const uint32_t idx = ePropertyDisplayRuntimeSupportValues; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); } void TargetProperties::SetDisplayRuntimeSupportValues (bool b) { const uint32_t idx = ePropertyDisplayRuntimeSupportValues; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool TargetProperties::GetNonStopModeEnabled () const { const uint32_t idx = ePropertyNonStopModeEnabled; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, false); } void TargetProperties::SetNonStopModeEnabled (bool b) { const uint32_t idx = ePropertyNonStopModeEnabled; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } const ProcessLaunchInfo & TargetProperties::GetProcessLaunchInfo () { m_launch_info.SetArg0(GetArg0()); // FIXME: Arg0 callback doesn't work return m_launch_info; } void TargetProperties::SetProcessLaunchInfo(const ProcessLaunchInfo &launch_info) { m_launch_info = launch_info; SetArg0(launch_info.GetArg0()); SetRunArguments(launch_info.GetArguments()); SetEnvironmentFromArgs(launch_info.GetEnvironmentEntries()); const FileAction *input_file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (input_file_action) { const char *input_path = input_file_action->GetPath(); if (input_path) SetStandardInputPath(input_path); } const FileAction *output_file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (output_file_action) { const char *output_path = output_file_action->GetPath(); if (output_path) SetStandardOutputPath(output_path); } const FileAction *error_file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (error_file_action) { const char *error_path = error_file_action->GetPath(); if (error_path) SetStandardErrorPath(error_path); } SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError)); SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)); SetDisableSTDIO(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableSTDIO)); } void TargetProperties::Arg0ValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.SetArg0(this_->GetArg0()); } void TargetProperties::RunArgsValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); Args args; if (this_->GetRunArguments(args)) this_->m_launch_info.GetArguments() = args; } void TargetProperties::EnvVarsValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); Args args; if (this_->GetEnvironmentAsArgs(args)) this_->m_launch_info.GetEnvironmentEntries() = args; } void TargetProperties::InputPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction(STDIN_FILENO, this_->GetStandardInputPath(), true, false); } void TargetProperties::OutputPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction(STDOUT_FILENO, this_->GetStandardOutputPath(), false, true); } void TargetProperties::ErrorPathValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); this_->m_launch_info.AppendOpenFileAction(STDERR_FILENO, this_->GetStandardErrorPath(), false, true); } void TargetProperties::DetachOnErrorValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDetachOnError()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDetachOnError); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDetachOnError); } void TargetProperties::DisableASLRValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDisableASLR()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableASLR); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableASLR); } void TargetProperties::DisableSTDIOValueChangedCallback(void *target_property_ptr, OptionValue *) { TargetProperties *this_ = reinterpret_cast(target_property_ptr); if (this_->GetDisableSTDIO()) this_->m_launch_info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO); else this_->m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO); } //---------------------------------------------------------------------- // Target::TargetEventData //---------------------------------------------------------------------- Target::TargetEventData::TargetEventData (const lldb::TargetSP &target_sp) : EventData (), m_target_sp (target_sp), m_module_list () { } Target::TargetEventData::TargetEventData (const lldb::TargetSP &target_sp, const ModuleList &module_list) : EventData (), m_target_sp (target_sp), m_module_list (module_list) { } Target::TargetEventData::~TargetEventData() = default; const ConstString & Target::TargetEventData::GetFlavorString () { static ConstString g_flavor ("Target::TargetEventData"); return g_flavor; } void Target::TargetEventData::Dump (Stream *s) const { } const Target::TargetEventData * Target::TargetEventData::GetEventDataFromEvent (const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == TargetEventData::GetFlavorString()) return static_cast (event_ptr->GetData()); } return nullptr; } TargetSP Target::TargetEventData::GetTargetFromEvent (const Event *event_ptr) { TargetSP target_sp; const TargetEventData *event_data = GetEventDataFromEvent (event_ptr); if (event_data) target_sp = event_data->m_target_sp; return target_sp; } ModuleList Target::TargetEventData::GetModuleListFromEvent (const Event *event_ptr) { ModuleList module_list; const TargetEventData *event_data = GetEventDataFromEvent (event_ptr); if (event_data) module_list = event_data->m_module_list; return module_list; } Index: vendor/lldb/dist/tools/lldb-server/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/lldb-server/CMakeLists.txt (revision 295597) +++ vendor/lldb/dist/tools/lldb-server/CMakeLists.txt (revision 295598) @@ -1,58 +1,53 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/Linux ../../source/Plugins/Process/POSIX ) endif () if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/FreeBSD ../../source/Plugins/Process/POSIX ) endif () if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/POSIX ) endif () include_directories(../../source) include(../../cmake/LLDBDependencies.cmake) add_lldb_executable(lldb-server Acceptor.cpp lldb-gdbserver.cpp lldb-platform.cpp lldb-server.cpp LLDBServerUtilities.cpp ) -if (BUILD_SHARED_LIBS ) - target_link_libraries(lldb-server liblldb) - target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) +# The Darwin linker doesn't understand --start-group/--end-group. +if (LLDB_LINKER_SUPPORTS_GROUPS) + target_link_libraries(lldb-server + -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) + target_link_libraries(lldb-server + -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) else() - # The Darwin linker doesn't understand --start-group/--end-group. - if (LLDB_LINKER_SUPPORTS_GROUPS) - target_link_libraries(lldb-server - -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) - target_link_libraries(lldb-server - -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) - else() - target_link_libraries(lldb-server ${LLDB_USED_LIBS}) - target_link_libraries(lldb-server ${CLANG_USED_LIBS}) - endif() - llvm_config(lldb-server ${LLVM_LINK_COMPONENTS}) - - target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) + target_link_libraries(lldb-server ${LLDB_USED_LIBS}) + target_link_libraries(lldb-server ${CLANG_USED_LIBS}) endif() +llvm_config(lldb-server ${LLVM_LINK_COMPONENTS}) + +target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) set_target_properties(lldb-server PROPERTIES VERSION ${LLDB_VERSION}) install(TARGETS lldb-server RUNTIME DESTINATION bin)