Index: vendor/lldb/dist/include/lldb/Core/Address.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/Address.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Core/Address.h (revision 319790) @@ -1,575 +1,581 @@ //===-- Address.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_Address_h_ #define liblldb_Address_h_ #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS #include "lldb/lldb-enumerations.h" // for AddressClass::eAddressClassInvalid #include "lldb/lldb-forward.h" // for SectionWP, SectionSP, ModuleSP #include "lldb/lldb-types.h" // for addr_t #include // for size_t #include // for uint32_t, UINT32_MAX, int64_t namespace lldb_private { class Block; } namespace lldb_private { class CompileUnit; } namespace lldb_private { class ExecutionContextScope; } namespace lldb_private { class Function; } namespace lldb_private { class SectionList; } namespace lldb_private { class Stream; } namespace lldb_private { class Symbol; } namespace lldb_private { class SymbolContext; } namespace lldb_private { class Target; } namespace lldb_private { struct LineEntry; } namespace lldb_private { //---------------------------------------------------------------------- /// @class Address Address.h "lldb/Core/Address.h" /// @brief A section + offset based address class. /// /// The Address class allows addresses to be relative to a section /// that can move during runtime due to images (executables, shared /// libraries, bundles, frameworks) being loaded at different /// addresses than the addresses found in the object file that /// represents them on disk. There are currently two types of addresses /// for a section: /// @li file addresses /// @li load addresses /// /// File addresses represent the virtual addresses that are in the "on /// disk" object files. These virtual addresses are converted to be /// relative to unique sections scoped to the object file so that /// when/if the addresses slide when the images are loaded/unloaded /// in memory, we can easily track these changes without having to /// update every object (compile unit ranges, line tables, function /// address ranges, lexical block and inlined subroutine address /// ranges, global and static variables) each time an image is loaded or /// unloaded. /// /// Load addresses represent the virtual addresses where each section /// ends up getting loaded at runtime. Before executing a program, it /// is common for all of the load addresses to be unresolved. When a /// DynamicLoader plug-in receives notification that shared libraries /// have been loaded/unloaded, the load addresses of the main executable /// and any images (shared libraries) will be resolved/unresolved. When /// this happens, breakpoints that are in one of these sections can be /// set/cleared. //---------------------------------------------------------------------- class Address { public: //------------------------------------------------------------------ /// Dump styles allow the Address::Dump(Stream *,DumpStyle) const /// function to display Address contents in a variety of ways. //------------------------------------------------------------------ typedef enum { DumpStyleInvalid, ///< Invalid dump style DumpStyleSectionNameOffset, ///< Display as the section name + offset. ///< \code /// // address for printf in libSystem.B.dylib as a section name + offset /// libSystem.B.dylib.__TEXT.__text + 0x0005cfdf /// \endcode DumpStyleSectionPointerOffset, ///< Display as the section pointer + offset ///(debug output). ///< \code /// // address for printf in libSystem.B.dylib as a section pointer + offset /// (lldb::Section *)0x35cc50 + 0x000000000005cfdf \endcode DumpStyleFileAddress, ///< Display as the file address (if any). ///< \code /// // address for printf in libSystem.B.dylib as a file address /// 0x000000000005dcff \endcode DumpStyleModuleWithFileAddress, ///< Display as the file address with the ///module name prepended (if any). ///< \code /// // address for printf in libSystem.B.dylib as a file address /// libSystem.B.dylib[0x000000000005dcff] \endcode DumpStyleLoadAddress, ///< Display as the load address (if resolved). ///< \code /// // address for printf in libSystem.B.dylib as a load address /// 0x00007fff8306bcff \endcode DumpStyleResolvedDescription, ///< Display the details about what an address ///resolves to. This can ///< be anything from a symbol context summary (module, function/symbol, ///< and file and line), to information about what the pointer points to ///< if the address is in a section (section of pointers, c strings, etc). DumpStyleResolvedDescriptionNoModule, DumpStyleResolvedDescriptionNoFunctionArguments, DumpStyleNoFunctionName, ///< Elide the function name; display an offset ///into the current function. ///< Used primarily in disassembly symbolication DumpStyleDetailedSymbolContext, ///< Detailed symbol context information for ///an address for all symbol ///< context members. DumpStyleResolvedPointerDescription ///< Dereference a pointer at the ///current address and then lookup the ///< dereferenced address using DumpStyleResolvedDescription } DumpStyle; //------------------------------------------------------------------ /// Default constructor. /// /// Initialize with a invalid section (NULL) and an invalid /// offset (LLDB_INVALID_ADDRESS). //------------------------------------------------------------------ Address() : m_section_wp(), m_offset(LLDB_INVALID_ADDRESS) {} //------------------------------------------------------------------ /// Copy constructor /// /// Makes a copy of the another Address object \a rhs. /// /// @param[in] rhs /// A const Address object reference to copy. //------------------------------------------------------------------ Address(const Address &rhs) : m_section_wp(rhs.m_section_wp), m_offset(rhs.m_offset) {} //------------------------------------------------------------------ /// Construct with a section pointer and offset. /// /// Initialize the address with the supplied \a section and \a /// offset. /// /// @param[in] section /// A section pointer to a valid lldb::Section, or NULL if the /// address doesn't have a section or will get resolved later. /// /// @param[in] offset /// The offset in bytes into \a section. //------------------------------------------------------------------ Address(const lldb::SectionSP §ion_sp, lldb::addr_t offset) : m_section_wp(), // Don't init with section_sp in case section_sp is // invalid (the weak_ptr will throw) m_offset(offset) { if (section_sp) m_section_wp = section_sp; } //------------------------------------------------------------------ /// Construct with a virtual address and section list. /// /// Initialize and resolve the address with the supplied virtual /// address \a file_addr. /// /// @param[in] file_addr /// A virtual file address. /// /// @param[in] section_list /// A list of sections, one of which may contain the \a file_addr. //------------------------------------------------------------------ Address(lldb::addr_t file_addr, const SectionList *section_list); Address(lldb::addr_t abs_addr); //------------------------------------------------------------------ /// Assignment operator. /// /// Copies the address value from another Address object \a rhs /// into \a this object. /// /// @param[in] rhs /// A const Address object reference to copy. /// /// @return /// A const Address object reference to \a this. //------------------------------------------------------------------ #ifndef SWIG const Address &operator=(const Address &rhs); #endif //------------------------------------------------------------------ /// Clear the object's state. /// /// Sets the section to an invalid value (NULL) and an invalid /// offset (LLDB_INVALID_ADDRESS). //------------------------------------------------------------------ void Clear() { m_section_wp.reset(); m_offset = LLDB_INVALID_ADDRESS; } //------------------------------------------------------------------ /// Compare two Address objects. /// /// @param[in] lhs /// The Left Hand Side const Address object reference. /// /// @param[in] rhs /// The Right Hand Side const Address object reference. /// /// @return /// @li -1 if lhs < rhs /// @li 0 if lhs == rhs /// @li 1 if lhs > rhs //------------------------------------------------------------------ static int CompareFileAddress(const Address &lhs, const Address &rhs); static int CompareLoadAddress(const Address &lhs, const Address &rhs, Target *target); static int CompareModulePointerAndOffset(const Address &lhs, const Address &rhs); // For use with std::map, std::multi_map class ModulePointerAndOffsetLessThanFunctionObject { public: ModulePointerAndOffsetLessThanFunctionObject() = default; bool operator()(const Address &a, const Address &b) const { return Address::CompareModulePointerAndOffset(a, b) < 0; } }; //------------------------------------------------------------------ /// Dump a description of this object to a Stream. /// /// Dump a description of the contents of this object to the /// supplied stream \a s. There are many ways to display a section /// offset based address, and \a style lets the user choose. /// /// @param[in] s /// The stream to which to dump the object description. /// /// @param[in] style /// The display style for the address. /// /// @param[in] fallback_style /// The display style for the address. /// /// @return /// Returns \b true if the address was able to be displayed. /// File and load addresses may be unresolved and it may not be /// possible to display a valid value, \b false will be returned /// in such cases. /// /// @see Address::DumpStyle //------------------------------------------------------------------ bool Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style = DumpStyleInvalid, uint32_t addr_byte_size = UINT32_MAX) const; lldb::AddressClass GetAddressClass() const; //------------------------------------------------------------------ /// Get the file address. /// /// If an address comes from a file on disk that has section /// relative addresses, then it has a virtual address that is /// relative to unique section in the object file. /// /// @return /// The valid file virtual address, or LLDB_INVALID_ADDRESS if /// the address doesn't have a file virtual address (image is /// from memory only with no representation on disk). //------------------------------------------------------------------ lldb::addr_t GetFileAddress() const; //------------------------------------------------------------------ /// Get the load address. /// /// If an address comes from a file on disk that has section /// relative addresses, then it has a virtual address that is /// relative to unique section in the object file. Sections get /// resolved at runtime by DynamicLoader plug-ins as images /// (executables and shared libraries) get loaded/unloaded. If a /// section is loaded, then the load address can be resolved. /// /// @return /// The valid load virtual address, or LLDB_INVALID_ADDRESS if /// the address is currently not loaded. //------------------------------------------------------------------ lldb::addr_t GetLoadAddress(Target *target) const; //------------------------------------------------------------------ /// Get the load address as a callable code load address. /// /// This function will first resolve its address to a load address. /// Then, if the address turns out to be in code address, return the /// load address that would be required to call or return to. The /// address might have extra bits set (bit zero will be set to Thumb /// functions for an ARM target) that are required when changing the /// program counter to setting a return address. /// /// @return /// The valid load virtual address, or LLDB_INVALID_ADDRESS if /// the address is currently not loaded. //------------------------------------------------------------------ lldb::addr_t GetCallableLoadAddress(Target *target, bool is_indirect = false) const; //------------------------------------------------------------------ /// Get the load address as an opcode load address. /// /// This function will first resolve its address to a load address. /// Then, if the address turns out to be in code address, return the /// load address for an opcode. This address object might have /// extra bits set (bit zero will be set to Thumb functions for an /// ARM target) that are required for changing the program counter /// and this function will remove any bits that are intended for /// these special purposes. The result of this function can be used /// to safely write a software breakpoint trap to memory. /// /// @return /// The valid load virtual address with extra callable bits /// removed, or LLDB_INVALID_ADDRESS if the address is currently /// not loaded. //------------------------------------------------------------------ lldb::addr_t GetOpcodeLoadAddress( Target *target, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const; //------------------------------------------------------------------ /// Get the section relative offset value. /// /// @return /// The current offset, or LLDB_INVALID_ADDRESS if this address /// doesn't contain a valid offset. //------------------------------------------------------------------ lldb::addr_t GetOffset() const { return m_offset; } //------------------------------------------------------------------ /// Check if an address is section offset. /// /// When converting a virtual file or load address into a section /// offset based address, we often need to know if, given a section /// list, if the address was able to be converted to section offset. /// This function returns true if the current value contained in /// this object is section offset based. /// /// @return /// Returns \b true if the address has a valid section and /// offset, \b false otherwise. //------------------------------------------------------------------ bool IsSectionOffset() const { return IsValid() && (GetSection().get() != nullptr); } //------------------------------------------------------------------ /// Check if the object state is valid. /// /// A valid Address object contains either a section pointer and /// and offset (for section offset based addresses), or just a valid /// offset (for absolute addresses that have no section). /// /// @return /// Returns \b true if the offset is valid, \b false /// otherwise. //------------------------------------------------------------------ bool IsValid() const { return m_offset != LLDB_INVALID_ADDRESS; } //------------------------------------------------------------------ /// Get the memory cost of this object. /// /// @return /// The number of bytes that this object occupies in memory. //------------------------------------------------------------------ size_t MemorySize() const; //------------------------------------------------------------------ /// Resolve a file virtual address using a section list. /// /// Given a list of sections, attempt to resolve \a addr as a /// an offset into one of the file sections. /// /// @return /// Returns \b true if \a addr was able to be resolved, \b false /// otherwise. //------------------------------------------------------------------ bool ResolveAddressUsingFileSections(lldb::addr_t addr, const SectionList *sections); //------------------------------------------------------------------ /// Set the address to represent \a load_addr. /// /// The address will attempt to find a loaded section within /// \a target that contains \a load_addr. If successful, this /// address object will have a valid section and offset. Else this /// address object will have no section (NULL) and the offset will /// be \a load_addr. /// /// @param[in] load_addr /// A load address from a current process. /// /// @param[in] target /// The target to use when trying resolve the address into /// a section + offset. The Target's SectionLoadList object /// is used to resolve the address. /// + /// @param[in] allow_section_end + /// If true, treat an address pointing to the end of the module as + /// belonging to that module. + /// /// @return /// Returns \b true if the load address was resolved to be /// section/offset, \b false otherwise. It is often ok for an /// address no not resolve to a section in a module, this often /// happens for JIT'ed code, or any load addresses on the stack /// or heap. //------------------------------------------------------------------ - bool SetLoadAddress(lldb::addr_t load_addr, Target *target); + bool SetLoadAddress(lldb::addr_t load_addr, Target *target, + bool allow_section_end = false); bool SetOpcodeLoadAddress( lldb::addr_t load_addr, Target *target, - lldb::AddressClass addr_class = lldb::eAddressClassInvalid); + lldb::AddressClass addr_class = lldb::eAddressClassInvalid, + bool allow_section_end = false); bool SetCallableLoadAddress(lldb::addr_t load_addr, Target *target); //------------------------------------------------------------------ /// Get accessor for the module for this address. /// /// @return /// Returns the Module pointer that this address is an offset /// in, or NULL if this address doesn't belong in a module, or /// isn't resolved yet. //------------------------------------------------------------------ lldb::ModuleSP GetModule() const; //------------------------------------------------------------------ /// Get const accessor for the section. /// /// @return /// Returns the const lldb::Section pointer that this address is an /// offset in, or NULL if this address is absolute. //------------------------------------------------------------------ lldb::SectionSP GetSection() const { return m_section_wp.lock(); } //------------------------------------------------------------------ /// Set accessor for the offset. /// /// @param[in] offset /// A new offset value for this object. /// /// @return /// Returns \b true if the offset changed, \b false otherwise. //------------------------------------------------------------------ bool SetOffset(lldb::addr_t offset) { bool changed = m_offset != offset; m_offset = offset; return changed; } void SetRawAddress(lldb::addr_t addr) { m_section_wp.reset(); m_offset = addr; } bool Slide(int64_t offset) { if (m_offset != LLDB_INVALID_ADDRESS) { m_offset += offset; return true; } return false; } //------------------------------------------------------------------ /// Set accessor for the section. /// /// @param[in] section /// A new lldb::Section pointer to use as the section base. Can /// be NULL for absolute addresses that are not relative to /// any section. //------------------------------------------------------------------ void SetSection(const lldb::SectionSP §ion_sp) { m_section_wp = section_sp; } void ClearSection() { m_section_wp.reset(); } //------------------------------------------------------------------ /// Reconstruct a symbol context from an address. /// /// This class doesn't inherit from SymbolContextScope because many /// address objects have short lifespans. Address objects that are /// section offset can reconstruct their symbol context by looking /// up the address in the module found in the section. /// /// @see SymbolContextScope::CalculateSymbolContext(SymbolContext*) //------------------------------------------------------------------ uint32_t CalculateSymbolContext( SymbolContext *sc, uint32_t resolve_scope = lldb::eSymbolContextEverything) const; lldb::ModuleSP CalculateSymbolContextModule() const; CompileUnit *CalculateSymbolContextCompileUnit() const; Function *CalculateSymbolContextFunction() const; Block *CalculateSymbolContextBlock() const; Symbol *CalculateSymbolContextSymbol() const; bool CalculateSymbolContextLineEntry(LineEntry &line_entry) const; //------------------------------------------------------------------ // Returns true if the section should be valid, but isn't because // the shared pointer to the section can't be reconstructed from // a weak pointer that contains a valid weak reference to a section. // Returns false if the section weak pointer has no reference to // a section, or if the section is still valid //------------------------------------------------------------------ bool SectionWasDeleted() const; protected: //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ lldb::SectionWP m_section_wp; ///< The section for the address, can be NULL. lldb::addr_t m_offset; ///< Offset into section if \a m_section_wp is valid... //------------------------------------------------------------------ // Returns true if the m_section_wp once had a reference to a valid // section shared pointer, but no longer does. This can happen if // we have an address from a module that gets unloaded and deleted. // This function should only be called if GetSection() returns an // empty shared pointer and you want to know if this address used to // have a valid section. //------------------------------------------------------------------ bool SectionWasDeletedPrivate() const; }; //---------------------------------------------------------------------- // NOTE: Be careful using this operator. It can correctly compare two // addresses from the same Module correctly. It can't compare two // addresses from different modules in any meaningful way, but it will // compare the module pointers. // // To sum things up: // - works great for addresses within the same module // - it works for addresses across multiple modules, but don't expect the // address results to make much sense // // This basically lets Address objects be used in ordered collection // classes. //---------------------------------------------------------------------- bool operator<(const Address &lhs, const Address &rhs); bool operator>(const Address &lhs, const Address &rhs); bool operator==(const Address &lhs, const Address &rhs); bool operator!=(const Address &lhs, const Address &rhs); } // namespace lldb_private #endif // liblldb_Address_h_ Index: vendor/lldb/dist/include/lldb/Core/Section.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/Section.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Core/Section.h (revision 319790) @@ -1,286 +1,287 @@ //===-- Section.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_Section_h_ #define liblldb_Section_h_ #include "lldb/Core/ModuleChild.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Flags.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-defines.h" // for DISALLOW_COPY_AND_ASSIGN #include "lldb/lldb-enumerations.h" // for SectionType #include "lldb/lldb-forward.h" // for SectionSP, ModuleSP, SectionWP #include "lldb/lldb-types.h" // for addr_t, offset_t, user_id_t #include // for enable_shared_from_this #include // for vector #include // for size_t #include // for uint32_t, UINT32_MAX namespace lldb_private { class Address; } namespace lldb_private { class DataExtractor; } namespace lldb_private { class ObjectFile; } namespace lldb_private { class Section; } namespace lldb_private { class Stream; } namespace lldb_private { class Target; } namespace lldb_private { class SectionList { public: typedef std::vector collection; typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; SectionList(); ~SectionList(); SectionList &operator=(const SectionList &rhs); size_t AddSection(const lldb::SectionSP §ion_sp); size_t AddUniqueSection(const lldb::SectionSP §ion_sp); size_t FindSectionIndex(const Section *sect); bool ContainsSection(lldb::user_id_t sect_id) const; void Dump(Stream *s, Target *target, bool show_header, uint32_t depth) const; lldb::SectionSP FindSectionByName(const ConstString §ion_dstr) const; lldb::SectionSP FindSectionByID(lldb::user_id_t sect_id) const; lldb::SectionSP FindSectionByType(lldb::SectionType sect_type, bool check_children, size_t start_idx = 0) const; lldb::SectionSP FindSectionContainingFileAddress(lldb::addr_t addr, uint32_t depth = UINT32_MAX) const; // Get the number of sections in this list only size_t GetSize() const { return m_sections.size(); } // Get the number of sections in this list, and any contained child sections size_t GetNumSections(uint32_t depth) const; bool ReplaceSection(lldb::user_id_t sect_id, const lldb::SectionSP §ion_sp, uint32_t depth = UINT32_MAX); // Warning, this can be slow as it's removing items from a std::vector. bool DeleteSection(size_t idx); lldb::SectionSP GetSectionAtIndex(size_t idx) const; size_t Slide(lldb::addr_t slide_amount, bool slide_children); void Clear() { m_sections.clear(); } protected: collection m_sections; }; class Section : public std::enable_shared_from_this
, public ModuleChild, public UserID, public Flags { public: // Create a root section (one that has no parent) Section(const lldb::ModuleSP &module_sp, ObjectFile *obj_file, lldb::user_id_t sect_id, const ConstString &name, lldb::SectionType sect_type, lldb::addr_t file_vm_addr, lldb::addr_t vm_size, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, uint32_t flags, uint32_t target_byte_size = 1); // Create a section that is a child of parent_section_sp Section(const lldb::SectionSP &parent_section_sp, // NULL for top level // sections, non-NULL for // child sections const lldb::ModuleSP &module_sp, ObjectFile *obj_file, lldb::user_id_t sect_id, const ConstString &name, lldb::SectionType sect_type, lldb::addr_t file_vm_addr, lldb::addr_t vm_size, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, uint32_t flags, uint32_t target_byte_size = 1); ~Section(); static int Compare(const Section &a, const Section &b); bool ContainsFileAddress(lldb::addr_t vm_addr) const; SectionList &GetChildren() { return m_children; } const SectionList &GetChildren() const { return m_children; } void Dump(Stream *s, Target *target, uint32_t depth) const; void DumpName(Stream *s) const; lldb::addr_t GetLoadBaseAddress(Target *target) const; - bool ResolveContainedAddress(lldb::addr_t offset, Address &so_addr) const; + bool ResolveContainedAddress(lldb::addr_t offset, Address &so_addr, + bool allow_section_end = false) const; lldb::offset_t GetFileOffset() const { return m_file_offset; } void SetFileOffset(lldb::offset_t file_offset) { m_file_offset = file_offset; } lldb::offset_t GetFileSize() const { return m_file_size; } void SetFileSize(lldb::offset_t file_size) { m_file_size = file_size; } lldb::addr_t GetFileAddress() const; bool SetFileAddress(lldb::addr_t file_addr); lldb::addr_t GetOffset() const; lldb::addr_t GetByteSize() const { return m_byte_size; } void SetByteSize(lldb::addr_t byte_size) { m_byte_size = byte_size; } bool IsFake() const { return m_fake; } void SetIsFake(bool fake) { m_fake = fake; } bool IsEncrypted() const { return m_encrypted; } void SetIsEncrypted(bool b) { m_encrypted = b; } bool IsDescendant(const Section *section); const ConstString &GetName() const { return m_name; } bool Slide(lldb::addr_t slide_amount, bool slide_children); lldb::SectionType GetType() const { return m_type; } lldb::SectionSP GetParent() const { return m_parent_wp.lock(); } bool IsThreadSpecific() const { return m_thread_specific; } void SetIsThreadSpecific(bool b) { m_thread_specific = b; } //------------------------------------------------------------------ /// Get the permissions as OR'ed bits from lldb::Permissions //------------------------------------------------------------------ uint32_t GetPermissions() const; //------------------------------------------------------------------ /// Set the permissions using bits OR'ed from lldb::Permissions //------------------------------------------------------------------ void SetPermissions(uint32_t permissions); ObjectFile *GetObjectFile() { return m_obj_file; } const ObjectFile *GetObjectFile() const { return m_obj_file; } //------------------------------------------------------------------ /// Read the section data from the object file that the section /// resides in. /// /// @param[in] dst /// Where to place the data /// /// @param[in] dst_len /// How many bytes of section data to read /// /// @param[in] offset /// The offset in bytes within this section's data at which to /// start copying data from. /// /// @return /// The number of bytes read from the section, or zero if the /// section has no data or \a offset is not a valid offset /// in this section. //------------------------------------------------------------------ lldb::offset_t GetSectionData(void *dst, lldb::offset_t dst_len, lldb::offset_t offset = 0); //------------------------------------------------------------------ /// Get the shared reference to the section data from the object /// file that the section resides in. No copies of the data will be /// make unless the object file has been read from memory. If the /// object file is on disk, it will shared the mmap data for the /// entire object file. /// /// @param[in] data /// Where to place the data, address byte size, and byte order /// /// @return /// The number of bytes read from the section, or zero if the /// section has no data or \a offset is not a valid offset /// in this section. //------------------------------------------------------------------ lldb::offset_t GetSectionData(DataExtractor &data) const; uint32_t GetLog2Align() { return m_log2align; } void SetLog2Align(uint32_t align) { m_log2align = align; } // Get the number of host bytes required to hold a target byte uint32_t GetTargetByteSize() const { return m_target_byte_size; } protected: ObjectFile *m_obj_file; // The object file that data for this section should // be read from lldb::SectionType m_type; // The type of this section lldb::SectionWP m_parent_wp; // Weak pointer to parent section ConstString m_name; // Name of this section lldb::addr_t m_file_addr; // The absolute file virtual address range of this // section if m_parent == NULL, // offset from parent file virtual address if m_parent != NULL lldb::addr_t m_byte_size; // Size in bytes that this section will occupy in // memory at runtime lldb::offset_t m_file_offset; // Object file offset (if any) lldb::offset_t m_file_size; // Object file size (can be smaller than // m_byte_size for zero filled sections...) uint32_t m_log2align; // log_2(align) of the section (i.e. section has to be // aligned to 2^m_log2align) SectionList m_children; // Child sections bool m_fake : 1, // If true, then this section only can contain the address if // one of its // children contains an address. This allows for gaps between the children // that are contained in the address range for this section, but do not // produce // hits unless the children contain the address. m_encrypted : 1, // Set to true if the contents are encrypted m_thread_specific : 1, // This section is thread specific m_readable : 1, // If this section has read permissions m_writable : 1, // If this section has write permissions m_executable : 1; // If this section has executable permissions uint32_t m_target_byte_size; // Some architectures have non-8-bit byte size. // This is specified as // as a multiple number of a host bytes private: DISALLOW_COPY_AND_ASSIGN(Section); }; } // namespace lldb_private #endif // liblldb_Section_h_ Index: vendor/lldb/dist/include/lldb/Core/dwarf.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/dwarf.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Core/dwarf.h (revision 319790) @@ -1,85 +1,85 @@ //===-- dwarf.h -------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef DebugBase_dwarf_h_ #define DebugBase_dwarf_h_ #include // Get the DWARF constant definitions from llvm -#include "llvm/Support/Dwarf.h" +#include "llvm/BinaryFormat/Dwarf.h" #include "lldb/Core/RangeMap.h" // and stuff them in our default namespace using namespace llvm::dwarf; typedef uint32_t dw_uleb128_t; typedef int32_t dw_sleb128_t; typedef uint16_t dw_attr_t; typedef uint16_t dw_form_t; typedef uint16_t dw_tag_t; typedef uint64_t dw_addr_t; // Dwarf address define that must be big enough for // any addresses in the compile units that get // parsed #ifdef DWARFUTILS_DWARF64 #define DWARF_REF_ADDR_SIZE 8 typedef uint64_t dw_offset_t; // Dwarf Debug Information Entry offset for any // offset into the file #else #define DWARF_REF_ADDR_SIZE 4 typedef uint32_t dw_offset_t; // Dwarf Debug Information Entry offset for any // offset into the file #endif /* Constants */ #define DW_INVALID_OFFSET (~(dw_offset_t)0) #define DW_INVALID_INDEX 0xFFFFFFFFul // #define DW_ADDR_none 0x0 #define DW_EH_PE_MASK_ENCODING 0x0F //// The following are used only internally within lldb - don't //// document them in the llvm Dwarf.h header file, we won't see //// them in executable files anywhere. //// These constants fit between DW_OP_lo_user (0xe0) and DW_OP_hi_user (0xff). // //#define DW_OP_APPLE_array_ref 0xEE // first pops index, then pops array; //pushes array[index] //#define DW_OP_APPLE_extern 0xEF // ULEB128 index of external object //(i.e., an entity from the program that was used in the expression) #define DW_OP_APPLE_uninit \ 0xF0 // This is actually generated by some apple compilers in locations lists //#define DW_OP_APPLE_assign 0xF1 // pops value off and assigns it to //second item on stack (2nd item must have assignable context) //#define DW_OP_APPLE_address_of 0xF2 // gets the address of the top stack //item (top item must be a variable, or have value_type that is an address //already) //#define DW_OP_APPLE_value_of 0xF3 // pops the value off the stack and //pushes the value of that object (top item must be a variable, or expression //local) //#define DW_OP_APPLE_deref_type 0xF4 // gets the address of the top stack //item (top item must be a variable, or a clang type) //#define DW_OP_APPLE_expr_local 0xF5 // ULEB128 expression local index //#define DW_OP_APPLE_constf 0xF6 // 1 byte float size, followed by //constant float data //#define DW_OP_APPLE_scalar_cast 0xF7 // Cast top of stack to 2nd in stack's //type leaving all items in place //#define DW_OP_APPLE_clang_cast 0xF8 // pointer size clang::Type * off the //stack and cast top stack item to this type //#define DW_OP_APPLE_clear 0xFE // clears the entire expression stack, //ok if the stack is empty //#define DW_OP_APPLE_error 0xFF // Stops expression evaluation and //returns an error (no args) typedef lldb_private::RangeArray DWARFRangeList; #endif // DebugBase_dwarf_h_ Index: vendor/lldb/dist/include/lldb/Target/SectionLoadList.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/SectionLoadList.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Target/SectionLoadList.h (revision 319790) @@ -1,79 +1,80 @@ //===-- SectionLoadList.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_SectionLoadList_h_ #define liblldb_SectionLoadList_h_ // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "llvm/ADT/DenseMap.h" // Project includes #include "lldb/Core/Section.h" #include "lldb/lldb-public.h" namespace lldb_private { class SectionLoadList { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ SectionLoadList() : m_addr_to_sect(), m_sect_to_addr(), m_mutex() {} SectionLoadList(const SectionLoadList &rhs); ~SectionLoadList() { // Call clear since this takes a lock and clears the section load list // in case another thread is currently using this section load list Clear(); } void operator=(const SectionLoadList &rhs); bool IsEmpty() const; void Clear(); lldb::addr_t GetSectionLoadAddress(const lldb::SectionSP §ion_sp) const; - bool ResolveLoadAddress(lldb::addr_t load_addr, Address &so_addr) const; + bool ResolveLoadAddress(lldb::addr_t load_addr, Address &so_addr, + bool allow_section_end = false) const; bool SetSectionLoadAddress(const lldb::SectionSP §ion_sp, lldb::addr_t load_addr, bool warn_multiple = false); // The old load address should be specified when unloading to ensure we get // the correct instance of the section as a shared library could be loaded // at more than one location. bool SetSectionUnloaded(const lldb::SectionSP §ion_sp, lldb::addr_t load_addr); // Unload all instances of a section. This function can be used on systems // that don't support multiple copies of the same shared library to be // loaded at the same time. size_t SetSectionUnloaded(const lldb::SectionSP §ion_sp); void Dump(Stream &s, Target *target); protected: typedef std::map addr_to_sect_collection; typedef llvm::DenseMap sect_to_addr_collection; addr_to_sect_collection m_addr_to_sect; sect_to_addr_collection m_sect_to_addr; mutable std::recursive_mutex m_mutex; }; } // namespace lldb_private #endif // liblldb_SectionLoadList_h_ Index: vendor/lldb/dist/include/lldb/Utility/SafeMachO.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/SafeMachO.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Utility/SafeMachO.h (revision 319790) @@ -1,119 +1,119 @@ //===-- SafeMachO.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_SafeMachO_h_ #define liblldb_SafeMachO_h_ // This header file is required to work around collisions between the defines in // mach/machine.h, and enum members // of the same name in llvm's MachO.h. If you want to use llvm/Support/MachO.h, // use this file instead. // The caveats are: // 1) You can only use the MachO.h enums, you can't use the defines. That won't // make a difference since the values // are the same. // 2) If you need any header file that relies on mach/machine.h, you must // include that first. // 3) This isn't a total solution, it doesn't undef every define that MachO.h // has borrowed from various system headers, // only the ones that come from mach/machine.h because that is the one we // ended up pulling in from various places. // #undef CPU_ARCH_MASK #undef CPU_ARCH_ABI64 #undef CPU_TYPE_ANY #undef CPU_TYPE_X86 #undef CPU_TYPE_I386 #undef CPU_TYPE_X86_64 #undef CPU_TYPE_MC98000 #undef CPU_TYPE_ARM #undef CPU_TYPE_ARM64 #undef CPU_TYPE_SPARC #undef CPU_TYPE_POWERPC #undef CPU_TYPE_POWERPC64 #undef CPU_SUB_TYPE_MASK #undef CPU_SUB_TYPE_LIB64 #undef CPU_SUBTYPE_MULTIPLE #undef CPU_SUBTYPE_I386_ALL #undef CPU_SUBTYPE_386 #undef CPU_SUBTYPE_486 #undef CPU_SUBTYPE_486SX #undef CPU_SUBTYPE_586 #undef CPU_SUBTYPE_PENT #undef CPU_SUBTYPE_PENTPRO #undef CPU_SUBTYPE_PENTII_M3 #undef CPU_SUBTYPE_PENTII_M5 #undef CPU_SUBTYPE_CELERON #undef CPU_SUBTYPE_CELERON_MOBILE #undef CPU_SUBTYPE_PENTIUM_3 #undef CPU_SUBTYPE_PENTIUM_3_M #undef CPU_SUBTYPE_PENTIUM_3_XEON #undef CPU_SUBTYPE_PENTIUM_M #undef CPU_SUBTYPE_PENTIUM_4 #undef CPU_SUBTYPE_PENTIUM_4_M #undef CPU_SUBTYPE_ITANIUM #undef CPU_SUBTYPE_ITANIUM_2 #undef CPU_SUBTYPE_XEON #undef CPU_SUBTYPE_XEON_MP #undef CPU_SUBTYPE_X86_ALL #undef CPU_SUBTYPE_X86_64_ALL #undef CPU_SUBTYPE_X86_ARCH1 #undef CPU_SUBTYPE_X86_64_H #undef CPU_SUBTYPE_INTEL #undef CPU_SUBTYPE_INTEL_FAMILY #undef CPU_SUBTYPE_INTEL_FAMILY_MAX #undef CPU_SUBTYPE_INTEL_MODEL #undef CPU_SUBTYPE_INTEL_MODEL_ALL #undef CPU_SUBTYPE_ARM #undef CPU_SUBTYPE_ARM_ALL #undef CPU_SUBTYPE_ARM_V4T #undef CPU_SUBTYPE_ARM_V6 #undef CPU_SUBTYPE_ARM_V5 #undef CPU_SUBTYPE_ARM_V5TEJ #undef CPU_SUBTYPE_ARM_XSCALE #undef CPU_SUBTYPE_ARM_V7 #undef CPU_SUBTYPE_ARM_V7S #undef CPU_SUBTYPE_ARM_V7K #undef CPU_SUBTYPE_ARM_V6M #undef CPU_SUBTYPE_ARM_V7M #undef CPU_SUBTYPE_ARM_V7EM #undef CPU_SUBTYPE_ARM64_ALL #undef CPU_SUBTYPE_SPARC_ALL #undef CPU_SUBTYPE_POWERPC #undef CPU_SUBTYPE_POWERPC_ALL #undef CPU_SUBTYPE_POWERPC_601 #undef CPU_SUBTYPE_POWERPC_602 #undef CPU_SUBTYPE_POWERPC_603 #undef CPU_SUBTYPE_POWERPC_603e #undef CPU_SUBTYPE_POWERPC_603ev #undef CPU_SUBTYPE_POWERPC_604 #undef CPU_SUBTYPE_POWERPC_604e #undef CPU_SUBTYPE_POWERPC_620 #undef CPU_SUBTYPE_POWERPC_750 #undef CPU_SUBTYPE_POWERPC_7400 #undef CPU_SUBTYPE_POWERPC_7450 #undef CPU_SUBTYPE_POWERPC_970 #undef CPU_SUBTYPE_MC980000_ALL #undef CPU_SUBTYPE_MC98601 -#include "llvm/Support/MachO.h" +#include "llvm/BinaryFormat/MachO.h" #endif // liblldb_SafeMachO_h_ Index: vendor/lldb/dist/include/lldb/Utility/TaskPool.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/TaskPool.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Utility/TaskPool.h (revision 319790) @@ -1,91 +1,92 @@ //===--------------------- TaskPool.h ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef utility_TaskPool_h_ #define utility_TaskPool_h_ +#include "llvm/ADT/STLExtras.h" #include // for bind, function #include #include #include // for make_shared #include // for mutex, unique_lock, condition_variable #include // for forward, result_of, move // Global TaskPool class for running tasks in parallel on a set of worker thread // created the first // time the task pool is used. The TaskPool provide no guarantee about the order // the task will be run // and about what tasks will run in parallel. None of the task added to the task // pool should block // on something (mutex, future, condition variable) what will be set only by the // completion of an // other task on the task pool as they may run on the same thread sequentally. class TaskPool { public: // Add a new task to the task pool and return a std::future belonging to the // newly created task. // The caller of this function has to wait on the future for this task to // complete. template static std::future::type> AddTask(F &&f, Args &&... args); // Run all of the specified tasks on the task pool and wait until all of them // are finished // before returning. This method is intended to be used for small number tasks // where listing // them as function arguments is acceptable. For running large number of tasks // you should use // AddTask for each task and then call wait() on each returned future. template static void RunTasks(T &&... tasks); private: TaskPool() = delete; template struct RunTaskImpl; static void AddTaskImpl(std::function &&task_fn); }; template std::future::type> TaskPool::AddTask(F &&f, Args &&... args) { auto task_sp = std::make_shared< std::packaged_task::type()>>( std::bind(std::forward(f), std::forward(args)...)); AddTaskImpl([task_sp]() { (*task_sp)(); }); return task_sp->get_future(); } template void TaskPool::RunTasks(T &&... tasks) { RunTaskImpl::Run(std::forward(tasks)...); } template struct TaskPool::RunTaskImpl { static void Run(Head &&h, Tail &&... t) { auto f = AddTask(std::forward(h)); RunTaskImpl::Run(std::forward(t)...); f.wait(); } }; template <> struct TaskPool::RunTaskImpl<> { static void Run() {} }; // Run 'func' on every value from begin .. end-1. Each worker will grab // 'batch_size' numbers at a time to work on, so for very fast functions, batch // should be large enough to avoid too much cache line contention. void TaskMapOverInt(size_t begin, size_t end, - std::function const &func); + const llvm::function_ref &func); #endif // #ifndef utility_TaskPool_h_ Index: vendor/lldb/dist/include/lldb/Utility/VMRange.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/VMRange.h (revision 319789) +++ vendor/lldb/dist/include/lldb/Utility/VMRange.h (revision 319790) @@ -1,133 +1,128 @@ //===-- VMRange.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_VMRange_h_ #define liblldb_VMRange_h_ #include "lldb/lldb-types.h" // for addr_t #include // for size_t #include // for uint32_t #include namespace lldb_private { class Stream; } namespace lldb_private { //---------------------------------------------------------------------- // A vm address range. These can represent offsets ranges or actual // addresses. //---------------------------------------------------------------------- class VMRange { public: typedef std::vector collection; typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; VMRange() : m_base_addr(0), m_byte_size(0) {} VMRange(lldb::addr_t start_addr, lldb::addr_t end_addr) : m_base_addr(start_addr), m_byte_size(end_addr > start_addr ? end_addr - start_addr : 0) {} ~VMRange() {} void Clear() { m_base_addr = 0; m_byte_size = 0; } // Set the start and end values void Reset(lldb::addr_t start_addr, lldb::addr_t end_addr) { SetBaseAddress(start_addr); SetEndAddress(end_addr); } // Set the start value for the range, and keep the same size void SetBaseAddress(lldb::addr_t base_addr) { m_base_addr = base_addr; } void SetEndAddress(lldb::addr_t end_addr) { const lldb::addr_t base_addr = GetBaseAddress(); if (end_addr > base_addr) m_byte_size = end_addr - base_addr; else m_byte_size = 0; } lldb::addr_t GetByteSize() const { return m_byte_size; } void SetByteSize(lldb::addr_t byte_size) { m_byte_size = byte_size; } lldb::addr_t GetBaseAddress() const { return m_base_addr; } lldb::addr_t GetEndAddress() const { return GetBaseAddress() + m_byte_size; } bool IsValid() const { return m_byte_size > 0; } bool Contains(lldb::addr_t addr) const { return (GetBaseAddress() <= addr) && (addr < GetEndAddress()); } bool Contains(const VMRange &range) const { if (Contains(range.GetBaseAddress())) { lldb::addr_t range_end = range.GetEndAddress(); return (GetBaseAddress() <= range_end) && (range_end <= GetEndAddress()); } return false; } void Dump(Stream *s, lldb::addr_t base_addr = 0, uint32_t addr_width = 8) const; class ValueInRangeUnaryPredicate { public: ValueInRangeUnaryPredicate(lldb::addr_t value) : _value(value) {} bool operator()(const VMRange &range) const { return range.Contains(_value); } lldb::addr_t _value; }; class RangeInRangeUnaryPredicate { public: RangeInRangeUnaryPredicate(VMRange range) : _range(range) {} bool operator()(const VMRange &range) const { return range.Contains(_range); } const VMRange &_range; }; static bool ContainsValue(const VMRange::collection &coll, lldb::addr_t value); static bool ContainsRange(const VMRange::collection &coll, const VMRange &range); - // Returns a valid index into coll when a match is found, else UINT32_MAX - // is returned - static size_t FindRangeIndexThatContainsValue(const VMRange::collection &coll, - lldb::addr_t value); - protected: lldb::addr_t m_base_addr; lldb::addr_t m_byte_size; }; bool operator==(const VMRange &lhs, const VMRange &rhs); bool operator!=(const VMRange &lhs, const VMRange &rhs); bool operator<(const VMRange &lhs, const VMRange &rhs); bool operator<=(const VMRange &lhs, const VMRange &rhs); bool operator>(const VMRange &lhs, const VMRange &rhs); bool operator>=(const VMRange &lhs, const VMRange &rhs); } // namespace lldb_private #endif // liblldb_VMRange_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 319789) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 319790) @@ -1,85 +1,85 @@ """ Test that we can backtrace correctly with 'noreturn' functions on the stack """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class NoreturnUnwind(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIfWindows # clang-cl does not support gcc style attributes. - @expectedFailureAndroid(bugnumber="llvm.org/pr31192") - @expectedFailureAll(bugnumber="llvm.org/pr31192", oslist=['linux'], compiler="gcc", archs=['arm']) + # clang does not preserve LR in noreturn functions, making unwinding impossible + @skipIf(compiler="clang", archs=['arm'], oslist=['linux']) def test(self): """Test that we can backtrace correctly with 'noreturn' functions on the stack""" self.build() self.setTearDownCleanup() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) process = target.LaunchSimple( None, None, self.get_process_working_directory()) if not process: self.fail("SBTarget.Launch() failed") 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())) thread = process.GetThreadAtIndex(0) abort_frame_number = 0 for f in thread.frames: # Some C libraries mangle the abort symbol into __GI_abort. if f.GetFunctionName() in ["abort", "__GI_abort"]: break abort_frame_number = abort_frame_number + 1 if self.TraceOn(): print("Backtrace once we're stopped:") for f in thread.frames: print(" %d %s" % (f.GetFrameID(), f.GetFunctionName())) # I'm going to assume that abort() ends up calling/invoking another # function before halting the process. In which case if abort_frame_number # equals 0, we didn't find abort() in the backtrace. if abort_frame_number == len(thread.frames): self.fail("Unable to find abort() in backtrace.") func_c_frame_number = abort_frame_number + 1 if thread.GetFrameAtIndex( func_c_frame_number).GetFunctionName() != "func_c": self.fail("Did not find func_c() above abort().") # This depends on whether we see the func_b inlined function in the backtrace # or not. I'm not interested in testing that aspect of the backtrace here # right now. if thread.GetFrameAtIndex( func_c_frame_number + 1).GetFunctionName() == "func_b": func_a_frame_number = func_c_frame_number + 2 else: func_a_frame_number = func_c_frame_number + 1 if thread.GetFrameAtIndex( func_a_frame_number).GetFunctionName() != "func_a": self.fail("Did not find func_a() above func_c().") main_frame_number = func_a_frame_number + 1 if thread.GetFrameAtIndex( main_frame_number).GetFunctionName() != "main": self.fail("Did not find main() above func_a().") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c (revision 319789) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/main.c (revision 319790) @@ -1,37 +1,35 @@ #include #include #include static void func_a (void) __attribute__((noinline)); static void func_b (void) __attribute__((noreturn)); static void func_c (void) __attribute__((noinline)); static void func_c (void) { abort (); } static void func_b (void) { func_c (); while (1) ; } static void func_a (void) { func_b (); } int main (int argc, char *argv[]) { - sleep (2); - func_a (); return 0; } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/TestNoReturnModuleEnd.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/TestNoReturnModuleEnd.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/TestNoReturnModuleEnd.py (revision 319790) @@ -0,0 +1,53 @@ +""" +Test that we properly display the backtrace when a noreturn function happens to +be at the end of the stack. +""" + +from __future__ import print_function + +import shutil +import struct + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestNoreturnModuleEnd(TestBase): + NO_DEBUG_INFO_TESTCASE = True + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + super(TestNoreturnModuleEnd, self).setUp() + self._initial_platform = lldb.DBG.GetSelectedPlatform() + + def tearDown(self): + lldb.DBG.SetSelectedPlatform(self._initial_platform) + super(TestNoreturnModuleEnd, self).tearDown() + + def test(self): + target = self.dbg.CreateTarget("test.out") + process = target.LoadCore("test.core") + self.assertTrue(process.IsValid(), PROCESS_IS_VALID) + self.assertEqual(process.GetNumThreads(), 1) + + thread = process.GetSelectedThread() + self.assertTrue(thread.IsValid()) + + backtrace = [ + ["func2", 3], + ["func1", 8], + ["_start", 8], + ] + self.assertEqual(thread.GetNumFrames(), len(backtrace)) + for i in range(len(backtrace)): + frame = thread.GetFrameAtIndex(i) + self.assertTrue(frame.IsValid()) + symbol = frame.GetSymbol() + self.assertTrue(symbol.IsValid()) + self.assertEqual(symbol.GetName(), backtrace[i][0]) + function_start = symbol.GetStartAddress().GetLoadAddress(target) + self.assertEquals(function_start + backtrace[i][1], frame.GetPC()) + + self.dbg.DeleteTarget(target) Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/TestNoReturnModuleEnd.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/unwind/noreturn/module-end/a.s =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/a.s (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/a.s (revision 319790) @@ -0,0 +1,35 @@ +# compile this with: +# as a.s -o a.o --32 && ld a.o -m elf_i386 +# generate core file with: +# ulimit -s 12 && ./a.out + +.text + +.globl func2 +.type func2, @function +func2: + pushl %ebp + movl %esp, %ebp + movl 0, %eax + popl %ebp + ret +.size func2, .-func2 + +.globl _start +.type _start, @function +_start: + pushl %ebp + movl %esp, %ebp + call func1 + popl %ebp + ret +.size _start, .-_start + +.globl func1 +.type func1, @function +func1: + pushl %ebp + movl %esp, %ebp + call func2 +.size func1, .-func1 + Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/a.s ___________________________________________________________________ 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/unwind/noreturn/module-end/test.core =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/x-coredump Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/test.core ___________________________________________________________________ Added: epic ## -0,0 +1 ## +fail \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +application/x-coredump \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/test.out =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/x-executable Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/module-end/test.out ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +application/x-executable \ No newline at end of property Index: vendor/lldb/dist/source/Commands/CommandObjectRegister.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectRegister.cpp (revision 319789) +++ vendor/lldb/dist/source/Commands/CommandObjectRegister.cpp (revision 319790) @@ -1,415 +1,410 @@ //===-- CommandObjectRegister.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/ADT/STLExtras.h" - -// Project includes #include "CommandObjectRegister.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionValueArray.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Interpreter/Options.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/DataExtractor.h" +#include "llvm/Support/Errno.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // "register read" //---------------------------------------------------------------------- static OptionDefinition g_register_read_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "alternate", 'A', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display register names using the alternate register name if there is one." }, { LLDB_OPT_SET_1, false, "set", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeIndex, "Specify which register sets to dump by index." }, { LLDB_OPT_SET_2, false, "all", 'a', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Show all register sets." }, // clang-format on }; class CommandObjectRegisterRead : public CommandObjectParsed { public: CommandObjectRegisterRead(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "register read", "Dump the contents of one or more register values from the current " "frame. If no register is specified, dumps them all.", nullptr, eCommandRequiresFrame | eCommandRequiresRegContext | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_option_group(), m_format_options(eFormatDefault), m_command_options() { CommandArgumentEntry arg; CommandArgumentData register_arg; // Define the first (and only) variant of this arg. register_arg.arg_type = eArgTypeRegisterName; register_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(register_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); // Add the "--format" m_option_group.Append(&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_ALL); m_option_group.Append(&m_command_options); m_option_group.Finalize(); } ~CommandObjectRegisterRead() override = default; Options *GetOptions() override { return &m_option_group; } bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm, RegisterContext *reg_ctx, const RegisterInfo *reg_info) { if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { strm.Indent(); bool prefix_with_altname = (bool)m_command_options.alternate_name; bool prefix_with_name = !prefix_with_altname; reg_value.Dump(&strm, reg_info, prefix_with_name, prefix_with_altname, m_format_options.GetFormat(), 8); if ((reg_info->encoding == eEncodingUint) || (reg_info->encoding == eEncodingSint)) { Process *process = exe_ctx.GetProcessPtr(); if (process && reg_info->byte_size == process->GetAddressByteSize()) { addr_t reg_addr = reg_value.GetAsUInt64(LLDB_INVALID_ADDRESS); if (reg_addr != LLDB_INVALID_ADDRESS) { Address so_reg_addr; if (exe_ctx.GetTargetRef() .GetSectionLoadList() .ResolveLoadAddress(reg_addr, so_reg_addr)) { strm.PutCString(" "); so_reg_addr.Dump(&strm, exe_ctx.GetBestExecutionContextScope(), Address::DumpStyleResolvedDescription); } } } } strm.EOL(); return true; } } return false; } bool DumpRegisterSet(const ExecutionContext &exe_ctx, Stream &strm, RegisterContext *reg_ctx, size_t set_idx, bool primitive_only = false) { uint32_t unavailable_count = 0; uint32_t available_count = 0; if (!reg_ctx) return false; // thread has no registers (i.e. core files are corrupt, // incomplete crash logs...) const RegisterSet *const reg_set = reg_ctx->GetRegisterSet(set_idx); if (reg_set) { strm.Printf("%s:\n", (reg_set->name ? reg_set->name : "unknown")); strm.IndentMore(); const size_t num_registers = reg_set->num_registers; for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) { const uint32_t reg = reg_set->registers[reg_idx]; const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg); // Skip the dumping of derived register if primitive_only is true. if (primitive_only && reg_info && reg_info->value_regs) continue; if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info)) ++available_count; else ++unavailable_count; } strm.IndentLess(); if (unavailable_count) { strm.Indent(); strm.Printf("%u registers were unavailable.\n", unavailable_count); } strm.EOL(); } return available_count > 0; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Stream &strm = result.GetOutputStream(); RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); const RegisterInfo *reg_info = nullptr; if (command.GetArgumentCount() == 0) { size_t set_idx; size_t num_register_sets = 1; const size_t set_array_size = m_command_options.set_indexes.GetSize(); if (set_array_size > 0) { for (size_t i = 0; i < set_array_size; ++i) { set_idx = m_command_options.set_indexes[i]->GetUInt64Value(UINT32_MAX, nullptr); if (set_idx < reg_ctx->GetRegisterSetCount()) { if (!DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx)) { if (errno) - result.AppendErrorWithFormat("register read failed: %s\n", - strerror(errno)); + result.AppendErrorWithFormatv("register read failed: {0}\n", + llvm::sys::StrError()); else result.AppendError("unknown error while reading registers.\n"); result.SetStatus(eReturnStatusFailed); break; } } else { result.AppendErrorWithFormat( "invalid register set index: %" PRIu64 "\n", (uint64_t)set_idx); result.SetStatus(eReturnStatusFailed); break; } } } else { if (m_command_options.dump_all_sets) num_register_sets = reg_ctx->GetRegisterSetCount(); for (set_idx = 0; set_idx < num_register_sets; ++set_idx) { // When dump_all_sets option is set, dump primitive as well as derived // registers. DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx, !m_command_options.dump_all_sets.GetCurrentValue()); } } } else { if (m_command_options.dump_all_sets) { result.AppendError("the --all option can't be used when registers " "names are supplied as arguments\n"); result.SetStatus(eReturnStatusFailed); } else if (m_command_options.set_indexes.GetSize() > 0) { result.AppendError("the --set option can't be used when " "registers names are supplied as arguments\n"); result.SetStatus(eReturnStatusFailed); } else { for (auto &entry : command) { // in most LLDB commands we accept $rbx as the name for register RBX - // and here we would reject it and non-existant. we should be more // consistent towards the user and allow them to say reg read $rbx - // internally, however, we should be strict and not allow ourselves // to call our registers $rbx in our own API auto arg_str = entry.ref; arg_str.consume_front("$"); reg_info = reg_ctx->GetRegisterInfoByName(arg_str); if (reg_info) { if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info)) strm.Printf("%-12s = error: unavailable\n", reg_info->name); } else { result.AppendErrorWithFormat("Invalid register name '%s'.\n", arg_str.str().c_str()); } } } } return result.Succeeded(); } class CommandOptions : public OptionGroup { public: CommandOptions() : OptionGroup(), set_indexes(OptionValue::ConvertTypeToMask(OptionValue::eTypeUInt64)), dump_all_sets(false, false), // Initial and default values are false alternate_name(false, false) {} ~CommandOptions() override = default; llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_register_read_options); } void OptionParsingStarting(ExecutionContext *execution_context) override { set_indexes.Clear(); dump_all_sets.Clear(); alternate_name.Clear(); } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, ExecutionContext *execution_context) override { Status error; const int short_option = GetDefinitions()[option_idx].short_option; switch (short_option) { case 's': { OptionValueSP value_sp(OptionValueUInt64::Create(option_value, error)); if (value_sp) set_indexes.AppendValue(value_sp); } break; case 'a': // When we don't use OptionValue::SetValueFromCString(const char *) to // set an option value, it won't be marked as being set in the options // so we make a call to let users know the value was set via option dump_all_sets.SetCurrentValue(true); dump_all_sets.SetOptionWasSet(); break; case 'A': // When we don't use OptionValue::SetValueFromCString(const char *) to // set an option value, it won't be marked as being set in the options // so we make a call to let users know the value was set via option alternate_name.SetCurrentValue(true); dump_all_sets.SetOptionWasSet(); break; default: error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); break; } return error; } // Instance variables to hold the values for command options. OptionValueArray set_indexes; OptionValueBoolean dump_all_sets; OptionValueBoolean alternate_name; }; OptionGroupOptions m_option_group; OptionGroupFormat m_format_options; CommandOptions m_command_options; }; //---------------------------------------------------------------------- // "register write" //---------------------------------------------------------------------- class CommandObjectRegisterWrite : public CommandObjectParsed { public: CommandObjectRegisterWrite(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "register write", "Modify a single register value.", nullptr, eCommandRequiresFrame | eCommandRequiresRegContext | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData register_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. register_arg.arg_type = eArgTypeRegisterName; register_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(register_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); } ~CommandObjectRegisterWrite() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { DataExtractor reg_data; RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); if (command.GetArgumentCount() != 2) { result.AppendError( "register write takes exactly 2 arguments: "); result.SetStatus(eReturnStatusFailed); } else { auto reg_name = command[0].ref; auto value_str = command[1].ref; // in most LLDB commands we accept $rbx as the name for register RBX - and // here we would reject it and non-existant. we should be more consistent // towards the user and allow them to say reg write $rbx - internally, // however, we should be strict and not allow ourselves to call our // registers $rbx in our own API reg_name.consume_front("$"); const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); if (reg_info) { RegisterValue reg_value; Status error(reg_value.SetValueFromString(reg_info, value_str)); if (error.Success()) { if (reg_ctx->WriteRegister(reg_info, reg_value)) { // Toss all frames and anything else in the thread // after a register has been written. m_exe_ctx.GetThreadRef().Flush(); result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } } if (error.AsCString()) { result.AppendErrorWithFormat( "Failed to write register '%s' with value '%s': %s\n", reg_name.str().c_str(), value_str.str().c_str(), error.AsCString()); } else { result.AppendErrorWithFormat( "Failed to write register '%s' with value '%s'", reg_name.str().c_str(), value_str.str().c_str()); } result.SetStatus(eReturnStatusFailed); } else { result.AppendErrorWithFormat("Register not found for '%s'.\n", reg_name.str().c_str()); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } }; //---------------------------------------------------------------------- // CommandObjectRegister constructor //---------------------------------------------------------------------- CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "register", "Commands to access registers for the current " "thread and stack frame.", "register [read|write] ...") { LoadSubCommand("read", CommandObjectSP(new CommandObjectRegisterRead(interpreter))); LoadSubCommand("write", CommandObjectSP(new CommandObjectRegisterWrite(interpreter))); } CommandObjectRegister::~CommandObjectRegister() = default; Index: vendor/lldb/dist/source/Core/Address.cpp =================================================================== --- vendor/lldb/dist/source/Core/Address.cpp (revision 319789) +++ vendor/lldb/dist/source/Core/Address.cpp (revision 319790) @@ -1,1011 +1,1013 @@ //===-- Address.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/Address.h" #include "lldb/Core/ArchSpec.h" // for ArchSpec #include "lldb/Core/DumpDataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" // for ModuleList #include "lldb/Core/Section.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Declaration.h" // for Declaration #include "lldb/Symbol/LineEntry.h" // for LineEntry #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" // for Symbol #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Symtab.h" // for Symtab #include "lldb/Symbol/Type.h" // for Type #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" // for ExecutionContextScope #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ConstString.h" // for ConstString #include "lldb/Utility/DataExtractor.h" // for DataExtractor #include "lldb/Utility/Endian.h" // for InlHostByteOrder #include "lldb/Utility/FileSpec.h" // for FileSpec #include "lldb/Utility/Status.h" // for Status #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StreamString.h" // for StreamString #include "llvm/ADT/StringRef.h" // for StringRef #include "llvm/ADT/Triple.h" #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include // for uint8_t, uint32_t #include // for shared_ptr, operator!= #include // for vector #include // for assert #include // for PRIu64, PRIx64 #include // for size_t, strlen namespace lldb_private { class CompileUnit; } namespace lldb_private { class Function; } using namespace lldb; using namespace lldb_private; static size_t ReadBytes(ExecutionContextScope *exe_scope, const Address &address, void *dst, size_t dst_len) { if (exe_scope == nullptr) return 0; TargetSP target_sp(exe_scope->CalculateTarget()); if (target_sp) { Status error; bool prefer_file_cache = false; return target_sp->ReadMemory(address, prefer_file_cache, dst, dst_len, error); } return 0; } static bool GetByteOrderAndAddressSize(ExecutionContextScope *exe_scope, const Address &address, ByteOrder &byte_order, uint32_t &addr_size) { byte_order = eByteOrderInvalid; addr_size = 0; if (exe_scope == nullptr) return false; TargetSP target_sp(exe_scope->CalculateTarget()); if (target_sp) { byte_order = target_sp->GetArchitecture().GetByteOrder(); addr_size = target_sp->GetArchitecture().GetAddressByteSize(); } if (byte_order == eByteOrderInvalid || addr_size == 0) { ModuleSP module_sp(address.GetModule()); if (module_sp) { byte_order = module_sp->GetArchitecture().GetByteOrder(); addr_size = module_sp->GetArchitecture().GetAddressByteSize(); } } return byte_order != eByteOrderInvalid && addr_size != 0; } static uint64_t ReadUIntMax64(ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, bool &success) { uint64_t uval64 = 0; if (exe_scope == nullptr || byte_size > sizeof(uint64_t)) { success = false; return 0; } uint64_t buf = 0; success = ReadBytes(exe_scope, address, &buf, byte_size) == byte_size; if (success) { ByteOrder byte_order = eByteOrderInvalid; uint32_t addr_size = 0; if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) { DataExtractor data(&buf, sizeof(buf), byte_order, addr_size); lldb::offset_t offset = 0; uval64 = data.GetU64(&offset); } else success = false; } return uval64; } static bool ReadAddress(ExecutionContextScope *exe_scope, const Address &address, uint32_t pointer_size, Address &deref_so_addr) { if (exe_scope == nullptr) return false; bool success = false; addr_t deref_addr = ReadUIntMax64(exe_scope, address, pointer_size, success); if (success) { ExecutionContext exe_ctx; exe_scope->CalculateExecutionContext(exe_ctx); // If we have any sections that are loaded, try and resolve using the // section load list Target *target = exe_ctx.GetTargetPtr(); if (target && !target->GetSectionLoadList().IsEmpty()) { if (target->GetSectionLoadList().ResolveLoadAddress(deref_addr, deref_so_addr)) return true; } else { // If we were not running, yet able to read an integer, we must // have a module ModuleSP module_sp(address.GetModule()); assert(module_sp); if (module_sp->ResolveFileAddress(deref_addr, deref_so_addr)) return true; } // We couldn't make "deref_addr" into a section offset value, but we were // able to read the address, so we return a section offset address with // no section and "deref_addr" as the offset (address). deref_so_addr.SetRawAddress(deref_addr); return true; } return false; } static bool DumpUInt(ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, Stream *strm) { if (exe_scope == nullptr || byte_size == 0) return 0; std::vector buf(byte_size, 0); if (ReadBytes(exe_scope, address, &buf[0], buf.size()) == buf.size()) { ByteOrder byte_order = eByteOrderInvalid; uint32_t addr_size = 0; if (GetByteOrderAndAddressSize(exe_scope, address, byte_order, addr_size)) { DataExtractor data(&buf.front(), buf.size(), byte_order, addr_size); DumpDataExtractor(data, strm, 0, // Start offset in "data" eFormatHex, // Print as characters buf.size(), // Size of item 1, // Items count UINT32_MAX, // num per line LLDB_INVALID_ADDRESS, // base address 0, // bitfield bit size 0); // bitfield bit offset return true; } } return false; } static size_t ReadCStringFromMemory(ExecutionContextScope *exe_scope, const Address &address, Stream *strm) { if (exe_scope == nullptr) return 0; const size_t k_buf_len = 256; char buf[k_buf_len + 1]; buf[k_buf_len] = '\0'; // NULL terminate // Byte order and address size don't matter for C string dumping.. DataExtractor data(buf, sizeof(buf), endian::InlHostByteOrder(), 4); size_t total_len = 0; size_t bytes_read; Address curr_address(address); strm->PutChar('"'); while ((bytes_read = ReadBytes(exe_scope, curr_address, buf, k_buf_len)) > 0) { size_t len = strlen(buf); if (len == 0) break; if (len > bytes_read) len = bytes_read; DumpDataExtractor(data, strm, 0, // Start offset in "data" eFormatChar, // Print as characters 1, // Size of item (1 byte for a char!) len, // How many bytes to print? UINT32_MAX, // num per line LLDB_INVALID_ADDRESS, // base address 0, // bitfield bit size 0); // bitfield bit offset total_len += bytes_read; if (len < k_buf_len) break; curr_address.SetOffset(curr_address.GetOffset() + bytes_read); } strm->PutChar('"'); return total_len; } Address::Address(lldb::addr_t abs_addr) : m_section_wp(), m_offset(abs_addr) {} Address::Address(addr_t address, const SectionList *section_list) : m_section_wp(), m_offset(LLDB_INVALID_ADDRESS) { ResolveAddressUsingFileSections(address, section_list); } const Address &Address::operator=(const Address &rhs) { if (this != &rhs) { m_section_wp = rhs.m_section_wp; m_offset = rhs.m_offset; } return *this; } bool Address::ResolveAddressUsingFileSections(addr_t file_addr, const SectionList *section_list) { if (section_list) { SectionSP section_sp( section_list->FindSectionContainingFileAddress(file_addr)); m_section_wp = section_sp; if (section_sp) { assert(section_sp->ContainsFileAddress(file_addr)); m_offset = file_addr - section_sp->GetFileAddress(); return true; // Successfully transformed addr into a section offset // address } } m_offset = file_addr; return false; // Failed to resolve this address to a section offset value } ModuleSP Address::GetModule() const { lldb::ModuleSP module_sp; SectionSP section_sp(GetSection()); if (section_sp) module_sp = section_sp->GetModule(); return module_sp; } addr_t Address::GetFileAddress() const { SectionSP section_sp(GetSection()); if (section_sp) { addr_t sect_file_addr = section_sp->GetFileAddress(); if (sect_file_addr == LLDB_INVALID_ADDRESS) { // Section isn't resolved, we can't return a valid file address return LLDB_INVALID_ADDRESS; } // We have a valid file range, so we can return the file based // address by adding the file base address to our offset return sect_file_addr + m_offset; } else if (SectionWasDeletedPrivate()) { // Used to have a valid section but it got deleted so the // offset doesn't mean anything without the section return LLDB_INVALID_ADDRESS; } // No section, we just return the offset since it is the value in this case return m_offset; } addr_t Address::GetLoadAddress(Target *target) const { SectionSP section_sp(GetSection()); if (section_sp) { if (target) { addr_t sect_load_addr = section_sp->GetLoadBaseAddress(target); if (sect_load_addr != LLDB_INVALID_ADDRESS) { // We have a valid file range, so we can return the file based // address by adding the file base address to our offset return sect_load_addr + m_offset; } } } else if (SectionWasDeletedPrivate()) { // Used to have a valid section but it got deleted so the // offset doesn't mean anything without the section return LLDB_INVALID_ADDRESS; } else { // We don't have a section so the offset is the load address return m_offset; } // The section isn't resolved or an invalid target was passed in // so we can't return a valid load address. return LLDB_INVALID_ADDRESS; } addr_t Address::GetCallableLoadAddress(Target *target, bool is_indirect) const { addr_t code_addr = LLDB_INVALID_ADDRESS; if (is_indirect && target) { ProcessSP processSP = target->GetProcessSP(); Status error; if (processSP) { code_addr = processSP->ResolveIndirectFunction(this, error); if (!error.Success()) code_addr = LLDB_INVALID_ADDRESS; } } else { code_addr = GetLoadAddress(target); } if (code_addr == LLDB_INVALID_ADDRESS) return code_addr; if (target) return target->GetCallableLoadAddress(code_addr, GetAddressClass()); return code_addr; } bool Address::SetCallableLoadAddress(lldb::addr_t load_addr, Target *target) { if (SetLoadAddress(load_addr, target)) { if (target) m_offset = target->GetCallableLoadAddress(m_offset, GetAddressClass()); return true; } return false; } addr_t Address::GetOpcodeLoadAddress(Target *target, AddressClass addr_class) const { addr_t code_addr = GetLoadAddress(target); if (code_addr != LLDB_INVALID_ADDRESS) { if (addr_class == eAddressClassInvalid) addr_class = GetAddressClass(); code_addr = target->GetOpcodeLoadAddress(code_addr, addr_class); } return code_addr; } bool Address::SetOpcodeLoadAddress(lldb::addr_t load_addr, Target *target, - AddressClass addr_class) { - if (SetLoadAddress(load_addr, target)) { + AddressClass addr_class, + bool allow_section_end) { + if (SetLoadAddress(load_addr, target, allow_section_end)) { if (target) { if (addr_class == eAddressClassInvalid) addr_class = GetAddressClass(); m_offset = target->GetOpcodeLoadAddress(m_offset, addr_class); } return true; } return false; } bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style, uint32_t addr_size) const { // If the section was nullptr, only load address is going to work unless we // are // trying to deref a pointer SectionSP section_sp(GetSection()); if (!section_sp && style != DumpStyleResolvedPointerDescription) style = DumpStyleLoadAddress; ExecutionContext exe_ctx(exe_scope); Target *target = exe_ctx.GetTargetPtr(); // If addr_byte_size is UINT32_MAX, then determine the correct address // byte size for the process or default to the size of addr_t if (addr_size == UINT32_MAX) { if (target) addr_size = target->GetArchitecture().GetAddressByteSize(); else addr_size = sizeof(addr_t); } Address so_addr; switch (style) { case DumpStyleInvalid: return false; case DumpStyleSectionNameOffset: if (section_sp) { section_sp->DumpName(s); s->Printf(" + %" PRIu64, m_offset); } else { s->Address(m_offset, addr_size); } break; case DumpStyleSectionPointerOffset: s->Printf("(Section *)%p + ", static_cast(section_sp.get())); s->Address(m_offset, addr_size); break; case DumpStyleModuleWithFileAddress: if (section_sp) { ModuleSP module_sp = section_sp->GetModule(); if (module_sp) s->Printf("%s[", module_sp->GetFileSpec().GetFilename().AsCString( "")); else s->Printf("%s[", ""); } LLVM_FALLTHROUGH; case DumpStyleFileAddress: { addr_t file_addr = GetFileAddress(); if (file_addr == LLDB_INVALID_ADDRESS) { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } s->Address(file_addr, addr_size); if (style == DumpStyleModuleWithFileAddress && section_sp) s->PutChar(']'); } break; case DumpStyleLoadAddress: { addr_t load_addr = GetLoadAddress(target); /* * MIPS: * Display address in compressed form for MIPS16 or microMIPS * if the address belongs to eAddressClassCodeAlternateISA. */ if (target) { const llvm::Triple::ArchType llvm_arch = target->GetArchitecture().GetMachine(); if (llvm_arch == llvm::Triple::mips || llvm_arch == llvm::Triple::mipsel || llvm_arch == llvm::Triple::mips64 || llvm_arch == llvm::Triple::mips64el) load_addr = GetCallableLoadAddress(target); } if (load_addr == LLDB_INVALID_ADDRESS) { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } s->Address(load_addr, addr_size); } break; case DumpStyleResolvedDescription: case DumpStyleResolvedDescriptionNoModule: case DumpStyleResolvedDescriptionNoFunctionArguments: case DumpStyleNoFunctionName: if (IsSectionOffset()) { uint32_t pointer_size = 4; ModuleSP module_sp(GetModule()); if (target) pointer_size = target->GetArchitecture().GetAddressByteSize(); else if (module_sp) pointer_size = module_sp->GetArchitecture().GetAddressByteSize(); bool showed_info = false; if (section_sp) { SectionType sect_type = section_sp->GetType(); switch (sect_type) { case eSectionTypeData: if (module_sp) { SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { const addr_t file_Addr = GetFileAddress(); Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_Addr); if (symbol) { const char *symbol_name = symbol->GetName().AsCString(); if (symbol_name) { s->PutCString(symbol_name); addr_t delta = file_Addr - symbol->GetAddressRef().GetFileAddress(); if (delta) s->Printf(" + %" PRIu64, delta); showed_info = true; } } } } } break; case eSectionTypeDataCString: // Read the C string from memory and display it showed_info = true; ReadCStringFromMemory(exe_scope, *this, s); break; case eSectionTypeDataCStringPointers: if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("(char *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(": "); #endif showed_info = true; ReadCStringFromMemory(exe_scope, so_addr, s); } break; case eSectionTypeDataObjCMessageRefs: if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { if (target && so_addr.IsSectionOffset()) { SymbolContext func_sc; target->GetImages().ResolveSymbolContextForAddress( so_addr, eSymbolContextEverything, func_sc); if (func_sc.function != nullptr || func_sc.symbol != nullptr) { showed_info = true; #if VERBOSE_OUTPUT s->PutCString("(objc_msgref *) -> { (func*)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); #else s->PutCString("{ "); #endif Address cstr_addr(*this); cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true, true); if (ReadAddress(exe_scope, cstr_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("), (char *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(" ("); #else s->PutCString(", "); #endif ReadCStringFromMemory(exe_scope, so_addr, s); } #if VERBOSE_OUTPUT s->PutCString(") }"); #else s->PutCString(" }"); #endif } } } break; case eSectionTypeDataObjCCFStrings: { Address cfstring_data_addr(*this); cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); if (ReadAddress(exe_scope, cfstring_data_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT s->PutCString("(CFString *) "); cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); s->PutCString(" -> @"); #else s->PutChar('@'); #endif if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) showed_info = true; } } break; case eSectionTypeData4: // Read the 4 byte data and display it showed_info = true; s->PutCString("(uint32_t) "); DumpUInt(exe_scope, *this, 4, s); break; case eSectionTypeData8: // Read the 8 byte data and display it showed_info = true; s->PutCString("(uint64_t) "); DumpUInt(exe_scope, *this, 8, s); break; case eSectionTypeData16: // Read the 16 byte data and display it showed_info = true; s->PutCString("(uint128_t) "); DumpUInt(exe_scope, *this, 16, s); break; case eSectionTypeDataPointers: // Read the pointer data and display it if (ReadAddress(exe_scope, *this, pointer_size, so_addr)) { s->PutCString("(void *)"); so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); showed_info = true; if (so_addr.IsSectionOffset()) { SymbolContext pointer_sc; if (target) { target->GetImages().ResolveSymbolContextForAddress( so_addr, eSymbolContextEverything, pointer_sc); if (pointer_sc.function != nullptr || pointer_sc.symbol != nullptr) { s->PutCString(": "); pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true, true); } } } } break; default: break; } } if (!showed_info) { if (module_sp) { SymbolContext sc; module_sp->ResolveSymbolContextForAddress( *this, eSymbolContextEverything, sc); if (sc.function || sc.symbol) { bool show_stop_context = true; const bool show_module = (style == DumpStyleResolvedDescription); const bool show_fullpaths = false; const bool show_inlined_frames = true; const bool show_function_arguments = (style != DumpStyleResolvedDescriptionNoFunctionArguments); const bool show_function_name = (style != DumpStyleNoFunctionName); if (sc.function == nullptr && sc.symbol != nullptr) { // If we have just a symbol make sure it is in the right section if (sc.symbol->ValueIsAddress()) { if (sc.symbol->GetAddressRef().GetSection() != GetSection()) { // don't show the module if the symbol is a trampoline symbol show_stop_context = false; } } } if (show_stop_context) { // We have a function or a symbol from the same // sections as this address. sc.DumpStopContext(s, exe_scope, *this, show_fullpaths, show_module, show_inlined_frames, show_function_arguments, show_function_name); } else { // We found a symbol but it was in a different // section so it isn't the symbol we should be // showing, just show the section name + offset Dump(s, exe_scope, DumpStyleSectionNameOffset); } } } } } else { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; case DumpStyleDetailedSymbolContext: if (IsSectionOffset()) { ModuleSP module_sp(GetModule()); if (module_sp) { SymbolContext sc; module_sp->ResolveSymbolContextForAddress( *this, eSymbolContextEverything | eSymbolContextVariable, sc); if (sc.symbol) { // If we have just a symbol make sure it is in the same section // as our address. If it isn't, then we might have just found // the last symbol that came before the address that we are // looking up that has nothing to do with our address lookup. if (sc.symbol->ValueIsAddress() && sc.symbol->GetAddressRef().GetSection() != GetSection()) sc.symbol = nullptr; } sc.GetDescription(s, eDescriptionLevelBrief, target); if (sc.block) { bool can_create = true; bool get_parent_variables = true; bool stop_if_block_is_inlined_function = false; VariableList variable_list; sc.block->AppendVariables(can_create, get_parent_variables, stop_if_block_is_inlined_function, [](Variable *) { return true; }, &variable_list); const size_t num_variables = variable_list.GetSize(); for (size_t var_idx = 0; var_idx < num_variables; ++var_idx) { Variable *var = variable_list.GetVariableAtIndex(var_idx).get(); if (var && var->LocationIsValidForAddress(*this)) { s->Indent(); s->Printf(" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"", var->GetID(), var->GetName().GetCString()); Type *type = var->GetType(); if (type) s->Printf(", type = \"%s\"", type->GetName().GetCString()); else s->PutCString(", type = "); s->PutCString(", location = "); var->DumpLocationForAddress(s, *this); s->PutCString(", decl = "); var->GetDeclaration().DumpStopContext(s, false); s->EOL(); } } } } } else { if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; case DumpStyleResolvedPointerDescription: { Process *process = exe_ctx.GetProcessPtr(); if (process) { addr_t load_addr = GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { Status memory_error; addr_t dereferenced_load_addr = process->ReadPointerFromMemory(load_addr, memory_error); if (dereferenced_load_addr != LLDB_INVALID_ADDRESS) { Address dereferenced_addr; if (dereferenced_addr.SetLoadAddress(dereferenced_load_addr, target)) { StreamString strm; if (dereferenced_addr.Dump(&strm, exe_scope, DumpStyleResolvedDescription, DumpStyleInvalid, addr_size)) { s->Address(dereferenced_load_addr, addr_size, " -> ", " "); s->Write(strm.GetString().data(), strm.GetSize()); return true; } } } } } if (fallback_style != DumpStyleInvalid) return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); return false; } break; } return true; } bool Address::SectionWasDeleted() const { if (GetSection()) return false; return SectionWasDeletedPrivate(); } bool Address::SectionWasDeletedPrivate() const { lldb::SectionWP empty_section_wp; // If either call to "std::weak_ptr::owner_before(...) value returns true, // this // indicates that m_section_wp once contained (possibly still does) a // reference // to a valid shared pointer. This helps us know if we had a valid reference // to // a section which is now invalid because the module it was in was // unloaded/deleted, // or if the address doesn't have a valid reference to a section. return empty_section_wp.owner_before(m_section_wp) || m_section_wp.owner_before(empty_section_wp); } uint32_t Address::CalculateSymbolContext(SymbolContext *sc, uint32_t resolve_scope) const { sc->Clear(false); // Absolute addresses don't have enough information to reconstruct even their // target. SectionSP section_sp(GetSection()); if (section_sp) { ModuleSP module_sp(section_sp->GetModule()); if (module_sp) { sc->module_sp = module_sp; if (sc->module_sp) return sc->module_sp->ResolveSymbolContextForAddress( *this, resolve_scope, *sc); } } return 0; } ModuleSP Address::CalculateSymbolContextModule() const { SectionSP section_sp(GetSection()); if (section_sp) return section_sp->GetModule(); return ModuleSP(); } CompileUnit *Address::CalculateSymbolContextCompileUnit() const { SectionSP section_sp(GetSection()); if (section_sp) { SymbolContext sc; sc.module_sp = section_sp->GetModule(); if (sc.module_sp) { sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextCompUnit, sc); return sc.comp_unit; } } return nullptr; } Function *Address::CalculateSymbolContextFunction() const { SectionSP section_sp(GetSection()); if (section_sp) { SymbolContext sc; sc.module_sp = section_sp->GetModule(); if (sc.module_sp) { sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextFunction, sc); return sc.function; } } return nullptr; } Block *Address::CalculateSymbolContextBlock() const { SectionSP section_sp(GetSection()); if (section_sp) { SymbolContext sc; sc.module_sp = section_sp->GetModule(); if (sc.module_sp) { sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextBlock, sc); return sc.block; } } return nullptr; } Symbol *Address::CalculateSymbolContextSymbol() const { SectionSP section_sp(GetSection()); if (section_sp) { SymbolContext sc; sc.module_sp = section_sp->GetModule(); if (sc.module_sp) { sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextSymbol, sc); return sc.symbol; } } return nullptr; } bool Address::CalculateSymbolContextLineEntry(LineEntry &line_entry) const { SectionSP section_sp(GetSection()); if (section_sp) { SymbolContext sc; sc.module_sp = section_sp->GetModule(); if (sc.module_sp) { sc.module_sp->ResolveSymbolContextForAddress(*this, eSymbolContextLineEntry, sc); if (sc.line_entry.IsValid()) { line_entry = sc.line_entry; return true; } } } line_entry.Clear(); return false; } int Address::CompareFileAddress(const Address &a, const Address &b) { addr_t a_file_addr = a.GetFileAddress(); addr_t b_file_addr = b.GetFileAddress(); if (a_file_addr < b_file_addr) return -1; if (a_file_addr > b_file_addr) return +1; return 0; } int Address::CompareLoadAddress(const Address &a, const Address &b, Target *target) { assert(target != nullptr); addr_t a_load_addr = a.GetLoadAddress(target); addr_t b_load_addr = b.GetLoadAddress(target); if (a_load_addr < b_load_addr) return -1; if (a_load_addr > b_load_addr) return +1; return 0; } int Address::CompareModulePointerAndOffset(const Address &a, const Address &b) { ModuleSP a_module_sp(a.GetModule()); ModuleSP b_module_sp(b.GetModule()); Module *a_module = a_module_sp.get(); Module *b_module = b_module_sp.get(); if (a_module < b_module) return -1; if (a_module > b_module) return +1; // Modules are the same, just compare the file address since they should // be unique addr_t a_file_addr = a.GetFileAddress(); addr_t b_file_addr = b.GetFileAddress(); if (a_file_addr < b_file_addr) return -1; if (a_file_addr > b_file_addr) return +1; return 0; } size_t Address::MemorySize() const { // Noting special for the memory size of a single Address object, // it is just the size of itself. return sizeof(Address); } //---------------------------------------------------------------------- // NOTE: Be careful using this operator. It can correctly compare two // addresses from the same Module correctly. It can't compare two // addresses from different modules in any meaningful way, but it will // compare the module pointers. // // To sum things up: // - works great for addresses within the same module // - it works for addresses across multiple modules, but don't expect the // address results to make much sense // // This basically lets Address objects be used in ordered collection // classes. //---------------------------------------------------------------------- bool lldb_private::operator<(const Address &lhs, const Address &rhs) { ModuleSP lhs_module_sp(lhs.GetModule()); ModuleSP rhs_module_sp(rhs.GetModule()); Module *lhs_module = lhs_module_sp.get(); Module *rhs_module = rhs_module_sp.get(); if (lhs_module == rhs_module) { // Addresses are in the same module, just compare the file addresses return lhs.GetFileAddress() < rhs.GetFileAddress(); } else { // The addresses are from different modules, just use the module // pointer value to get consistent ordering return lhs_module < rhs_module; } } bool lldb_private::operator>(const Address &lhs, const Address &rhs) { ModuleSP lhs_module_sp(lhs.GetModule()); ModuleSP rhs_module_sp(rhs.GetModule()); Module *lhs_module = lhs_module_sp.get(); Module *rhs_module = rhs_module_sp.get(); if (lhs_module == rhs_module) { // Addresses are in the same module, just compare the file addresses return lhs.GetFileAddress() > rhs.GetFileAddress(); } else { // The addresses are from different modules, just use the module // pointer value to get consistent ordering return lhs_module > rhs_module; } } // The operator == checks for exact equality only (same section, same offset) bool lldb_private::operator==(const Address &a, const Address &rhs) { return a.GetOffset() == rhs.GetOffset() && a.GetSection() == rhs.GetSection(); } // The operator != checks for exact inequality only (differing section, or // different offset) bool lldb_private::operator!=(const Address &a, const Address &rhs) { return a.GetOffset() != rhs.GetOffset() || a.GetSection() != rhs.GetSection(); } AddressClass Address::GetAddressClass() const { ModuleSP module_sp(GetModule()); if (module_sp) { ObjectFile *obj_file = module_sp->GetObjectFile(); if (obj_file) { // Give the symbol vendor a chance to add to the unified section list. module_sp->GetSymbolVendor(); return obj_file->GetAddressClass(GetFileAddress()); } } return eAddressClassUnknown; } -bool Address::SetLoadAddress(lldb::addr_t load_addr, Target *target) { - if (target && - target->GetSectionLoadList().ResolveLoadAddress(load_addr, *this)) +bool Address::SetLoadAddress(lldb::addr_t load_addr, Target *target, + bool allow_section_end) { + if (target && target->GetSectionLoadList().ResolveLoadAddress( + load_addr, *this, allow_section_end)) return true; m_section_wp.reset(); m_offset = load_addr; return false; } Index: vendor/lldb/dist/source/Core/ArchSpec.cpp =================================================================== --- vendor/lldb/dist/source/Core/ArchSpec.cpp (revision 319789) +++ vendor/lldb/dist/source/Core/ArchSpec.cpp (revision 319790) @@ -1,1672 +1,1672 @@ //===-- ArchSpec.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/ArchSpec.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Platform.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StringList.h" #include "lldb/lldb-defines.h" // for LLDB_INVALID_C... #include "lldb/lldb-forward.h" // for RegisterContextSP #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/InstructionUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" // for Twine -#include "llvm/Support/COFF.h" -#include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH -#include "llvm/Support/ELF.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/BinaryFormat/MachO.h" // for CPUType::CPU_T... +#include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include "llvm/Support/Host.h" -#include "llvm/Support/MachO.h" // for CPUType::CPU_T... #include // for shared_ptr #include #include // for tie, tuple using namespace lldb; using namespace lldb_private; static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match); namespace lldb_private { struct CoreDefinition { ByteOrder default_byte_order; uint32_t addr_byte_size; uint32_t min_opcode_byte_size; uint32_t max_opcode_byte_size; llvm::Triple::ArchType machine; ArchSpec::Core core; const char *const name; }; } // namespace lldb_private // This core information can be looked using the ArchSpec::Core as the index static const CoreDefinition g_core_definitions[] = { {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_generic, "arm"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4, "armv4"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4t, "armv4t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5, "armv5"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5e, "armv5e"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5t, "armv5t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6, "armv6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6m, "armv6m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7, "armv7"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7f, "armv7f"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7s, "armv7s"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7k, "armv7k"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7m, "armv7m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7em, "armv7em"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_xscale, "xscale"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumb, "thumb"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv4t, "thumbv4t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5, "thumbv5"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5e, "thumbv5e"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6, "thumbv6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6m, "thumbv6m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7, "thumbv7"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7f, "thumbv7f"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7s, "thumbv7s"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7k, "thumbv7k"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7m, "thumbv7m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7em, "thumbv7em"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_arm64, "arm64"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_armv8, "armv8"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_aarch64, "aarch64"}, // mips32, mips32r2, mips32r3, mips32r5, mips32r6 {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32, "mips"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r2, "mipsr2"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r3, "mipsr3"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r5, "mipsr5"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r6, "mipsr6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el, "mipsel"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r2el, "mipsr2el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r3el, "mipsr3el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r5el, "mipsr5el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r6el, "mipsr6el"}, // mips64, mips64r2, mips64r3, mips64r5, mips64r6 {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64, "mips64"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r2, "mips64r2"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r3, "mips64r3"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r5, "mips64r5"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r6, "mips64r6"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64el, "mips64el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r2el, "mips64r2el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r3el, "mips64r3el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r5el, "mips64r5el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r6el, "mips64r6el"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_generic, "powerpc"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc601, "ppc601"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc602, "ppc602"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603, "ppc603"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603e, "ppc603e"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603ev, "ppc603ev"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604, "ppc604"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604e, "ppc604e"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc620, "ppc620"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc750, "ppc750"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7400, "ppc7400"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7450, "ppc7450"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc970, "ppc970"}, {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_generic, "powerpc64"}, {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_ppc970_64, "ppc970-64"}, {eByteOrderBig, 8, 2, 6, llvm::Triple::systemz, ArchSpec::eCore_s390x_generic, "s390x"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc, ArchSpec::eCore_sparc_generic, "sparc"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, ArchSpec::eCore_sparc9_generic, "sparcv9"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i386, "i386"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486, "i486"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486sx, "i486sx"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i686, "i686"}, {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, ArchSpec::eCore_x86_64_x86_64, "x86_64"}, {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, ArchSpec::eCore_x86_64_x86_64h, "x86_64h"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_generic, "hexagon"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_hexagonv4, "hexagonv4"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_hexagonv5, "hexagonv5"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach32, "unknown-mach-32"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach64, "unknown-mach-64"}, {eByteOrderBig, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba3, "kalimba3"}, {eByteOrderLittle, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba4, "kalimba4"}, {eByteOrderLittle, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba5, "kalimba5"}}; // Ensure that we have an entry in the g_core_definitions for each core. If you // comment out an entry above, // you will need to comment out the corresponding ArchSpec::Core enumeration. static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) == ArchSpec::kNumCores, "make sure we have one core definition for each core"); struct ArchDefinitionEntry { ArchSpec::Core core; uint32_t cpu; uint32_t sub; uint32_t cpu_mask; uint32_t sub_mask; }; struct ArchDefinition { ArchitectureType type; size_t num_entries; const ArchDefinitionEntry *entries; const char *name; }; size_t ArchSpec::AutoComplete(llvm::StringRef name, StringList &matches) { if (!name.empty()) { for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { if (NameMatches(g_core_definitions[i].name, NameMatch::StartsWith, name)) matches.AppendString(g_core_definitions[i].name); } } else { for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) matches.AppendString(g_core_definitions[i].name); } return matches.GetSize(); } #define CPU_ANY (UINT32_MAX) //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. #define SUBTYPE_MASK 0x00FFFFFFu static const ArchDefinitionEntry g_macho_arch_entries[] = { {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv4, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5t, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_xscale, llvm::MachO::CPU_TYPE_ARM, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 1, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 13, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumb, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc601, llvm::MachO::CPU_TYPE_POWERPC, 1, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc602, llvm::MachO::CPU_TYPE_POWERPC, 2, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603, llvm::MachO::CPU_TYPE_POWERPC, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603e, llvm::MachO::CPU_TYPE_POWERPC, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603ev, llvm::MachO::CPU_TYPE_POWERPC, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc604, llvm::MachO::CPU_TYPE_POWERPC, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc604e, llvm::MachO::CPU_TYPE_POWERPC, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc620, llvm::MachO::CPU_TYPE_POWERPC, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc750, llvm::MachO::CPU_TYPE_POWERPC, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc7400, llvm::MachO::CPU_TYPE_POWERPC, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc7450, llvm::MachO::CPU_TYPE_POWERPC, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc970, llvm::MachO::CPU_TYPE_POWERPC, 100, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc64_generic, llvm::MachO::CPU_TYPE_POWERPC64, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc64_ppc970_64, llvm::MachO::CPU_TYPE_POWERPC64, 100, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i486, llvm::MachO::CPU_TYPE_I386, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i486sx, llvm::MachO::CPU_TYPE_I386, 0x84, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64h, llvm::MachO::CPU_TYPE_X86_64, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, CPU_ANY, UINT32_MAX, UINT32_MAX}, // Catch any unknown mach architectures so we can always use the object and // symbol mach-o files {ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u}, {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}}; static const ArchDefinition g_macho_arch_def = { eArchTypeMachO, llvm::array_lengthof(g_macho_arch_entries), g_macho_arch_entries, "mach-o"}; //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. static const ArchDefinitionEntry g_elf_arch_entries[] = { {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386 {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct? {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64 {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64 {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9 {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32 {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2 {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6 {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64 {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2 {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6 {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON {ArchSpec::eCore_kalimba3, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v3, 0xFFFFFFFFu, 0xFFFFFFFFu}, // KALIMBA {ArchSpec::eCore_kalimba4, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v4, 0xFFFFFFFFu, 0xFFFFFFFFu}, // KALIMBA {ArchSpec::eCore_kalimba5, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v5, 0xFFFFFFFFu, 0xFFFFFFFFu} // KALIMBA }; static const ArchDefinition g_elf_arch_def = { eArchTypeELF, llvm::array_lengthof(g_elf_arch_entries), g_elf_arch_entries, "elf", }; static const ArchDefinitionEntry g_coff_arch_entries[] = { {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86 {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU) {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // AMD64 }; static const ArchDefinition g_coff_arch_def = { eArchTypeCOFF, llvm::array_lengthof(g_coff_arch_entries), g_coff_arch_entries, "pe-coff", }; //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def}; static const size_t k_num_arch_definitions = llvm::array_lengthof(g_arch_definitions); //===----------------------------------------------------------------------===// // Static helper functions. // Get the architecture definition for a given object type. static const ArchDefinition *FindArchDefinition(ArchitectureType arch_type) { for (unsigned int i = 0; i < k_num_arch_definitions; ++i) { const ArchDefinition *def = g_arch_definitions[i]; if (def->type == arch_type) return def; } return nullptr; } // Get an architecture definition by name. static const CoreDefinition *FindCoreDefinition(llvm::StringRef name) { for (unsigned int i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { if (name.equals_lower(g_core_definitions[i].name)) return &g_core_definitions[i]; } return nullptr; } static inline const CoreDefinition *FindCoreDefinition(ArchSpec::Core core) { if (core >= 0 && core < llvm::array_lengthof(g_core_definitions)) return &g_core_definitions[core]; return nullptr; } // Get a definition entry by cpu type and subtype. static const ArchDefinitionEntry * FindArchDefinitionEntry(const ArchDefinition *def, uint32_t cpu, uint32_t sub) { if (def == nullptr) return nullptr; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].cpu == (cpu & entries[i].cpu_mask)) if (entries[i].sub == (sub & entries[i].sub_mask)) return &entries[i]; } return nullptr; } static const ArchDefinitionEntry * FindArchDefinitionEntry(const ArchDefinition *def, ArchSpec::Core core) { if (def == nullptr) return nullptr; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].core == core) return &entries[i]; } return nullptr; } //===----------------------------------------------------------------------===// // Constructors and destructors. ArchSpec::ArchSpec() {} ArchSpec::ArchSpec(const char *triple_cstr, Platform *platform) { if (triple_cstr) SetTriple(triple_cstr, platform); } ArchSpec::ArchSpec(llvm::StringRef triple_str, Platform *platform) { SetTriple(triple_str, platform); } ArchSpec::ArchSpec(const char *triple_cstr) { if (triple_cstr) SetTriple(triple_cstr); } ArchSpec::ArchSpec(llvm::StringRef triple_str) { SetTriple(triple_str); } ArchSpec::ArchSpec(const llvm::Triple &triple) { SetTriple(triple); } ArchSpec::ArchSpec(ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) { SetArchitecture(arch_type, cpu, subtype); } ArchSpec::~ArchSpec() = default; //===----------------------------------------------------------------------===// // Assignment and initialization. const ArchSpec &ArchSpec::operator=(const ArchSpec &rhs) { if (this != &rhs) { m_triple = rhs.m_triple; m_core = rhs.m_core; m_byte_order = rhs.m_byte_order; m_distribution_id = rhs.m_distribution_id; m_flags = rhs.m_flags; } return *this; } void ArchSpec::Clear() { m_triple = llvm::Triple(); m_core = kCore_invalid; m_byte_order = eByteOrderInvalid; m_distribution_id.Clear(); m_flags = 0; } //===----------------------------------------------------------------------===// // Predicates. const char *ArchSpec::GetArchitectureName() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->name; return "unknown"; } bool ArchSpec::IsMIPS() const { const llvm::Triple::ArchType machine = GetMachine(); if (machine == llvm::Triple::mips || machine == llvm::Triple::mipsel || machine == llvm::Triple::mips64 || machine == llvm::Triple::mips64el) return true; return false; } std::string ArchSpec::GetTargetABI() const { std::string abi; if (IsMIPS()) { switch (GetFlags() & ArchSpec::eMIPSABI_mask) { case ArchSpec::eMIPSABI_N64: abi = "n64"; return abi; case ArchSpec::eMIPSABI_N32: abi = "n32"; return abi; case ArchSpec::eMIPSABI_O32: abi = "o32"; return abi; default: return abi; } } return abi; } void ArchSpec::SetFlags(std::string elf_abi) { uint32_t flag = GetFlags(); if (IsMIPS()) { if (elf_abi == "n64") flag |= ArchSpec::eMIPSABI_N64; else if (elf_abi == "n32") flag |= ArchSpec::eMIPSABI_N32; else if (elf_abi == "o32") flag |= ArchSpec::eMIPSABI_O32; } SetFlags(flag); } std::string ArchSpec::GetClangTargetCPU() const { std::string cpu; const llvm::Triple::ArchType machine = GetMachine(); if (machine == llvm::Triple::mips || machine == llvm::Triple::mipsel || machine == llvm::Triple::mips64 || machine == llvm::Triple::mips64el) { switch (m_core) { case ArchSpec::eCore_mips32: case ArchSpec::eCore_mips32el: cpu = "mips32"; break; case ArchSpec::eCore_mips32r2: case ArchSpec::eCore_mips32r2el: cpu = "mips32r2"; break; case ArchSpec::eCore_mips32r3: case ArchSpec::eCore_mips32r3el: cpu = "mips32r3"; break; case ArchSpec::eCore_mips32r5: case ArchSpec::eCore_mips32r5el: cpu = "mips32r5"; break; case ArchSpec::eCore_mips32r6: case ArchSpec::eCore_mips32r6el: cpu = "mips32r6"; break; case ArchSpec::eCore_mips64: case ArchSpec::eCore_mips64el: cpu = "mips64"; break; case ArchSpec::eCore_mips64r2: case ArchSpec::eCore_mips64r2el: cpu = "mips64r2"; break; case ArchSpec::eCore_mips64r3: case ArchSpec::eCore_mips64r3el: cpu = "mips64r3"; break; case ArchSpec::eCore_mips64r5: case ArchSpec::eCore_mips64r5el: cpu = "mips64r5"; break; case ArchSpec::eCore_mips64r6: case ArchSpec::eCore_mips64r6el: cpu = "mips64r6"; break; default: break; } } return cpu; } uint32_t ArchSpec::GetMachOCPUType() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->cpu; } } return LLDB_INVALID_CPUTYPE; } uint32_t ArchSpec::GetMachOCPUSubType() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->sub; } } return LLDB_INVALID_CPUTYPE; } uint32_t ArchSpec::GetDataByteSize() const { switch (m_core) { case eCore_kalimba3: return 4; case eCore_kalimba4: return 1; case eCore_kalimba5: return 4; default: return 1; } return 1; } uint32_t ArchSpec::GetCodeByteSize() const { switch (m_core) { case eCore_kalimba3: return 4; case eCore_kalimba4: return 1; case eCore_kalimba5: return 1; default: return 1; } return 1; } llvm::Triple::ArchType ArchSpec::GetMachine() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->machine; return llvm::Triple::UnknownArch; } const ConstString &ArchSpec::GetDistributionId() const { return m_distribution_id; } void ArchSpec::SetDistributionId(const char *distribution_id) { m_distribution_id.SetCString(distribution_id); } uint32_t ArchSpec::GetAddressByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { if (core_def->machine == llvm::Triple::mips64 || core_def->machine == llvm::Triple::mips64el) { // For N32/O32 applications Address size is 4 bytes. if (m_flags & (eMIPSABI_N32 | eMIPSABI_O32)) return 4; } return core_def->addr_byte_size; } return 0; } ByteOrder ArchSpec::GetDefaultEndian() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->default_byte_order; return eByteOrderInvalid; } bool ArchSpec::CharIsSignedByDefault() const { switch (m_triple.getArch()) { default: return true; case llvm::Triple::aarch64: case llvm::Triple::aarch64_be: case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: return m_triple.isOSDarwin() || m_triple.isOSWindows(); case llvm::Triple::ppc: case llvm::Triple::ppc64: return m_triple.isOSDarwin(); case llvm::Triple::ppc64le: case llvm::Triple::systemz: case llvm::Triple::xcore: return false; } } lldb::ByteOrder ArchSpec::GetByteOrder() const { if (m_byte_order == eByteOrderInvalid) return GetDefaultEndian(); return m_byte_order; } //===----------------------------------------------------------------------===// // Mutators. bool ArchSpec::SetTriple(const llvm::Triple &triple) { m_triple = triple; UpdateCore(); return IsValid(); } bool lldb_private::ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str, ArchSpec &arch) { // Accept "12-10" or "12.10" as cpu type/subtype if (triple_str.empty()) return false; size_t pos = triple_str.find_first_of("-."); if (pos == llvm::StringRef::npos) return false; llvm::StringRef cpu_str = triple_str.substr(0, pos); llvm::StringRef remainder = triple_str.substr(pos + 1); if (cpu_str.empty() || remainder.empty()) return false; llvm::StringRef sub_str; llvm::StringRef vendor; llvm::StringRef os; std::tie(sub_str, remainder) = remainder.split('-'); std::tie(vendor, os) = remainder.split('-'); uint32_t cpu = 0; uint32_t sub = 0; if (cpu_str.getAsInteger(10, cpu) || sub_str.getAsInteger(10, sub)) return false; if (!arch.SetArchitecture(eArchTypeMachO, cpu, sub)) return false; if (!vendor.empty() && !os.empty()) { arch.GetTriple().setVendorName(vendor); arch.GetTriple().setOSName(os); } return true; } bool ArchSpec::SetTriple(const char *triple_cstr) { llvm::StringRef str(triple_cstr ? triple_cstr : ""); return SetTriple(str); } bool ArchSpec::SetTriple(const char *triple_cstr, Platform *platform) { llvm::StringRef str(triple_cstr ? triple_cstr : ""); return SetTriple(str, platform); } bool ArchSpec::SetTriple(llvm::StringRef triple) { if (triple.empty()) { Clear(); return false; } if (ParseMachCPUDashSubtypeTriple(triple, *this)) return true; if (triple.startswith(LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple.equals(LLDB_ARCH_DEFAULT_32BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind32); else if (triple.equals(LLDB_ARCH_DEFAULT_64BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind64); else if (triple.equals(LLDB_ARCH_DEFAULT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); } else { SetTriple(llvm::Triple(llvm::Triple::normalize(triple))); } return IsValid(); } bool ArchSpec::SetTriple(llvm::StringRef triple, Platform *platform) { if (triple.empty()) { Clear(); return false; } if (ParseMachCPUDashSubtypeTriple(triple, *this)) return true; if (triple.startswith(LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple.equals(LLDB_ARCH_DEFAULT_32BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind32); else if (triple.equals(LLDB_ARCH_DEFAULT_64BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind64); else if (triple.equals(LLDB_ARCH_DEFAULT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); return IsValid(); } ArchSpec raw_arch(triple); llvm::Triple normalized_triple(llvm::Triple::normalize(triple)); const bool os_specified = !normalized_triple.getOSName().empty(); const bool vendor_specified = !normalized_triple.getVendorName().empty(); const bool env_specified = !normalized_triple.getEnvironmentName().empty(); if (os_specified || vendor_specified || env_specified) { SetTriple(normalized_triple); return IsValid(); } // We got an arch only. If there is no platform, fallback to the host system // for defaults. if (!platform) { llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple()); if (!vendor_specified) normalized_triple.setVendor(host_triple.getVendor()); if (!vendor_specified) normalized_triple.setOS(host_triple.getOS()); if (!env_specified && host_triple.getEnvironmentName().size()) normalized_triple.setEnvironment(host_triple.getEnvironment()); SetTriple(normalized_triple); return IsValid(); } // If we were given a platform, use the platform's system architecture. If // this is not available (might not be connected) use the first supported // architecture. ArchSpec compatible_arch; if (!platform->IsCompatibleArchitecture(raw_arch, false, &compatible_arch)) { *this = raw_arch; return IsValid(); } if (compatible_arch.IsValid()) { const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); if (!vendor_specified) normalized_triple.setVendor(compatible_triple.getVendor()); if (!os_specified) normalized_triple.setOS(compatible_triple.getOS()); if (!env_specified && compatible_triple.hasEnvironment()) normalized_triple.setEnvironment(compatible_triple.getEnvironment()); } SetTriple(normalized_triple); return IsValid(); } void ArchSpec::MergeFrom(const ArchSpec &other) { if (TripleVendorIsUnspecifiedUnknown() && !other.TripleVendorIsUnspecifiedUnknown()) GetTriple().setVendor(other.GetTriple().getVendor()); if (TripleOSIsUnspecifiedUnknown() && !other.TripleOSIsUnspecifiedUnknown()) GetTriple().setOS(other.GetTriple().getOS()); if (GetTriple().getArch() == llvm::Triple::UnknownArch) { GetTriple().setArch(other.GetTriple().getArch()); UpdateCore(); } if (GetTriple().getEnvironment() == llvm::Triple::UnknownEnvironment && !TripleVendorWasSpecified()) { if (other.TripleVendorWasSpecified()) GetTriple().setEnvironment(other.GetTriple().getEnvironment()); } // If this and other are both arm ArchSpecs and this ArchSpec is a generic // "some kind of arm" // spec but the other ArchSpec is a specific arm core, adopt the specific arm // core. if (GetTriple().getArch() == llvm::Triple::arm && other.GetTriple().getArch() == llvm::Triple::arm && IsCompatibleMatch(other) && GetCore() == ArchSpec::eCore_arm_generic && other.GetCore() != ArchSpec::eCore_arm_generic) { m_core = other.GetCore(); CoreUpdated(true); } } bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu, uint32_t sub, uint32_t os) { m_core = kCore_invalid; bool update_triple = true; const ArchDefinition *arch_def = FindArchDefinition(arch_type); if (arch_def) { const ArchDefinitionEntry *arch_def_entry = FindArchDefinitionEntry(arch_def, cpu, sub); if (arch_def_entry) { const CoreDefinition *core_def = FindCoreDefinition(arch_def_entry->core); if (core_def) { m_core = core_def->core; update_triple = false; // Always use the architecture name because it might be more descriptive // than the architecture enum ("armv7" -> llvm::Triple::arm). m_triple.setArchName(llvm::StringRef(core_def->name)); if (arch_type == eArchTypeMachO) { m_triple.setVendor(llvm::Triple::Apple); // Don't set the OS. It could be simulator, macosx, ios, watchos, // tvos. We could // get close with the cpu type - but we can't get it right all of the // time. Better // to leave this unset so other sections of code will set it when they // have more // information. // NB: don't call m_triple.setOS (llvm::Triple::UnknownOS). That sets // the OSName to // "unknown" and the ArchSpec::TripleVendorWasSpecified() method says // that any // OSName setting means it was specified. } else if (arch_type == eArchTypeELF) { switch (os) { case llvm::ELF::ELFOSABI_AIX: m_triple.setOS(llvm::Triple::OSType::AIX); break; case llvm::ELF::ELFOSABI_FREEBSD: m_triple.setOS(llvm::Triple::OSType::FreeBSD); break; case llvm::ELF::ELFOSABI_GNU: m_triple.setOS(llvm::Triple::OSType::Linux); break; case llvm::ELF::ELFOSABI_NETBSD: m_triple.setOS(llvm::Triple::OSType::NetBSD); break; case llvm::ELF::ELFOSABI_OPENBSD: m_triple.setOS(llvm::Triple::OSType::OpenBSD); break; case llvm::ELF::ELFOSABI_SOLARIS: m_triple.setOS(llvm::Triple::OSType::Solaris); break; } } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) { m_triple.setVendor(llvm::Triple::PC); m_triple.setOS(llvm::Triple::Win32); } else { m_triple.setVendor(llvm::Triple::UnknownVendor); m_triple.setOS(llvm::Triple::UnknownOS); } // Fall back onto setting the machine type if the arch by name failed... if (m_triple.getArch() == llvm::Triple::UnknownArch) m_triple.setArch(core_def->machine); } } } CoreUpdated(update_triple); return IsValid(); } uint32_t ArchSpec::GetMinimumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->min_opcode_byte_size; return 0; } uint32_t ArchSpec::GetMaximumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->max_opcode_byte_size; return 0; } bool ArchSpec::IsExactMatch(const ArchSpec &rhs) const { return IsEqualTo(rhs, true); } bool ArchSpec::IsCompatibleMatch(const ArchSpec &rhs) const { return IsEqualTo(rhs, false); } static bool isCompatibleEnvironment(llvm::Triple::EnvironmentType lhs, llvm::Triple::EnvironmentType rhs) { if (lhs == rhs) return true; // If any of the environment is unknown then they are compatible if (lhs == llvm::Triple::UnknownEnvironment || rhs == llvm::Triple::UnknownEnvironment) return true; // If one of the environment is Android and the other one is EABI then they // are considered to // be compatible. This is required as a workaround for shared libraries // compiled for Android // without the NOTE section indicating that they are using the Android ABI. if ((lhs == llvm::Triple::Android && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::Android && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABI && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::GNUEABI && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABIHF && rhs == llvm::Triple::EABIHF) || (rhs == llvm::Triple::GNUEABIHF && lhs == llvm::Triple::EABIHF)) return true; return false; } bool ArchSpec::IsEqualTo(const ArchSpec &rhs, bool exact_match) const { // explicitly ignoring m_distribution_id in this method. if (GetByteOrder() != rhs.GetByteOrder()) return false; const ArchSpec::Core lhs_core = GetCore(); const ArchSpec::Core rhs_core = rhs.GetCore(); const bool core_match = cores_match(lhs_core, rhs_core, true, exact_match); if (core_match) { const llvm::Triple &lhs_triple = GetTriple(); const llvm::Triple &rhs_triple = rhs.GetTriple(); const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); if (lhs_triple_vendor != rhs_triple_vendor) { const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); const bool lhs_vendor_specified = TripleVendorWasSpecified(); // Both architectures had the vendor specified, so if they aren't // equal then we return false if (rhs_vendor_specified && lhs_vendor_specified) return false; // Only fail if both vendor types are not unknown if (lhs_triple_vendor != llvm::Triple::UnknownVendor && rhs_triple_vendor != llvm::Triple::UnknownVendor) return false; } const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); if (lhs_triple_os != rhs_triple_os) { const bool rhs_os_specified = rhs.TripleOSWasSpecified(); const bool lhs_os_specified = TripleOSWasSpecified(); // Both architectures had the OS specified, so if they aren't // equal then we return false if (rhs_os_specified && lhs_os_specified) return false; // Only fail if both os types are not unknown if (lhs_triple_os != llvm::Triple::UnknownOS && rhs_triple_os != llvm::Triple::UnknownOS) return false; } const llvm::Triple::EnvironmentType lhs_triple_env = lhs_triple.getEnvironment(); const llvm::Triple::EnvironmentType rhs_triple_env = rhs_triple.getEnvironment(); if (!isCompatibleEnvironment(lhs_triple_env, rhs_triple_env)) return false; return true; } return false; } void ArchSpec::UpdateCore() { llvm::StringRef arch_name(m_triple.getArchName()); const CoreDefinition *core_def = FindCoreDefinition(arch_name); if (core_def) { m_core = core_def->core; // Set the byte order to the default byte order for an architecture. // This can be modified if needed for cases when cores handle both // big and little endian m_byte_order = core_def->default_byte_order; } else { Clear(); } } //===----------------------------------------------------------------------===// // Helper methods. void ArchSpec::CoreUpdated(bool update_triple) { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { if (update_triple) m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); m_byte_order = core_def->default_byte_order; } else { if (update_triple) m_triple = llvm::Triple(); m_byte_order = eByteOrderInvalid; } } //===----------------------------------------------------------------------===// // Operators. static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match) { if (core1 == core2) return true; switch (core1) { case ArchSpec::kCore_any: return true; case ArchSpec::eCore_arm_generic: if (enforce_exact_match) break; LLVM_FALLTHROUGH; case ArchSpec::kCore_arm_any: if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) return true; if (core2 >= ArchSpec::kCore_thumb_first && core2 <= ArchSpec::kCore_thumb_last) return true; if (core2 == ArchSpec::kCore_arm_any) return true; break; case ArchSpec::kCore_x86_32_any: if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) return true; break; case ArchSpec::kCore_x86_64_any: if ((core2 >= ArchSpec::kCore_x86_64_first && core2 <= ArchSpec::kCore_x86_64_last) || (core2 == ArchSpec::kCore_x86_64_any)) return true; break; case ArchSpec::kCore_ppc_any: if ((core2 >= ArchSpec::kCore_ppc_first && core2 <= ArchSpec::kCore_ppc_last) || (core2 == ArchSpec::kCore_ppc_any)) return true; break; case ArchSpec::kCore_ppc64_any: if ((core2 >= ArchSpec::kCore_ppc64_first && core2 <= ArchSpec::kCore_ppc64_last) || (core2 == ArchSpec::kCore_ppc64_any)) return true; break; case ArchSpec::eCore_arm_armv6m: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; try_inverse = false; if (core2 == ArchSpec::eCore_arm_armv7) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; } break; case ArchSpec::kCore_hexagon_any: if ((core2 >= ArchSpec::kCore_hexagon_first && core2 <= ArchSpec::kCore_hexagon_last) || (core2 == ArchSpec::kCore_hexagon_any)) return true; break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization // Cortex-M0 - ARMv6-M - armv6m // Cortex-M3 - ARMv7-M - armv7m // Cortex-M4 - ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7em: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv7m) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; try_inverse = true; } break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization // Cortex-M0 - ARMv6-M - armv6m // Cortex-M3 - ARMv7-M - armv7m // Cortex-M4 - ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7m: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; if (core2 == ArchSpec::eCore_arm_armv7em) return true; try_inverse = true; } break; case ArchSpec::eCore_arm_armv7f: case ArchSpec::eCore_arm_armv7k: case ArchSpec::eCore_arm_armv7s: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; try_inverse = false; } break; case ArchSpec::eCore_x86_64_x86_64h: if (!enforce_exact_match) { try_inverse = false; if (core2 == ArchSpec::eCore_x86_64_x86_64) return true; } break; case ArchSpec::eCore_arm_armv8: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_arm64) return true; if (core2 == ArchSpec::eCore_arm_aarch64) return true; try_inverse = false; } break; case ArchSpec::eCore_arm_aarch64: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_arm64) return true; if (core2 == ArchSpec::eCore_arm_armv8) return true; try_inverse = false; } break; case ArchSpec::eCore_arm_arm64: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_aarch64) return true; if (core2 == ArchSpec::eCore_arm_armv8) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= ArchSpec::kCore_mips32_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= ArchSpec::kCore_mips32el_last) return true; try_inverse = true; } break; case ArchSpec::eCore_mips64: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= ArchSpec::kCore_mips32_last) return true; if (core2 >= ArchSpec::kCore_mips64_first && core2 <= ArchSpec::kCore_mips64_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= ArchSpec::kCore_mips32el_last) return true; if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= ArchSpec::kCore_mips64el_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64r2: case ArchSpec::eCore_mips64r3: case ArchSpec::eCore_mips64r5: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= (core1 - 10)) return true; if (core2 >= ArchSpec::kCore_mips64_first && core2 <= (core1 - 1)) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64r2el: case ArchSpec::eCore_mips64r3el: case ArchSpec::eCore_mips64r5el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= (core1 - 10)) return true; if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= (core1 - 1)) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32r2: case ArchSpec::eCore_mips32r3: case ArchSpec::eCore_mips32r5: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= core1) return true; } break; case ArchSpec::eCore_mips32r2el: case ArchSpec::eCore_mips32r3el: case ArchSpec::eCore_mips32r5el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= core1) return true; } break; case ArchSpec::eCore_mips32r6: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) return true; } break; case ArchSpec::eCore_mips32r6el: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32el || core2 == ArchSpec::eCore_mips32r6el) return true; } break; case ArchSpec::eCore_mips64r6: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) return true; if (core2 == ArchSpec::eCore_mips64 || core2 == ArchSpec::eCore_mips64r6) return true; } break; case ArchSpec::eCore_mips64r6el: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32el || core2 == ArchSpec::eCore_mips32r6el) return true; if (core2 == ArchSpec::eCore_mips64el || core2 == ArchSpec::eCore_mips64r6el) return true; } break; default: break; } if (try_inverse) return cores_match(core2, core1, false, enforce_exact_match); return false; } bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) { const ArchSpec::Core lhs_core = lhs.GetCore(); const ArchSpec::Core rhs_core = rhs.GetCore(); return lhs_core < rhs_core; } static void StopInfoOverrideCallbackTypeARM(lldb_private::Thread &thread) { // We need to check if we are stopped in Thumb mode in a IT instruction // and detect if the condition doesn't pass. If this is the case it means // we won't actually execute this instruction. If this happens we need to // clear the stop reason to no thread plans think we are stopped for a // reason and the plans should keep going. // // We do this because when single stepping many ARM processes, debuggers // often use the BVR/BCR registers that says "stop when the PC is not // equal to its current value". This method of stepping means we can end // up stopping on instructions inside an if/then block that wouldn't get // executed. By fixing this we can stop the debugger from seeming like // you stepped through both the "if" _and_ the "else" clause when source // level stepping because the debugger stops regardless due to the BVR/BCR // triggering a stop. // // It also means we can set breakpoints on instructions inside an an // if/then block and correctly skip them if we use the BKPT instruction. // The ARM and Thumb BKPT instructions are unconditional even when executed // in a Thumb IT block. // // If your debugger inserts software traps in ARM/Thumb code, it will // need to use 16 and 32 bit instruction for 16 and 32 bit thumb // instructions respectively. If your debugger inserts a 16 bit thumb // trap on top of a 32 bit thumb instruction for an opcode that is inside // an if/then, it will change the it/then to conditionally execute your // 16 bit trap and then cause your program to crash if it executes the // trailing 16 bits (the second half of the 32 bit thumb instruction you // partially overwrote). RegisterContextSP reg_ctx_sp(thread.GetRegisterContext()); if (reg_ctx_sp) { const uint32_t cpsr = reg_ctx_sp->GetFlags(0); if (cpsr != 0) { // Read the J and T bits to get the ISETSTATE const uint32_t J = Bit32(cpsr, 24); const uint32_t T = Bit32(cpsr, 5); const uint32_t ISETSTATE = J << 1 | T; if (ISETSTATE == 0) { // NOTE: I am pretty sure we want to enable the code below // that detects when we stop on an instruction in ARM mode // that is conditional and the condition doesn't pass. This // can happen if you set a breakpoint on an instruction that // is conditional. We currently will _always_ stop on the // instruction which is bad. You can also run into this while // single stepping and you could appear to run code in the "if" // and in the "else" clause because it would stop at all of the // conditional instructions in both. // In such cases, we really don't want to stop at this location. // I will check with the lldb-dev list first before I enable this. #if 0 // ARM mode: check for condition on intsruction const addr_t pc = reg_ctx_sp->GetPC(); Status error; // If we fail to read the opcode we will get UINT64_MAX as the // result in "opcode" which we can use to detect if we read a // valid opcode. const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error); if (opcode <= UINT32_MAX) { const uint32_t condition = Bits32((uint32_t)opcode, 31, 28); if (!ARMConditionPassed(condition, cpsr)) { // We ARE stopped on an ARM instruction whose condition doesn't // pass so this instruction won't get executed. // Regardless of why it stopped, we need to clear the stop info thread.SetStopInfo (StopInfoSP()); } } #endif } else if (ISETSTATE == 1) { // Thumb mode const uint32_t ITSTATE = Bits32(cpsr, 15, 10) << 2 | Bits32(cpsr, 26, 25); if (ITSTATE != 0) { const uint32_t condition = Bits32(ITSTATE, 7, 4); if (!ARMConditionPassed(condition, cpsr)) { // We ARE stopped in a Thumb IT instruction on an instruction whose // condition doesn't pass so this instruction won't get executed. // Regardless of why it stopped, we need to clear the stop info thread.SetStopInfo(StopInfoSP()); } } } } } } ArchSpec::StopInfoOverrideCallbackType ArchSpec::GetStopInfoOverrideCallback() const { const llvm::Triple::ArchType machine = GetMachine(); if (machine == llvm::Triple::arm) return StopInfoOverrideCallbackTypeARM; return nullptr; } bool ArchSpec::IsFullySpecifiedTriple() const { const auto &user_specified_triple = GetTriple(); bool user_triple_fully_specified = false; if ((user_specified_triple.getOS() != llvm::Triple::UnknownOS) || TripleOSWasSpecified()) { if ((user_specified_triple.getVendor() != llvm::Triple::UnknownVendor) || TripleVendorWasSpecified()) { const unsigned unspecified = 0; if (user_specified_triple.getOSMajorVersion() != unspecified) { user_triple_fully_specified = true; } } } return user_triple_fully_specified; } void ArchSpec::PiecewiseTripleCompare( const ArchSpec &other, bool &arch_different, bool &vendor_different, bool &os_different, bool &os_version_different, bool &env_different) { const llvm::Triple &me(GetTriple()); const llvm::Triple &them(other.GetTriple()); arch_different = (me.getArch() != them.getArch()); vendor_different = (me.getVendor() != them.getVendor()); os_different = (me.getOS() != them.getOS()); os_version_different = (me.getOSMajorVersion() != them.getOSMajorVersion()); env_different = (me.getEnvironment() != them.getEnvironment()); } bool ArchSpec::IsAlwaysThumbInstructions() const { std::string Status; if (GetTriple().getArch() == llvm::Triple::arm || GetTriple().getArch() == llvm::Triple::thumb) { // v. https://en.wikipedia.org/wiki/ARM_Cortex-M // // Cortex-M0 through Cortex-M7 are ARM processor cores which can only // execute thumb instructions. We map the cores to arch names like this: // // Cortex-M0, Cortex-M0+, Cortex-M1: armv6m // Cortex-M3: armv7m // Cortex-M4, Cortex-M7: armv7em if (GetCore() == ArchSpec::Core::eCore_arm_armv7m || GetCore() == ArchSpec::Core::eCore_arm_armv7em || GetCore() == ArchSpec::Core::eCore_arm_armv6m) { return true; } } return false; } void ArchSpec::DumpTriple(Stream &s) const { const llvm::Triple &triple = GetTriple(); llvm::StringRef arch_str = triple.getArchName(); llvm::StringRef vendor_str = triple.getVendorName(); llvm::StringRef os_str = triple.getOSName(); llvm::StringRef environ_str = triple.getEnvironmentName(); s.Printf("%s-%s-%s", arch_str.empty() ? "*" : arch_str.str().c_str(), vendor_str.empty() ? "*" : vendor_str.str().c_str(), os_str.empty() ? "*" : os_str.str().c_str()); if (!environ_str.empty()) s.Printf("-%s", environ_str.str().c_str()); } Index: vendor/lldb/dist/source/Core/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Core/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/source/Core/CMakeLists.txt (revision 319790) @@ -1,76 +1,77 @@ add_lldb_library(lldbCore Address.cpp AddressRange.cpp AddressResolver.cpp AddressResolverFileLine.cpp AddressResolverName.cpp ArchSpec.cpp Broadcaster.cpp Communication.cpp Connection.cpp Debugger.cpp Disassembler.cpp DumpDataExtractor.cpp DynamicLoader.cpp EmulateInstruction.cpp Event.cpp FileLineResolver.cpp FileSpecList.cpp FormatEntity.cpp IOHandler.cpp Listener.cpp Mangled.cpp Module.cpp ModuleChild.cpp ModuleList.cpp Opcode.cpp PluginManager.cpp RegisterValue.cpp Scalar.cpp SearchFilter.cpp Section.cpp SourceManager.cpp State.cpp StreamAsynchronousIO.cpp StreamFile.cpp StructuredData.cpp Timer.cpp UserSettingsController.cpp Value.cpp ValueObject.cpp ValueObjectCast.cpp ValueObjectChild.cpp ValueObjectConstResult.cpp ValueObjectConstResultCast.cpp ValueObjectConstResultChild.cpp ValueObjectConstResultImpl.cpp ValueObjectDynamicValue.cpp ValueObjectList.cpp ValueObjectMemory.cpp ValueObjectRegister.cpp ValueObjectSyntheticFilter.cpp ValueObjectVariable.cpp LINK_LIBS clangAST lldbBreakpoint lldbDataFormatters lldbExpression lldbHost lldbInterpreter lldbSymbol lldbTarget lldbUtility lldbPluginProcessUtility lldbPluginCPlusPlusLanguage lldbPluginObjCLanguage lldbPluginObjectFileJIT LINK_COMPONENTS + BinaryFormat Support Demangle ) # Needed to properly resolve references in a debug build. # TODO: Remove once we have better layering set_target_properties(lldbCore PROPERTIES LINK_INTERFACE_MULTIPLICITY 4) Index: vendor/lldb/dist/source/Core/Section.cpp =================================================================== --- vendor/lldb/dist/source/Core/Section.cpp (revision 319789) +++ vendor/lldb/dist/source/Core/Section.cpp (revision 319790) @@ -1,611 +1,611 @@ //===-- Section.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/Section.h" #include "lldb/Core/Address.h" // for Address #include "lldb/Core/Module.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" // for FileSpec #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/VMRange.h" // for VMRange #include // for PRIx64 #include // for numeric_limits #include // for distance namespace lldb_private { class DataExtractor; } using namespace lldb; using namespace lldb_private; static const char *GetSectionTypeAsCString(lldb::SectionType sect_type) { switch (sect_type) { case eSectionTypeInvalid: return "invalid"; case eSectionTypeCode: return "code"; case eSectionTypeContainer: return "container"; case eSectionTypeData: return "data"; case eSectionTypeDataCString: return "data-cstr"; case eSectionTypeDataCStringPointers: return "data-cstr-ptr"; case eSectionTypeDataSymbolAddress: return "data-symbol-addr"; case eSectionTypeData4: return "data-4-byte"; case eSectionTypeData8: return "data-8-byte"; case eSectionTypeData16: return "data-16-byte"; case eSectionTypeDataPointers: return "data-ptrs"; case eSectionTypeDebug: return "debug"; case eSectionTypeZeroFill: return "zero-fill"; case eSectionTypeDataObjCMessageRefs: return "objc-message-refs"; case eSectionTypeDataObjCCFStrings: return "objc-cfstrings"; case eSectionTypeDWARFDebugAbbrev: return "dwarf-abbrev"; case eSectionTypeDWARFDebugAddr: return "dwarf-addr"; case eSectionTypeDWARFDebugAranges: return "dwarf-aranges"; case eSectionTypeDWARFDebugFrame: return "dwarf-frame"; case eSectionTypeDWARFDebugInfo: return "dwarf-info"; case eSectionTypeDWARFDebugLine: return "dwarf-line"; case eSectionTypeDWARFDebugLoc: return "dwarf-loc"; case eSectionTypeDWARFDebugMacInfo: return "dwarf-macinfo"; case eSectionTypeDWARFDebugMacro: return "dwarf-macro"; case eSectionTypeDWARFDebugPubNames: return "dwarf-pubnames"; case eSectionTypeDWARFDebugPubTypes: return "dwarf-pubtypes"; case eSectionTypeDWARFDebugRanges: return "dwarf-ranges"; case eSectionTypeDWARFDebugStr: return "dwarf-str"; case eSectionTypeDWARFDebugStrOffsets: return "dwarf-str-offsets"; case eSectionTypeELFSymbolTable: return "elf-symbol-table"; case eSectionTypeELFDynamicSymbols: return "elf-dynamic-symbols"; case eSectionTypeELFRelocationEntries: return "elf-relocation-entries"; case eSectionTypeELFDynamicLinkInfo: return "elf-dynamic-link-info"; case eSectionTypeDWARFAppleNames: return "apple-names"; case eSectionTypeDWARFAppleTypes: return "apple-types"; case eSectionTypeDWARFAppleNamespaces: return "apple-namespaces"; case eSectionTypeDWARFAppleObjC: return "apple-objc"; case eSectionTypeEHFrame: return "eh-frame"; case eSectionTypeARMexidx: return "ARM.exidx"; case eSectionTypeARMextab: return "ARM.extab"; case eSectionTypeCompactUnwind: return "compact-unwind"; case eSectionTypeGoSymtab: return "go-symtab"; case eSectionTypeAbsoluteAddress: return "absolute"; case eSectionTypeOther: return "regular"; } return "unknown"; } Section::Section(const ModuleSP &module_sp, ObjectFile *obj_file, user_id_t sect_id, const ConstString &name, SectionType sect_type, addr_t file_addr, addr_t byte_size, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, uint32_t flags, uint32_t target_byte_size /*=1*/) : ModuleChild(module_sp), UserID(sect_id), Flags(flags), m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name), m_file_addr(file_addr), m_byte_size(byte_size), m_file_offset(file_offset), m_file_size(file_size), m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false), m_thread_specific(false), m_readable(false), m_writable(false), m_executable(false), m_target_byte_size(target_byte_size) { // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s\n", // this, module_sp.get(), sect_id, file_addr, file_addr + // byte_size, file_offset, file_offset + file_size, flags, // name.GetCString()); } Section::Section(const lldb::SectionSP &parent_section_sp, const ModuleSP &module_sp, ObjectFile *obj_file, user_id_t sect_id, const ConstString &name, SectionType sect_type, addr_t file_addr, addr_t byte_size, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, uint32_t flags, uint32_t target_byte_size /*=1*/) : ModuleChild(module_sp), UserID(sect_id), Flags(flags), m_obj_file(obj_file), m_type(sect_type), m_parent_wp(), m_name(name), m_file_addr(file_addr), m_byte_size(byte_size), m_file_offset(file_offset), m_file_size(file_size), m_log2align(log2align), m_children(), m_fake(false), m_encrypted(false), m_thread_specific(false), m_readable(false), m_writable(false), m_executable(false), m_target_byte_size(target_byte_size) { // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", // addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " // - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s.%s\n", // this, module_sp.get(), sect_id, file_addr, file_addr + // byte_size, file_offset, file_offset + file_size, flags, // parent_section_sp->GetName().GetCString(), name.GetCString()); if (parent_section_sp) m_parent_wp = parent_section_sp; } Section::~Section() { // printf ("Section::~Section(%p)\n", this); } addr_t Section::GetFileAddress() const { SectionSP parent_sp(GetParent()); if (parent_sp) { // This section has a parent which means m_file_addr is an offset into // the parent section, so the file address for this section is the file // address of the parent plus the offset return parent_sp->GetFileAddress() + m_file_addr; } // This section has no parent, so m_file_addr is the file base address return m_file_addr; } bool Section::SetFileAddress(lldb::addr_t file_addr) { SectionSP parent_sp(GetParent()); if (parent_sp) { if (m_file_addr >= file_addr) return parent_sp->SetFileAddress(m_file_addr - file_addr); return false; } else { // This section has no parent, so m_file_addr is the file base address m_file_addr = file_addr; return true; } } lldb::addr_t Section::GetOffset() const { // This section has a parent which means m_file_addr is an offset. SectionSP parent_sp(GetParent()); if (parent_sp) return m_file_addr; // This section has no parent, so there is no offset to be had return 0; } addr_t Section::GetLoadBaseAddress(Target *target) const { addr_t load_base_addr = LLDB_INVALID_ADDRESS; SectionSP parent_sp(GetParent()); if (parent_sp) { load_base_addr = parent_sp->GetLoadBaseAddress(target); if (load_base_addr != LLDB_INVALID_ADDRESS) load_base_addr += GetOffset(); } if (load_base_addr == LLDB_INVALID_ADDRESS) { load_base_addr = target->GetSectionLoadList().GetSectionLoadAddress( const_cast
(this)->shared_from_this()); } return load_base_addr; } -bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr) const { +bool Section::ResolveContainedAddress(addr_t offset, Address &so_addr, + bool allow_section_end) const { const size_t num_children = m_children.GetSize(); - if (num_children > 0) { - for (size_t i = 0; i < num_children; i++) { - Section *child_section = m_children.GetSectionAtIndex(i).get(); + for (size_t i = 0; i < num_children; i++) { + Section *child_section = m_children.GetSectionAtIndex(i).get(); - addr_t child_offset = child_section->GetOffset(); - if (child_offset <= offset && - offset - child_offset < child_section->GetByteSize()) - return child_section->ResolveContainedAddress(offset - child_offset, - so_addr); - } + addr_t child_offset = child_section->GetOffset(); + if (child_offset <= offset && + offset - child_offset < + child_section->GetByteSize() + (allow_section_end ? 1 : 0)) + return child_section->ResolveContainedAddress(offset - child_offset, + so_addr, allow_section_end); } so_addr.SetOffset(offset); so_addr.SetSection(const_cast
(this)->shared_from_this()); #ifdef LLDB_CONFIGURATION_DEBUG // For debug builds, ensure that there are no orphaned (i.e., moduleless) // sections. assert(GetModule().get()); #endif return true; } bool Section::ContainsFileAddress(addr_t vm_addr) const { const addr_t file_addr = GetFileAddress(); if (file_addr != LLDB_INVALID_ADDRESS) { if (file_addr <= vm_addr) { const addr_t offset = (vm_addr - file_addr) * m_target_byte_size; return offset < GetByteSize(); } } return false; } int Section::Compare(const Section &a, const Section &b) { if (&a == &b) return 0; const ModuleSP a_module_sp = a.GetModule(); const ModuleSP b_module_sp = b.GetModule(); if (a_module_sp == b_module_sp) { user_id_t a_sect_uid = a.GetID(); user_id_t b_sect_uid = b.GetID(); if (a_sect_uid < b_sect_uid) return -1; if (a_sect_uid > b_sect_uid) return 1; return 0; } else { // The modules are different, just compare the module pointers if (a_module_sp.get() < b_module_sp.get()) return -1; else return 1; // We already know the modules aren't equal } } void Section::Dump(Stream *s, Target *target, uint32_t depth) const { // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); s->Indent(); s->Printf("0x%8.8" PRIx64 " %-16s ", GetID(), GetSectionTypeAsCString(m_type)); bool resolved = true; addr_t addr = LLDB_INVALID_ADDRESS; if (GetByteSize() == 0) s->Printf("%39s", ""); else { if (target) addr = GetLoadBaseAddress(target); if (addr == LLDB_INVALID_ADDRESS) { if (target) resolved = false; addr = GetFileAddress(); } VMRange range(addr, addr + m_byte_size); range.Dump(s, 0); } s->Printf("%c %c%c%c 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ", resolved ? ' ' : '*', m_readable ? 'r' : '-', m_writable ? 'w' : '-', m_executable ? 'x' : '-', m_file_offset, m_file_size, Get()); DumpName(s); s->EOL(); if (depth > 0) m_children.Dump(s, target, false, depth - 1); } void Section::DumpName(Stream *s) const { SectionSP parent_sp(GetParent()); if (parent_sp) { parent_sp->DumpName(s); s->PutChar('.'); } else { // The top most section prints the module basename const char *name = NULL; ModuleSP module_sp(GetModule()); const FileSpec &file_spec = m_obj_file->GetFileSpec(); if (m_obj_file) name = file_spec.GetFilename().AsCString(); if ((!name || !name[0]) && module_sp) name = module_sp->GetFileSpec().GetFilename().AsCString(); if (name && name[0]) s->Printf("%s.", name); } m_name.Dump(s); } bool Section::IsDescendant(const Section *section) { if (this == section) return true; SectionSP parent_sp(GetParent()); if (parent_sp) return parent_sp->IsDescendant(section); return false; } bool Section::Slide(addr_t slide_amount, bool slide_children) { if (m_file_addr != LLDB_INVALID_ADDRESS) { if (slide_amount == 0) return true; m_file_addr += slide_amount; if (slide_children) m_children.Slide(slide_amount, slide_children); return true; } return false; } //------------------------------------------------------------------ /// Get the permissions as OR'ed bits from lldb::Permissions //------------------------------------------------------------------ uint32_t Section::GetPermissions() const { uint32_t permissions = 0; if (m_readable) permissions |= ePermissionsReadable; if (m_writable) permissions |= ePermissionsWritable; if (m_executable) permissions |= ePermissionsExecutable; return permissions; } //------------------------------------------------------------------ /// Set the permissions using bits OR'ed from lldb::Permissions //------------------------------------------------------------------ void Section::SetPermissions(uint32_t permissions) { m_readable = (permissions & ePermissionsReadable) != 0; m_writable = (permissions & ePermissionsWritable) != 0; m_executable = (permissions & ePermissionsExecutable) != 0; } lldb::offset_t Section::GetSectionData(void *dst, lldb::offset_t dst_len, lldb::offset_t offset) { if (m_obj_file) return m_obj_file->ReadSectionData(this, offset, dst, dst_len); return 0; } lldb::offset_t Section::GetSectionData(DataExtractor §ion_data) const { if (m_obj_file) return m_obj_file->ReadSectionData(this, section_data); return 0; } #pragma mark SectionList SectionList::SectionList() : m_sections() {} SectionList::~SectionList() {} SectionList &SectionList::operator=(const SectionList &rhs) { if (this != &rhs) m_sections = rhs.m_sections; return *this; } size_t SectionList::AddSection(const lldb::SectionSP §ion_sp) { if (section_sp) { size_t section_index = m_sections.size(); m_sections.push_back(section_sp); return section_index; } return std::numeric_limits::max(); } // Warning, this can be slow as it's removing items from a std::vector. bool SectionList::DeleteSection(size_t idx) { if (idx < m_sections.size()) { m_sections.erase(m_sections.begin() + idx); return true; } return false; } size_t SectionList::FindSectionIndex(const Section *sect) { iterator sect_iter; iterator begin = m_sections.begin(); iterator end = m_sections.end(); for (sect_iter = begin; sect_iter != end; ++sect_iter) { if (sect_iter->get() == sect) { // The secton was already in this section list return std::distance(begin, sect_iter); } } return UINT32_MAX; } size_t SectionList::AddUniqueSection(const lldb::SectionSP §_sp) { size_t sect_idx = FindSectionIndex(sect_sp.get()); if (sect_idx == UINT32_MAX) { sect_idx = AddSection(sect_sp); } return sect_idx; } bool SectionList::ReplaceSection(user_id_t sect_id, const lldb::SectionSP §_sp, uint32_t depth) { iterator sect_iter, end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) { if ((*sect_iter)->GetID() == sect_id) { *sect_iter = sect_sp; return true; } else if (depth > 0) { if ((*sect_iter) ->GetChildren() .ReplaceSection(sect_id, sect_sp, depth - 1)) return true; } } return false; } size_t SectionList::GetNumSections(uint32_t depth) const { size_t count = m_sections.size(); if (depth > 0) { const_iterator sect_iter, end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) { count += (*sect_iter)->GetChildren().GetNumSections(depth - 1); } } return count; } SectionSP SectionList::GetSectionAtIndex(size_t idx) const { SectionSP sect_sp; if (idx < m_sections.size()) sect_sp = m_sections[idx]; return sect_sp; } SectionSP SectionList::FindSectionByName(const ConstString §ion_dstr) const { SectionSP sect_sp; // Check if we have a valid section string if (section_dstr && !m_sections.empty()) { const_iterator sect_iter; const_iterator end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) { Section *child_section = sect_iter->get(); if (child_section) { if (child_section->GetName() == section_dstr) { sect_sp = *sect_iter; } else { sect_sp = child_section->GetChildren().FindSectionByName(section_dstr); } } } } return sect_sp; } SectionSP SectionList::FindSectionByID(user_id_t sect_id) const { SectionSP sect_sp; if (sect_id) { const_iterator sect_iter; const_iterator end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) { if ((*sect_iter)->GetID() == sect_id) { sect_sp = *sect_iter; break; } else { sect_sp = (*sect_iter)->GetChildren().FindSectionByID(sect_id); } } } return sect_sp; } SectionSP SectionList::FindSectionByType(SectionType sect_type, bool check_children, size_t start_idx) const { SectionSP sect_sp; size_t num_sections = m_sections.size(); for (size_t idx = start_idx; idx < num_sections; ++idx) { if (m_sections[idx]->GetType() == sect_type) { sect_sp = m_sections[idx]; break; } else if (check_children) { sect_sp = m_sections[idx]->GetChildren().FindSectionByType( sect_type, check_children, 0); if (sect_sp) break; } } return sect_sp; } SectionSP SectionList::FindSectionContainingFileAddress(addr_t vm_addr, uint32_t depth) const { SectionSP sect_sp; const_iterator sect_iter; const_iterator end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end && sect_sp.get() == NULL; ++sect_iter) { Section *sect = sect_iter->get(); if (sect->ContainsFileAddress(vm_addr)) { // The file address is in this section. We need to make sure one of our // child // sections doesn't contain this address as well as obeying the depth // limit // that was passed in. if (depth > 0) sect_sp = sect->GetChildren().FindSectionContainingFileAddress( vm_addr, depth - 1); if (sect_sp.get() == NULL && !sect->IsFake()) sect_sp = *sect_iter; } } return sect_sp; } bool SectionList::ContainsSection(user_id_t sect_id) const { return FindSectionByID(sect_id).get() != NULL; } void SectionList::Dump(Stream *s, Target *target, bool show_header, uint32_t depth) const { bool target_has_loaded_sections = target && !target->GetSectionLoadList().IsEmpty(); if (show_header && !m_sections.empty()) { s->Indent(); s->Printf("SectID Type %s Address " " Perm File Off. File Size Flags " " Section Name\n", target_has_loaded_sections ? "Load" : "File"); s->Indent(); s->PutCString("---------- ---------------- " "--------------------------------------- ---- ---------- " "---------- " "---------- ----------------------------\n"); } const_iterator sect_iter; const_iterator end = m_sections.end(); for (sect_iter = m_sections.begin(); sect_iter != end; ++sect_iter) { (*sect_iter)->Dump(s, target_has_loaded_sections ? target : NULL, depth); } if (show_header && !m_sections.empty()) s->IndentLess(); } size_t SectionList::Slide(addr_t slide_amount, bool slide_children) { size_t count = 0; const_iterator pos, end = m_sections.end(); for (pos = m_sections.begin(); pos != end; ++pos) { if ((*pos)->Slide(slide_amount, slide_children)) ++count; } return count; } Index: vendor/lldb/dist/source/DataFormatters/TypeCategory.cpp =================================================================== --- vendor/lldb/dist/source/DataFormatters/TypeCategory.cpp (revision 319789) +++ vendor/lldb/dist/source/DataFormatters/TypeCategory.cpp (revision 319790) @@ -1,643 +1,628 @@ //===-- TypeCategory.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/DataFormatters/TypeCategory.h" #include "lldb/Target/Language.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes using namespace lldb; using namespace lldb_private; TypeCategoryImpl::TypeCategoryImpl( IFormatChangeListener *clist, ConstString name, std::initializer_list langs) : m_format_cont("format", "regex-format", clist), m_summary_cont("summary", "regex-summary", clist), m_filter_cont("filter", "regex-filter", clist), #ifndef LLDB_DISABLE_PYTHON m_synth_cont("synth", "regex-synth", clist), #endif m_validator_cont("validator", "regex-validator", clist), m_enabled(false), m_change_listener(clist), m_mutex(), m_name(name), m_languages() { for (const lldb::LanguageType lang : langs) AddLanguage(lang); } static bool IsApplicable(lldb::LanguageType category_lang, lldb::LanguageType valobj_lang) { switch (category_lang) { - // these are not languages that LLDB would ordinarily deal with - // only allow an exact equality here, since we really don't know - // any better - case eLanguageTypeAda83: - case eLanguageTypeCobol74: - case eLanguageTypeCobol85: - case eLanguageTypeFortran77: - case eLanguageTypeFortran90: - case eLanguageTypePascal83: - case eLanguageTypeModula2: - case eLanguageTypeJava: - case eLanguageTypeAda95: - case eLanguageTypeFortran95: - case eLanguageTypePLI: - case eLanguageTypeUPC: - case eLanguageTypeD: - case eLanguageTypePython: + // Unless we know better, allow only exact equality. + default: return category_lang == valobj_lang; // the C family, we consider it as one case eLanguageTypeC89: case eLanguageTypeC: case eLanguageTypeC99: return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || valobj_lang == eLanguageTypeC99; // ObjC knows about C and itself case eLanguageTypeObjC: return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || valobj_lang == eLanguageTypeC99 || valobj_lang == eLanguageTypeObjC; // C++ knows about C and C++ case eLanguageTypeC_plus_plus: return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || valobj_lang == eLanguageTypeC99 || valobj_lang == eLanguageTypeC_plus_plus; // ObjC++ knows about C,C++,ObjC and ObjC++ case eLanguageTypeObjC_plus_plus: return valobj_lang == eLanguageTypeC89 || valobj_lang == eLanguageTypeC || valobj_lang == eLanguageTypeC99 || valobj_lang == eLanguageTypeC_plus_plus || valobj_lang == eLanguageTypeObjC; - default: + // Categories with unspecified language match everything. case eLanguageTypeUnknown: return true; } } bool TypeCategoryImpl::IsApplicable(ValueObject &valobj) { lldb::LanguageType valobj_lang = valobj.GetObjectRuntimeLanguage(); for (size_t idx = 0; idx < GetNumLanguages(); idx++) { const lldb::LanguageType category_lang = GetLanguageAtIndex(idx); if (::IsApplicable(category_lang, valobj_lang)) return true; } return false; } size_t TypeCategoryImpl::GetNumLanguages() { if (m_languages.empty()) return 1; return m_languages.size(); } lldb::LanguageType TypeCategoryImpl::GetLanguageAtIndex(size_t idx) { if (m_languages.empty()) return lldb::eLanguageTypeUnknown; return m_languages[idx]; } void TypeCategoryImpl::AddLanguage(lldb::LanguageType lang) { m_languages.push_back(lang); } bool TypeCategoryImpl::HasLanguage(lldb::LanguageType lang) { const auto iter = std::find(m_languages.begin(), m_languages.end(), lang), end = m_languages.end(); return (iter != end); } bool TypeCategoryImpl::Get(ValueObject &valobj, const FormattersMatchVector &candidates, lldb::TypeFormatImplSP &entry, uint32_t *reason) { if (!IsEnabled() || !IsApplicable(valobj)) return false; if (GetTypeFormatsContainer()->Get(candidates, entry, reason)) return true; bool regex = GetRegexTypeFormatsContainer()->Get(candidates, entry, reason); if (regex && reason) *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; return regex; } bool TypeCategoryImpl::Get(ValueObject &valobj, const FormattersMatchVector &candidates, lldb::TypeSummaryImplSP &entry, uint32_t *reason) { if (!IsEnabled() || !IsApplicable(valobj)) return false; if (GetTypeSummariesContainer()->Get(candidates, entry, reason)) return true; bool regex = GetRegexTypeSummariesContainer()->Get(candidates, entry, reason); if (regex && reason) *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; return regex; } bool TypeCategoryImpl::Get(ValueObject &valobj, const FormattersMatchVector &candidates, lldb::SyntheticChildrenSP &entry, uint32_t *reason) { if (!IsEnabled() || !IsApplicable(valobj)) return false; TypeFilterImpl::SharedPointer filter_sp; uint32_t reason_filter = 0; bool regex_filter = false; // first find both Filter and Synth, and then check which is most recent if (!GetTypeFiltersContainer()->Get(candidates, filter_sp, &reason_filter)) regex_filter = GetRegexTypeFiltersContainer()->Get(candidates, filter_sp, &reason_filter); #ifndef LLDB_DISABLE_PYTHON bool regex_synth = false; uint32_t reason_synth = 0; bool pick_synth = false; ScriptedSyntheticChildren::SharedPointer synth; if (!GetTypeSyntheticsContainer()->Get(candidates, synth, &reason_synth)) regex_synth = GetRegexTypeSyntheticsContainer()->Get(candidates, synth, &reason_synth); if (!filter_sp.get() && !synth.get()) return false; else if (!filter_sp.get() && synth.get()) pick_synth = true; else if (filter_sp.get() && !synth.get()) pick_synth = false; else /*if (filter_sp.get() && synth.get())*/ { if (filter_sp->GetRevision() > synth->GetRevision()) pick_synth = false; else pick_synth = true; } if (pick_synth) { if (regex_synth && reason) *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; entry = synth; return true; } else { if (regex_filter && reason) *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; entry = filter_sp; return true; } #else if (filter_sp) { entry = filter_sp; return true; } #endif return false; } bool TypeCategoryImpl::Get(ValueObject &valobj, const FormattersMatchVector &candidates, lldb::TypeValidatorImplSP &entry, uint32_t *reason) { if (!IsEnabled()) return false; if (GetTypeValidatorsContainer()->Get(candidates, entry, reason)) return true; bool regex = GetRegexTypeValidatorsContainer()->Get(candidates, entry, reason); if (regex && reason) *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; return regex; } void TypeCategoryImpl::Clear(FormatCategoryItems items) { if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue) GetTypeFormatsContainer()->Clear(); if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue) GetRegexTypeFormatsContainer()->Clear(); if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary) GetTypeSummariesContainer()->Clear(); if ((items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary) GetRegexTypeSummariesContainer()->Clear(); if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter) GetTypeFiltersContainer()->Clear(); if ((items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter) GetRegexTypeFiltersContainer()->Clear(); #ifndef LLDB_DISABLE_PYTHON if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth) GetTypeSyntheticsContainer()->Clear(); if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth) GetRegexTypeSyntheticsContainer()->Clear(); #endif if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator) GetTypeValidatorsContainer()->Clear(); if ((items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator) GetRegexTypeValidatorsContainer()->Clear(); } bool TypeCategoryImpl::Delete(ConstString name, FormatCategoryItems items) { bool success = false; if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue) success = GetTypeFormatsContainer()->Delete(name) || success; if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue) success = GetRegexTypeFormatsContainer()->Delete(name) || success; if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary) success = GetTypeSummariesContainer()->Delete(name) || success; if ((items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary) success = GetRegexTypeSummariesContainer()->Delete(name) || success; if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter) success = GetTypeFiltersContainer()->Delete(name) || success; if ((items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter) success = GetRegexTypeFiltersContainer()->Delete(name) || success; #ifndef LLDB_DISABLE_PYTHON if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth) success = GetTypeSyntheticsContainer()->Delete(name) || success; if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth) success = GetRegexTypeSyntheticsContainer()->Delete(name) || success; #endif if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator) success = GetTypeValidatorsContainer()->Delete(name) || success; if ((items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator) success = GetRegexTypeValidatorsContainer()->Delete(name) || success; return success; } uint32_t TypeCategoryImpl::GetCount(FormatCategoryItems items) { uint32_t count = 0; if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue) count += GetTypeFormatsContainer()->GetCount(); if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue) count += GetRegexTypeFormatsContainer()->GetCount(); if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary) count += GetTypeSummariesContainer()->GetCount(); if ((items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary) count += GetRegexTypeSummariesContainer()->GetCount(); if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter) count += GetTypeFiltersContainer()->GetCount(); if ((items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter) count += GetRegexTypeFiltersContainer()->GetCount(); #ifndef LLDB_DISABLE_PYTHON if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth) count += GetTypeSyntheticsContainer()->GetCount(); if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth) count += GetRegexTypeSyntheticsContainer()->GetCount(); #endif if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator) count += GetTypeValidatorsContainer()->GetCount(); if ((items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator) count += GetRegexTypeValidatorsContainer()->GetCount(); return count; } bool TypeCategoryImpl::AnyMatches(ConstString type_name, FormatCategoryItems items, bool only_enabled, const char **matching_category, FormatCategoryItems *matching_type) { if (!IsEnabled() && only_enabled) return false; lldb::TypeFormatImplSP format_sp; lldb::TypeSummaryImplSP summary_sp; TypeFilterImpl::SharedPointer filter_sp; #ifndef LLDB_DISABLE_PYTHON ScriptedSyntheticChildren::SharedPointer synth_sp; #endif TypeValidatorImpl::SharedPointer validator_sp; if ((items & eFormatCategoryItemValue) == eFormatCategoryItemValue) { if (GetTypeFormatsContainer()->Get(type_name, format_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemValue; return true; } } if ((items & eFormatCategoryItemRegexValue) == eFormatCategoryItemRegexValue) { if (GetRegexTypeFormatsContainer()->Get(type_name, format_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemRegexValue; return true; } } if ((items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary) { if (GetTypeSummariesContainer()->Get(type_name, summary_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemSummary; return true; } } if ((items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary) { if (GetRegexTypeSummariesContainer()->Get(type_name, summary_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemRegexSummary; return true; } } if ((items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter) { if (GetTypeFiltersContainer()->Get(type_name, filter_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemFilter; return true; } } if ((items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter) { if (GetRegexTypeFiltersContainer()->Get(type_name, filter_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemRegexFilter; return true; } } #ifndef LLDB_DISABLE_PYTHON if ((items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth) { if (GetTypeSyntheticsContainer()->Get(type_name, synth_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemSynth; return true; } } if ((items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth) { if (GetRegexTypeSyntheticsContainer()->Get(type_name, synth_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemRegexSynth; return true; } } #endif if ((items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator) { if (GetTypeValidatorsContainer()->Get(type_name, validator_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemValidator; return true; } } if ((items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator) { if (GetRegexTypeValidatorsContainer()->Get(type_name, validator_sp)) { if (matching_category) *matching_category = m_name.GetCString(); if (matching_type) *matching_type = eFormatCategoryItemRegexValidator; return true; } } return false; } TypeCategoryImpl::FormatContainer::MapValueType TypeCategoryImpl::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) { FormatContainer::MapValueType retval; if (type_sp) { if (type_sp->IsRegex()) GetRegexTypeFormatsContainer()->GetExact(ConstString(type_sp->GetName()), retval); else GetTypeFormatsContainer()->GetExact(ConstString(type_sp->GetName()), retval); } return retval; } TypeCategoryImpl::SummaryContainer::MapValueType TypeCategoryImpl::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) { SummaryContainer::MapValueType retval; if (type_sp) { if (type_sp->IsRegex()) GetRegexTypeSummariesContainer()->GetExact( ConstString(type_sp->GetName()), retval); else GetTypeSummariesContainer()->GetExact(ConstString(type_sp->GetName()), retval); } return retval; } TypeCategoryImpl::FilterContainer::MapValueType TypeCategoryImpl::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) { FilterContainer::MapValueType retval; if (type_sp) { if (type_sp->IsRegex()) GetRegexTypeFiltersContainer()->GetExact(ConstString(type_sp->GetName()), retval); else GetTypeFiltersContainer()->GetExact(ConstString(type_sp->GetName()), retval); } return retval; } #ifndef LLDB_DISABLE_PYTHON TypeCategoryImpl::SynthContainer::MapValueType TypeCategoryImpl::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) { SynthContainer::MapValueType retval; if (type_sp) { if (type_sp->IsRegex()) GetRegexTypeSyntheticsContainer()->GetExact( ConstString(type_sp->GetName()), retval); else GetTypeSyntheticsContainer()->GetExact(ConstString(type_sp->GetName()), retval); } return retval; } #endif TypeCategoryImpl::ValidatorContainer::MapValueType TypeCategoryImpl::GetValidatorForType(lldb::TypeNameSpecifierImplSP type_sp) { ValidatorContainer::MapValueType retval; if (type_sp) { if (type_sp->IsRegex()) GetRegexTypeValidatorsContainer()->GetExact( ConstString(type_sp->GetName()), retval); else GetTypeValidatorsContainer()->GetExact(ConstString(type_sp->GetName()), retval); } return retval; } lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex(size_t index) { if (index < GetTypeSummariesContainer()->GetCount()) return GetTypeSummariesContainer()->GetTypeNameSpecifierAtIndex(index); else return GetRegexTypeSummariesContainer()->GetTypeNameSpecifierAtIndex( index - GetTypeSummariesContainer()->GetCount()); } TypeCategoryImpl::FormatContainer::MapValueType TypeCategoryImpl::GetFormatAtIndex(size_t index) { if (index < GetTypeFormatsContainer()->GetCount()) return GetTypeFormatsContainer()->GetAtIndex(index); else return GetRegexTypeFormatsContainer()->GetAtIndex( index - GetTypeFormatsContainer()->GetCount()); } TypeCategoryImpl::SummaryContainer::MapValueType TypeCategoryImpl::GetSummaryAtIndex(size_t index) { if (index < GetTypeSummariesContainer()->GetCount()) return GetTypeSummariesContainer()->GetAtIndex(index); else return GetRegexTypeSummariesContainer()->GetAtIndex( index - GetTypeSummariesContainer()->GetCount()); } TypeCategoryImpl::FilterContainer::MapValueType TypeCategoryImpl::GetFilterAtIndex(size_t index) { if (index < GetTypeFiltersContainer()->GetCount()) return GetTypeFiltersContainer()->GetAtIndex(index); else return GetRegexTypeFiltersContainer()->GetAtIndex( index - GetTypeFiltersContainer()->GetCount()); } lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForFormatAtIndex(size_t index) { if (index < GetTypeFormatsContainer()->GetCount()) return GetTypeFormatsContainer()->GetTypeNameSpecifierAtIndex(index); else return GetRegexTypeFormatsContainer()->GetTypeNameSpecifierAtIndex( index - GetTypeFormatsContainer()->GetCount()); } lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForFilterAtIndex(size_t index) { if (index < GetTypeFiltersContainer()->GetCount()) return GetTypeFiltersContainer()->GetTypeNameSpecifierAtIndex(index); else return GetRegexTypeFiltersContainer()->GetTypeNameSpecifierAtIndex( index - GetTypeFiltersContainer()->GetCount()); } #ifndef LLDB_DISABLE_PYTHON TypeCategoryImpl::SynthContainer::MapValueType TypeCategoryImpl::GetSyntheticAtIndex(size_t index) { if (index < GetTypeSyntheticsContainer()->GetCount()) return GetTypeSyntheticsContainer()->GetAtIndex(index); else return GetRegexTypeSyntheticsContainer()->GetAtIndex( index - GetTypeSyntheticsContainer()->GetCount()); } lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex(size_t index) { if (index < GetTypeSyntheticsContainer()->GetCount()) return GetTypeSyntheticsContainer()->GetTypeNameSpecifierAtIndex(index); else return GetRegexTypeSyntheticsContainer()->GetTypeNameSpecifierAtIndex( index - GetTypeSyntheticsContainer()->GetCount()); } #endif TypeCategoryImpl::ValidatorContainer::MapValueType TypeCategoryImpl::GetValidatorAtIndex(size_t index) { if (index < GetTypeValidatorsContainer()->GetCount()) return GetTypeValidatorsContainer()->GetAtIndex(index); else return GetRegexTypeValidatorsContainer()->GetAtIndex( index - GetTypeValidatorsContainer()->GetCount()); } lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForValidatorAtIndex(size_t index) { if (index < GetTypeValidatorsContainer()->GetCount()) return GetTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex(index); else return GetRegexTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex( index - GetTypeValidatorsContainer()->GetCount()); } void TypeCategoryImpl::Enable(bool value, uint32_t position) { std::lock_guard guard(m_mutex); if ((m_enabled = value)) m_enabled_position = position; if (m_change_listener) m_change_listener->Changed(); } std::string TypeCategoryImpl::GetDescription() { StreamString stream; stream.Printf("%s (%s", GetName(), (IsEnabled() ? "enabled" : "disabled")); StreamString lang_stream; lang_stream.Printf(", applicable for language(s): "); bool print_lang = false; for (size_t idx = 0; idx < GetNumLanguages(); idx++) { const lldb::LanguageType lang = GetLanguageAtIndex(idx); if (lang != lldb::eLanguageTypeUnknown) print_lang = true; lang_stream.Printf("%s%s", Language::GetNameForLanguageType(lang), idx + 1 < GetNumLanguages() ? ", " : ""); } if (print_lang) stream.PutCString(lang_stream.GetString()); stream.PutChar(')'); return stream.GetString(); } Index: vendor/lldb/dist/source/Host/common/Host.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Host.cpp (revision 319789) +++ vendor/lldb/dist/source/Host/common/Host.cpp (revision 319790) @@ -1,990 +1,990 @@ //===-- Host.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 #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #endif #if defined(__APPLE__) #include #include #include #endif #if defined(__linux__) || defined(__FreeBSD__) || \ defined(__FreeBSD_kernel__) || defined(__APPLE__) || \ defined(__NetBSD__) || defined(__OpenBSD__) #if !defined(__ANDROID__) #include #endif #include #include #endif #if defined(__FreeBSD__) #include #endif #if defined(__NetBSD__) #include #endif // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/MonitoringProcessLauncher.h" #include "lldb/Host/Predicate.h" #include "lldb/Host/ProcessLauncher.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/CleanUp.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/lldb-private-forward.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #if defined(_WIN32) #include "lldb/Host/windows/ProcessLauncherWindows.h" #elif defined(__linux__) || defined(__NetBSD__) #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #else #include "lldb/Host/posix/ProcessLauncherPosix.h" #endif #if defined(__APPLE__) #ifndef _POSIX_SPAWN_DISABLE_ASLR #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 #endif extern "C" { int __pthread_chdir(const char *path); int __pthread_fchdir(int fildes); } #endif using namespace lldb; using namespace lldb_private; #if !defined(__APPLE__) && !defined(_WIN32) struct MonitorInfo { lldb::pid_t pid; // The process ID to monitor Host::MonitorChildProcessCallback callback; // The callback function to call when "pid" exits or signals bool monitor_signals; // If true, call the callback when "pid" gets signaled. }; static thread_result_t MonitorChildProcessThreadFunction(void *arg); HostThread Host::StartMonitoringChildProcess( const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, bool monitor_signals) { MonitorInfo *info_ptr = new MonitorInfo(); info_ptr->pid = pid; info_ptr->callback = callback; info_ptr->monitor_signals = monitor_signals; char thread_name[256]; ::snprintf(thread_name, sizeof(thread_name), "", pid); return ThreadLauncher::LaunchThread( thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL); } #ifndef __linux__ //------------------------------------------------------------------ // Scoped class that will disable thread canceling when it is // constructed, and exception safely restore the previous value it // when it goes out of scope. //------------------------------------------------------------------ class ScopedPThreadCancelDisabler { public: ScopedPThreadCancelDisabler() { // Disable the ability for this thread to be cancelled int err = ::pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &m_old_state); if (err != 0) m_old_state = -1; } ~ScopedPThreadCancelDisabler() { // Restore the ability for this thread to be cancelled to what it // previously was. if (m_old_state != -1) ::pthread_setcancelstate(m_old_state, 0); } private: int m_old_state; // Save the old cancelability state. }; #endif // __linux__ #ifdef __linux__ #if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) static __thread volatile sig_atomic_t g_usr1_called; #else static thread_local volatile sig_atomic_t g_usr1_called; #endif static void SigUsr1Handler(int) { g_usr1_called = 1; } #endif // __linux__ static bool CheckForMonitorCancellation() { #ifdef __linux__ if (g_usr1_called) { g_usr1_called = 0; return true; } #else ::pthread_testcancel(); #endif return false; } static thread_result_t MonitorChildProcessThreadFunction(void *arg) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); const char *function = __FUNCTION__; if (log) log->Printf("%s (arg = %p) thread starting...", function, arg); MonitorInfo *info = (MonitorInfo *)arg; const Host::MonitorChildProcessCallback callback = info->callback; const bool monitor_signals = info->monitor_signals; assert(info->pid <= UINT32_MAX); const ::pid_t pid = monitor_signals ? -1 * getpgid(info->pid) : info->pid; delete info; int status = -1; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #define __WALL 0 #endif const int options = __WALL; #ifdef __linux__ // This signal is only used to interrupt the thread from waitpid struct sigaction sigUsr1Action; memset(&sigUsr1Action, 0, sizeof(sigUsr1Action)); sigUsr1Action.sa_handler = SigUsr1Handler; ::sigaction(SIGUSR1, &sigUsr1Action, nullptr); #endif // __linux__ while (1) { log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i)...", function, pid, options); if (CheckForMonitorCancellation()) break; // Get signals from all children with same process group of pid const ::pid_t wait_pid = ::waitpid(pid, &status, options); if (CheckForMonitorCancellation()) break; if (wait_pid == -1) { if (errno == EINTR) continue; else { - if (log) - log->Printf( - "%s (arg = %p) thread exiting because waitpid failed (%s)...", - __FUNCTION__, arg, strerror(errno)); + LLDB_LOG(log, + "arg = {0}, thread exiting because waitpid failed ({1})...", + arg, llvm::sys::StrError()); break; } } else if (wait_pid > 0) { bool exited = false; int signal = 0; int exit_status = 0; const char *status_cstr = NULL; if (WIFSTOPPED(status)) { signal = WSTOPSIG(status); status_cstr = "STOPPED"; } else if (WIFEXITED(status)) { exit_status = WEXITSTATUS(status); status_cstr = "EXITED"; exited = true; } else if (WIFSIGNALED(status)) { signal = WTERMSIG(status); status_cstr = "SIGNALED"; if (wait_pid == abs(pid)) { exited = true; exit_status = -1; } } else { status_cstr = "(\?\?\?)"; } // Scope for pthread_cancel_disabler { #ifndef __linux__ ScopedPThreadCancelDisabler pthread_cancel_disabler; #endif log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s ::waitpid (pid = %" PRIi32 ", &status, options = %i) => pid = %" PRIi32 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", function, pid, options, wait_pid, status, status_cstr, signal, exit_status); if (exited || (signal != 0 && monitor_signals)) { bool callback_return = false; if (callback) callback_return = callback(wait_pid, exited, signal, exit_status); // If our process exited, then this thread should exit if (exited && wait_pid == abs(pid)) { if (log) log->Printf("%s (arg = %p) thread exiting because pid received " "exit signal...", __FUNCTION__, arg); break; } // If the callback returns true, it means this process should // exit if (callback_return) { if (log) log->Printf("%s (arg = %p) thread exiting because callback " "returned true...", __FUNCTION__, arg); break; } } } } } log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS); if (log) log->Printf("%s (arg = %p) thread exiting...", __FUNCTION__, arg); return NULL; } #endif // #if !defined (__APPLE__) && !defined (_WIN32) #if !defined(__APPLE__) void Host::SystemLog(SystemLogType type, const char *format, va_list args) { vfprintf(stderr, format, args); } #endif void Host::SystemLog(SystemLogType type, const char *format, ...) { va_list args; va_start(args, format); SystemLog(type, format, args); va_end(args); } lldb::pid_t Host::GetCurrentProcessID() { return ::getpid(); } #ifndef _WIN32 lldb::thread_t Host::GetCurrentThread() { return lldb::thread_t(pthread_self()); } const char *Host::GetSignalAsCString(int signo) { switch (signo) { case SIGHUP: return "SIGHUP"; // 1 hangup case SIGINT: return "SIGINT"; // 2 interrupt case SIGQUIT: return "SIGQUIT"; // 3 quit case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught) case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught) case SIGABRT: return "SIGABRT"; // 6 abort() #if defined(SIGPOLL) #if !defined(SIGIO) || (SIGPOLL != SIGIO) // Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to // fail with 'multiple define cases with same value' case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) #endif #endif #if defined(SIGEMT) case SIGEMT: return "SIGEMT"; // 7 EMT instruction #endif case SIGFPE: return "SIGFPE"; // 8 floating point exception case SIGKILL: return "SIGKILL"; // 9 kill (cannot be caught or ignored) case SIGBUS: return "SIGBUS"; // 10 bus error case SIGSEGV: return "SIGSEGV"; // 11 segmentation violation case SIGSYS: return "SIGSYS"; // 12 bad argument to system call case SIGPIPE: return "SIGPIPE"; // 13 write on a pipe with no one to read it case SIGALRM: return "SIGALRM"; // 14 alarm clock case SIGTERM: return "SIGTERM"; // 15 software termination signal from kill case SIGURG: return "SIGURG"; // 16 urgent condition on IO channel case SIGSTOP: return "SIGSTOP"; // 17 sendable stop signal not from tty case SIGTSTP: return "SIGTSTP"; // 18 stop signal from tty case SIGCONT: return "SIGCONT"; // 19 continue a stopped process case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local<OSTOP) #if defined(SIGIO) case SIGIO: return "SIGIO"; // 23 input/output possible signal #endif case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm case SIGPROF: return "SIGPROF"; // 27 profiling time alarm #if defined(SIGWINCH) case SIGWINCH: return "SIGWINCH"; // 28 window size changes #endif #if defined(SIGINFO) case SIGINFO: return "SIGINFO"; // 29 information request #endif case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1 case SIGUSR2: return "SIGUSR2"; // 31 user defined signal 2 default: break; } return NULL; } #endif #ifndef _WIN32 lldb::thread_key_t Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) { pthread_key_t key; ::pthread_key_create(&key, callback); return key; } void *Host::ThreadLocalStorageGet(lldb::thread_key_t key) { return ::pthread_getspecific(key); } void Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) { ::pthread_setspecific(key, value); } #endif #if !defined(__APPLE__) // see Host.mm bool Host::GetBundleDirectory(const FileSpec &file, FileSpec &bundle) { bundle.Clear(); return false; } bool Host::ResolveExecutableInBundle(FileSpec &file) { return false; } #endif #ifndef _WIN32 FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { FileSpec module_filespec; #if !defined(__ANDROID__) Dl_info info; if (::dladdr(host_addr, &info)) { if (info.dli_fname) module_filespec.SetFile(info.dli_fname, true); } #endif return module_filespec; } #endif #if !defined(__linux__) bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { return false; } #endif struct ShellInfo { ShellInfo() : process_reaped(false), pid(LLDB_INVALID_PROCESS_ID), signo(-1), status(-1) {} lldb_private::Predicate process_reaped; lldb::pid_t pid; int signo; int status; }; static bool MonitorShellCommand(std::shared_ptr shell_info, lldb::pid_t pid, bool exited, // True if the process did exit int signo, // Zero for no signal int status) // Exit value of process if signal is zero { shell_info->pid = pid; shell_info->signo = signo; shell_info->status = status; // Let the thread running Host::RunShellCommand() know that the process // exited and that ShellInfo has been filled in by broadcasting to it shell_info->process_reaped.SetValue(true, eBroadcastAlways); return true; } Status Host::RunShellCommand(const char *command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, uint32_t timeout_sec, bool run_in_default_shell) { return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr, command_output_ptr, timeout_sec, run_in_default_shell); } Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, uint32_t timeout_sec, bool run_in_default_shell) { Status error; ProcessLaunchInfo launch_info; launch_info.SetArchitecture(HostInfo::GetArchitecture()); if (run_in_default_shell) { // Run the command in a shell launch_info.SetShell(HostInfo::GetDefaultShell()); launch_info.GetArguments().AppendArguments(args); const bool localhost = true; const bool will_debug = false; const bool first_arg_is_full_shell_command = false; launch_info.ConvertArgumentsForLaunchingInShell( error, localhost, will_debug, first_arg_is_full_shell_command, 0); } else { // No shell, just run it const bool first_arg_is_executable = true; launch_info.SetArguments(args, first_arg_is_executable); } if (working_dir) launch_info.SetWorkingDirectory(working_dir); llvm::SmallString output_file_path; if (command_output_ptr) { // Create a temporary file to get the stdout/stderr and redirect the // output of the command into this file. We will later read this file // if all goes well and fill the data into "command_output_ptr" FileSpec tmpdir_file_spec; if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%"); llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath(), output_file_path); } else { llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path); } } FileSpec output_file_spec{output_file_path.c_str(), false}; launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); if (output_file_spec) { launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false, true); launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); } else { launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); } std::shared_ptr shell_info_sp(new ShellInfo()); const bool monitor_signals = false; launch_info.SetMonitorProcessCallback( std::bind(MonitorShellCommand, shell_info_sp, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), monitor_signals); error = LaunchProcess(launch_info); const lldb::pid_t pid = launch_info.GetProcessID(); if (error.Success() && pid == LLDB_INVALID_PROCESS_ID) error.SetErrorString("failed to get process ID"); if (error.Success()) { bool timed_out = false; shell_info_sp->process_reaped.WaitForValueEqualTo( true, std::chrono::seconds(timeout_sec), &timed_out); if (timed_out) { error.SetErrorString("timed out waiting for shell command to complete"); // Kill the process since it didn't complete within the timeout specified Kill(pid, SIGKILL); // Wait for the monitor callback to get the message timed_out = false; shell_info_sp->process_reaped.WaitForValueEqualTo( true, std::chrono::seconds(1), &timed_out); } else { if (status_ptr) *status_ptr = shell_info_sp->status; if (signo_ptr) *signo_ptr = shell_info_sp->signo; if (command_output_ptr) { command_output_ptr->clear(); uint64_t file_size = output_file_spec.GetByteSize(); if (file_size > 0) { if (file_size > command_output_ptr->max_size()) { error.SetErrorStringWithFormat( "shell command output is too large to fit into a std::string"); } else { auto Buffer = DataBufferLLVM::CreateFromPath(output_file_spec.GetPath()); if (error.Success()) command_output_ptr->assign(Buffer->GetChars(), Buffer->GetByteSize()); } } } } } llvm::sys::fs::remove(output_file_spec.GetPath()); return error; } // LaunchProcessPosixSpawn for Apple, Linux, FreeBSD, NetBSD and other GLIBC // systems #if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \ defined(__GLIBC__) || defined(__NetBSD__) #if !defined(__ANDROID__) // this method needs to be visible to macosx/Host.cpp and // common/Host.cpp. short Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; #if defined(__APPLE__) if (launch_info.GetFlags().Test(eLaunchFlagExec)) flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag if (launch_info.GetFlags().Test(eLaunchFlagDebug)) flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag if (launch_info.GetLaunchInSeparateProcessGroup()) flags |= POSIX_SPAWN_SETPGROUP; #ifdef POSIX_SPAWN_CLOEXEC_DEFAULT #if defined(__APPLE__) && (defined(__x86_64__) || defined(__i386__)) static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate; if (g_use_close_on_exec_flag == eLazyBoolCalculate) { g_use_close_on_exec_flag = eLazyBoolNo; uint32_t major, minor, update; if (HostInfo::GetOSVersion(major, minor, update)) { // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or // earlier if (major > 10 || (major == 10 && minor > 7)) { // Only enable for 10.8 and later OS versions g_use_close_on_exec_flag = eLazyBoolYes; } } } #else static LazyBool g_use_close_on_exec_flag = eLazyBoolYes; #endif // Close all files exception those with file actions if this is supported. if (g_use_close_on_exec_flag == eLazyBoolYes) flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; #endif #endif // #if defined (__APPLE__) return flags; } Status Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid) { Status error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); posix_spawnattr_t attr; error.SetError(::posix_spawnattr_init(&attr), eErrorTypePOSIX); if (error.Fail()) { LLDB_LOG(log, "error: {0}, ::posix_spawnattr_init ( &attr )", error); return error; } // Make a quick class that will cleanup the posix spawn attributes in case // we return in the middle of this function. lldb_utility::CleanUp posix_spawnattr_cleanup( &attr, posix_spawnattr_destroy); sigset_t no_signals; sigset_t all_signals; sigemptyset(&no_signals); sigfillset(&all_signals); ::posix_spawnattr_setsigmask(&attr, &no_signals); #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) ::posix_spawnattr_setsigdefault(&attr, &no_signals); #else ::posix_spawnattr_setsigdefault(&attr, &all_signals); #endif short flags = GetPosixspawnFlags(launch_info); error.SetError(::posix_spawnattr_setflags(&attr, flags), eErrorTypePOSIX); if (error.Fail()) { LLDB_LOG(log, "error: {0}, ::posix_spawnattr_setflags ( &attr, flags={1:x} )", error, flags); return error; } // posix_spawnattr_setbinpref_np appears to be an Apple extension per: // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/ #if defined(__APPLE__) && !defined(__arm__) // Don't set the binpref if a shell was provided. After all, that's only // going to affect what version of the shell // is launched, not what fork of the binary is launched. We insert "arch // --arch as part of the shell invocation // to do that job on OSX. if (launch_info.GetShell() == nullptr) { // We don't need to do this for ARM, and we really shouldn't now that we // have multiple CPU subtypes and no posix_spawnattr call that allows us // to set which CPU subtype to launch... const ArchSpec &arch_spec = launch_info.GetArchitecture(); cpu_type_t cpu = arch_spec.GetMachOCPUType(); cpu_type_t sub = arch_spec.GetMachOCPUSubType(); if (cpu != 0 && cpu != static_cast(UINT32_MAX) && cpu != static_cast(LLDB_INVALID_CPUTYPE) && !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try // to set the CPU type or we will fail { size_t ocount = 0; error.SetError(::posix_spawnattr_setbinpref_np(&attr, 1, &cpu, &ocount), eErrorTypePOSIX); if (error.Fail()) LLDB_LOG(log, "error: {0}, ::posix_spawnattr_setbinpref_np ( &attr, 1, " "cpu_type = {1:x}, count => {2} )", error, cpu, ocount); if (error.Fail() || ocount != 1) return error; } } #endif const char *tmp_argv[2]; char *const *argv = const_cast( launch_info.GetArguments().GetConstArgumentVector()); char *const *envp = const_cast( launch_info.GetEnvironmentEntries().GetConstArgumentVector()); if (argv == NULL) { // posix_spawn gets very unhappy if it doesn't have at least the program // name in argv[0]. One of the side affects I have noticed is the // environment // variables don't make it into the child process if "argv == NULL"!!! tmp_argv[0] = exe_path; tmp_argv[1] = NULL; argv = const_cast(tmp_argv); } #if !defined(__APPLE__) // manage the working directory char current_dir[PATH_MAX]; current_dir[0] = '\0'; #endif FileSpec working_dir{launch_info.GetWorkingDirectory()}; if (working_dir) { #if defined(__APPLE__) // Set the working directory on this thread only if (__pthread_chdir(working_dir.GetCString()) < 0) { if (errno == ENOENT) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir.GetCString()); } else if (errno == ENOTDIR) { error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir.GetCString()); } else { error.SetErrorStringWithFormat("An unknown error occurred when " "changing directory for process " "execution."); } return error; } #else if (::getcwd(current_dir, sizeof(current_dir)) == NULL) { error.SetError(errno, eErrorTypePOSIX); LLDB_LOG(log, "error: {0}, unable to save the current directory", error); return error; } if (::chdir(working_dir.GetCString()) == -1) { error.SetError(errno, eErrorTypePOSIX); LLDB_LOG(log, "error: {0}, unable to change working directory to {1}", error, working_dir); return error; } #endif } ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; const size_t num_file_actions = launch_info.GetNumFileActions(); if (num_file_actions > 0) { posix_spawn_file_actions_t file_actions; error.SetError(::posix_spawn_file_actions_init(&file_actions), eErrorTypePOSIX); if (error.Fail()) { LLDB_LOG(log, "error: {0}, ::posix_spawn_file_actions_init ( &file_actions )", error); return error; } // Make a quick class that will cleanup the posix spawn attributes in case // we return in the middle of this function. lldb_utility::CleanUp posix_spawn_file_actions_cleanup(&file_actions, posix_spawn_file_actions_destroy); for (size_t i = 0; i < num_file_actions; ++i) { const FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); if (launch_file_action) { if (!AddPosixSpawnFileAction(&file_actions, launch_file_action, log, error)) return error; } } error.SetError( ::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail()) { LLDB_LOG(log, "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', " "file_actions = {3}, " "attr = {4}, argv = {5}, envp = {6} )", error, result_pid, exe_path, &file_actions, &attr, argv, envp); if (log) { for (int ii = 0; argv[ii]; ++ii) LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); } } } else { error.SetError( ::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail()) { LLDB_LOG(log, "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', " "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )", error, result_pid, exe_path, &attr, argv, envp); if (log) { for (int ii = 0; argv[ii]; ++ii) LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]); } } } pid = result_pid; if (working_dir) { #if defined(__APPLE__) // No more thread specific current working directory __pthread_fchdir(-1); #else if (::chdir(current_dir) == -1 && error.Success()) { error.SetError(errno, eErrorTypePOSIX); LLDB_LOG(log, "error: {0}, unable to change current directory back to {1}", error, current_dir); } #endif } return error; } bool Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Status &error) { if (info == NULL) return false; posix_spawn_file_actions_t *file_actions = reinterpret_cast(_file_actions); switch (info->GetAction()) { case FileAction::eFileActionNone: error.Clear(); break; case FileAction::eFileActionClose: if (info->GetFD() == -1) error.SetErrorString( "invalid fd for posix_spawn_file_actions_addclose(...)"); else { error.SetError( ::posix_spawn_file_actions_addclose(file_actions, info->GetFD()), eErrorTypePOSIX); if (error.Fail()) LLDB_LOG(log, "error: {0}, posix_spawn_file_actions_addclose " "(action={1}, fd={2})", error, file_actions, info->GetFD()); } break; case FileAction::eFileActionDuplicate: if (info->GetFD() == -1) error.SetErrorString( "invalid fd for posix_spawn_file_actions_adddup2(...)"); else if (info->GetActionArgument() == -1) error.SetErrorString( "invalid duplicate fd for posix_spawn_file_actions_adddup2(...)"); else { error.SetError( ::posix_spawn_file_actions_adddup2(file_actions, info->GetFD(), info->GetActionArgument()), eErrorTypePOSIX); if (error.Fail()) LLDB_LOG(log, "error: {0}, posix_spawn_file_actions_adddup2 " "(action={1}, fd={2}, dup_fd={3})", error, file_actions, info->GetFD(), info->GetActionArgument()); } break; case FileAction::eFileActionOpen: if (info->GetFD() == -1) error.SetErrorString( "invalid fd in posix_spawn_file_actions_addopen(...)"); else { int oflag = info->GetActionArgument(); mode_t mode = 0; if (oflag & O_CREAT) mode = 0640; error.SetError(::posix_spawn_file_actions_addopen( file_actions, info->GetFD(), info->GetPath().str().c_str(), oflag, mode), eErrorTypePOSIX); if (error.Fail()) LLDB_LOG( log, "error: {0}, posix_spawn_file_actions_addopen (action={1}, " "fd={2}, path='{3}', oflag={4}, mode={5})", error, file_actions, info->GetFD(), info->GetPath(), oflag, mode); } break; } return error.Success(); } #endif // !defined(__ANDROID__) #endif // defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || // defined (__GLIBC__) || defined(__NetBSD__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) || \ defined(__NetBSD__) || defined(_WIN32) // The functions below implement process launching via posix_spawn() for Linux, // FreeBSD and NetBSD. Status Host::LaunchProcess(ProcessLaunchInfo &launch_info) { std::unique_ptr delegate_launcher; #if defined(_WIN32) delegate_launcher.reset(new ProcessLauncherWindows()); #elif defined(__linux__) || defined(__NetBSD__) delegate_launcher.reset(new ProcessLauncherPosixFork()); #else delegate_launcher.reset(new ProcessLauncherPosix()); #endif MonitoringProcessLauncher launcher(std::move(delegate_launcher)); Status error; HostProcess process = launcher.LaunchProcess(launch_info, error); // TODO(zturner): It would be better if the entire HostProcess were returned // instead of writing // it into this structure. launch_info.SetProcessID(process.GetProcessId()); return error; } #endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) #ifndef _WIN32 void Host::Kill(lldb::pid_t pid, int signo) { ::kill(pid, signo); } #endif #if !defined(__APPLE__) bool Host::OpenFileInExternalEditor(const FileSpec &file_spec, uint32_t line_no) { return false; } #endif const UnixSignalsSP &Host::GetUnixSignals() { static const auto s_unix_signals_sp = UnixSignals::Create(HostInfo::GetArchitecture()); return s_unix_signals_sp; } Index: vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp (revision 319789) +++ vendor/lldb/dist/source/Host/posix/ConnectionFileDescriptorPosix.cpp (revision 319790) @@ -1,793 +1,792 @@ //===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(__APPLE__) // Enable this special support for Apple builds where we can have unlimited // select bounds. We tried switching to poll() and kqueue and we were panicing // the kernel, so we have to stick with select for now. #define _DARWIN_UNLIMITED_SELECT #endif #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" #include "lldb/Host/Config.h" #include "lldb/Host/IOObject.h" #include "lldb/Host/Socket.h" #include "lldb/Host/SocketAddress.h" #include "lldb/Utility/SelectHelper.h" // C Includes #include #include #include #include #include #ifndef LLDB_DISABLE_POSIX #include #endif // C++ Includes #include // Other libraries and framework includes +#include "llvm/Support/Errno.h" #include "llvm/Support/ErrorHandling.h" #if defined(__APPLE__) #include "llvm/ADT/SmallVector.h" #endif // Project includes #include "lldb/Core/Communication.h" #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" #include "lldb/Host/Socket.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" using namespace lldb; using namespace lldb_private; const char *ConnectionFileDescriptor::LISTEN_SCHEME = "listen"; const char *ConnectionFileDescriptor::ACCEPT_SCHEME = "accept"; const char *ConnectionFileDescriptor::UNIX_ACCEPT_SCHEME = "unix-accept"; const char *ConnectionFileDescriptor::CONNECT_SCHEME = "connect"; const char *ConnectionFileDescriptor::TCP_CONNECT_SCHEME = "tcp-connect"; const char *ConnectionFileDescriptor::UDP_SCHEME = "udp"; const char *ConnectionFileDescriptor::UNIX_CONNECT_SCHEME = "unix-connect"; const char *ConnectionFileDescriptor::UNIX_ABSTRACT_CONNECT_SCHEME = "unix-abstract-connect"; const char *ConnectionFileDescriptor::FD_SCHEME = "fd"; const char *ConnectionFileDescriptor::FILE_SCHEME = "file"; namespace { llvm::Optional GetURLAddress(llvm::StringRef url, llvm::StringRef scheme) { if (!url.consume_front(scheme)) return llvm::None; if (!url.consume_front("://")) return llvm::None; return url; } } ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(child_processes_inherit) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast(this)); } ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(false) { m_write_sp.reset(new File(fd, owns_fd)); m_read_sp.reset(new File(fd, false)); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = " "%i, owns_fd = %i)", static_cast(this), fd, owns_fd); OpenCommandPipe(); } ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket) : Connection(), m_pipe(), m_mutex(), m_shutting_down(false), m_waiting_for_accept(false), m_child_processes_inherit(false) { InitializeSocket(socket); } ConnectionFileDescriptor::~ConnectionFileDescriptor() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast(this)); Disconnect(NULL); CloseCommandPipe(); } void ConnectionFileDescriptor::OpenCommandPipe() { CloseCommandPipe(); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); // Make the command file descriptor here: Status result = m_pipe.CreateNew(m_child_processes_inherit); if (!result.Success()) { if (log) log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not " "make pipe: %s", static_cast(this), result.AsCString()); } else { if (log) log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success " "readfd=%d writefd=%d", static_cast(this), m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor()); } } void ConnectionFileDescriptor::CloseCommandPipe() { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast(this)); m_pipe.Close(); } bool ConnectionFileDescriptor::IsConnected() const { return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); } ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path, Status *error_ptr) { std::lock_guard guard(m_mutex); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast(this), path.str().c_str()); OpenCommandPipe(); if (!path.empty()) { llvm::Optional addr; if ((addr = GetURLAddress(path, LISTEN_SCHEME))) { // listen://HOST:PORT return SocketListenAndAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, ACCEPT_SCHEME))) { // unix://SOCKNAME return NamedSocketAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_ACCEPT_SCHEME))) { // unix://SOCKNAME return NamedSocketAccept(*addr, error_ptr); } else if ((addr = GetURLAddress(path, CONNECT_SCHEME))) { return ConnectTCP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, TCP_CONNECT_SCHEME))) { return ConnectTCP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UDP_SCHEME))) { return ConnectUDP(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_CONNECT_SCHEME))) { // unix-connect://SOCKNAME return NamedSocketConnect(*addr, error_ptr); } else if ((addr = GetURLAddress(path, UNIX_ABSTRACT_CONNECT_SCHEME))) { // unix-abstract-connect://SOCKNAME return UnixAbstractSocketConnect(*addr, error_ptr); } #ifndef LLDB_DISABLE_POSIX else if ((addr = GetURLAddress(path, FD_SCHEME))) { // Just passing a native file descriptor within this current process // that is already opened (possibly from a service or other source). int fd = -1; if (!addr->getAsInteger(0, fd)) { // We have what looks to be a valid file descriptor, but we // should make sure it is. We currently are doing this by trying to // get the flags from the file descriptor and making sure it // isn't a bad fd. errno = 0; int flags = ::fcntl(fd, F_GETFL, 0); if (flags == -1 || errno == EBADF) { if (error_ptr) error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", path.str().c_str()); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; } else { // Don't take ownership of a file descriptor that gets passed // to us since someone else opened the file descriptor and // handed it to us. // TODO: Since are using a URL to open connection we should // eventually parse options using the web standard where we // have "fd://123?opt1=value;opt2=value" and we can have an // option be "owns=1" or "owns=0" or something like this to // allow us to specify this. For now, we assume we must // assume we don't own it. std::unique_ptr tcp_socket; tcp_socket.reset(new TCPSocket(fd, false, false)); // Try and get a socket option from this file descriptor to // see if this is a socket and set m_is_socket accordingly. int resuse; bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); if (is_socket) { m_read_sp = std::move(tcp_socket); m_write_sp = m_read_sp; } else { m_read_sp.reset(new File(fd, false)); m_write_sp.reset(new File(fd, false)); } m_uri = *addr; return eConnectionStatusSuccess; } } if (error_ptr) error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"", path.str().c_str()); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; } else if ((addr = GetURLAddress(path, FILE_SCHEME))) { std::string addr_str = addr->str(); // file:///PATH int fd = -1; do { fd = ::open(addr_str.c_str(), O_RDWR); } while (fd == -1 && errno == EINTR); if (fd == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); return eConnectionStatusError; } if (::isatty(fd)) { // Set up serial terminal emulation struct termios options; ::tcgetattr(fd, &options); // Set port speed to maximum ::cfsetospeed(&options, B115200); ::cfsetispeed(&options, B115200); // Raw input, disable echo and signals options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Make sure only one character is needed to return from a read options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; ::tcsetattr(fd, TCSANOW, &options); } int flags = ::fcntl(fd, F_GETFL, 0); if (flags >= 0) { if ((flags & O_NONBLOCK) == 0) { flags |= O_NONBLOCK; ::fcntl(fd, F_SETFL, flags); } } m_read_sp.reset(new File(fd, true)); m_write_sp.reset(new File(fd, false)); return eConnectionStatusSuccess; } #endif if (error_ptr) error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", path.str().c_str()); return eConnectionStatusError; } if (error_ptr) error_ptr->SetErrorString("invalid connect arguments"); return eConnectionStatusError; } bool ConnectionFileDescriptor::InterruptRead() { size_t bytes_written = 0; Status result = m_pipe.Write("i", 1, bytes_written); return result.Success(); } ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast(this)); ConnectionStatus status = eConnectionStatusSuccess; if (!IsConnected()) { if (log) log->Printf( "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast(this)); return eConnectionStatusSuccess; } if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) static_cast(*m_read_sp).PreDisconnect(); // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite // likely // because somebody is doing a blocking read on our file descriptor. If // that's the case, // then send the "q" char to the command file channel so the read will wake up // and the connection // will then know to shut down. m_shutting_down = true; std::unique_lock locker(m_mutex, std::defer_lock); if (!locker.try_lock()) { if (m_pipe.CanWrite()) { size_t bytes_written = 0; Status result = m_pipe.Write("q", 1, bytes_written); if (log) log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get " "the lock, sent 'q' to %d, error = '%s'.", static_cast(this), m_pipe.GetWriteFileDescriptor(), result.AsCString()); } else if (log) { log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the " "lock, but no command pipe is available.", static_cast(this)); } locker.lock(); } Status error = m_read_sp->Close(); Status error2 = m_write_sp->Close(); if (error.Fail() || error2.Fail()) status = eConnectionStatusError; if (error_ptr) *error_ptr = error.Fail() ? error : error2; // Close any pipes we were using for async interrupts m_pipe.Close(); m_uri.clear(); m_shutting_down = false; return status; } size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len, const Timeout &timeout, ConnectionStatus &status, Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); std::unique_lock locker(m_mutex, std::defer_lock); if (!locker.try_lock()) { if (log) log->Printf("%p ConnectionFileDescriptor::Read () failed to get the " "connection lock.", static_cast(this)); if (error_ptr) error_ptr->SetErrorString("failed to get the connection lock for read."); status = eConnectionStatusTimedOut; return 0; } if (m_shutting_down) { status = eConnectionStatusError; return 0; } status = BytesAvailable(timeout, error_ptr); if (status != eConnectionStatusSuccess) return 0; Status error; size_t bytes_read = dst_len; error = m_read_sp->Read(dst, bytes_read); if (log) { log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", static_cast(this), static_cast(m_read_sp->GetWaitableHandle()), static_cast(dst), static_cast(dst_len), static_cast(bytes_read), error.AsCString()); } if (bytes_read == 0) { error.Clear(); // End-of-file. Do not automatically close; pass along for // the end-of-file handlers. status = eConnectionStatusEndOfFile; } if (error_ptr) *error_ptr = error; if (error.Fail()) { uint32_t error_value = error.GetError(); switch (error_value) { case EAGAIN: // The file was marked for non-blocking I/O, and no data were // ready to be read. if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) status = eConnectionStatusTimedOut; else status = eConnectionStatusSuccess; return 0; case EFAULT: // Buf points outside the allocated address space. case EINTR: // A read from a slow device was interrupted before any data // arrived by the delivery of a signal. case EINVAL: // The pointer associated with fildes was negative. case EIO: // An I/O error occurred while reading from the file system. // The process group is orphaned. // The file is a regular file, nbyte is greater than 0, // the starting position is before the end-of-file, and // the starting position is greater than or equal to the // offset maximum established for the open file // descriptor associated with fildes. case EISDIR: // An attempt is made to read a directory. case ENOBUFS: // An attempt to allocate a memory buffer fails. case ENOMEM: // Insufficient memory is available. status = eConnectionStatusError; break; // Break to close.... case ENOENT: // no such file or directory case EBADF: // fildes is not a valid file or socket descriptor open for // reading. case ENXIO: // An action is requested of a device that does not exist.. // A requested action cannot be performed by the device. case ECONNRESET: // The connection is closed by the peer during a read // attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a // socket. status = eConnectionStatusTimedOut; return 0; default: - if (log) - log->Printf( - "%p ConnectionFileDescriptor::Read (), unexpected error: %s", - static_cast(this), strerror(error_value)); + LLDB_LOG(log, "this = {0}, unexpected error: {1}", this, + llvm::sys::StrError(error_value)); status = eConnectionStatusError; break; // Break to close.... } return 0; } return bytes_read; } size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Status *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf( "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast(this), static_cast(src), static_cast(src_len)); if (!IsConnected()) { if (error_ptr) error_ptr->SetErrorString("not connected"); status = eConnectionStatusNoConnection; return 0; } Status error; size_t bytes_sent = src_len; error = m_write_sp->Write(src, bytes_sent); if (log) { log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", static_cast(this), static_cast(m_write_sp->GetWaitableHandle()), static_cast(src), static_cast(src_len), static_cast(bytes_sent), error.AsCString()); } if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EAGAIN: case EINTR: status = eConnectionStatusSuccess; return 0; case ECONNRESET: // The connection is closed by the peer during a read // attempt on a socket. case ENOTCONN: // A read is attempted on an unconnected socket. status = eConnectionStatusLostConnection; break; // Break to close.... default: status = eConnectionStatusError; break; // Break to close.... } return 0; } status = eConnectionStatusSuccess; return bytes_sent; } std::string ConnectionFileDescriptor::GetURI() { return m_uri; } // This ConnectionFileDescriptor::BytesAvailable() uses select() via // SelectHelper // // PROS: // - select is consistent across most unix platforms // - The Apple specific version allows for unlimited fds in the fd_sets by // setting the _DARWIN_UNLIMITED_SELECT define prior to including the // required header files. // CONS: // - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE. // This implementation will assert if it runs into that hard limit to let // users know that another ConnectionFileDescriptor::BytesAvailable() should // be used or a new version of ConnectionFileDescriptor::BytesAvailable() // should be written for the system that is running into the limitations. ConnectionStatus ConnectionFileDescriptor::BytesAvailable(const Timeout &timeout, Status *error_ptr) { // Don't need to take the mutex here separately since we are only called from // Read. If we // ever get used more generally we will need to lock here as well. Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout); // Make a copy of the file descriptors to make sure we don't // have another thread change these values out from under us // and cause problems in the loop below where like in FS_SET() const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); const int pipe_fd = m_pipe.GetReadFileDescriptor(); if (handle != IOObject::kInvalidHandleValue) { SelectHelper select_helper; if (timeout) select_helper.SetTimeout(*timeout); select_helper.FDSetRead(handle); #if defined(_MSC_VER) // select() won't accept pipes on Windows. The entire Windows codepath // needs to be // converted over to using WaitForMultipleObjects and event HANDLEs, but for // now at least // this will allow ::select() to not return an error. const bool have_pipe_fd = false; #else const bool have_pipe_fd = pipe_fd >= 0; #endif if (have_pipe_fd) select_helper.FDSetRead(pipe_fd); while (handle == m_read_sp->GetWaitableHandle()) { Status error = select_helper.Select(); if (error_ptr) *error_ptr = error; if (error.Fail()) { switch (error.GetError()) { case EBADF: // One of the descriptor sets specified an invalid // descriptor. return eConnectionStatusLostConnection; case EINVAL: // The specified time limit is invalid. One of its // components is negative or too large. default: // Other unknown error return eConnectionStatusError; case ETIMEDOUT: return eConnectionStatusTimedOut; case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO case EINTR: // A signal was delivered before the time limit // expired and before any of the selected events // occurred. break; // Lets keep reading to until we timeout } } else { if (select_helper.FDIsSetRead(handle)) return eConnectionStatusSuccess; if (select_helper.FDIsSetRead(pipe_fd)) { // There is an interrupt or exit command in the command pipe // Read the data from that pipe: char buffer[1]; ssize_t bytes_read; do { bytes_read = ::read(pipe_fd, buffer, sizeof(buffer)); } while (bytes_read < 0 && errno == EINTR); switch (buffer[0]) { case 'q': if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable() " "got data: %c from the command channel.", static_cast(this), buffer[0]); return eConnectionStatusEndOfFile; case 'i': // Interrupt the current read return eConnectionStatusInterrupted; } } } } } if (error_ptr) error_ptr->SetErrorString("not connected"); return eConnectionStatusLostConnection; } ConnectionStatus ConnectionFileDescriptor::NamedSocketAccept(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::NamedSocketConnect(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } lldb::ConnectionStatus ConnectionFileDescriptor::UnixAbstractSocketConnect(llvm::StringRef socket_name, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UnixAbstractConnect(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(socket_name); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::SocketListenAndAccept(llvm::StringRef s, Status *error_ptr) { m_port_predicate.SetValue(0, eBroadcastNever); Socket *socket = nullptr; m_waiting_for_accept = true; Status error = Socket::TcpListen(s, m_child_processes_inherit, socket, &m_port_predicate); if (error_ptr) *error_ptr = error; if (error.Fail()) return eConnectionStatusError; std::unique_ptr listening_socket_up; listening_socket_up.reset(socket); socket = nullptr; error = listening_socket_up->Accept(socket); listening_socket_up.reset(); if (error_ptr) *error_ptr = error; if (error.Fail()) return eConnectionStatusError; InitializeSocket(socket); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::ConnectTCP(llvm::StringRef s, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::TcpConnect(s, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(s); return eConnectionStatusSuccess; } ConnectionStatus ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s, Status *error_ptr) { Socket *socket = nullptr; Status error = Socket::UdpConnect(s, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); m_read_sp = m_write_sp; if (error.Fail()) { return eConnectionStatusError; } m_uri.assign(s); return eConnectionStatusSuccess; } uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) { uint16_t bound_port = 0; if (timeout_sec == UINT32_MAX) m_port_predicate.WaitForValueNotEqualTo(0, bound_port); else m_port_predicate.WaitForValueNotEqualTo(0, bound_port, std::chrono::seconds(timeout_sec)); return bound_port; } bool ConnectionFileDescriptor::GetChildProcessesInherit() const { return m_child_processes_inherit; } void ConnectionFileDescriptor::SetChildProcessesInherit( bool child_processes_inherit) { m_child_processes_inherit = child_processes_inherit; } void ConnectionFileDescriptor::InitializeSocket(Socket *socket) { assert(socket->GetSocketProtocol() == Socket::ProtocolTcp); TCPSocket *tcp_socket = static_cast(socket); m_write_sp.reset(socket); m_read_sp = m_write_sp; StreamString strm; strm.Printf("connect://%s:%u", tcp_socket->GetRemoteIPAddress().c_str(), tcp_socket->GetRemotePortNumber()); m_uri = strm.GetString(); } Index: vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp =================================================================== --- vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp (revision 319789) +++ vendor/lldb/dist/source/Host/posix/ProcessLauncherPosixFork.cpp (revision 319790) @@ -1,231 +1,232 @@ //===-- ProcessLauncherLinux.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/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/Pipe.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" +#include "llvm/Support/Errno.h" #include #include #include #include #ifdef __ANDROID__ #include #define PT_TRACE_ME PTRACE_TRACEME #endif #if defined(__ANDROID_API__) && __ANDROID_API__ < 21 #include #elif defined(__linux__) #include #endif using namespace lldb; using namespace lldb_private; static void FixupEnvironment(Args &env) { #ifdef __ANDROID__ // If there is no PATH variable specified inside the environment then set the // path to /system/bin. It is required because the default path used by // execve() is wrong on android. static const char *path = "PATH="; for (auto &entry : env.entries()) { if (entry.ref.startswith(path)) return; } env.AppendArgument(llvm::StringRef("PATH=/system/bin")); #endif } static void LLVM_ATTRIBUTE_NORETURN ExitWithError(int error_fd, const char *operation) { std::ostringstream os; os << operation << " failed: " << strerror(errno); write(error_fd, os.str().data(), os.str().size()); close(error_fd); _exit(1); } static void DisableASLRIfRequested(int error_fd, const ProcessLaunchInfo &info) { #if defined(__linux__) if (info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)) { const unsigned long personality_get_current = 0xffffffff; int value = personality(personality_get_current); if (value == -1) ExitWithError(error_fd, "personality get"); value = personality(ADDR_NO_RANDOMIZE | value); if (value == -1) ExitWithError(error_fd, "personality set"); } #endif } static void DupDescriptor(int error_fd, const FileSpec &file_spec, int fd, int flags) { int target_fd = ::open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) ExitWithError(error_fd, "DupDescriptor-open"); if (target_fd == fd) return; if (::dup2(target_fd, fd) == -1) ExitWithError(error_fd, "DupDescriptor-dup2"); ::close(target_fd); return; } static void LLVM_ATTRIBUTE_NORETURN ChildFunc(int error_fd, const ProcessLaunchInfo &info) { // First, make sure we disable all logging. If we are logging to stdout, our // logs can be mistaken for inferior output. Log::DisableAllLogChannels(); // Do not inherit setgid powers. if (setgid(getgid()) != 0) ExitWithError(error_fd, "setgid"); if (info.GetFlags().Test(eLaunchFlagLaunchInSeparateProcessGroup)) { if (setpgid(0, 0) != 0) ExitWithError(error_fd, "setpgid"); } for (size_t i = 0; i < info.GetNumFileActions(); ++i) { const FileAction &action = *info.GetFileActionAtIndex(i); switch (action.GetAction()) { case FileAction::eFileActionClose: if (close(action.GetFD()) != 0) ExitWithError(error_fd, "close"); break; case FileAction::eFileActionDuplicate: if (dup2(action.GetFD(), action.GetActionArgument()) == -1) ExitWithError(error_fd, "dup2"); break; case FileAction::eFileActionOpen: DupDescriptor(error_fd, action.GetFileSpec(), action.GetFD(), action.GetActionArgument()); break; case FileAction::eFileActionNone: break; } } const char **argv = info.GetArguments().GetConstArgumentVector(); // Change working directory if (info.GetWorkingDirectory() && 0 != ::chdir(info.GetWorkingDirectory().GetCString())) ExitWithError(error_fd, "chdir"); DisableASLRIfRequested(error_fd, info); Args env = info.GetEnvironmentEntries(); FixupEnvironment(env); const char **envp = env.GetConstArgumentVector(); // Clear the signal mask to prevent the child from being affected by // any masking done by the parent. sigset_t set; if (sigemptyset(&set) != 0 || pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) ExitWithError(error_fd, "pthread_sigmask"); if (info.GetFlags().Test(eLaunchFlagDebug)) { // HACK: // Close everything besides stdin, stdout, and stderr that has no file // action to avoid leaking. Only do this when debugging, as elsewhere we // actually rely on // passing open descriptors to child processes. for (int fd = 3; fd < sysconf(_SC_OPEN_MAX); ++fd) if (!info.GetFileActionForFD(fd) && fd != error_fd) close(fd); // Start tracing this child that is about to exec. if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) ExitWithError(error_fd, "ptrace"); } // Execute. We should never return... execve(argv[0], const_cast(argv), const_cast(envp)); #if defined(__linux__) if (errno == ETXTBSY) { // On android M and earlier we can get this error because the adb deamon can // hold a write // handle on the executable even after it has finished uploading it. This // state lasts // only a short time and happens only when there are many concurrent adb // commands being // issued, such as when running the test suite. (The file remains open when // someone does // an "adb shell" command in the fork() child before it has had a chance to // exec.) Since // this state should clear up quickly, wait a while and then give it one // more go. usleep(50000); execve(argv[0], const_cast(argv), const_cast(envp)); } #endif // ...unless exec fails. In which case we definitely need to end the child // here. ExitWithError(error_fd, "execve"); } HostProcess ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) { char exe_path[PATH_MAX]; launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); // A pipe used by the child process to report errors. PipePosix pipe; const bool child_processes_inherit = false; error = pipe.CreateNew(child_processes_inherit); if (error.Fail()) return HostProcess(); ::pid_t pid = ::fork(); if (pid == -1) { // Fork failed - error.SetErrorStringWithFormat("Fork failed with error message: %s", - strerror(errno)); + error.SetErrorStringWithFormatv("Fork failed with error message: {0}", + llvm::sys::StrError()); return HostProcess(LLDB_INVALID_PROCESS_ID); } if (pid == 0) { // child process pipe.CloseReadFileDescriptor(); ChildFunc(pipe.ReleaseWriteFileDescriptor(), launch_info); } // parent process pipe.CloseWriteFileDescriptor(); char buf[1000]; int r = read(pipe.GetReadFileDescriptor(), buf, sizeof buf); if (r == 0) return HostProcess(pid); // No error. We're done. error.SetErrorString(buf); waitpid(pid, nullptr, 0); return HostProcess(); } Index: vendor/lldb/dist/source/Plugins/Language/ObjC/ObjCLanguage.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Language/ObjC/ObjCLanguage.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Language/ObjC/ObjCLanguage.cpp (revision 319790) @@ -1,1096 +1,1096 @@ //===-- ObjCLanguage.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 #include // Other libraries and framework includes // Project includes #include "ObjCLanguage.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/ValueObject.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/StreamString.h" #include "llvm/Support/Threading.h" #include "CF.h" #include "Cocoa.h" #include "CoreMedia.h" #include "NSDictionary.h" #include "NSSet.h" #include "NSString.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; void ObjCLanguage::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "Objective-C Language", CreateInstance); } void ObjCLanguage::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ObjCLanguage::GetPluginNameStatic() { static ConstString g_name("objc"); return g_name; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString ObjCLanguage::GetPluginName() { return GetPluginNameStatic(); } uint32_t ObjCLanguage::GetPluginVersion() { return 1; } //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ Language *ObjCLanguage::CreateInstance(lldb::LanguageType language) { switch (language) { case lldb::eLanguageTypeObjC: return new ObjCLanguage(); default: return nullptr; } } void ObjCLanguage::MethodName::Clear() { m_full.Clear(); m_class.Clear(); m_category.Clear(); m_selector.Clear(); m_type = eTypeUnspecified; m_category_is_valid = false; } bool ObjCLanguage::MethodName::SetName(llvm::StringRef name, bool strict) { Clear(); if (name.empty()) return IsValid(strict); // If "strict" is true. then the method must be specified with a // '+' or '-' at the beginning. If "strict" is false, then the '+' // or '-' can be omitted bool valid_prefix = false; - if (name[0] == '+' || name[0] == '-') { + if (name.size() > 1 && (name[0] == '+' || name[0] == '-')) { valid_prefix = name[1] == '['; if (name[0] == '+') m_type = eTypeClassMethod; else m_type = eTypeInstanceMethod; } else if (!strict) { // "strict" is false, the name just needs to start with '[' valid_prefix = name[0] == '['; } if (valid_prefix) { int name_len = name.size(); // Objective C methods must have at least: // "-[" or "+[" prefix // One character for a class name // One character for the space between the class name // One character for the method name // "]" suffix if (name_len >= (5 + (strict ? 1 : 0)) && name.back() == ']') { m_full.SetString(name); } } return IsValid(strict); } bool ObjCLanguage::MethodName::SetName(const char *name, bool strict) { return SetName(llvm::StringRef(name), strict); } const ConstString &ObjCLanguage::MethodName::GetClassName() { if (!m_class) { if (IsValid(false)) { const char *full = m_full.GetCString(); const char *class_start = (full[0] == '[' ? full + 1 : full + 2); const char *paren_pos = strchr(class_start, '('); if (paren_pos) { m_class.SetCStringWithLength(class_start, paren_pos - class_start); } else { // No '(' was found in the full name, we can definitively say // that our category was valid (and empty). m_category_is_valid = true; const char *space_pos = strchr(full, ' '); if (space_pos) { m_class.SetCStringWithLength(class_start, space_pos - class_start); if (!m_class_category) { // No category in name, so we can also fill in the m_class_category m_class_category = m_class; } } } } } return m_class; } const ConstString &ObjCLanguage::MethodName::GetClassNameWithCategory() { if (!m_class_category) { if (IsValid(false)) { const char *full = m_full.GetCString(); const char *class_start = (full[0] == '[' ? full + 1 : full + 2); const char *space_pos = strchr(full, ' '); if (space_pos) { m_class_category.SetCStringWithLength(class_start, space_pos - class_start); // If m_class hasn't been filled in and the class with category doesn't // contain a '(', then we can also fill in the m_class if (!m_class && strchr(m_class_category.GetCString(), '(') == nullptr) { m_class = m_class_category; // No '(' was found in the full name, we can definitively say // that our category was valid (and empty). m_category_is_valid = true; } } } } return m_class_category; } const ConstString &ObjCLanguage::MethodName::GetSelector() { if (!m_selector) { if (IsValid(false)) { const char *full = m_full.GetCString(); const char *space_pos = strchr(full, ' '); if (space_pos) { ++space_pos; // skip the space m_selector.SetCStringWithLength(space_pos, m_full.GetLength() - (space_pos - full) - 1); } } } return m_selector; } const ConstString &ObjCLanguage::MethodName::GetCategory() { if (!m_category_is_valid && !m_category) { if (IsValid(false)) { m_category_is_valid = true; const char *full = m_full.GetCString(); const char *class_start = (full[0] == '[' ? full + 1 : full + 2); const char *open_paren_pos = strchr(class_start, '('); if (open_paren_pos) { ++open_paren_pos; // Skip the open paren const char *close_paren_pos = strchr(open_paren_pos, ')'); if (close_paren_pos) m_category.SetCStringWithLength(open_paren_pos, close_paren_pos - open_paren_pos); } } } return m_category; } ConstString ObjCLanguage::MethodName::GetFullNameWithoutCategory( bool empty_if_no_category) { if (IsValid(false)) { if (HasCategory()) { StreamString strm; if (m_type == eTypeClassMethod) strm.PutChar('+'); else if (m_type == eTypeInstanceMethod) strm.PutChar('-'); strm.Printf("[%s %s]", GetClassName().GetCString(), GetSelector().GetCString()); return ConstString(strm.GetString()); } if (!empty_if_no_category) { // Just return the full name since it doesn't have a category return GetFullName(); } } return ConstString(); } size_t ObjCLanguage::MethodName::GetFullNames(std::vector &names, bool append) { if (!append) names.clear(); if (IsValid(false)) { StreamString strm; const bool is_class_method = m_type == eTypeClassMethod; const bool is_instance_method = m_type == eTypeInstanceMethod; const ConstString &category = GetCategory(); if (is_class_method || is_instance_method) { names.push_back(m_full); if (category) { strm.Printf("%c[%s %s]", is_class_method ? '+' : '-', GetClassName().GetCString(), GetSelector().GetCString()); names.emplace_back(strm.GetString()); } } else { const ConstString &class_name = GetClassName(); const ConstString &selector = GetSelector(); strm.Printf("+[%s %s]", class_name.GetCString(), selector.GetCString()); names.emplace_back(strm.GetString()); strm.Clear(); strm.Printf("-[%s %s]", class_name.GetCString(), selector.GetCString()); names.emplace_back(strm.GetString()); strm.Clear(); if (category) { strm.Printf("+[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString()); names.emplace_back(strm.GetString()); strm.Clear(); strm.Printf("-[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString()); names.emplace_back(strm.GetString()); } } } return names.size(); } static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) { if (!objc_category_sp) return; TypeSummaryImpl::Flags objc_flags; objc_flags.SetCascades(false) .SetSkipPointers(true) .SetSkipReferences(true) .SetDontShowChildren(true) .SetDontShowValue(true) .SetShowMembersOneLiner(false) .SetHideItemNames(false); lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat( objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider, "")); objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL"), ObjC_BOOL_summary); objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL &"), ObjC_BOOL_summary); objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL *"), ObjC_BOOL_summary); #ifndef LLDB_DISABLE_PYTHON // we need to skip pointers here since we are special casing a SEL* when // retrieving its value objc_flags.SetSkipPointers(true); AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("SEL"), objc_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("struct objc_selector"), objc_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector"), objc_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector *"), objc_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("SEL *"), objc_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCClassSummaryProvider, "Class summary provider", ConstString("Class"), objc_flags); SyntheticChildren::Flags class_synth_flags; class_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences( false); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::ObjCClassSyntheticFrontEndCreator, "Class synthetic children", ConstString("Class"), class_synth_flags); #endif // LLDB_DISABLE_PYTHON objc_flags.SetSkipPointers(false); objc_flags.SetCascades(true); objc_flags.SetSkipReferences(false); AddStringSummary(objc_category_sp, "${var.__FuncPtr%A}", ConstString("__block_literal_generic"), objc_flags); AddStringSummary(objc_category_sp, "${var.years} years, ${var.months} " "months, ${var.days} days, ${var.hours} " "hours, ${var.minutes} minutes " "${var.seconds} seconds", ConstString("CFGregorianUnits"), objc_flags); AddStringSummary(objc_category_sp, "location=${var.location} length=${var.length}", ConstString("CFRange"), objc_flags); AddStringSummary(objc_category_sp, "location=${var.location}, length=${var.length}", ConstString("NSRange"), objc_flags); AddStringSummary(objc_category_sp, "(${var.origin}, ${var.size}), ...", ConstString("NSRectArray"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("NSPoint"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("NSSize"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("NSRect"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("CGSize"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("CGPoint"), objc_flags); AddOneLineSummary(objc_category_sp, ConstString("CGRect"), objc_flags); AddStringSummary(objc_category_sp, "red=${var.red} green=${var.green} blue=${var.blue}", ConstString("RGBColor"), objc_flags); AddStringSummary( objc_category_sp, "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})", ConstString("Rect"), objc_flags); AddStringSummary(objc_category_sp, "{(v=${var.v}, h=${var.h})}", ConstString("Point"), objc_flags); AddStringSummary(objc_category_sp, "${var.month}/${var.day}/${var.year} ${var.hour} " ":${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}", ConstString("DateTimeRect *"), objc_flags); AddStringSummary(objc_category_sp, "${var.ld.month}/${var.ld.day}/" "${var.ld.year} ${var.ld.hour} " ":${var.ld.minute} :${var.ld.second} " "dayOfWeek:${var.ld.dayOfWeek}", ConstString("LongDateRect"), objc_flags); AddStringSummary(objc_category_sp, "(x=${var.x}, y=${var.y})", ConstString("HIPoint"), objc_flags); AddStringSummary(objc_category_sp, "origin=${var.origin} size=${var.size}", ConstString("HIRect"), objc_flags); TypeSummaryImpl::Flags appkit_flags; appkit_flags.SetCascades(true) .SetSkipPointers(false) .SetSkipReferences(false) .SetDontShowChildren(true) .SetDontShowValue(false) .SetShowMembersOneLiner(false) .SetHideItemNames(false); appkit_flags.SetDontShowChildren(false); #ifndef LLDB_DISABLE_PYTHON AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSArray"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayI"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArray0"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSSingleObjectArrayI"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayM"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSCFArray"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFArrayRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFMutableArrayRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSDictionary"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSMutableDictionary"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSCFDictionary"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryI"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSSingleEntryDictionaryI"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryM"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFDictionaryRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFMutableDictionaryRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSSet summary", ConstString("NSSet"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFSetRef summary", ConstString("CFSetRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetI summary", ConstString("__NSSetI"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetM summary", ConstString("__NSSetM"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSOrderedSet summary", ConstString("NSOrderedSet"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetI summary", ConstString("__NSOrderedSetI"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetM summary", ConstString("__NSOrderedSetM"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSError_SummaryProvider, "NSError summary provider", ConstString("NSError"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSException_SummaryProvider, "NSException summary provider", ConstString("NSException"), appkit_flags); // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}", // ConstString("$_lldb_typegen_nspair"), appkit_flags); appkit_flags.SetDontShowChildren(true); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayM"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArray0"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSSingleObjectArrayI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSArray"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSMutableArray"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSCFArray"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFMutableArrayRef"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFArrayRef"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryM"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSSingleEntryDictionaryI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSCFDictionary"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSDictionary"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSMutableDictionary"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFDictionaryRef"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSErrorSyntheticFrontEndCreator, "NSError synthetic children", ConstString("NSError"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSExceptionSyntheticFrontEndCreator, "NSException synthetic children", ConstString("NSException"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("NSSet"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetI synthetic children", ConstString("__NSSetI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetM synthetic children", ConstString("__NSSetM"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSMutableSet synthetic children", ConstString("NSMutableSet"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSOrderedSet synthetic children", ConstString("NSOrderedSet"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic( objc_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(objc_category_sp, lldb_private::formatters::NSIndexPathSyntheticFrontEndCreator, "NSIndexPath synthetic children", ConstString("NSIndexPath"), ScriptedSyntheticChildren::Flags()); AddCXXSummary( objc_category_sp, lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFBagRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("__CFBag"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("const struct __CFBag"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFMutableBagRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("CFBinaryHeapRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("__CFBinaryHeap"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFStringRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__CFString"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFMutableStringRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSMutableString"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFConstantString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFString"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFConstantString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSPathStore2"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSTaggedPointerString"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSAttributedStringSummaryProvider, "NSAttributedString summary provider", ConstString("NSAttributedString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSMutableAttributedString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSConcreteMutableAttributedString"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSBundleSummaryProvider, "NSBundle summary provider", ConstString("NSBundle"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSData"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("_NSInlineData"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteData"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteMutableData"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSMutableData"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("__NSCFData"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFDataRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFMutableDataRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSMachPortSummaryProvider, "NSMachPort summary provider", ConstString("NSMachPort"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSNotification"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSConcreteNotification"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSNumber"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFNumber"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("NSURL"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("CFURLRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSDate"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSDate"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSTaggedDate"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("CFTimeZoneRef"), appkit_flags); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("__NSTimeZone"), appkit_flags); // CFAbsoluteTime is actually a double rather than a pointer to an object // we do not care about the numeric value, since it is probably meaningless to // users appkit_flags.SetDontShowValue(true); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFAbsoluteTimeSummaryProvider, "CFAbsoluteTime summary provider", ConstString("CFAbsoluteTime"), appkit_flags); appkit_flags.SetDontShowValue(false); AddCXXSummary( objc_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSIndexSet"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSMutableIndexSet"), appkit_flags); AddStringSummary(objc_category_sp, "@\"${var.month%d}/${var.day%d}/${var.year%d} " "${var.hour%d}:${var.minute%d}:${var.second}\"", ConstString("CFGregorianDate"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFBitVectorRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFMutableBitVectorRef"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFBitVector"), appkit_flags); AddCXXSummary(objc_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFMutableBitVector"), appkit_flags); #endif // LLDB_DISABLE_PYTHON } static void LoadCoreMediaFormatters(TypeCategoryImplSP objc_category_sp) { if (!objc_category_sp) return; TypeSummaryImpl::Flags cm_flags; cm_flags.SetCascades(true) .SetDontShowChildren(false) .SetDontShowValue(false) .SetHideItemNames(false) .SetShowMembersOneLiner(false) .SetSkipPointers(false) .SetSkipReferences(false); #ifndef LLDB_DISABLE_PYTHON AddCXXSummary(objc_category_sp, lldb_private::formatters::CMTimeSummaryProvider, "CMTime summary provider", ConstString("CMTime"), cm_flags); #endif // LLDB_DISABLE_PYTHON } lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() { static llvm::once_flag g_initialize; static TypeCategoryImplSP g_category; llvm::call_once(g_initialize, [this]() -> void { DataVisualization::Categories::GetCategory(GetPluginName(), g_category); if (g_category) { LoadCoreMediaFormatters(g_category); LoadObjCFormatters(g_category); } }); return g_category; } std::vector ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj, lldb::DynamicValueType use_dynamic) { std::vector result; if (use_dynamic == lldb::eNoDynamicValues) return result; CompilerType compiler_type(valobj.GetCompilerType()); const bool check_cpp = false; const bool check_objc = true; bool canBeObjCDynamic = compiler_type.IsPossibleDynamicType(nullptr, check_cpp, check_objc); if (canBeObjCDynamic) { do { lldb::ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) break; ObjCLanguageRuntime *runtime = process_sp->GetObjCLanguageRuntime(); if (runtime == nullptr) break; ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp( runtime->GetClassDescriptor(valobj)); if (!objc_class_sp) break; if (ConstString name = objc_class_sp->GetClassName()) result.push_back(name); } while (false); } return result; } std::unique_ptr ObjCLanguage::GetTypeScavenger() { class ObjCScavengerResult : public Language::TypeScavenger::Result { public: ObjCScavengerResult(CompilerType type) : Language::TypeScavenger::Result(), m_compiler_type(type) {} bool IsValid() override { return m_compiler_type.IsValid(); } bool DumpToStream(Stream &stream, bool print_help_if_available) override { if (IsValid()) { m_compiler_type.DumpTypeDescription(&stream); stream.EOL(); return true; } return false; } private: CompilerType m_compiler_type; }; class ObjCRuntimeScavenger : public Language::TypeScavenger { protected: bool Find_Impl(ExecutionContextScope *exe_scope, const char *key, ResultSet &results) override { bool result = false; Process *process = exe_scope->CalculateProcess().get(); if (process) { const bool create_on_demand = false; auto objc_runtime = process->GetObjCLanguageRuntime(create_on_demand); if (objc_runtime) { auto decl_vendor = objc_runtime->GetDeclVendor(); if (decl_vendor) { std::vector decls; ConstString name(key); decl_vendor->FindDecls(name, true, UINT32_MAX, decls); for (auto decl : decls) { if (decl) { if (CompilerType candidate = ClangASTContext::GetTypeForDecl(decl)) { result = true; std::unique_ptr result( new ObjCScavengerResult(candidate)); results.insert(std::move(result)); } } } } } } return result; } friend class lldb_private::ObjCLanguage; }; class ObjCModulesScavenger : public Language::TypeScavenger { protected: bool Find_Impl(ExecutionContextScope *exe_scope, const char *key, ResultSet &results) override { bool result = false; Target *target = exe_scope->CalculateTarget().get(); if (target) { if (auto clang_modules_decl_vendor = target->GetClangModulesDeclVendor()) { std::vector decls; ConstString key_cs(key); if (clang_modules_decl_vendor->FindDecls(key_cs, false, UINT32_MAX, decls) > 0 && !decls.empty()) { CompilerType module_type = ClangASTContext::GetTypeForDecl(decls.front()); result = true; std::unique_ptr result( new ObjCScavengerResult(module_type)); results.insert(std::move(result)); } } } return result; } friend class lldb_private::ObjCLanguage; }; class ObjCDebugInfoScavenger : public Language::ImageListTypeScavenger { public: virtual CompilerType AdjustForInclusion(CompilerType &candidate) override { LanguageType lang_type(candidate.GetMinimumLanguage()); if (!Language::LanguageIsObjC(lang_type)) return CompilerType(); if (candidate.IsTypedefType()) return candidate.GetTypedefedType(); return candidate; } }; return std::unique_ptr( new Language::EitherTypeScavenger()); } bool ObjCLanguage::GetFormatterPrefixSuffix(ValueObject &valobj, ConstString type_hint, std::string &prefix, std::string &suffix) { static ConstString g_CFBag("CFBag"); static ConstString g_CFBinaryHeap("CFBinaryHeap"); static ConstString g_NSNumberChar("NSNumber:char"); static ConstString g_NSNumberShort("NSNumber:short"); static ConstString g_NSNumberInt("NSNumber:int"); static ConstString g_NSNumberLong("NSNumber:long"); static ConstString g_NSNumberFloat("NSNumber:float"); static ConstString g_NSNumberDouble("NSNumber:double"); static ConstString g_NSData("NSData"); static ConstString g_NSArray("NSArray"); static ConstString g_NSString("NSString"); static ConstString g_NSStringStar("NSString*"); if (type_hint.IsEmpty()) return false; prefix.clear(); suffix.clear(); if (type_hint == g_CFBag || type_hint == g_CFBinaryHeap) { prefix = "@"; return true; } if (type_hint == g_NSNumberChar) { prefix = "(char)"; return true; } if (type_hint == g_NSNumberShort) { prefix = "(short)"; return true; } if (type_hint == g_NSNumberInt) { prefix = "(int)"; return true; } if (type_hint == g_NSNumberLong) { prefix = "(long)"; return true; } if (type_hint == g_NSNumberFloat) { prefix = "(float)"; return true; } if (type_hint == g_NSNumberDouble) { prefix = "(double)"; return true; } if (type_hint == g_NSData || type_hint == g_NSArray) { prefix = "@\""; suffix = "\""; return true; } if (type_hint == g_NSString || type_hint == g_NSStringStar) { prefix = "@"; return true; } return false; } bool ObjCLanguage::IsNilReference(ValueObject &valobj) { const uint32_t mask = eTypeIsObjC | eTypeIsPointer; bool isObjCpointer = (((valobj.GetCompilerType().GetTypeInfo(nullptr)) & mask) == mask); if (!isObjCpointer) return false; bool canReadValue = true; bool isZero = valobj.GetValueAsUnsigned(0, &canReadValue) == 0; return canReadValue && isZero; } Index: vendor/lldb/dist/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp (revision 319790) @@ -1,440 +1,441 @@ //===-- AppleObjCRuntimeV1.cpp --------------------------------------*- C++ //-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "AppleObjCRuntimeV1.h" #include "AppleObjCDeclVendor.h" #include "AppleObjCTrampolineHandler.h" #include "clang/AST/Type.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Scalar.h" #include "lldb/Expression/FunctionCaller.h" #include "lldb/Expression/UtilityFunction.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include using namespace lldb; using namespace lldb_private; AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process) : AppleObjCRuntime(process), m_hash_signature(), m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS) {} // for V1 runtime we just try to return a class name as that is the minimum // level of support // required for the data formatters to work bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress( ValueObject &in_value, lldb::DynamicValueType use_dynamic, TypeAndOrName &class_type_or_name, Address &address, Value::ValueType &value_type) { class_type_or_name.Clear(); value_type = Value::ValueType::eValueTypeScalar; if (CouldHaveDynamicValue(in_value)) { auto class_descriptor(GetClassDescriptor(in_value)); if (class_descriptor && class_descriptor->IsValid() && class_descriptor->GetClassName()) { const addr_t object_ptr = in_value.GetPointerValue(); address.SetRawAddress(object_ptr); class_type_or_name.SetName(class_descriptor->GetClassName()); } } return class_type_or_name.IsEmpty() == false; } //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ lldb_private::LanguageRuntime * AppleObjCRuntimeV1::CreateInstance(Process *process, lldb::LanguageType language) { // FIXME: This should be a MacOS or iOS process, and we need to look for the // OBJC section to make // sure we aren't using the V1 runtime. if (language == eLanguageTypeObjC) { ModuleSP objc_module_sp; if (AppleObjCRuntime::GetObjCVersion(process, objc_module_sp) == ObjCRuntimeVersions::eAppleObjC_V1) return new AppleObjCRuntimeV1(process); else return NULL; } else return NULL; } void AppleObjCRuntimeV1::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), "Apple Objective C Language Runtime - Version 1", CreateInstance); } void AppleObjCRuntimeV1::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString AppleObjCRuntimeV1::GetPluginNameStatic() { static ConstString g_name("apple-objc-v1"); return g_name; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString AppleObjCRuntimeV1::GetPluginName() { return GetPluginNameStatic(); } uint32_t AppleObjCRuntimeV1::GetPluginVersion() { return 1; } BreakpointResolverSP AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, bool throw_bp) { BreakpointResolverSP resolver_sp; if (throw_bp) resolver_sp.reset(new BreakpointResolverName( bkpt, "objc_exception_throw", eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo)); // FIXME: don't do catch yet. return resolver_sp; } struct BufStruct { char contents[2048]; }; UtilityFunction *AppleObjCRuntimeV1::CreateObjectChecker(const char *name) { std::unique_ptr buf(new BufStruct); int strformatsize = snprintf(&buf->contents[0], sizeof(buf->contents), "struct __objc_class " " \n" "{ " " \n" " struct __objc_class *isa; " " \n" " struct __objc_class *super_class; " " \n" " const char *name; " " \n" " // rest of struct elided because unused " " \n" "}; " " \n" " " " \n" "struct __objc_object " " \n" "{ " " \n" " struct __objc_class *isa; " " \n" "}; " " \n" " " " \n" "extern \"C\" void " " \n" "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) " " \n" "{ " " \n" " struct __objc_object *obj = (struct " "__objc_object*)$__lldb_arg_obj; \n" " if ($__lldb_arg_obj == (void *)0) " " \n" " return; // nil is ok " " (int)strlen(obj->isa->name); " " \n" "} " " \n", name); assert(strformatsize < (int)sizeof(buf->contents)); + (void)strformatsize; Status error; return GetTargetRef().GetUtilityFunctionForLanguage( buf->contents, eLanguageTypeObjC, name, error); } AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( ValueObject &isa_pointer) { Initialize(isa_pointer.GetValueAsUnsigned(0), isa_pointer.GetProcessSP()); } AppleObjCRuntimeV1::ClassDescriptorV1::ClassDescriptorV1( ObjCISA isa, lldb::ProcessSP process_sp) { Initialize(isa, process_sp); } void AppleObjCRuntimeV1::ClassDescriptorV1::Initialize( ObjCISA isa, lldb::ProcessSP process_sp) { if (!isa || !process_sp) { m_valid = false; return; } m_valid = true; Status error; m_isa = process_sp->ReadPointerFromMemory(isa, error); if (error.Fail()) { m_valid = false; return; } uint32_t ptr_size = process_sp->GetAddressByteSize(); if (!IsPointerValid(m_isa, ptr_size)) { m_valid = false; return; } m_parent_isa = process_sp->ReadPointerFromMemory(m_isa + ptr_size, error); if (error.Fail()) { m_valid = false; return; } if (!IsPointerValid(m_parent_isa, ptr_size, true)) { m_valid = false; return; } lldb::addr_t name_ptr = process_sp->ReadPointerFromMemory(m_isa + 2 * ptr_size, error); if (error.Fail()) { m_valid = false; return; } lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); size_t count = process_sp->ReadCStringFromMemory( name_ptr, (char *)buffer_sp->GetBytes(), 1024, error); if (error.Fail()) { m_valid = false; return; } if (count) m_name = ConstString((char *)buffer_sp->GetBytes()); else m_name = ConstString(); m_instance_size = process_sp->ReadUnsignedIntegerFromMemory( m_isa + 5 * ptr_size, ptr_size, 0, error); if (error.Fail()) { m_valid = false; return; } m_process_wp = lldb::ProcessWP(process_sp); } AppleObjCRuntime::ClassDescriptorSP AppleObjCRuntimeV1::ClassDescriptorV1::GetSuperclass() { if (!m_valid) return AppleObjCRuntime::ClassDescriptorSP(); ProcessSP process_sp = m_process_wp.lock(); if (!process_sp) return AppleObjCRuntime::ClassDescriptorSP(); return ObjCLanguageRuntime::ClassDescriptorSP( new AppleObjCRuntimeV1::ClassDescriptorV1(m_parent_isa, process_sp)); } AppleObjCRuntime::ClassDescriptorSP AppleObjCRuntimeV1::ClassDescriptorV1::GetMetaclass() const { return ClassDescriptorSP(); } bool AppleObjCRuntimeV1::ClassDescriptorV1::Describe( std::function const &superclass_func, std::function const &instance_method_func, std::function const &class_method_func, std::function const &ivar_func) const { return false; } lldb::addr_t AppleObjCRuntimeV1::GetISAHashTablePointer() { if (m_isa_hash_table_ptr == LLDB_INVALID_ADDRESS) { ModuleSP objc_module_sp(GetObjCModule()); if (!objc_module_sp) return LLDB_INVALID_ADDRESS; static ConstString g_objc_debug_class_hash("_objc_debug_class_hash"); const Symbol *symbol = objc_module_sp->FindFirstSymbolWithNameAndType( g_objc_debug_class_hash, lldb::eSymbolTypeData); if (symbol && symbol->ValueIsAddress()) { Process *process = GetProcess(); if (process) { lldb::addr_t objc_debug_class_hash_addr = symbol->GetAddressRef().GetLoadAddress(&process->GetTarget()); if (objc_debug_class_hash_addr != LLDB_INVALID_ADDRESS) { Status error; lldb::addr_t objc_debug_class_hash_ptr = process->ReadPointerFromMemory(objc_debug_class_hash_addr, error); if (objc_debug_class_hash_ptr != 0 && objc_debug_class_hash_ptr != LLDB_INVALID_ADDRESS) { m_isa_hash_table_ptr = objc_debug_class_hash_ptr; } } } } } return m_isa_hash_table_ptr; } void AppleObjCRuntimeV1::UpdateISAToDescriptorMapIfNeeded() { // TODO: implement HashTableSignature... Process *process = GetProcess(); if (process) { // Update the process stop ID that indicates the last time we updated the // map, whether it was successful or not. m_isa_to_descriptor_stop_id = process->GetStopID(); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); ProcessSP process_sp = process->shared_from_this(); ModuleSP objc_module_sp(GetObjCModule()); if (!objc_module_sp) return; uint32_t isa_count = 0; lldb::addr_t hash_table_ptr = GetISAHashTablePointer(); if (hash_table_ptr != LLDB_INVALID_ADDRESS) { // Read the NXHashTable struct: // // typedef struct { // const NXHashTablePrototype *prototype; // unsigned count; // unsigned nbBuckets; // void *buckets; // const void *info; // } NXHashTable; Status error; DataBufferHeap buffer(1024, 0); if (process->ReadMemory(hash_table_ptr, buffer.GetBytes(), 20, error) == 20) { const uint32_t addr_size = m_process->GetAddressByteSize(); const ByteOrder byte_order = m_process->GetByteOrder(); DataExtractor data(buffer.GetBytes(), buffer.GetByteSize(), byte_order, addr_size); lldb::offset_t offset = addr_size; // Skip prototype const uint32_t count = data.GetU32(&offset); const uint32_t num_buckets = data.GetU32(&offset); const addr_t buckets_ptr = data.GetPointer(&offset); if (m_hash_signature.NeedsUpdate(count, num_buckets, buckets_ptr)) { m_hash_signature.UpdateSignature(count, num_buckets, buckets_ptr); const uint32_t data_size = num_buckets * 2 * sizeof(uint32_t); buffer.SetByteSize(data_size); if (process->ReadMemory(buckets_ptr, buffer.GetBytes(), data_size, error) == data_size) { data.SetData(buffer.GetBytes(), buffer.GetByteSize(), byte_order); offset = 0; for (uint32_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx) { const uint32_t bucket_isa_count = data.GetU32(&offset); const lldb::addr_t bucket_data = data.GetU32(&offset); if (bucket_isa_count == 0) continue; isa_count += bucket_isa_count; ObjCISA isa; if (bucket_isa_count == 1) { // When we only have one entry in the bucket, the bucket data is // the "isa" isa = bucket_data; if (isa) { if (!ISAIsCached(isa)) { ClassDescriptorSP descriptor_sp( new ClassDescriptorV1(isa, process_sp)); if (log && log->GetVerbose()) log->Printf("AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to " "isa->descriptor cache", isa); AddClass(isa, descriptor_sp); } } } else { // When we have more than one entry in the bucket, the bucket // data is a pointer // to an array of "isa" values addr_t isa_addr = bucket_data; for (uint32_t isa_idx = 0; isa_idx < bucket_isa_count; ++isa_idx, isa_addr += addr_size) { isa = m_process->ReadPointerFromMemory(isa_addr, error); if (isa && isa != LLDB_INVALID_ADDRESS) { if (!ISAIsCached(isa)) { ClassDescriptorSP descriptor_sp( new ClassDescriptorV1(isa, process_sp)); if (log && log->GetVerbose()) log->Printf( "AppleObjCRuntimeV1 added (ObjCISA)0x%" PRIx64 " from _objc_debug_class_hash to isa->descriptor " "cache", isa); AddClass(isa, descriptor_sp); } } } } } } } } } } else { m_isa_to_descriptor_stop_id = UINT32_MAX; } } DeclVendor *AppleObjCRuntimeV1::GetDeclVendor() { return nullptr; } Index: vendor/lldb/dist/source/Plugins/ObjectFile/ELF/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/ELF/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/source/Plugins/ObjectFile/ELF/CMakeLists.txt (revision 319790) @@ -1,12 +1,13 @@ add_lldb_library(lldbPluginObjectFileELF PLUGIN ELFHeader.cpp ObjectFileELF.cpp LINK_LIBS lldbCore lldbHost lldbSymbol lldbTarget LINK_COMPONENTS + BinaryFormat Support ) Index: vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ELFHeader.h =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ELFHeader.h (revision 319789) +++ vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ELFHeader.h (revision 319790) @@ -1,412 +1,412 @@ //===-- ELFHeader.h ------------------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // /// @file /// @brief Generic structures and typedefs for ELF files. /// /// This file provides definitions for the various entities comprising an ELF /// file. The structures are generic in the sense that they do not correspond /// to the exact binary layout of an ELF, but can be used to hold the /// information present in both 32 and 64 bit variants of the format. Each /// entity provides a \c Parse method which is capable of transparently reading /// both 32 and 64 bit instances of the object. //===----------------------------------------------------------------------===// #ifndef liblldb_ELFHeader_h_ #define liblldb_ELFHeader_h_ -#include "llvm/Support/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-types.h" namespace lldb_private { class DataExtractor; } // End namespace lldb_private. namespace elf { //------------------------------------------------------------------------------ /// @name ELF type definitions. /// /// Types used to represent the various components of ELF structures. All types /// are signed or unsigned integral types wide enough to hold values from both /// 32 and 64 bit ELF variants. //@{ typedef uint64_t elf_addr; typedef uint64_t elf_off; typedef uint16_t elf_half; typedef uint32_t elf_word; typedef int32_t elf_sword; typedef uint64_t elf_size; typedef uint64_t elf_xword; typedef int64_t elf_sxword; //@} //------------------------------------------------------------------------------ /// @class ELFHeader /// @brief Generic representation of an ELF file header. /// /// This object is used to identify the general attributes on an ELF file and to /// locate additional sections within the file. struct ELFHeader { unsigned char e_ident[llvm::ELF::EI_NIDENT]; ///< ELF file identification. elf_addr e_entry; ///< Virtual address program entry point. elf_off e_phoff; ///< File offset of program header table. elf_off e_shoff; ///< File offset of section header table. elf_word e_flags; ///< Processor specific flags. elf_word e_version; ///< Version of object file (always 1). elf_half e_type; ///< Object file type. elf_half e_machine; ///< Target architecture. elf_half e_ehsize; ///< Byte size of the ELF header. elf_half e_phentsize; ///< Size of a program header table entry. elf_half e_phnum_hdr; ///< Number of program header entries. elf_half e_shentsize; ///< Size of a section header table entry. elf_half e_shnum_hdr; ///< Number of section header entries. elf_half e_shstrndx_hdr; ///< String table section index. // In some cases these numbers do not fit in 16 bits and they are // stored outside of the header in section #0. Here are the actual // values. elf_word e_phnum; ///< Number of program header entries. elf_word e_shnum; ///< Number of section header entries. elf_word e_shstrndx; ///< String table section index. ELFHeader(); //-------------------------------------------------------------------------- /// Returns true if this is a 32 bit ELF file header. /// /// @return /// True if this is a 32 bit ELF file header. bool Is32Bit() const { return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32; } //-------------------------------------------------------------------------- /// Returns true if this is a 64 bit ELF file header. /// /// @return /// True if this is a 64 bit ELF file header. bool Is64Bit() const { return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64; } //-------------------------------------------------------------------------- /// The byte order of this ELF file header. /// /// @return /// The byte order of this ELF file as described by the header. lldb::ByteOrder GetByteOrder() const; //-------------------------------------------------------------------------- /// The jump slot relocation type of this ELF. unsigned GetRelocationJumpSlotType() const; //-------------------------------------------------------------------------- /// Check if there should be header extension in section header #0 /// /// @return /// True if parsing the ELFHeader requires reading header extension /// and false otherwise. bool HasHeaderExtension() const; //-------------------------------------------------------------------------- /// Parse an ELFHeader entry starting at position \p offset and /// update the data extractor with the address size and byte order /// attributes as defined by the header. /// /// @param[in,out] data /// The DataExtractor to read from. Updated with the address size and /// byte order attributes appropriate to this header. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFHeader was successfully read and false /// otherwise. bool Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset); //-------------------------------------------------------------------------- /// Examines at most EI_NIDENT bytes starting from the given pointer and /// determines if the magic ELF identification exists. /// /// @return /// True if the given sequence of bytes identifies an ELF file. static bool MagicBytesMatch(const uint8_t *magic); //-------------------------------------------------------------------------- /// Examines at most EI_NIDENT bytes starting from the given address and /// determines the address size of the underlying ELF file. This function /// should only be called on an pointer for which MagicBytesMatch returns /// true. /// /// @return /// The number of bytes forming an address in the ELF file (either 4 or /// 8), else zero if the address size could not be determined. static unsigned AddressSizeInBytes(const uint8_t *magic); private: //-------------------------------------------------------------------------- /// Parse an ELFHeader header extension entry. This method is called /// by Parse(). /// /// @param[in] data /// The DataExtractor to read from. void ParseHeaderExtension(lldb_private::DataExtractor &data); }; //------------------------------------------------------------------------------ /// @class ELFSectionHeader /// @brief Generic representation of an ELF section header. struct ELFSectionHeader { elf_word sh_name; ///< Section name string index. elf_word sh_type; ///< Section type. elf_xword sh_flags; ///< Section attributes. elf_addr sh_addr; ///< Virtual address of the section in memory. elf_off sh_offset; ///< Start of section from beginning of file. elf_xword sh_size; ///< Number of bytes occupied in the file. elf_word sh_link; ///< Index of associated section. elf_word sh_info; ///< Extra section info (overloaded). elf_xword sh_addralign; ///< Power of two alignment constraint. elf_xword sh_entsize; ///< Byte size of each section entry. ELFSectionHeader(); //-------------------------------------------------------------------------- /// Parse an ELFSectionHeader entry from the given DataExtracter starting at /// position \p offset. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFSectionHeader was successfully read and false /// otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); }; //------------------------------------------------------------------------------ /// @class ELFProgramHeader /// @brief Generic representation of an ELF program header. struct ELFProgramHeader { elf_word p_type; ///< Type of program segment. elf_word p_flags; ///< Segment attributes. elf_off p_offset; ///< Start of segment from beginning of file. elf_addr p_vaddr; ///< Virtual address of segment in memory. elf_addr p_paddr; ///< Physical address (for non-VM systems). elf_xword p_filesz; ///< Byte size of the segment in file. elf_xword p_memsz; ///< Byte size of the segment in memory. elf_xword p_align; ///< Segment alignment constraint. ELFProgramHeader(); /// Parse an ELFProgramHeader entry from the given DataExtractor starting at /// position \p offset. The address size of the DataExtractor determines if /// a 32 or 64 bit object is to be parsed. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFProgramHeader was successfully read and false /// otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); }; //------------------------------------------------------------------------------ /// @class ELFSymbol /// @brief Represents a symbol within an ELF symbol table. struct ELFSymbol { elf_addr st_value; ///< Absolute or relocatable address. elf_xword st_size; ///< Size of the symbol or zero. elf_word st_name; ///< Symbol name string index. unsigned char st_info; ///< Symbol type and binding attributes. unsigned char st_other; ///< Reserved for future use. elf_half st_shndx; ///< Section to which this symbol applies. ELFSymbol(); /// Returns the binding attribute of the st_info member. unsigned char getBinding() const { return st_info >> 4; } /// Returns the type attribute of the st_info member. unsigned char getType() const { return st_info & 0x0F; } /// Sets the binding and type of the st_info member. void setBindingAndType(unsigned char binding, unsigned char type) { st_info = (binding << 4) + (type & 0x0F); } static const char *bindingToCString(unsigned char binding); static const char *typeToCString(unsigned char type); static const char * sectionIndexToCString(elf_half shndx, const lldb_private::SectionList *section_list); /// Parse an ELFSymbol entry from the given DataExtractor starting at /// position \p offset. The address size of the DataExtractor determines if /// a 32 or 64 bit object is to be parsed. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFSymbol was successfully read and false otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); void Dump(lldb_private::Stream *s, uint32_t idx, const lldb_private::DataExtractor *strtab_data, const lldb_private::SectionList *section_list); }; //------------------------------------------------------------------------------ /// @class ELFDynamic /// @brief Represents an entry in an ELF dynamic table. struct ELFDynamic { elf_sxword d_tag; ///< Type of dynamic table entry. union { elf_xword d_val; ///< Integer value of the table entry. elf_addr d_ptr; ///< Pointer value of the table entry. }; ELFDynamic(); /// Parse an ELFDynamic entry from the given DataExtractor starting at /// position \p offset. The address size of the DataExtractor determines if /// a 32 or 64 bit object is to be parsed. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFDynamic entry was successfully read and false /// otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); }; //------------------------------------------------------------------------------ /// @class ELFRel /// @brief Represents a relocation entry with an implicit addend. struct ELFRel { elf_addr r_offset; ///< Address of reference. elf_xword r_info; ///< symbol index and type of relocation. ELFRel(); /// Parse an ELFRel entry from the given DataExtractor starting at position /// \p offset. The address size of the DataExtractor determines if a 32 or /// 64 bit object is to be parsed. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFRel entry was successfully read and false otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); /// Returns the type when the given entry represents a 32-bit relocation. static unsigned RelocType32(const ELFRel &rel) { return rel.r_info & 0x0ff; } /// Returns the type when the given entry represents a 64-bit relocation. static unsigned RelocType64(const ELFRel &rel) { return rel.r_info & 0xffffffff; } /// Returns the symbol index when the given entry represents a 32-bit /// relocation. static unsigned RelocSymbol32(const ELFRel &rel) { return rel.r_info >> 8; } /// Returns the symbol index when the given entry represents a 64-bit /// relocation. static unsigned RelocSymbol64(const ELFRel &rel) { return rel.r_info >> 32; } }; //------------------------------------------------------------------------------ /// @class ELFRela /// @brief Represents a relocation entry with an explicit addend. struct ELFRela { elf_addr r_offset; ///< Address of reference. elf_xword r_info; ///< Symbol index and type of relocation. elf_sxword r_addend; ///< Constant part of expression. ELFRela(); /// Parse an ELFRela entry from the given DataExtractor starting at position /// \p offset. The address size of the DataExtractor determines if a 32 or /// 64 bit object is to be parsed. /// /// @param[in] data /// The DataExtractor to read from. The address size of the extractor /// determines if a 32 or 64 bit object should be read. /// /// @param[in,out] offset /// Pointer to an offset in the data. On return the offset will be /// advanced by the number of bytes read. /// /// @return /// True if the ELFRela entry was successfully read and false otherwise. bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); /// Returns the type when the given entry represents a 32-bit relocation. static unsigned RelocType32(const ELFRela &rela) { return rela.r_info & 0x0ff; } /// Returns the type when the given entry represents a 64-bit relocation. static unsigned RelocType64(const ELFRela &rela) { return rela.r_info & 0xffffffff; } /// Returns the symbol index when the given entry represents a 32-bit /// relocation. static unsigned RelocSymbol32(const ELFRela &rela) { return rela.r_info >> 8; } /// Returns the symbol index when the given entry represents a 64-bit /// relocation. static unsigned RelocSymbol64(const ELFRela &rela) { return rela.r_info >> 32; } }; } // End namespace elf. #endif // #ifndef liblldb_ELFHeader_h_ Index: vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt (revision 319790) @@ -1,12 +1,13 @@ add_lldb_library(lldbPluginObjectFilePECOFF PLUGIN ObjectFilePECOFF.cpp WindowsMiniDump.cpp LINK_LIBS lldbCore lldbHost lldbSymbol lldbTarget LINK_COMPONENTS + BinaryFormat Support ) Index: vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp (revision 319790) @@ -1,1051 +1,1051 @@ //===-- 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 "llvm/BinaryFormat/COFF.h" #include "lldb/Core/ArchSpec.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/Timer.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UUID.h" #include "llvm/Support/MemoryBuffer.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 = DataBufferLLVM::CreateSliceFromPath(file->GetPath(), length, file_offset); if (!data_sp) return nullptr; data_offset = 0; } if (!ObjectFilePECOFF::MagicBytesMatch(data_sp)) return nullptr; // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = DataBufferLLVM::CreateSliceFromPath(file->GetPath(), length, file_offset); if (!data_sp) return nullptr; } auto objfile_ap = llvm::make_unique( module_sp, data_sp, data_offset, file, file_offset, length); if (!objfile_ap || !objfile_ap->ParseHeader()) return nullptr; return objfile_ap.release(); } ObjectFile *ObjectFilePECOFF::CreateMemoryInstance( const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (!data_sp || !ObjectFilePECOFF::MagicBytesMatch(data_sp)) return nullptr; auto objfile_ap = llvm::make_unique( module_sp, data_sp, process_sp, header_addr); if (objfile_ap.get() && objfile_ap->ParseHeader()) { return objfile_ap.release(); } return nullptr; } 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::Status &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(), m_entry_point_address() { ::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(const lldb::ModuleSP &module_sp, DataBufferSP &header_data_sp, const lldb::ProcessSP &process_sp, addr_t header_addr) : ObjectFile(module_sp, process_sp, header_addr, header_data_sp), m_dos_header(), m_coff_header(), m_coff_header_opt(), m_sect_headers(), m_entry_point_address() { ::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) { std::lock_guard guard(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 < num_data_dir_entries; i++) { m_coff_header_opt.data_dirs[i].vmaddr = m_data.GetU32(offset_ptr); m_coff_header_opt.data_dirs[i].vmsize = m_data.GetU32(offset_ptr); } m_file_offset = m_coff_header_opt.image_base; m_image_base = m_coff_header_opt.image_base; } } } // Make sure we are on track for section data which follows *offset_ptr = end_offset; return success; } DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) { if (m_file) { // A bit of a hack, but we intend to write to this buffer, so we can't // mmap it. auto buffer_sp = DataBufferLLVM::CreateSliceFromPath(m_file.GetPath(), size, offset, true); return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize()); } ProcessSP process_sp(m_process_wp.lock()); DataExtractor data; if (process_sp) { auto data_ap = llvm::make_unique(size, 0); Status readmem_error; size_t bytes_read = process_sp->ReadMemory(m_image_base + offset, data_ap->GetBytes(), data_ap->GetByteSize(), readmem_error); if (bytes_read == size) { DataBufferSP buffer_sp(data_ap.release()); data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); } } return data; } //---------------------------------------------------------------------- // ParseSectionHeaders //---------------------------------------------------------------------- bool ObjectFilePECOFF::ParseSectionHeaders( uint32_t section_header_data_offset) { const uint32_t nsects = m_coff_header.nsects; m_sect_headers.clear(); if (nsects > 0) { const size_t section_header_byte_size = nsects * sizeof(section_header_t); DataExtractor section_header_data = ReadImageData(section_header_data_offset, section_header_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; idx < nsects; ++idx) { const void *name_data = section_header_data.GetData(&offset, 8); if (name_data) { memcpy(m_sect_headers[idx].name, name_data, 8); m_sect_headers[idx].vmsize = section_header_data.GetU32(&offset); m_sect_headers[idx].vmaddr = section_header_data.GetU32(&offset); m_sect_headers[idx].size = section_header_data.GetU32(&offset); m_sect_headers[idx].offset = section_header_data.GetU32(&offset); m_sect_headers[idx].reloff = section_header_data.GetU32(&offset); m_sect_headers[idx].lineoff = section_header_data.GetU32(&offset); m_sect_headers[idx].nreloc = section_header_data.GetU16(&offset); m_sect_headers[idx].nline = section_header_data.GetU16(&offset); m_sect_headers[idx].flags = section_header_data.GetU32(&offset); } } } } return m_sect_headers.empty() == false; } bool ObjectFilePECOFF::GetSectionName(std::string §_name, const section_header_t §) { if (sect.name[0] == '/') { lldb::offset_t stroff = strtoul(§.name[1], NULL, 10); lldb::offset_t string_file_offset = m_coff_header.symoff + (m_coff_header.nsyms * 18) + stroff; const char *name = m_data.GetCStr(&string_file_offset); if (name) { sect_name = name; return true; } return false; } sect_name = sect.name; return true; } //---------------------------------------------------------------------- // GetNListSymtab //---------------------------------------------------------------------- Symtab *ObjectFilePECOFF::GetSymtab() { ModuleSP module_sp(GetModule()); if (module_sp) { std::lock_guard guard(module_sp->GetMutex()); if (m_symtab_ap.get() == NULL) { SectionList *sect_list = GetSectionList(); m_symtab_ap.reset(new Symtab(this)); std::lock_guard guard(m_symtab_ap->GetMutex()); const uint32_t num_syms = m_coff_header.nsyms; if (m_file && num_syms > 0 && m_coff_header.symoff > 0) { const uint32_t symbol_size = 18; const size_t symbol_data_size = num_syms * symbol_size; // Include the 4-byte string table size at the end of the symbols DataExtractor symtab_data = ReadImageData(m_coff_header.symoff, symbol_data_size + 4); lldb::offset_t offset = symbol_data_size; const uint32_t strtab_size = symtab_data.GetU32(&offset); if (strtab_size > 0) { DataExtractor strtab_data = ReadImageData( m_coff_header.symoff + symbol_data_size, strtab_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.GetDataStart(); 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 < num_syms; ++i) { coff_symbol_t symbol; const uint32_t symbol_offset = offset; const char *symbol_name_cstr = NULL; // If the first 4 bytes of the symbol string are zero, then they // are followed by a 4-byte string table offset. Else these // 8 bytes contain the symbol name if (symtab_data.GetU32(&offset) == 0) { // Long string that doesn't fit into the symbol table name, // so now we must read the 4 byte string table offset uint32_t strtab_offset = symtab_data.GetU32(&offset); symbol_name_cstr = strtab_data.PeekCStr(strtab_offset); symbol_name.assign(symbol_name_cstr); } else { // Short string that fits into the symbol table name which is 8 // bytes offset += sizeof(symbol.name) - 4; // Skip remaining symbol_name_cstr = symtab_data.PeekCStr(symbol_offset); if (symbol_name_cstr == NULL) break; symbol_name.assign(symbol_name_cstr, sizeof(symbol.name)); } symbol.value = symtab_data.GetU32(&offset); symbol.sect = symtab_data.GetU16(&offset); symbol.type = symtab_data.GetU16(&offset); symbol.storage = symtab_data.GetU8(&offset); symbol.naux = symtab_data.GetU8(&offset); symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); if ((int16_t)symbol.sect >= 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; uint32_t address_rva = data_start; if (m_file) { Address address(m_coff_header_opt.image_base + data_start, sect_list); address_rva = address.GetSection()->GetFileOffset() + address.GetOffset(); } DataExtractor symtab_data = ReadImageData(address_rva, m_coff_header_opt.data_dirs[0].vmsize); 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); } } m_symtab_ap->CalculateSymbolSizes(); } } 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) { std::lock_guard guard(module_sp->GetMutex()); const uint32_t nsects = m_sect_headers.size(); ModuleSP module_sp(GetModule()); for (uint32_t idx = 0; idx < nsects; ++idx) { std::string sect_name; GetSectionName(sect_name, m_sect_headers[idx]); ConstString const_sect_name(sect_name.c_str()); static ConstString g_code_sect_name(".code"); static ConstString g_CODE_sect_name("CODE"); static ConstString g_data_sect_name(".data"); static ConstString g_DATA_sect_name("DATA"); static ConstString g_bss_sect_name(".bss"); static ConstString g_BSS_sect_name("BSS"); static ConstString g_debug_sect_name(".debug"); static ConstString g_reloc_sect_name(".reloc"); static ConstString g_stab_sect_name(".stab"); static ConstString g_stabstr_sect_name(".stabstr"); static ConstString g_sect_name_dwarf_debug_abbrev(".debug_abbrev"); static ConstString g_sect_name_dwarf_debug_aranges(".debug_aranges"); static ConstString g_sect_name_dwarf_debug_frame(".debug_frame"); static ConstString g_sect_name_dwarf_debug_info(".debug_info"); static ConstString g_sect_name_dwarf_debug_line(".debug_line"); static ConstString g_sect_name_dwarf_debug_loc(".debug_loc"); static ConstString g_sect_name_dwarf_debug_macinfo(".debug_macinfo"); static ConstString g_sect_name_dwarf_debug_pubnames(".debug_pubnames"); static ConstString g_sect_name_dwarf_debug_pubtypes(".debug_pubtypes"); static ConstString g_sect_name_dwarf_debug_ranges(".debug_ranges"); static ConstString g_sect_name_dwarf_debug_str(".debug_str"); static ConstString g_sect_name_eh_frame(".eh_frame"); static ConstString g_sect_name_go_symtab(".gosymtab"); SectionType section_type = eSectionTypeOther; if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_CODE && ((const_sect_name == g_code_sect_name) || (const_sect_name == g_CODE_sect_name))) { section_type = eSectionTypeCode; } else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA && ((const_sect_name == g_data_sect_name) || (const_sect_name == g_DATA_sect_name))) { section_type = eSectionTypeData; } else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA && ((const_sect_name == g_bss_sect_name) || (const_sect_name == g_BSS_sect_name))) { if (m_sect_headers[idx].size == 0) section_type = eSectionTypeZeroFill; else section_type = eSectionTypeData; } else if (const_sect_name == g_debug_sect_name) { section_type = eSectionTypeDebug; } else if (const_sect_name == g_stabstr_sect_name) { section_type = eSectionTypeDataCString; } else if (const_sect_name == g_reloc_sect_name) { section_type = eSectionTypeOther; } else if (const_sect_name == g_sect_name_dwarf_debug_abbrev) section_type = eSectionTypeDWARFDebugAbbrev; else if (const_sect_name == g_sect_name_dwarf_debug_aranges) section_type = eSectionTypeDWARFDebugAranges; else if (const_sect_name == g_sect_name_dwarf_debug_frame) section_type = eSectionTypeDWARFDebugFrame; else if (const_sect_name == g_sect_name_dwarf_debug_info) section_type = eSectionTypeDWARFDebugInfo; else if (const_sect_name == g_sect_name_dwarf_debug_line) section_type = eSectionTypeDWARFDebugLine; else if (const_sect_name == g_sect_name_dwarf_debug_loc) section_type = eSectionTypeDWARFDebugLoc; else if (const_sect_name == g_sect_name_dwarf_debug_macinfo) section_type = eSectionTypeDWARFDebugMacInfo; else if (const_sect_name == g_sect_name_dwarf_debug_pubnames) section_type = eSectionTypeDWARFDebugPubNames; else if (const_sect_name == g_sect_name_dwarf_debug_pubtypes) section_type = eSectionTypeDWARFDebugPubTypes; else if (const_sect_name == g_sect_name_dwarf_debug_ranges) section_type = eSectionTypeDWARFDebugRanges; else if (const_sect_name == g_sect_name_dwarf_debug_str) section_type = eSectionTypeDWARFDebugStr; else if (const_sect_name == g_sect_name_eh_frame) section_type = eSectionTypeEHFrame; else if (const_sect_name == g_sect_name_go_symtab) section_type = eSectionTypeGoSymtab; else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_CODE) { section_type = eSectionTypeCode; } else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) { section_type = eSectionTypeData; } else if (m_sect_headers[idx].flags & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { if (m_sect_headers[idx].size == 0) section_type = eSectionTypeZeroFill; else section_type = eSectionTypeData; } // Use a segment ID of the segment index shifted left by 8 so they // never conflict with any of the sections. SectionSP section_sp(new Section( module_sp, // Module to which this section belongs this, // Object file to which this section belongs idx + 1, // Section ID is the 1 based segment index shifted right by // 8 bits as not to collide with any of the 256 section IDs // that are possible const_sect_name, // Name of this section section_type, // This section is a container of other sections. m_coff_header_opt.image_base + m_sect_headers[idx].vmaddr, // File VM address == addresses as // they are found in the object file m_sect_headers[idx].vmsize, // VM size in bytes of this section m_sect_headers[idx] .offset, // Offset to the data for this section in the file m_sect_headers[idx] .size, // Size in bytes of this section as found in the file m_coff_header_opt.sect_alignment, // Section alignment m_sect_headers[idx].flags)); // Flags for this section // section_sp->SetIsEncrypted (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; } lldb_private::Address ObjectFilePECOFF::GetEntryPointAddress() { if (m_entry_point_address.IsValid()) return m_entry_point_address; if (!ParseHeader() || !IsExecutable()) return m_entry_point_address; SectionList *section_list = GetSectionList(); addr_t offset = m_coff_header_opt.entry; if (!section_list) m_entry_point_address.SetOffset(offset); else m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); return m_entry_point_address; } //---------------------------------------------------------------------- // 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) { std::lock_guard guard(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; i < header.data_dirs.size(); i++) { s->Printf(" 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::IsWindowsSubsystem() { switch (m_coff_header_opt.subsystem) { case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI: case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE_WINDOWS: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CE_GUI: case llvm::COFF::IMAGE_SUBSYSTEM_XBOX: case llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION: return true; default: return false; } } 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, IsWindowsSubsystem() ? llvm::Triple::Win32 : llvm::Triple::UnknownOS); 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/Process/Darwin/DarwinProcessLauncher.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/Darwin/DarwinProcessLauncher.cpp (revision 319790) @@ -1,653 +1,651 @@ //===-- DarwinProcessLauncher.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // DarwinProcessLauncher.cpp // lldb // // Created by Todd Fiala on 8/30/16. // // #include "DarwinProcessLauncher.h" // C includes #include #include #include #include #ifndef _POSIX_SPAWN_DISABLE_ASLR #define _POSIX_SPAWN_DISABLE_ASLR 0x0100 #endif // LLDB includes #include "lldb/lldb-enumerations.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" +#include "llvm/Support/Errno.h" #include "CFBundle.h" #include "CFString.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_darwin; using namespace lldb_private::darwin_process_launcher; namespace { static LaunchFlavor g_launch_flavor = LaunchFlavor::Default; } namespace lldb_private { namespace darwin_process_launcher { static uint32_t GetCPUTypeForLocalProcess(::pid_t pid) { int mib[CTL_MAXNAME] = { 0, }; size_t len = CTL_MAXNAME; if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) return 0; mib[len] = pid; len++; cpu_type_t cpu; size_t cpu_len = sizeof(cpu); if (::sysctl(mib, static_cast(len), &cpu, &cpu_len, 0, 0)) cpu = 0; return cpu; } static bool ResolveExecutablePath(const char *path, char *resolved_path, size_t resolved_path_size) { if (path == NULL || path[0] == '\0') return false; char max_path[PATH_MAX]; std::string result; CFString::GlobPath(path, result); if (result.empty()) result = path; struct stat path_stat; if (::stat(path, &path_stat) == 0) { if ((path_stat.st_mode & S_IFMT) == S_IFDIR) { CFBundle bundle(path); CFReleaser url(bundle.CopyExecutableURL()); if (url.get()) { if (::CFURLGetFileSystemRepresentation( url.get(), true, (UInt8 *)resolved_path, resolved_path_size)) return true; } } } if (realpath(path, max_path)) { // Found the path relatively... ::strncpy(resolved_path, max_path, resolved_path_size); return strlen(resolved_path) + 1 < resolved_path_size; } else { // Not a relative path, check the PATH environment variable if the const char *PATH = getenv("PATH"); if (PATH) { const char *curr_path_start = PATH; const char *curr_path_end; while (curr_path_start && *curr_path_start) { curr_path_end = strchr(curr_path_start, ':'); if (curr_path_end == NULL) { result.assign(curr_path_start); curr_path_start = NULL; } else if (curr_path_end > curr_path_start) { size_t len = curr_path_end - curr_path_start; result.assign(curr_path_start, len); curr_path_start += len + 1; } else break; result += '/'; result += path; struct stat s; if (stat(result.c_str(), &s) == 0) { ::strncpy(resolved_path, result.c_str(), resolved_path_size); return result.size() + 1 < resolved_path_size; } } } } return false; } // TODO check if we have a general purpose fork and exec. We may be // able to get rid of this entirely. static Status ForkChildForPTraceDebugging(const char *path, char const *argv[], char const *envp[], ::pid_t *pid, int *pty_fd) { Status error; if (!path || !argv || !envp || !pid || !pty_fd) { error.SetErrorString("invalid arguments"); return error; } // Use a fork that ties the child process's stdin/out/err to a pseudo // terminal so we can read it in our MachProcess::STDIOThread // as unbuffered io. lldb_utility::PseudoTerminal pty; char fork_error[256]; memset(fork_error, 0, sizeof(fork_error)); *pid = static_cast<::pid_t>(pty.Fork(fork_error, sizeof(fork_error))); if (*pid < 0) { //-------------------------------------------------------------- // Status during fork. //-------------------------------------------------------------- *pid = static_cast<::pid_t>(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat("%s(): fork failed: %s", __FUNCTION__, fork_error); return error; } else if (pid == 0) { //-------------------------------------------------------------- // Child process //-------------------------------------------------------------- // Debug this process. ::ptrace(PT_TRACE_ME, 0, 0, 0); // Get BSD signals as mach exceptions. ::ptrace(PT_SIGEXC, 0, 0, 0); // If our parent is setgid, lets make sure we don't inherit those // extra powers due to nepotism. if (::setgid(getgid()) == 0) { // Let the child have its own process group. We need to execute // this call in both the child and parent to avoid a race // condition between the two processes. // Set the child process group to match its pid. ::setpgid(0, 0); // Sleep a bit to before the exec call. ::sleep(1); // Turn this process into the given executable. ::execv(path, (char *const *)argv); } // Exit with error code. Child process should have taken // over in above exec call and if the exec fails it will // exit the child process below. ::exit(127); } else { //-------------------------------------------------------------- // Parent process //-------------------------------------------------------------- // Let the child have its own process group. We need to execute // this call in both the child and parent to avoid a race condition // between the two processes. // Set the child process group to match its pid ::setpgid(*pid, *pid); if (pty_fd) { // Release our master pty file descriptor so the pty class doesn't // close it and so we can continue to use it in our STDIO thread *pty_fd = pty.ReleaseMasterFileDescriptor(); } } return error; } static Status CreatePosixSpawnFileAction(const FileAction &action, posix_spawn_file_actions_t *file_actions) { Status error; // Log it. Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { StreamString stream; stream.PutCString("converting file action for posix_spawn(): "); action.Dump(stream); stream.Flush(); log->PutCString(stream.GetString().c_str()); } // Validate args. if (!file_actions) { error.SetErrorString("mandatory file_actions arg is null"); return error; } // Build the posix file action. switch (action.GetAction()) { case FileAction::eFileActionOpen: { const int error_code = ::posix_spawn_file_actions_addopen( file_actions, action.GetFD(), action.GetPath(), action.GetActionArgument(), 0); if (error_code != 0) { error.SetError(error_code, eErrorTypePOSIX); return error; } break; } case FileAction::eFileActionClose: { const int error_code = ::posix_spawn_file_actions_addclose(file_actions, action.GetFD()); if (error_code != 0) { error.SetError(error_code, eErrorTypePOSIX); return error; } break; } case FileAction::eFileActionDuplicate: { const int error_code = ::posix_spawn_file_actions_adddup2( file_actions, action.GetFD(), action.GetActionArgument()); if (error_code != 0) { error.SetError(error_code, eErrorTypePOSIX); return error; } break; } case FileAction::eFileActionNone: default: if (log) log->Printf("%s(): unsupported file action %u", __FUNCTION__, action.GetAction()); break; } return error; } static Status PosixSpawnChildForPTraceDebugging(const char *path, ProcessLaunchInfo &launch_info, ::pid_t *pid, cpu_type_t *actual_cpu_type) { Status error; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!pid) { error.SetErrorStringWithFormat("%s(): pid arg cannot be null", __FUNCTION__); return error; } posix_spawnattr_t attr; short flags; if (log) { StreamString stream; stream.Printf("%s(path='%s',...)\n", __FUNCTION__, path); launch_info.Dump(stream, nullptr); stream.Flush(); log->PutCString(stream.GetString().c_str()); } int error_code; if ((error_code = ::posix_spawnattr_init(&attr)) != 0) { if (log) log->Printf("::posix_spawnattr_init(&attr) failed"); error.SetError(error_code, eErrorTypePOSIX); return error; } // Ensure we clean up the spawnattr structure however we exit this // function. std::unique_ptr spawnattr_up( &attr, ::posix_spawnattr_destroy); flags = POSIX_SPAWN_START_SUSPENDED | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; if (launch_info.GetFlags().Test(eLaunchFlagDisableASLR)) flags |= _POSIX_SPAWN_DISABLE_ASLR; sigset_t no_signals; sigset_t all_signals; sigemptyset(&no_signals); sigfillset(&all_signals); ::posix_spawnattr_setsigmask(&attr, &no_signals); ::posix_spawnattr_setsigdefault(&attr, &all_signals); if ((error_code = ::posix_spawnattr_setflags(&attr, flags)) != 0) { - if (log) - log->Printf("::posix_spawnattr_setflags(&attr, " - "POSIX_SPAWN_START_SUSPENDED%s) failed: %s", - flags & _POSIX_SPAWN_DISABLE_ASLR - ? " | _POSIX_SPAWN_DISABLE_ASLR" - : "", - strerror(error_code)); + LLDB_LOG(log, + "::posix_spawnattr_setflags(&attr, " + "POSIX_SPAWN_START_SUSPENDED{0}) failed: {1}", + flags & _POSIX_SPAWN_DISABLE_ASLR ? " | _POSIX_SPAWN_DISABLE_ASLR" + : "", + llvm::sys::StrError(error_code)); error.SetError(error_code, eErrorTypePOSIX); return error; } #if !defined(__arm__) // We don't need to do this for ARM, and we really shouldn't now that we // have multiple CPU subtypes and no posix_spawnattr call that allows us // to set which CPU subtype to launch... cpu_type_t desired_cpu_type = launch_info.GetArchitecture().GetMachOCPUType(); if (desired_cpu_type != LLDB_INVALID_CPUTYPE) { size_t ocount = 0; error_code = ::posix_spawnattr_setbinpref_np(&attr, 1, &desired_cpu_type, &ocount); if (error_code != 0) { - if (log) - log->Printf("::posix_spawnattr_setbinpref_np(&attr, 1, " - "cpu_type = 0x%8.8x, count => %llu): %s", - desired_cpu_type, (uint64_t)ocount, strerror(error_code)); + LLDB_LOG(log, + "::posix_spawnattr_setbinpref_np(&attr, 1, " + "cpu_type = {0:x8}, count => {1}): {2}", + desired_cpu_type, ocount, llvm::sys::StrError(error_code)); error.SetError(error_code, eErrorTypePOSIX); return error; } if (ocount != 1) { error.SetErrorStringWithFormat("posix_spawnattr_setbinpref_np " "did not set the expected number " "of cpu_type entries: expected 1 " "but was %zu", ocount); return error; } } #endif posix_spawn_file_actions_t file_actions; if ((error_code = ::posix_spawn_file_actions_init(&file_actions)) != 0) { - if (log) - log->Printf("::posix_spawn_file_actions_init(&file_actions) " - "failed: %s", - strerror(error_code)); + LLDB_LOG(log, "::posix_spawn_file_actions_init(&file_actions) failed: {0}", + llvm::sys::StrError(error_code)); error.SetError(error_code, eErrorTypePOSIX); return error; } // Ensure we clean up file actions however we exit this. When the // file_actions_up below goes out of scope, we'll get our file action // cleanup. std::unique_ptr file_actions_up(&file_actions, ::posix_spawn_file_actions_destroy); // We assume the caller has setup the file actions appropriately. We // are not in the business of figuring out what we really need here. // lldb-server will have already called FinalizeFileActions() as well // to button these up properly. const size_t num_actions = launch_info.GetNumFileActions(); for (size_t action_index = 0; action_index < num_actions; ++action_index) { const FileAction *const action = launch_info.GetFileActionAtIndex(action_index); if (!action) continue; error = CreatePosixSpawnFileAction(*action, &file_actions); if (!error.Success()) { if (log) log->Printf("%s(): error converting FileAction to posix_spawn " "file action: %s", __FUNCTION__, error.AsCString()); return error; } } // TODO: Verify if we can set the working directory back immediately // after the posix_spawnp call without creating a race condition??? const char *const working_directory = launch_info.GetWorkingDirectory().GetCString(); if (working_directory && working_directory[0]) ::chdir(working_directory); auto argv = launch_info.GetArguments().GetArgumentVector(); auto envp = launch_info.GetEnvironmentEntries().GetArgumentVector(); error_code = ::posix_spawnp(pid, path, &file_actions, &attr, (char *const *)argv, (char *const *)envp); if (error_code != 0) { - if (log) - log->Printf("::posix_spawnp(pid => %p, path = '%s', file_actions " - "= %p, attr = %p, argv = %p, envp = %p) failed: %s", - pid, path, &file_actions, &attr, argv, envp, - strerror(error_code)); + LLDB_LOG(log, + "::posix_spawnp(pid => {0}, path = '{1}', file_actions " + "= {2}, attr = {3}, argv = {4}, envp = {5}) failed: {6}", + pid, path, &file_actions, &attr, argv, envp, + llvm::sys::StrError(error_code)); error.SetError(error_code, eErrorTypePOSIX); return error; } // Validate we got a pid. if (pid == LLDB_INVALID_PROCESS_ID) { error.SetErrorString("posix_spawn() did not indicate a failure but it " "failed to return a pid, aborting."); return error; } if (actual_cpu_type) { *actual_cpu_type = GetCPUTypeForLocalProcess(*pid); if (log) log->Printf("%s(): cpu type for launched process pid=%i: " "cpu_type=0x%8.8x", __FUNCTION__, *pid, *actual_cpu_type); } return error; } Status LaunchInferior(ProcessLaunchInfo &launch_info, int *pty_master_fd, LaunchFlavor *launch_flavor) { Status error; Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (!launch_flavor) { error.SetErrorString("mandatory launch_flavor field was null"); return error; } if (log) { StreamString stream; stream.Printf("NativeProcessDarwin::%s(): launching with the " "following launch info:", __FUNCTION__); launch_info.Dump(stream, nullptr); stream.Flush(); log->PutCString(stream.GetString().c_str()); } // Retrieve the binary name given to us. char given_path[PATH_MAX]; given_path[0] = '\0'; launch_info.GetExecutableFile().GetPath(given_path, sizeof(given_path)); // Determine the manner in which we'll launch. *launch_flavor = g_launch_flavor; if (*launch_flavor == LaunchFlavor::Default) { // Our default launch method is posix spawn *launch_flavor = LaunchFlavor::PosixSpawn; #if defined WITH_FBS // Check if we have an app bundle, if so launch using BackBoard Services. if (strstr(given_path, ".app")) { *launch_flavor = eLaunchFlavorFBS; } #elif defined WITH_BKS // Check if we have an app bundle, if so launch using BackBoard Services. if (strstr(given_path, ".app")) { *launch_flavor = eLaunchFlavorBKS; } #elif defined WITH_SPRINGBOARD // Check if we have an app bundle, if so launch using SpringBoard. if (strstr(given_path, ".app")) { *launch_flavor = eLaunchFlavorSpringBoard; } #endif } // Attempt to resolve the binary name to an absolute path. char resolved_path[PATH_MAX]; resolved_path[0] = '\0'; if (log) log->Printf("%s(): attempting to resolve given binary path: \"%s\"", __FUNCTION__, given_path); // If we fail to resolve the path to our executable, then just use what we // were given and hope for the best if (!ResolveExecutablePath(given_path, resolved_path, sizeof(resolved_path))) { if (log) log->Printf("%s(): failed to resolve binary path, using " "what was given verbatim and hoping for the best", __FUNCTION__); ::strncpy(resolved_path, given_path, sizeof(resolved_path)); } else { if (log) log->Printf("%s(): resolved given binary path to: \"%s\"", __FUNCTION__, resolved_path); } char launch_err_str[PATH_MAX]; launch_err_str[0] = '\0'; // TODO figure out how to handle QSetProcessEvent // const char *process_event = ctx.GetProcessEvent(); // Ensure the binary is there. struct stat path_stat; if (::stat(resolved_path, &path_stat) == -1) { error.SetErrorToErrno(); return error; } // Fork a child process for debugging // state_callback(eStateLaunching); const auto argv = launch_info.GetArguments().GetConstArgumentVector(); const auto envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector(); switch (*launch_flavor) { case LaunchFlavor::ForkExec: { ::pid_t pid = LLDB_INVALID_PROCESS_ID; error = ForkChildForPTraceDebugging(resolved_path, argv, envp, &pid, pty_master_fd); if (error.Success()) { launch_info.SetProcessID(static_cast(pid)); } else { // Reset any variables that might have been set during a failed // launch attempt. if (pty_master_fd) *pty_master_fd = -1; // We're done. return error; } } break; #ifdef WITH_FBS case LaunchFlavor::FBS: { const char *app_ext = strstr(path, ".app"); if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) { std::string app_bundle_path(path, app_ext + strlen(".app")); m_flags |= eMachProcessFlagsUsingFBS; if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. else break; // We tried a FBS launch, but didn't succeed lets get out } } break; #endif #ifdef WITH_BKS case LaunchFlavor::BKS: { const char *app_ext = strstr(path, ".app"); if (app_ext && (app_ext[4] == '\0' || app_ext[4] == '/')) { std::string app_bundle_path(path, app_ext + strlen(".app")); m_flags |= eMachProcessFlagsUsingBKS; if (BoardServiceLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, event_data, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. else break; // We tried a BKS launch, but didn't succeed lets get out } } break; #endif #ifdef WITH_SPRINGBOARD case LaunchFlavor::SpringBoard: { // .../whatever.app/whatever ? // Or .../com.apple.whatever.app/whatever -- be careful of ".app" in // "com.apple.whatever" here const char *app_ext = strstr(path, ".app/"); if (app_ext == NULL) { // .../whatever.app ? int len = strlen(path); if (len > 5) { if (strcmp(path + len - 4, ".app") == 0) { app_ext = path + len - 4; } } } if (app_ext) { std::string app_bundle_path(path, app_ext + strlen(".app")); if (SBLaunchForDebug(app_bundle_path.c_str(), argv, envp, no_stdio, disable_aslr, launch_err) != 0) return m_pid; // A successful SBLaunchForDebug() returns and assigns a // non-zero m_pid. else break; // We tried a springboard launch, but didn't succeed lets get out } } break; #endif case LaunchFlavor::PosixSpawn: { ::pid_t pid = LLDB_INVALID_PROCESS_ID; // Retrieve paths for stdin/stdout/stderr. cpu_type_t actual_cpu_type = 0; error = PosixSpawnChildForPTraceDebugging(resolved_path, launch_info, &pid, &actual_cpu_type); if (error.Success()) { launch_info.SetProcessID(static_cast(pid)); if (pty_master_fd) *pty_master_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); } else { // Reset any variables that might have been set during a failed // launch attempt. if (pty_master_fd) *pty_master_fd = -1; // We're done. return error; } break; } default: // Invalid launch flavor. error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): unknown " "launch flavor %d", __FUNCTION__, (int)*launch_flavor); return error; } if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) { // If we don't have a valid process ID and no one has set the error, // then return a generic error. if (error.Success()) error.SetErrorStringWithFormat("%s(): failed to launch, no reason " "specified", __FUNCTION__); } // We're done with the launch side of the operation. return error; } } } // namespaces Index: vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp (revision 319790) @@ -1,1463 +1,1462 @@ //===-- ProcessMonitor.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 #include #include #include #include #include #include #include #include #include #include // C++ Includes // Other libraries and framework includes #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/Host.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Status.h" +#include "llvm/Support/Errno.h" #include "FreeBSDThread.h" #include "Plugins/Process/POSIX/CrashReason.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "ProcessFreeBSD.h" #include "ProcessMonitor.h" extern "C" { extern char **environ; } using namespace lldb; using namespace lldb_private; // We disable the tracing of ptrace calls for integration builds to // avoid the additional indirection and checks. #ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION // Wrapper for ptrace to catch errors and log calls. const char *Get_PT_IO_OP(int op) { switch (op) { case PIOD_READ_D: return "READ_D"; case PIOD_WRITE_D: return "WRITE_D"; case PIOD_READ_I: return "READ_I"; case PIOD_WRITE_I: return "WRITE_I"; default: return "Unknown op"; } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 is reserved as a valid // result. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, const char *reqName, const char *file, int line) { long int result; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (log) { log->Printf("ptrace(%s, %" PRIu64 ", %p, %x) called from file %s line %d", reqName, pid, addr, data, file, line); if (req == PT_IO) { struct ptrace_io_desc *pi = (struct ptrace_io_desc *)addr; log->Printf("PT_IO: op=%s offs=%zx size=%zu", Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len); } } // PtraceDisplayBytes(req, data); errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); // PtraceDisplayBytes(req, data); if (log && errno != 0) { const char *str; switch (errno) { case ESRCH: str = "ESRCH"; break; case EINVAL: str = "EINVAL"; break; case EBUSY: str = "EBUSY"; break; case EPERM: str = "EPERM"; break; default: str = ""; } log->Printf("ptrace() failed; errno=%d (%s)", errno, str); } if (log) { #ifdef __amd64__ if (req == PT_GETREGS) { struct reg *r = (struct reg *)addr; log->Printf("PT_GETREGS: rip=0x%lx rsp=0x%lx rbp=0x%lx rax=0x%lx", r->r_rip, r->r_rsp, r->r_rbp, r->r_rax); } if (req == PT_GETDBREGS || req == PT_SETDBREGS) { struct dbreg *r = (struct dbreg *)addr; char setget = (req == PT_GETDBREGS) ? 'G' : 'S'; for (int i = 0; i <= 7; i++) log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]); } #endif } return result; } // Wrapper for ptrace when logging is not required. // Sets errno to 0 prior to calling ptrace. extern long PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) { long result = 0; errno = 0; result = ptrace(req, pid, (caddr_t)addr, data); return result; } #define PTRACE(req, pid, addr, data) \ PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) #else PtraceWrapper((req), (pid), (addr), (data)) #endif //------------------------------------------------------------------------------ // Static implementations of ProcessMonitor::ReadMemory and // ProcessMonitor::WriteMemory. This enables mutual recursion between these // functions without needed to go thru the thread funnel. static size_t DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, Status &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_READ_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } static size_t DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, size_t size, Status &error) { struct ptrace_io_desc pi_desc; pi_desc.piod_op = PIOD_WRITE_D; pi_desc.piod_offs = (void *)vm_addr; pi_desc.piod_addr = (void *)buf; pi_desc.piod_len = size; if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) error.SetErrorToErrno(); return pi_desc.piod_len; } // Simple helper function to ensure flags are enabled on the given file // descriptor. static bool EnsureFDFlags(int fd, int flags, Status &error) { int status; if ((status = fcntl(fd, F_GETFL)) == -1) { error.SetErrorToErrno(); return false; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return false; } return true; } //------------------------------------------------------------------------------ /// @class Operation /// @brief Represents a ProcessMonitor operation. /// /// Under FreeBSD, it is not possible to ptrace() from any other thread but the /// one that spawned or attached to the process from the start. Therefore, when /// a ProcessMonitor is asked to deliver or change the state of an inferior /// process the operation must be "funneled" to a specific thread to perform the /// task. The Operation class provides an abstract base for all services the /// ProcessMonitor must perform via the single virtual function Execute, thus /// encapsulating the code that needs to run in the privileged context. class Operation { public: virtual ~Operation() {} virtual void Execute(ProcessMonitor *monitor) = 0; }; //------------------------------------------------------------------------------ /// @class ReadOperation /// @brief Implements ProcessMonitor::ReadMemory. class ReadOperation : public Operation { public: ReadOperation(lldb::addr_t addr, void *buff, size_t size, Status &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; void *m_buff; size_t m_size; Status &m_error; size_t &m_result; }; void ReadOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class WriteOperation /// @brief Implements ProcessMonitor::WriteMemory. class WriteOperation : public Operation { public: WriteOperation(lldb::addr_t addr, const void *buff, size_t size, Status &error, size_t &result) : m_addr(addr), m_buff(buff), m_size(size), m_error(error), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::addr_t m_addr; const void *m_buff; size_t m_size; Status &m_error; size_t &m_result; }; void WriteOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); } //------------------------------------------------------------------------------ /// @class ReadRegOperation /// @brief Implements ProcessMonitor::ReadRegisterValue. class ReadRegOperation : public Operation { public: ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; int rc; if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { // 'struct reg' contains only 32- or 64-bit register values. Punt on // others. Also, not all entries may be uintptr_t sized, such as 32-bit // processes on powerpc64 (probably the same for i386 on amd64) if (m_size == sizeof(uint32_t)) m_value = *(uint32_t *)(((caddr_t)®s) + m_offset); else if (m_size == sizeof(uint64_t)) m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); else memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteRegOperation /// @brief Implements ProcessMonitor::WriteRegisterValue. class WriteRegOperation : public Operation { public: WriteRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteRegOperation::Execute(ProcessMonitor *monitor) { struct reg regs; if (PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadDebugRegOperation /// @brief Implements ProcessMonitor::ReadDebugRegisterValue. class ReadDebugRegOperation : public Operation { public: ReadDebugRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_size(size), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; unsigned m_size; RegisterValue &m_value; bool &m_result; }; void ReadDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; int rc; if ((rc = PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { if (m_size == sizeof(uintptr_t)) m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); else memcpy((void *)&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } } //------------------------------------------------------------------------------ /// @class WriteDebugRegOperation /// @brief Implements ProcessMonitor::WriteDebugRegisterValue. class WriteDebugRegOperation : public Operation { public: WriteDebugRegOperation(lldb::tid_t tid, unsigned offset, const RegisterValue &value, bool &result) : m_tid(tid), m_offset(offset), m_value(value), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned m_offset; const RegisterValue &m_value; bool &m_result; }; void WriteDebugRegOperation::Execute(ProcessMonitor *monitor) { struct dbreg regs; if (PTRACE(PT_GETDBREGS, m_tid, (caddr_t)®s, 0) < 0) { m_result = false; return; } *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); if (PTRACE(PT_SETDBREGS, m_tid, (caddr_t)®s, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadGPROperation /// @brief Implements ProcessMonitor::ReadGPR. class ReadGPROperation : public Operation { public: ReadGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadGPROperation::Execute(ProcessMonitor *monitor) { int rc; errno = 0; rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0); if (errno != 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ReadFPROperation /// @brief Implements ProcessMonitor::ReadFPR. class ReadFPROperation : public Operation { public: ReadFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void ReadFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteGPROperation /// @brief Implements ProcessMonitor::WriteGPR. class WriteGPROperation : public Operation { public: WriteGPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteGPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class WriteFPROperation /// @brief Implements ProcessMonitor::WriteFPR. class WriteFPROperation : public Operation { public: WriteFPROperation(lldb::tid_t tid, void *buf, bool &result) : m_tid(tid), m_buf(buf), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_buf; bool &m_result; }; void WriteFPROperation::Execute(ProcessMonitor *monitor) { if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements ProcessMonitor::Resume. class ResumeOperation : public Operation { public: ResumeOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) {} void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void ResumeOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_CONTINUE, pid, (caddr_t)1, data)) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); - - if (log) - log->Printf("ResumeOperation (%" PRIu64 ") failed: %s", pid, - strerror(errno)); + LLDB_LOG(log, "ResumeOperation ({0}) failed: {1}", pid, + llvm::sys::StrError(errno)); m_result = false; } else m_result = true; } //------------------------------------------------------------------------------ /// @class SingleStepOperation /// @brief Implements ProcessMonitor::SingleStep. class SingleStepOperation : public Operation { public: SingleStepOperation(uint32_t signo, bool &result) : m_signo(signo), m_result(result) {} void Execute(ProcessMonitor *monitor); private: uint32_t m_signo; bool &m_result; }; void SingleStepOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); int data = 0; if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) data = m_signo; if (PTRACE(PT_STEP, pid, NULL, data)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class LwpInfoOperation /// @brief Implements ProcessMonitor::GetLwpInfo. class LwpInfoOperation : public Operation { public: LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; void *m_info; bool &m_result; int &m_err; }; void LwpInfoOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) { m_result = false; m_err = errno; } else { memcpy(m_info, &plwp, sizeof(plwp)); m_result = true; } } //------------------------------------------------------------------------------ /// @class ThreadSuspendOperation /// @brief Implements ProcessMonitor::ThreadSuspend. class ThreadSuspendOperation : public Operation { public: ThreadSuspendOperation(lldb::tid_t tid, bool suspend, bool &result) : m_tid(tid), m_suspend(suspend), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; bool m_suspend; bool &m_result; }; void ThreadSuspendOperation::Execute(ProcessMonitor *monitor) { m_result = !PTRACE(m_suspend ? PT_SUSPEND : PT_RESUME, m_tid, NULL, 0); } //------------------------------------------------------------------------------ /// @class EventMessageOperation /// @brief Implements ProcessMonitor::GetEventMessage. class EventMessageOperation : public Operation { public: EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) : m_tid(tid), m_message(message), m_result(result) {} void Execute(ProcessMonitor *monitor); private: lldb::tid_t m_tid; unsigned long *m_message; bool &m_result; }; void EventMessageOperation::Execute(ProcessMonitor *monitor) { struct ptrace_lwpinfo plwp; if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) m_result = false; else { if (plwp.pl_flags & PL_FLAG_FORKED) { *m_message = plwp.pl_child_pid; m_result = true; } else m_result = false; } } //------------------------------------------------------------------------------ /// @class KillOperation /// @brief Implements ProcessMonitor::Kill. class KillOperation : public Operation { public: KillOperation(bool &result) : m_result(result) {} void Execute(ProcessMonitor *monitor); private: bool &m_result; }; void KillOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_KILL, pid, NULL, 0)) m_result = false; else m_result = true; } //------------------------------------------------------------------------------ /// @class DetachOperation /// @brief Implements ProcessMonitor::Detach. class DetachOperation : public Operation { public: DetachOperation(Status &result) : m_error(result) {} void Execute(ProcessMonitor *monitor); private: Status &m_error; }; void DetachOperation::Execute(ProcessMonitor *monitor) { lldb::pid_t pid = monitor->GetPID(); if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) m_error.SetErrorToErrno(); } ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) : m_monitor(monitor) { sem_init(&m_semaphore, 0, 0); } ProcessMonitor::OperationArgs::~OperationArgs() { sem_destroy(&m_semaphore); } ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, lldb_private::Module *module, char const **argv, char const **envp, const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir) : OperationArgs(monitor), m_module(module), m_argv(argv), m_envp(envp), m_stdin_file_spec(stdin_file_spec), m_stdout_file_spec(stdout_file_spec), m_stderr_file_spec(stderr_file_spec), m_working_dir(working_dir) {} ProcessMonitor::LaunchArgs::~LaunchArgs() {} ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, lldb::pid_t pid) : OperationArgs(monitor), m_pid(pid) {} ProcessMonitor::AttachArgs::~AttachArgs() {} //------------------------------------------------------------------------------ /// The basic design of the ProcessMonitor is built around two threads. /// /// One thread (@see SignalThread) simply blocks on a call to waitpid() looking /// for changes in the debugee state. When a change is detected a /// ProcessMessage is sent to the associated ProcessFreeBSD instance. This /// thread /// "drives" state changes in the debugger. /// /// The second thread (@see OperationThread) is responsible for two things 1) /// launching or attaching to the inferior process, and then 2) servicing /// operations such as register reads/writes, stepping, etc. See the comments /// on the Operation class for more info as to why this is needed. ProcessMonitor::ProcessMonitor( ProcessFreeBSD *process, Module *module, const char *argv[], const char *envp[], const FileSpec &stdin_file_spec, const FileSpec &stdout_file_spec, const FileSpec &stderr_file_spec, const FileSpec &working_dir, const lldb_private::ProcessLaunchInfo & /* launch_info */, lldb_private::Status &error) : m_process(static_cast(process)), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) { using namespace std::placeholders; std::unique_ptr args( new LaunchArgs(this, module, argv, envp, stdin_file_spec, stdout_file_spec, stderr_file_spec, working_dir)); sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); StartLaunchOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the launch was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process launch failed."); return; } } ProcessMonitor::ProcessMonitor(ProcessFreeBSD *process, lldb::pid_t pid, lldb_private::Status &error) : m_process(static_cast(process)), m_pid(pid), m_terminal_fd(-1), m_operation(0) { using namespace std::placeholders; sem_init(&m_operation_pending, 0, 0); sem_init(&m_operation_done, 0, 0); std::unique_ptr args(new AttachArgs(this, pid)); StartAttachOpThread(args.get(), error); if (!error.Success()) return; WAIT_AGAIN: // Wait for the operation thread to initialize. if (sem_wait(&args->m_semaphore)) { if (errno == EINTR) goto WAIT_AGAIN; else { error.SetErrorToErrno(); return; } } // Check that the attach was a success. if (!args->m_error.Success()) { StopOpThread(); error = args->m_error; return; } // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( std::bind(&ProcessMonitor::MonitorCallback, this, _1, _2, _3, _4), GetPID(), true); if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process attach failed."); return; } } ProcessMonitor::~ProcessMonitor() { StopMonitor(); } //------------------------------------------------------------------------------ // Thread setup and tear down. void ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Status &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args, &error); } void *ProcessMonitor::LaunchOpThread(void *arg) { LaunchArgs *args = static_cast(arg); if (!Launch(args)) { sem_post(&args->m_semaphore); return NULL; } ServeOperation(args); return NULL; } bool ProcessMonitor::Launch(LaunchArgs *args) { ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); const char **argv = args->m_argv; const char **envp = args->m_envp; const FileSpec &stdin_file_spec = args->m_stdin_file_spec; const FileSpec &stdout_file_spec = args->m_stdout_file_spec; const FileSpec &stderr_file_spec = args->m_stderr_file_spec; const FileSpec &working_dir = args->m_working_dir; lldb_utility::PseudoTerminal terminal; const size_t err_len = 1024; char err_str[err_len]; ::pid_t pid; // Propagate the environment if one is not supplied. if (envp == NULL || envp[0] == NULL) envp = const_cast(environ); if ((pid = terminal.Fork(err_str, err_len)) == -1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Process fork failed."); goto FINISH; } // Recognized child exit status codes. enum { ePtraceFailed = 1, eDupStdinFailed, eDupStdoutFailed, eDupStderrFailed, eChdirFailed, eExecFailed, eSetGidFailed }; // Child process. if (pid == 0) { // Trace this process. if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) exit(ePtraceFailed); // terminal has already dupped the tty descriptors to stdin/out/err. // This closes original fd from which they were copied (and avoids // leaking descriptors to the debugged process. terminal.CloseSlaveFileDescriptor(); // Do not inherit setgid powers. if (setgid(getgid()) != 0) exit(eSetGidFailed); // Let us have our own process group. setpgid(0, 0); // Dup file descriptors if needed. // // FIXME: If two or more of the paths are the same we needlessly open // the same file multiple times. if (stdin_file_spec) if (!DupDescriptor(stdin_file_spec, STDIN_FILENO, O_RDONLY)) exit(eDupStdinFailed); if (stdout_file_spec) if (!DupDescriptor(stdout_file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT)) exit(eDupStdoutFailed); if (stderr_file_spec) if (!DupDescriptor(stderr_file_spec, STDERR_FILENO, O_WRONLY | O_CREAT)) exit(eDupStderrFailed); // Change working directory if (working_dir && 0 != ::chdir(working_dir.GetCString())) exit(eChdirFailed); // Execute. We should never return. execve(argv[0], const_cast(argv), const_cast(envp)); exit(eExecFailed); } // Wait for the child process to to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } else if (WIFEXITED(status)) { // open, dup or execve likely failed for some reason. args->m_error.SetErrorToGenericError(); switch (WEXITSTATUS(status)) { case ePtraceFailed: args->m_error.SetErrorString("Child ptrace failed."); break; case eDupStdinFailed: args->m_error.SetErrorString("Child open stdin failed."); break; case eDupStdoutFailed: args->m_error.SetErrorString("Child open stdout failed."); break; case eDupStderrFailed: args->m_error.SetErrorString("Child open stderr failed."); break; case eChdirFailed: args->m_error.SetErrorString("Child failed to set working directory."); break; case eExecFailed: args->m_error.SetErrorString("Child exec failed."); break; case eSetGidFailed: args->m_error.SetErrorString("Child setgid failed."); break; default: args->m_error.SetErrorString("Child returned unknown exit status."); break; } goto FINISH; } assert(WIFSTOPPED(status) && wpid == (::pid_t)pid && "Could not sync with inferior process."); #ifdef notyet // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) { args->m_error.SetErrorToErrno(); goto FINISH; } #endif // Release the master terminal descriptor and pass it off to the // ProcessMonitor instance. Similarly stash the inferior pid. monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); monitor->m_pid = pid; // Set the terminal fd to be in non blocking mode (it simplifies the // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking // descriptor to read from). if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) goto FINISH; process.SendMessage(ProcessMessage::Attach(pid)); FINISH: return args->m_error.Success(); } void ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Status &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; if (m_operation_thread.IsJoinable()) return; m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args, &error); } void *ProcessMonitor::AttachOpThread(void *arg) { AttachArgs *args = static_cast(arg); Attach(args); ServeOperation(args); return NULL; } void ProcessMonitor::Attach(AttachArgs *args) { lldb::pid_t pid = args->m_pid; ProcessMonitor *monitor = args->m_monitor; ProcessFreeBSD &process = monitor->GetProcess(); if (pid <= 1) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Attaching to process 1 is not allowed."); return; } // Attach to the requested process. if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) { args->m_error.SetErrorToErrno(); return; } int status; if ((status = waitpid(pid, NULL, 0)) < 0) { args->m_error.SetErrorToErrno(); return; } process.SendMessage(ProcessMessage::Attach(pid)); } size_t ProcessMonitor::GetCurrentThreadIDs(std::vector &thread_ids) { lwpid_t *tids; int tdcnt; thread_ids.clear(); tdcnt = PTRACE(PT_GETNUMLWPS, m_pid, NULL, 0); if (tdcnt <= 0) return 0; tids = (lwpid_t *)malloc(tdcnt * sizeof(*tids)); if (tids == NULL) return 0; if (PTRACE(PT_GETLWPLIST, m_pid, (void *)tids, tdcnt) < 0) { free(tids); return 0; } thread_ids = std::vector(tids, tids + tdcnt); free(tids); return thread_ids.size(); } bool ProcessMonitor::MonitorCallback(ProcessMonitor *monitor, lldb::pid_t pid, bool exited, int signal, int status) { ProcessMessage message; ProcessFreeBSD *process = monitor->m_process; assert(process); bool stop_monitoring; struct ptrace_lwpinfo plwp; int ptrace_err; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (exited) { if (log) log->Printf("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Exit(pid, status); process->SendMessage(message); return pid == process->GetID(); } if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err)) stop_monitoring = true; // pid is gone. Bail. else { switch (plwp.pl_siginfo.si_signo) { case SIGTRAP: message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; default: message = MonitorSignal(monitor, &plwp.pl_siginfo, plwp.pl_lwpid); break; } process->SendMessage(message); stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; } return stop_monitoring; } ProcessMessage ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); assert(monitor); assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); switch (info->si_code) { default: assert(false && "Unexpected SIGTRAP code!"); break; case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): { // The inferior process is about to exit. Maintain the process in a // state of "limbo" until we are explicitly commanded to detach, // destroy, resume, etc. unsigned long data = 0; if (!monitor->GetEventMessage(tid, &data)) data = -1; if (log) log->Printf("ProcessMonitor::%s() received exit? event, data = %lx, tid " "= %" PRIu64, __FUNCTION__, data, tid); message = ProcessMessage::Limbo(tid, (data >> 8)); break; } case 0: case TRAP_TRACE: #ifdef TRAP_CAP // Map TRAP_CAP to a trace trap in the absense of a more specific handler. case TRAP_CAP: #endif if (log) log->Printf("ProcessMonitor::%s() received trace event, tid = %" PRIu64 " : si_code = %d", __FUNCTION__, tid, info->si_code); message = ProcessMessage::Trace(tid); break; case SI_KERNEL: case TRAP_BRKPT: if (monitor->m_process->IsSoftwareStepBreakpoint(tid)) { if (log) log->Printf("ProcessMonitor::%s() received sw single step breakpoint " "event, tid = %" PRIu64, __FUNCTION__, tid); message = ProcessMessage::Trace(tid); } else { if (log) log->Printf( "ProcessMonitor::%s() received breakpoint event, tid = %" PRIu64, __FUNCTION__, tid); message = ProcessMessage::Break(tid); } break; } return message; } ProcessMessage ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, const siginfo_t *info, lldb::tid_t tid) { ProcessMessage message; int signo = info->si_signo; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. if (info->si_code == SI_USER) { if (log) log->Printf( "ProcessMonitor::%s() received signal %s with code %s, pid = %d", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo), "SI_USER", info->si_pid); if (info->si_pid == getpid()) return ProcessMessage::SignalDelivered(tid, signo); else return ProcessMessage::Signal(tid, signo); } if (log) log->Printf( "ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals()->GetSignalAsCString(signo)); switch (signo) { case SIGSEGV: case SIGILL: case SIGFPE: case SIGBUS: lldb::addr_t fault_addr = reinterpret_cast(info->si_addr); const auto reason = GetCrashReason(*info); return ProcessMessage::Crash(tid, reason, signo, fault_addr); } // Everything else is "normal" and does not require any special action on // our part. return ProcessMessage::Signal(tid, signo); } void ProcessMonitor::ServeOperation(OperationArgs *args) { ProcessMonitor *monitor = args->m_monitor; // We are finised with the arguments and are ready to go. Sync with the // parent thread and start serving operations on the inferior. sem_post(&args->m_semaphore); for (;;) { // wait for next pending operation sem_wait(&monitor->m_operation_pending); monitor->m_operation->Execute(monitor); // notify calling thread that operation is complete sem_post(&monitor->m_operation_done); } } void ProcessMonitor::DoOperation(Operation *op) { std::lock_guard guard(m_operation_mutex); m_operation = op; // notify operation thread that an operation is ready to be processed sem_post(&m_operation_pending); // wait for operation to complete sem_wait(&m_operation_done); } size_t ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) { size_t result; ReadOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } size_t ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, lldb_private::Status &error) { size_t result; WriteOperation op(vm_addr, buf, size, error, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, RegisterValue &value) { bool result; ReadRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, const RegisterValue &value) { bool result; WriteRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadDebugRegisterValue( lldb::tid_t tid, unsigned offset, const char *reg_name, unsigned size, lldb_private::RegisterValue &value) { bool result; ReadDebugRegOperation op(tid, offset, size, value, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteDebugRegisterValue( lldb::tid_t tid, unsigned offset, const char *reg_name, const lldb_private::RegisterValue &value) { bool result; WriteDebugRegOperation op(tid, offset, value, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; ReadFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteGPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) { bool result; WriteFPROperation op(tid, buf, result); DoOperation(&op); return result; } bool ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) { return false; } bool ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) { return false; } bool ProcessMonitor::Resume(lldb::tid_t unused, uint32_t signo) { bool result; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (log) { const char *signame = m_process->GetUnixSignals()->GetSignalAsCString(signo); if (signame == nullptr) signame = ""; log->Printf("ProcessMonitor::%s() resuming pid %" PRIu64 " with signal %s", __FUNCTION__, GetPID(), signame); } ResumeOperation op(signo, result); DoOperation(&op); if (log) log->Printf("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); return result; } bool ProcessMonitor::SingleStep(lldb::tid_t unused, uint32_t signo) { bool result; SingleStepOperation op(signo, result); DoOperation(&op); return result; } bool ProcessMonitor::Kill() { bool result; KillOperation op(result); DoOperation(&op); return result; } bool ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err) { bool result; LwpInfoOperation op(tid, lwpinfo, result, ptrace_err); DoOperation(&op); return result; } bool ProcessMonitor::ThreadSuspend(lldb::tid_t tid, bool suspend) { bool result; ThreadSuspendOperation op(tid, suspend, result); DoOperation(&op); return result; } bool ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) { bool result; EventMessageOperation op(tid, message, result); DoOperation(&op); return result; } lldb_private::Status ProcessMonitor::Detach(lldb::tid_t tid) { lldb_private::Status error; if (tid != LLDB_INVALID_THREAD_ID) { DetachOperation op(error); DoOperation(&op); } return error; } bool ProcessMonitor::DupDescriptor(const FileSpec &file_spec, int fd, int flags) { int target_fd = open(file_spec.GetCString(), flags, 0666); if (target_fd == -1) return false; if (dup2(target_fd, fd) == -1) return false; return (close(target_fd) == -1) ? false : true; } void ProcessMonitor::StopMonitoringChildProcess() { if (m_monitor_thread.IsJoinable()) { m_monitor_thread.Cancel(); m_monitor_thread.Join(nullptr); m_monitor_thread.Reset(); } } void ProcessMonitor::StopMonitor() { StopMonitoringChildProcess(); StopOpThread(); sem_destroy(&m_operation_pending); sem_destroy(&m_operation_done); if (m_terminal_fd >= 0) { close(m_terminal_fd); m_terminal_fd = -1; } } // FIXME: On Linux, when a new thread is created, we receive to notifications, // (1) a SIGTRAP|PTRACE_EVENT_CLONE from the main process thread with the // child thread id as additional information, and (2) a SIGSTOP|SI_USER from // the new child thread indicating that it has is stopped because we attached. // We have no guarantee of the order in which these arrive, but we need both // before we are ready to proceed. We currently keep a list of threads which // have sent the initial SIGSTOP|SI_USER event. Then when we receive the // SIGTRAP|PTRACE_EVENT_CLONE notification, if the initial stop has not occurred // we call ProcessMonitor::WaitForInitialTIDStop() to wait for it. // // Right now, the above logic is in ProcessPOSIX, so we need a definition of // this function in the FreeBSD ProcessMonitor implementation even if it isn't // logically needed. // // We really should figure out what actually happens on FreeBSD and move the // Linux-specific logic out of ProcessPOSIX as needed. bool ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) { return true; } void ProcessMonitor::StopOpThread() { if (!m_operation_thread.IsJoinable()) return; m_operation_thread.Cancel(); m_operation_thread.Join(nullptr); m_operation_thread.Reset(); } Index: vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/Linux/NativeProcessLinux.cpp (revision 319790) @@ -1,2453 +1,2453 @@ //===-- NativeProcessLinux.cpp -------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "NativeProcessLinux.h" // C Includes #include #include #include #include // C++ Includes #include #include #include #include #include // Other libraries and framework includes #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostProcess.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/common/NativeBreakpoint.h" #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/linux/Ptrace.h" #include "lldb/Host/linux/Uio.h" #include "lldb/Host/posix/ProcessLauncherPosixFork.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Process.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StringExtractor.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Threading.h" #include "NativeThreadLinux.h" #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "Procfs.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Threading.h" - #include #include #include #include #include #include // Support hardware breakpoints in case it has not been defined #ifndef TRAP_HWBKPT #define TRAP_HWBKPT 4 #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_linux; using namespace llvm; // Private bits we only need internally. static bool ProcessVmReadvSupported() { static bool is_supported; static llvm::once_flag flag; llvm::call_once(flag, [] { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); uint32_t source = 0x47424742; uint32_t dest = 0; struct iovec local, remote; remote.iov_base = &source; local.iov_base = &dest; remote.iov_len = local.iov_len = sizeof source; // We shall try if cross-process-memory reads work by attempting to read a // value from our own process. ssize_t res = process_vm_readv(getpid(), &local, 1, &remote, 1, 0); is_supported = (res == sizeof(source) && source == dest); if (is_supported) LLDB_LOG(log, "Detected kernel support for process_vm_readv syscall. " "Fast memory reads enabled."); else LLDB_LOG(log, "syscall process_vm_readv failed (error: {0}). Fast memory " "reads disabled.", - strerror(errno)); + llvm::sys::StrError()); }); return is_supported; } namespace { void MaybeLogLaunchInfo(const ProcessLaunchInfo &info) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); if (!log) return; if (const FileAction *action = info.GetFileActionForFD(STDIN_FILENO)) LLDB_LOG(log, "setting STDIN to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDIN as is"); if (const FileAction *action = info.GetFileActionForFD(STDOUT_FILENO)) LLDB_LOG(log, "setting STDOUT to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDOUT as is"); if (const FileAction *action = info.GetFileActionForFD(STDERR_FILENO)) LLDB_LOG(log, "setting STDERR to '{0}'", action->GetFileSpec()); else LLDB_LOG(log, "leaving STDERR as is"); int i = 0; for (const char **args = info.GetArguments().GetConstArgumentVector(); *args; ++args, ++i) LLDB_LOG(log, "arg {0}: '{1}'", i, *args); } void DisplayBytes(StreamString &s, void *bytes, uint32_t count) { uint8_t *ptr = (uint8_t *)bytes; const uint32_t loop_count = std::min(DEBUG_PTRACE_MAXBYTES, count); for (uint32_t i = 0; i < loop_count; i++) { s.Printf("[%x]", *ptr); ptr++; } } void PtraceDisplayBytes(int &req, void *data, size_t data_size) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); if (!log) return; StreamString buf; switch (req) { case PTRACE_POKETEXT: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKETEXT {0}", buf.GetData()); break; } case PTRACE_POKEDATA: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKEDATA {0}", buf.GetData()); break; } case PTRACE_POKEUSER: { DisplayBytes(buf, &data, 8); LLDB_LOGV(log, "PTRACE_POKEUSER {0}", buf.GetData()); break; } case PTRACE_SETREGS: { DisplayBytes(buf, data, data_size); LLDB_LOGV(log, "PTRACE_SETREGS {0}", buf.GetData()); break; } case PTRACE_SETFPREGS: { DisplayBytes(buf, data, data_size); LLDB_LOGV(log, "PTRACE_SETFPREGS {0}", buf.GetData()); break; } case PTRACE_SETSIGINFO: { DisplayBytes(buf, data, sizeof(siginfo_t)); LLDB_LOGV(log, "PTRACE_SETSIGINFO {0}", buf.GetData()); break; } case PTRACE_SETREGSET: { // Extract iov_base from data, which is a pointer to the struct IOVEC DisplayBytes(buf, *(void **)data, data_size); LLDB_LOGV(log, "PTRACE_SETREGSET {0}", buf.GetData()); break; } default: {} } } static constexpr unsigned k_ptrace_word_size = sizeof(void *); static_assert(sizeof(long) >= k_ptrace_word_size, "Size of long must be larger than ptrace word size"); } // end of anonymous namespace // Simple helper function to ensure flags are enabled on the given file // descriptor. static Status EnsureFDFlags(int fd, int flags) { Status error; int status = fcntl(fd, F_GETFL); if (status == -1) { error.SetErrorToErrno(); return error; } if (fcntl(fd, F_SETFL, status | flags) == -1) { error.SetErrorToErrno(); return error; } return error; } // ----------------------------------------------------------------------------- // Public Static Methods // ----------------------------------------------------------------------------- Status NativeProcessProtocol::Launch( ProcessLaunchInfo &launch_info, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); Status error; // Verify the working directory is valid if one was specified. FileSpec working_dir{launch_info.GetWorkingDirectory()}; if (working_dir && (!working_dir.ResolvePath() || !llvm::sys::fs::is_directory(working_dir.GetPath()))) { error.SetErrorStringWithFormat("No such file or directory: %s", working_dir.GetCString()); return error; } // Create the NativeProcessLinux in launch mode. native_process_sp.reset(new NativeProcessLinux()); if (!native_process_sp->RegisterNativeDelegate(native_delegate)) { native_process_sp.reset(); error.SetErrorStringWithFormat("failed to register the native delegate"); return error; } error = std::static_pointer_cast(native_process_sp) ->LaunchInferior(mainloop, launch_info); if (error.Fail()) { native_process_sp.reset(); LLDB_LOG(log, "failed to launch process: {0}", error); return error; } launch_info.SetProcessID(native_process_sp->GetID()); return error; } Status NativeProcessProtocol::Attach( lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); // Retrieve the architecture for the running process. ArchSpec process_arch; Status error = ResolveProcessArchitecture(pid, process_arch); if (!error.Success()) return error; std::shared_ptr native_process_linux_sp( new NativeProcessLinux()); if (!native_process_linux_sp->RegisterNativeDelegate(native_delegate)) { error.SetErrorStringWithFormat("failed to register the native delegate"); return error; } native_process_linux_sp->AttachToInferior(mainloop, pid, error); if (!error.Success()) return error; native_process_sp = native_process_linux_sp; return error; } // ----------------------------------------------------------------------------- // Public Instance Methods // ----------------------------------------------------------------------------- NativeProcessLinux::NativeProcessLinux() : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID), m_arch(), m_supports_mem_region(eLazyBoolCalculate), m_mem_region_cache(), m_pending_notification_tid(LLDB_INVALID_THREAD_ID) {} void NativeProcessLinux::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, Status &error) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid = {0:x}", pid); m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error); if (!m_sigchld_handle) return; error = ResolveProcessArchitecture(pid, m_arch); if (!error.Success()) return; // Set the architecture to the exe architecture. LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, m_arch.GetArchitectureName()); m_pid = pid; SetState(eStateAttaching); Attach(pid, error); } Status NativeProcessLinux::LaunchInferior(MainLoop &mainloop, ProcessLaunchInfo &launch_info) { Status error; m_sigchld_handle = mainloop.RegisterSignal( SIGCHLD, [this](MainLoopBase &) { SigchldHandler(); }, error); if (!m_sigchld_handle) return error; SetState(eStateLaunching); MaybeLogLaunchInfo(launch_info); ::pid_t pid = ProcessLauncherPosixFork().LaunchProcess(launch_info, error).GetProcessId(); if (error.Fail()) return error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Wait for the child process to trap on its call to execve. ::pid_t wpid; int status; if ((wpid = waitpid(pid, &status, 0)) < 0) { error.SetErrorToErrno(); LLDB_LOG(log, "waitpid for inferior failed with %s", error); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, // using eStateInvalid. SetState(StateType::eStateInvalid); return error; } assert(WIFSTOPPED(status) && (wpid == static_cast<::pid_t>(pid)) && "Could not sync with inferior process."); LLDB_LOG(log, "inferior started, now in stopped state"); error = SetDefaultPtraceOpts(pid); if (error.Fail()) { LLDB_LOG(log, "failed to set default ptrace options: {0}", error); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For now, // using eStateInvalid. SetState(StateType::eStateInvalid); return error; } // Release the master terminal descriptor and pass it off to the // NativeProcessLinux instance. Similarly stash the inferior pid. m_terminal_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); m_pid = pid; launch_info.SetProcessID(pid); if (m_terminal_fd != -1) { error = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); if (error.Fail()) { LLDB_LOG(log, "inferior EnsureFDFlags failed for ensuring terminal " "O_NONBLOCK setting: {0}", error); // Mark the inferior as invalid. // FIXME this could really use a new state - eStateLaunchFailure. For // now, using eStateInvalid. SetState(StateType::eStateInvalid); return error; } } LLDB_LOG(log, "adding pid = {0}", pid); ResolveProcessArchitecture(m_pid, m_arch); NativeThreadLinuxSP thread_sp = AddThread(pid); assert(thread_sp && "AddThread() returned a nullptr thread"); thread_sp->SetStoppedBySignal(SIGSTOP); ThreadWasCreated(*thread_sp); // Let our process instance know the thread has stopped. SetCurrentThreadID(thread_sp->GetID()); SetState(StateType::eStateStopped); if (error.Fail()) LLDB_LOG(log, "inferior launching failed {0}", error); return error; } ::pid_t NativeProcessLinux::Attach(lldb::pid_t pid, Status &error) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Use a map to keep track of the threads which we have attached/need to // attach. Host::TidMap tids_to_attach; if (pid <= 1) { error.SetErrorToGenericError(); error.SetErrorString("Attaching to process 1 is not allowed."); return -1; } while (Host::FindProcessThreads(pid, tids_to_attach)) { for (Host::TidMap::iterator it = tids_to_attach.begin(); it != tids_to_attach.end();) { if (it->second == false) { lldb::tid_t tid = it->first; // Attach to the requested process. // An attach will cause the thread to stop with a SIGSTOP. error = PtraceWrapper(PTRACE_ATTACH, tid); if (error.Fail()) { // No such thread. The thread may have exited. // More error handling may be needed. if (error.GetError() == ESRCH) { it = tids_to_attach.erase(it); continue; } else return -1; } int status; // Need to use __WALL otherwise we receive an error with errno=ECHLD // At this point we should have a thread stopped if waitpid succeeds. if ((status = waitpid(tid, NULL, __WALL)) < 0) { // No such thread. The thread may have exited. // More error handling may be needed. if (errno == ESRCH) { it = tids_to_attach.erase(it); continue; } else { error.SetErrorToErrno(); return -1; } } error = SetDefaultPtraceOpts(tid); if (error.Fail()) return -1; LLDB_LOG(log, "adding tid = {0}", tid); it->second = true; // Create the thread, mark it as stopped. NativeThreadLinuxSP thread_sp(AddThread(static_cast(tid))); assert(thread_sp && "AddThread() returned a nullptr"); // This will notify this is a new thread and tell the system it is // stopped. thread_sp->SetStoppedBySignal(SIGSTOP); ThreadWasCreated(*thread_sp); SetCurrentThreadID(thread_sp->GetID()); } // move the loop forward ++it; } } if (tids_to_attach.size() > 0) { m_pid = pid; // Let our process instance know the thread has stopped. SetState(StateType::eStateStopped); } else { error.SetErrorToGenericError(); error.SetErrorString("No such process."); return -1; } return pid; } Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { long ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in // limbo until it is destroyed. ptrace_opts |= PTRACE_O_TRACEEXIT; // Have the tracer trace threads which spawn in the inferior process. // TODO: if we want to support tracing the inferiors' child, add the // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) ptrace_opts |= PTRACE_O_TRACECLONE; // Have the tracer notify us before execve returns // (needed to disable legacy SIGTRAP generation) ptrace_opts |= PTRACE_O_TRACEEXEC; return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } static ExitType convert_pid_status_to_exit_type(int status) { if (WIFEXITED(status)) return ExitType::eExitTypeExit; else if (WIFSIGNALED(status)) return ExitType::eExitTypeSignal; else if (WIFSTOPPED(status)) return ExitType::eExitTypeStop; else { // We don't know what this is. return ExitType::eExitTypeInvalid; } } static int convert_pid_status_to_return_code(int status) { if (WIFEXITED(status)) return WEXITSTATUS(status); else if (WIFSIGNALED(status)) return WTERMSIG(status); else if (WIFSTOPPED(status)) return WSTOPSIG(status); else { // We don't know what this is. return ExitType::eExitTypeInvalid; } } // Handles all waitpid events from the inferior process. void NativeProcessLinux::MonitorCallback(lldb::pid_t pid, bool exited, int signal, int status) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); // Certain activities differ based on whether the pid is the tid of the main // thread. const bool is_main_thread = (pid == GetID()); // Handle when the thread exits. if (exited) { LLDB_LOG(log, "got exit signal({0}) , tid = {1} ({2} main thread)", signal, pid, is_main_thread ? "is" : "is not"); // This is a thread that exited. Ensure we're not tracking it anymore. const bool thread_found = StopTrackingThread(pid); if (is_main_thread) { // We only set the exit status and notify the delegate if we haven't // already set the process // state to an exited state. We normally should have received a SIGTRAP | // (PTRACE_EVENT_EXIT << 8) // for the main thread. const bool already_notified = (GetState() == StateType::eStateExited) || (GetState() == StateType::eStateCrashed); if (!already_notified) { LLDB_LOG( log, "tid = {0} handling main thread exit ({1}), expected exit state " "already set but state was {2} instead, setting exit state now", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found", GetState()); // The main thread exited. We're done monitoring. Report to delegate. SetExitStatus(convert_pid_status_to_exit_type(status), convert_pid_status_to_return_code(status), nullptr, true); // Notify delegate that our process has exited. SetState(StateType::eStateExited, true); } else LLDB_LOG(log, "tid = {0} main thread now exited (%s)", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); } else { // Do we want to report to the delegate in this case? I think not. If // this was an orderly thread exit, we would already have received the // SIGTRAP | (PTRACE_EVENT_EXIT << 8) signal, and we would have done an // all-stop then. LLDB_LOG(log, "tid = {0} handling non-main thread exit (%s)", pid, thread_found ? "stopped tracking thread metadata" : "thread metadata not found"); } return; } siginfo_t info; const auto info_err = GetSignalInfo(pid, &info); auto thread_sp = GetThreadByID(pid); if (!thread_sp) { // Normally, the only situation when we cannot find the thread is if we have // just received a new thread notification. This is indicated by // GetSignalInfo() returning si_code == SI_USER and si_pid == 0 LLDB_LOG(log, "received notification about an unknown tid {0}.", pid); if (info_err.Fail()) { LLDB_LOG(log, "(tid {0}) GetSignalInfo failed ({1}). " "Ingoring this notification.", pid, info_err); return; } LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code, info.si_pid); auto thread_sp = AddThread(pid); // Resume the newly created thread. ResumeThread(*thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); ThreadWasCreated(*thread_sp); return; } // Get details on the signal raised. if (info_err.Success()) { // We have retrieved the signal info. Dispatch appropriately. if (info.si_signo == SIGTRAP) MonitorSIGTRAP(info, *thread_sp); else MonitorSignal(info, *thread_sp, exited); } else { if (info_err.GetError() == EINVAL) { // This is a group stop reception for this tid. // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU // into the tracee, triggering the group-stop mechanism. Normally // receiving these would stop the process, pending a SIGCONT. Simulating // this state in a debugger is hard and is generally not needed (one use // case is debugging background task being managed by a shell). For // general use, it is sufficient to stop the process in a signal-delivery // stop which happens before the group stop. This done by MonitorSignal // and works correctly for all signals. LLDB_LOG(log, "received a group stop for pid {0} tid {1}. Transparent " "handling of group stops not supported, resuming the " "thread.", GetID(), pid); ResumeThread(*thread_sp, thread_sp->GetState(), LLDB_INVALID_SIGNAL_NUMBER); } else { // ptrace(GETSIGINFO) failed (but not due to group-stop). // A return value of ESRCH means the thread/process is no longer on the // system, so it was killed somehow outside of our control. Either way, // we can't do anything with it anymore. // Stop tracking the metadata for the thread since it's entirely off the // system now. const bool thread_found = StopTrackingThread(pid); LLDB_LOG(log, "GetSignalInfo failed: {0}, tid = {1}, signal = {2}, " "status = {3}, main_thread = {4}, thread_found: {5}", info_err, pid, signal, status, is_main_thread, thread_found); if (is_main_thread) { // Notify the delegate - our process is not available but appears to // have been killed outside // our control. Is eStateExited the right exit state in this case? SetExitStatus(convert_pid_status_to_exit_type(status), convert_pid_status_to_return_code(status), nullptr, true); SetState(StateType::eStateExited, true); } else { // This thread was pulled out from underneath us. Anything to do here? // Do we want to do an all stop? LLDB_LOG(log, "pid {0} tid {1} non-main thread exit occurred, didn't " "tell delegate anything since thread disappeared out " "from underneath us", GetID(), pid); } } } } void NativeProcessLinux::WaitForNewThread(::pid_t tid) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); NativeThreadLinuxSP new_thread_sp = GetThreadByID(tid); if (new_thread_sp) { // We are already tracking the thread - we got the event on the new thread // (see // MonitorSignal) before this one. We are done. return; } // The thread is not tracked yet, let's wait for it to appear. int status = -1; ::pid_t wait_pid; do { LLDB_LOG(log, "received thread creation event for tid {0}. tid not tracked " "yet, waiting for thread to appear...", tid); wait_pid = waitpid(tid, &status, __WALL); } while (wait_pid == -1 && errno == EINTR); // Since we are waiting on a specific tid, this must be the creation event. // But let's do some checks just in case. if (wait_pid != tid) { LLDB_LOG(log, "waiting for tid {0} failed. Assuming the thread has " "disappeared in the meantime", tid); // The only way I know of this could happen is if the whole process was // SIGKILLed in the mean time. In any case, we can't do anything about that // now. return; } if (WIFEXITED(status)) { LLDB_LOG(log, "waiting for tid {0} returned an 'exited' event. Not " "tracking the thread.", tid); // Also a very improbable event. return; } LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid); new_thread_sp = AddThread(tid); ResumeThread(*new_thread_sp, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); ThreadWasCreated(*new_thread_sp); } void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); const bool is_main_thread = (thread.GetID() == GetID()); assert(info.si_signo == SIGTRAP && "Unexpected child signal!"); switch (info.si_code) { // TODO: these two cases are required if we want to support tracing of the // inferiors' children. We'd need this to debug a monitor. // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): { // This is the notification on the parent thread which informs us of new // thread // creation. // We don't want to do anything with the parent thread so we just resume it. // In case we // want to implement "break on thread creation" functionality, we would need // to stop // here. unsigned long event_message = 0; if (GetEventMessage(thread.GetID(), &event_message).Fail()) { LLDB_LOG(log, "pid {0} received thread creation event but " "GetEventMessage failed so we don't know the new tid", thread.GetID()); } else WaitForNewThread(event_message); ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); break; } case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): { NativeThreadLinuxSP main_thread_sp; LLDB_LOG(log, "received exec event, code = {0}", info.si_code ^ SIGTRAP); // Exec clears any pending notifications. m_pending_notification_tid = LLDB_INVALID_THREAD_ID; // Remove all but the main thread here. Linux fork creates a new process // which only copies the main thread. LLDB_LOG(log, "exec received, stop tracking all but main thread"); for (auto thread_sp : m_threads) { const bool is_main_thread = thread_sp && thread_sp->GetID() == GetID(); if (is_main_thread) { main_thread_sp = std::static_pointer_cast(thread_sp); LLDB_LOG(log, "found main thread with tid {0}, keeping", main_thread_sp->GetID()); } else { LLDB_LOG(log, "discarding non-main-thread tid {0} due to exec", thread_sp->GetID()); } } m_threads.clear(); if (main_thread_sp) { m_threads.push_back(main_thread_sp); SetCurrentThreadID(main_thread_sp->GetID()); main_thread_sp->SetStoppedByExec(); } else { SetCurrentThreadID(LLDB_INVALID_THREAD_ID); LLDB_LOG(log, "pid {0} no main thread found, discarded all threads, " "we're in a no-thread state!", GetID()); } // Tell coordinator about about the "new" (since exec) stopped main thread. ThreadWasCreated(*main_thread_sp); // Let our delegate know we have just exec'd. NotifyDidExec(); // If we have a main thread, indicate we are stopped. assert(main_thread_sp && "exec called during ptraced process but no main " "thread metadata tracked"); // Let the process know we're stopped. StopRunningThreads(main_thread_sp->GetID()); break; } case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): { // The inferior process or one of its threads is about to exit. // We don't want to do anything with the thread so we just resume it. In // case we // want to implement "break on thread exit" functionality, we would need to // stop // here. unsigned long data = 0; if (GetEventMessage(thread.GetID(), &data).Fail()) data = -1; LLDB_LOG(log, "received PTRACE_EVENT_EXIT, data = {0:x}, WIFEXITED={1}, " "WIFSIGNALED={2}, pid = {3}, main_thread = {4}", data, WIFEXITED(data), WIFSIGNALED(data), thread.GetID(), is_main_thread); if (is_main_thread) { SetExitStatus(convert_pid_status_to_exit_type(data), convert_pid_status_to_return_code(data), nullptr, true); } StateType state = thread.GetState(); if (!StateIsRunningState(state)) { // Due to a kernel bug, we may sometimes get this stop after the inferior // gets a // SIGKILL. This confuses our state tracking logic in ResumeThread(), // since normally, // we should not be receiving any ptrace events while the inferior is // stopped. This // makes sure that the inferior is resumed and exits normally. state = eStateRunning; } ResumeThread(thread, state, LLDB_INVALID_SIGNAL_NUMBER); break; } case 0: case TRAP_TRACE: // We receive this on single stepping. case TRAP_HWBKPT: // We receive this on watchpoint hit { // If a watchpoint was hit, report it uint32_t wp_index; Status error = thread.GetRegisterContext()->GetWatchpointHitIndex( wp_index, (uintptr_t)info.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, error = {1}", thread.GetID(), error); if (wp_index != LLDB_INVALID_INDEX32) { MonitorWatchpoint(thread, wp_index); break; } // If a breakpoint was hit, report it uint32_t bp_index; error = thread.GetRegisterContext()->GetHardwareBreakHitIndex( bp_index, (uintptr_t)info.si_addr); if (error.Fail()) LLDB_LOG(log, "received error while checking for hardware " "breakpoint hits, pid = {0}, error = {1}", thread.GetID(), error); if (bp_index != LLDB_INVALID_INDEX32) { MonitorBreakpoint(thread); break; } // Otherwise, report step over MonitorTrace(thread); break; } case SI_KERNEL: #if defined __mips__ // For mips there is no special signal for watchpoint // So we check for watchpoint in kernel trap { // If a watchpoint was hit, report it uint32_t wp_index; Status error = thread.GetRegisterContext()->GetWatchpointHitIndex( wp_index, LLDB_INVALID_ADDRESS); if (error.Fail()) LLDB_LOG(log, "received error while checking for watchpoint hits, pid = " "{0}, error = {1}", thread.GetID(), error); if (wp_index != LLDB_INVALID_INDEX32) { MonitorWatchpoint(thread, wp_index); break; } } // NO BREAK #endif case TRAP_BRKPT: MonitorBreakpoint(thread); break; case SIGTRAP: case (SIGTRAP | 0x80): LLDB_LOG( log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}, resuming", info.si_code, GetID(), thread.GetID()); // Ignore these signals until we know more about them. ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER); break; default: LLDB_LOG( log, "received unknown SIGTRAP stop event ({0}, pid {1} tid {2}, resuming", info.si_code, GetID(), thread.GetID()); llvm_unreachable("Unexpected SIGTRAP code!"); break; } } void NativeProcessLinux::MonitorTrace(NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "received trace event, pid = {0}", thread.GetID()); // This thread is currently stopped. thread.SetStoppedByTrace(); StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorBreakpoint(NativeThreadLinux &thread) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); LLDB_LOG(log, "received breakpoint event, pid = {0}", thread.GetID()); // Mark the thread as stopped at breakpoint. thread.SetStoppedByBreakpoint(); Status error = FixupBreakpointPCAsNeeded(thread); if (error.Fail()) LLDB_LOG(log, "pid = {0} fixup: {1}", thread.GetID(), error); if (m_threads_stepping_with_breakpoint.find(thread.GetID()) != m_threads_stepping_with_breakpoint.end()) thread.SetStoppedByTrace(); StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index) { Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_WATCHPOINTS)); LLDB_LOG(log, "received watchpoint event, pid = {0}, wp_index = {1}", thread.GetID(), wp_index); // Mark the thread as stopped at watchpoint. // The address is at (lldb::addr_t)info->si_addr if we need it. thread.SetStoppedByWatchpoint(wp_index); // We need to tell all other running threads before we notify the delegate // about this stop. StopRunningThreads(thread.GetID()); } void NativeProcessLinux::MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited) { const int signo = info.si_signo; const bool is_from_llgs = info.si_pid == getpid(); Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on Linux. // // IOW, user generated signals never generate what we consider to be a // "crash". // // Similarly, ACK signals generated by this monitor. // Handle the signal. LLDB_LOG(log, "received signal {0} ({1}) with code {2}, (siginfo pid = {3}, " "waitpid pid = {4})", Host::GetSignalAsCString(signo), signo, info.si_code, thread.GetID()); // Check for thread stop notification. if (is_from_llgs && (info.si_code == SI_TKILL) && (signo == SIGSTOP)) { // This is a tgkill()-based stop. LLDB_LOG(log, "pid {0} tid {1}, thread stopped", GetID(), thread.GetID()); // Check that we're not already marked with a stop reason. // Note this thread really shouldn't already be marked as stopped - if we // were, that would imply that the kernel signaled us with the thread // stopping which we handled and marked as stopped, and that, without an // intervening resume, we received another stop. It is more likely that we // are missing the marking of a run state somewhere if we find that the // thread was marked as stopped. const StateType thread_state = thread.GetState(); if (!StateIsStoppedState(thread_state, false)) { // An inferior thread has stopped because of a SIGSTOP we have sent it. // Generally, these are not important stops and we don't want to report // them as they are just used to stop other threads when one thread (the // one with the *real* stop reason) hits a breakpoint (watchpoint, // etc...). However, in the case of an asynchronous Interrupt(), this *is* // the real stop reason, so we leave the signal intact if this is the // thread that was chosen as the triggering thread. if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { if (m_pending_notification_tid == thread.GetID()) thread.SetStoppedBySignal(SIGSTOP, &info); else thread.SetStoppedWithNoReason(); SetCurrentThreadID(thread.GetID()); SignalIfAllThreadsStopped(); } else { // We can end up here if stop was initiated by LLGS but by this time a // thread stop has occurred - maybe initiated by another event. Status error = ResumeThread(thread, thread.GetState(), 0); if (error.Fail()) LLDB_LOG(log, "failed to resume thread {0}: {1}", thread.GetID(), error); } } else { LLDB_LOG(log, "pid {0} tid {1}, thread was already marked as a stopped " "state (state={2}), leaving stop signal as is", GetID(), thread.GetID(), thread_state); SignalIfAllThreadsStopped(); } // Done handling. return; } // Check if debugger should stop at this signal or just ignore it // and resume the inferior. if (m_signals_to_ignore.find(signo) != m_signals_to_ignore.end()) { ResumeThread(thread, thread.GetState(), signo); return; } // This thread is stopped. LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); thread.SetStoppedBySignal(signo, &info); // Send a stop to the debugger after we get all other threads to stop. StopRunningThreads(thread.GetID()); } namespace { struct EmulatorBaton { NativeProcessLinux *m_process; NativeRegisterContext *m_reg_context; // eRegisterKindDWARF -> RegsiterValue std::unordered_map m_register_values; EmulatorBaton(NativeProcessLinux *process, NativeRegisterContext *reg_context) : m_process(process), m_reg_context(reg_context) {} }; } // anonymous namespace static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, size_t length) { EmulatorBaton *emulator_baton = static_cast(baton); size_t bytes_read; emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); return bytes_read; } static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, const RegisterInfo *reg_info, RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); auto it = emulator_baton->m_register_values.find( reg_info->kinds[eRegisterKindDWARF]); if (it != emulator_baton->m_register_values.end()) { reg_value = it->second; return true; } // The emulator only fill in the dwarf regsiter numbers (and in some case // the generic register numbers). Get the full register info from the // register context based on the dwarf register numbers. const RegisterInfo *full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]); Status error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); if (error.Success()) return true; return false; } static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { EmulatorBaton *emulator_baton = static_cast(baton); emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] = reg_value; return true; } static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t length) { return length; } static lldb::addr_t ReadFlags(NativeRegisterContext *regsiter_context) { const RegisterInfo *flags_info = regsiter_context->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); return regsiter_context->ReadRegisterAsUnsigned(flags_info, LLDB_INVALID_ADDRESS); } Status NativeProcessLinux::SetupSoftwareSingleStepping(NativeThreadLinux &thread) { Status error; NativeRegisterContextSP register_context_sp = thread.GetRegisterContext(); std::unique_ptr emulator_ap( EmulateInstruction::FindPlugin(m_arch, eInstructionTypePCModifying, nullptr)); if (emulator_ap == nullptr) return Status("Instruction emulator not found!"); EmulatorBaton baton(this, register_context_sp.get()); emulator_ap->SetBaton(&baton); emulator_ap->SetReadMemCallback(&ReadMemoryCallback); emulator_ap->SetReadRegCallback(&ReadRegisterCallback); emulator_ap->SetWriteMemCallback(&WriteMemoryCallback); emulator_ap->SetWriteRegCallback(&WriteRegisterCallback); if (!emulator_ap->ReadInstruction()) return Status("Read instruction failed!"); bool emulation_result = emulator_ap->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); const RegisterInfo *reg_info_pc = register_context_sp->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); const RegisterInfo *reg_info_flags = register_context_sp->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); auto pc_it = baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]); auto flags_it = baton.m_register_values.find(reg_info_flags->kinds[eRegisterKindDWARF]); lldb::addr_t next_pc; lldb::addr_t next_flags; if (emulation_result) { assert(pc_it != baton.m_register_values.end() && "Emulation was successfull but PC wasn't updated"); next_pc = pc_it->second.GetAsUInt64(); if (flags_it != baton.m_register_values.end()) next_flags = flags_it->second.GetAsUInt64(); else next_flags = ReadFlags(register_context_sp.get()); } else if (pc_it == baton.m_register_values.end()) { // Emulate instruction failed and it haven't changed PC. Advance PC // with the size of the current opcode because the emulation of all // PC modifying instruction should be successful. The failure most // likely caused by a not supported instruction which don't modify PC. next_pc = register_context_sp->GetPC() + emulator_ap->GetOpcode().GetByteSize(); next_flags = ReadFlags(register_context_sp.get()); } else { // The instruction emulation failed after it modified the PC. It is an // unknown error where we can't continue because the next instruction is // modifying the PC but we don't know how. return Status("Instruction emulation failed unexpectedly."); } if (m_arch.GetMachine() == llvm::Triple::arm) { if (next_flags & 0x20) { // Thumb mode error = SetSoftwareBreakpoint(next_pc, 2); } else { // Arm mode error = SetSoftwareBreakpoint(next_pc, 4); } } else if (m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) error = SetSoftwareBreakpoint(next_pc, 4); else { // No size hint is given for the next breakpoint error = SetSoftwareBreakpoint(next_pc, 0); } // If setting the breakpoint fails because next_pc is out of // the address space, ignore it and let the debugee segfault. if (error.GetError() == EIO || error.GetError() == EFAULT) { return Status(); } else if (error.Fail()) return error; m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc}); return Status(); } bool NativeProcessLinux::SupportHardwareSingleStepping() const { if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.GetMachine() == llvm::Triple::mips64 || m_arch.GetMachine() == llvm::Triple::mips64el || m_arch.GetMachine() == llvm::Triple::mips || m_arch.GetMachine() == llvm::Triple::mipsel) return false; return true; } Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); bool software_single_step = !SupportHardwareSingleStepping(); if (software_single_step) { for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) continue; if (action->state == eStateStepping) { Status error = SetupSoftwareSingleStepping( static_cast(*thread_sp)); if (error.Fail()) return error; } } } for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); const ResumeAction *const action = resume_actions.GetActionForThread(thread_sp->GetID(), true); if (action == nullptr) { LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(), thread_sp->GetID()); continue; } LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}", action->state, GetID(), thread_sp->GetID()); switch (action->state) { case eStateRunning: case eStateStepping: { // Run the thread, possibly feeding it the signal. const int signo = action->signal; ResumeThread(static_cast(*thread_sp), action->state, signo); break; } case eStateSuspended: case eStateStopped: llvm_unreachable("Unexpected state"); default: return Status("NativeProcessLinux::%s (): unexpected state %s specified " "for pid %" PRIu64 ", tid %" PRIu64, __FUNCTION__, StateAsCString(action->state), GetID(), thread_sp->GetID()); } } return Status(); } Status NativeProcessLinux::Halt() { Status error; if (kill(GetID(), SIGSTOP) != 0) error.SetErrorToErrno(); return error; } Status NativeProcessLinux::Detach() { Status error; // Stop monitoring the inferior. m_sigchld_handle.reset(); // Tell ptrace to detach from the process. if (GetID() == LLDB_INVALID_PROCESS_ID) return error; for (auto thread_sp : m_threads) { Status e = Detach(thread_sp->GetID()); if (e.Fail()) error = e; // Save the error, but still attempt to detach from other threads. } return error; } Status NativeProcessLinux::Signal(int signo) { Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "sending signal {0} ({1}) to pid {1}", signo, Host::GetSignalAsCString(signo), GetID()); if (kill(GetID(), signo)) error.SetErrorToErrno(); return error; } Status NativeProcessLinux::Interrupt() { // Pick a running thread (or if none, a not-dead stopped thread) as // the chosen thread that will be the stop-reason thread. Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); NativeThreadProtocolSP running_thread_sp; NativeThreadProtocolSP stopped_thread_sp; LLDB_LOG(log, "selecting running thread for interrupt target"); for (auto thread_sp : m_threads) { // The thread shouldn't be null but lets just cover that here. if (!thread_sp) continue; // If we have a running or stepping thread, we'll call that the // target of the interrupt. const auto thread_state = thread_sp->GetState(); if (thread_state == eStateRunning || thread_state == eStateStepping) { running_thread_sp = thread_sp; break; } else if (!stopped_thread_sp && StateIsStoppedState(thread_state, true)) { // Remember the first non-dead stopped thread. We'll use that as a backup // if there are no running threads. stopped_thread_sp = thread_sp; } } if (!running_thread_sp && !stopped_thread_sp) { Status error("found no running/stepping or live stopped threads as target " "for interrupt"); LLDB_LOG(log, "skipping due to error: {0}", error); return error; } NativeThreadProtocolSP deferred_signal_thread_sp = running_thread_sp ? running_thread_sp : stopped_thread_sp; LLDB_LOG(log, "pid {0} {1} tid {2} chosen for interrupt target", GetID(), running_thread_sp ? "running" : "stopped", deferred_signal_thread_sp->GetID()); StopRunningThreads(deferred_signal_thread_sp->GetID()); return Status(); } Status NativeProcessLinux::Kill() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "pid {0}", GetID()); Status error; switch (m_state) { case StateType::eStateInvalid: case StateType::eStateExited: case StateType::eStateCrashed: case StateType::eStateDetached: case StateType::eStateUnloaded: // Nothing to do - the process is already dead. LLDB_LOG(log, "ignored for PID {0} due to current state: {1}", GetID(), m_state); return error; case StateType::eStateConnected: case StateType::eStateAttaching: case StateType::eStateLaunching: case StateType::eStateStopped: case StateType::eStateRunning: case StateType::eStateStepping: case StateType::eStateSuspended: // We can try to kill a process in these states. break; } if (kill(GetID(), SIGKILL) != 0) { error.SetErrorToErrno(); return error; } return error; } static Status ParseMemoryRegionInfoFromProcMapsLine(llvm::StringRef &maps_line, MemoryRegionInfo &memory_region_info) { memory_region_info.Clear(); StringExtractor line_extractor(maps_line); // Format: {address_start_hex}-{address_end_hex} perms offset dev inode // pathname // perms: rwxp (letter is present if set, '-' if not, final character is // p=private, s=shared). // Parse out the starting address lldb::addr_t start_address = line_extractor.GetHexMaxU64(false, 0); // Parse out hyphen separating start and end address from range. if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != '-')) return Status( "malformed /proc/{pid}/maps entry, missing dash between address range"); // Parse out the ending address lldb::addr_t end_address = line_extractor.GetHexMaxU64(false, start_address); // Parse out the space after the address. if (!line_extractor.GetBytesLeft() || (line_extractor.GetChar() != ' ')) return Status( "malformed /proc/{pid}/maps entry, missing space after range"); // Save the range. memory_region_info.GetRange().SetRangeBase(start_address); memory_region_info.GetRange().SetRangeEnd(end_address); // Any memory region in /proc/{pid}/maps is by definition mapped into the // process. memory_region_info.SetMapped(MemoryRegionInfo::OptionalBool::eYes); // Parse out each permission entry. if (line_extractor.GetBytesLeft() < 4) return Status("malformed /proc/{pid}/maps entry, missing some portion of " "permissions"); // Handle read permission. const char read_perm_char = line_extractor.GetChar(); if (read_perm_char == 'r') memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eYes); else if (read_perm_char == '-') memory_region_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps read permission char"); // Handle write permission. const char write_perm_char = line_extractor.GetChar(); if (write_perm_char == 'w') memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eYes); else if (write_perm_char == '-') memory_region_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps write permission char"); // Handle execute permission. const char exec_perm_char = line_extractor.GetChar(); if (exec_perm_char == 'x') memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eYes); else if (exec_perm_char == '-') memory_region_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); else return Status("unexpected /proc/{pid}/maps exec permission char"); line_extractor.GetChar(); // Read the private bit line_extractor.SkipSpaces(); // Skip the separator line_extractor.GetHexMaxU64(false, 0); // Read the offset line_extractor.GetHexMaxU64(false, 0); // Read the major device number line_extractor.GetChar(); // Read the device id separator line_extractor.GetHexMaxU64(false, 0); // Read the major device number line_extractor.SkipSpaces(); // Skip the separator line_extractor.GetU64(0, 10); // Read the inode number line_extractor.SkipSpaces(); const char *name = line_extractor.Peek(); if (name) memory_region_info.SetName(name); return Status(); } Status NativeProcessLinux::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { // FIXME review that the final memory region returned extends to the end of // the virtual address space, // with no perms if it is not mapped. // Use an approach that reads memory regions from /proc/{pid}/maps. // Assume proc maps entries are in ascending order. // FIXME assert if we find differently. if (m_supports_mem_region == LazyBool::eLazyBoolNo) { // We're done. return Status("unsupported"); } Status error = PopulateMemoryRegionCache(); if (error.Fail()) { return error; } lldb::addr_t prev_base_address = 0; // FIXME start by finding the last region that is <= target address using // binary search. Data is sorted. // There can be a ton of regions on pthreads apps with lots of threads. for (auto it = m_mem_region_cache.begin(); it != m_mem_region_cache.end(); ++it) { MemoryRegionInfo &proc_entry_info = it->first; // Sanity check assumption that /proc/{pid}/maps entries are ascending. assert((proc_entry_info.GetRange().GetRangeBase() >= prev_base_address) && "descending /proc/pid/maps entries detected, unexpected"); prev_base_address = proc_entry_info.GetRange().GetRangeBase(); UNUSED_IF_ASSERT_DISABLED(prev_base_address); // If the target address comes before this entry, indicate distance to next // region. if (load_addr < proc_entry_info.GetRange().GetRangeBase()) { range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetByteSize( proc_entry_info.GetRange().GetRangeBase() - load_addr); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } else if (proc_entry_info.GetRange().Contains(load_addr)) { // The target address is within the memory region we're processing here. range_info = proc_entry_info; return error; } // The target memory address comes somewhere after the region we just // parsed. } // If we made it here, we didn't find an entry that contained the given // address. Return the // load_addr as start and the amount of bytes betwwen load address and the end // of the memory as // size. range_info.GetRange().SetRangeBase(load_addr); range_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); range_info.SetReadable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetWritable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetExecutable(MemoryRegionInfo::OptionalBool::eNo); range_info.SetMapped(MemoryRegionInfo::OptionalBool::eNo); return error; } Status NativeProcessLinux::PopulateMemoryRegionCache() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // If our cache is empty, pull the latest. There should always be at least // one memory region if memory region handling is supported. if (!m_mem_region_cache.empty()) { LLDB_LOG(log, "reusing {0} cached memory region entries", m_mem_region_cache.size()); return Status(); } auto BufferOrError = getProcFile(GetID(), "maps"); if (!BufferOrError) { m_supports_mem_region = LazyBool::eLazyBoolNo; return BufferOrError.getError(); } StringRef Rest = BufferOrError.get()->getBuffer(); while (! Rest.empty()) { StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); MemoryRegionInfo info; const Status parse_error = ParseMemoryRegionInfoFromProcMapsLine(Line, info); if (parse_error.Fail()) { LLDB_LOG(log, "failed to parse proc maps line '{0}': {1}", Line, parse_error); m_supports_mem_region = LazyBool::eLazyBoolNo; return parse_error; } m_mem_region_cache.emplace_back( info, FileSpec(info.GetName().GetCString(), true)); } if (m_mem_region_cache.empty()) { // No entries after attempting to read them. This shouldn't happen if // /proc/{pid}/maps is supported. Assume we don't support map entries // via procfs. m_supports_mem_region = LazyBool::eLazyBoolNo; LLDB_LOG(log, "failed to find any procfs maps entries, assuming no support " "for memory region metadata retrieval"); return Status("not supported"); } LLDB_LOG(log, "read {0} memory region entries from /proc/{1}/maps", m_mem_region_cache.size(), GetID()); // We support memory retrieval, remember that. m_supports_mem_region = LazyBool::eLazyBoolYes; return Status(); } void NativeProcessLinux::DoStopIDBumped(uint32_t newBumpId) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "newBumpId={0}", newBumpId); LLDB_LOG(log, "clearing {0} entries from memory region cache", m_mem_region_cache.size()); m_mem_region_cache.clear(); } Status NativeProcessLinux::AllocateMemory(size_t size, uint32_t permissions, lldb::addr_t &addr) { // FIXME implementing this requires the equivalent of // InferiorCallPOSIX::InferiorCallMmap, which depends on // functional ThreadPlans working with Native*Protocol. #if 1 return Status("not implemented yet"); #else addr = LLDB_INVALID_ADDRESS; unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; // TODO implement this directly in NativeProcessLinux // (and lift to NativeProcessPOSIX if/when that class is // refactored out). if (InferiorCallMmap(this, addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { m_addr_to_mmap_size[addr] = size; return Status(); } else { addr = LLDB_INVALID_ADDRESS; return Status("unable to allocate %" PRIu64 " bytes of memory with permissions %s", size, GetPermissionsAsCString(permissions)); } #endif } Status NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) { // FIXME see comments in AllocateMemory - required lower-level // bits not in place yet (ThreadPlans) return Status("not implemented"); } lldb::addr_t NativeProcessLinux::GetSharedLibraryInfoAddress() { // punt on this for now return LLDB_INVALID_ADDRESS; } size_t NativeProcessLinux::UpdateThreads() { // The NativeProcessLinux monitoring threads are always up to date // with respect to thread state and they keep the thread list // populated properly. All this method needs to do is return the // thread count. return m_threads.size(); } bool NativeProcessLinux::GetArchitecture(ArchSpec &arch) const { arch = m_arch; return true; } Status NativeProcessLinux::GetSoftwareBreakpointPCOffset( uint32_t &actual_opcode_size) { // FIXME put this behind a breakpoint protocol class that can be // set per architecture. Need ARM, MIPS support here. static const uint8_t g_i386_opcode[] = {0xCC}; static const uint8_t g_s390x_opcode[] = {0x00, 0x01}; switch (m_arch.GetMachine()) { case llvm::Triple::x86: case llvm::Triple::x86_64: actual_opcode_size = static_cast(sizeof(g_i386_opcode)); return Status(); case llvm::Triple::systemz: actual_opcode_size = static_cast(sizeof(g_s390x_opcode)); return Status(); case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::mips: case llvm::Triple::mipsel: // On these architectures the PC don't get updated for breakpoint hits actual_opcode_size = 0; return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } Status NativeProcessLinux::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { if (hardware) return SetHardwareBreakpoint(addr, size); else return SetSoftwareBreakpoint(addr, size); } Status NativeProcessLinux::RemoveBreakpoint(lldb::addr_t addr, bool hardware) { if (hardware) return RemoveHardwareBreakpoint(addr); else return NativeProcessProtocol::RemoveBreakpoint(addr); } Status NativeProcessLinux::GetSoftwareBreakpointTrapOpcode( size_t trap_opcode_size_hint, size_t &actual_opcode_size, const uint8_t *&trap_opcode_bytes) { // FIXME put this behind a breakpoint protocol class that can be set per // architecture. Need MIPS support here. static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; // The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the // linux kernel does otherwise. static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; static const uint8_t g_i386_opcode[] = {0xCC}; static const uint8_t g_mips64_opcode[] = {0x00, 0x00, 0x00, 0x0d}; static const uint8_t g_mips64el_opcode[] = {0x0d, 0x00, 0x00, 0x00}; static const uint8_t g_s390x_opcode[] = {0x00, 0x01}; static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde}; switch (m_arch.GetMachine()) { case llvm::Triple::aarch64: trap_opcode_bytes = g_aarch64_opcode; actual_opcode_size = sizeof(g_aarch64_opcode); return Status(); case llvm::Triple::arm: switch (trap_opcode_size_hint) { case 2: trap_opcode_bytes = g_thumb_breakpoint_opcode; actual_opcode_size = sizeof(g_thumb_breakpoint_opcode); return Status(); case 4: trap_opcode_bytes = g_arm_breakpoint_opcode; actual_opcode_size = sizeof(g_arm_breakpoint_opcode); return Status(); default: assert(false && "Unrecognised trap opcode size hint!"); return Status("Unrecognised trap opcode size hint!"); } case llvm::Triple::x86: case llvm::Triple::x86_64: trap_opcode_bytes = g_i386_opcode; actual_opcode_size = sizeof(g_i386_opcode); return Status(); case llvm::Triple::mips: case llvm::Triple::mips64: trap_opcode_bytes = g_mips64_opcode; actual_opcode_size = sizeof(g_mips64_opcode); return Status(); case llvm::Triple::mipsel: case llvm::Triple::mips64el: trap_opcode_bytes = g_mips64el_opcode; actual_opcode_size = sizeof(g_mips64el_opcode); return Status(); case llvm::Triple::systemz: trap_opcode_bytes = g_s390x_opcode; actual_opcode_size = sizeof(g_s390x_opcode); return Status(); default: assert(false && "CPU type not supported!"); return Status("CPU type not supported"); } } #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGSEGV(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGSEGV); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGSEGV"); break; case SI_KERNEL: // Linux will occasionally send spurious SI_KERNEL codes. // (this is poorly documented in sigaction) // One way to get this is via unaligned SIMD loads. reason = ProcessMessage::eInvalidAddress; // for lack of anything better break; case SEGV_MAPERR: reason = ProcessMessage::eInvalidAddress; break; case SEGV_ACCERR: reason = ProcessMessage::ePrivilegedAddress; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGILL(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGILL); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGILL"); break; case ILL_ILLOPC: reason = ProcessMessage::eIllegalOpcode; break; case ILL_ILLOPN: reason = ProcessMessage::eIllegalOperand; break; case ILL_ILLADR: reason = ProcessMessage::eIllegalAddressingMode; break; case ILL_ILLTRP: reason = ProcessMessage::eIllegalTrap; break; case ILL_PRVOPC: reason = ProcessMessage::ePrivilegedOpcode; break; case ILL_PRVREG: reason = ProcessMessage::ePrivilegedRegister; break; case ILL_COPROC: reason = ProcessMessage::eCoprocessorError; break; case ILL_BADSTK: reason = ProcessMessage::eInternalStackError; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGFPE(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGFPE); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGFPE"); break; case FPE_INTDIV: reason = ProcessMessage::eIntegerDivideByZero; break; case FPE_INTOVF: reason = ProcessMessage::eIntegerOverflow; break; case FPE_FLTDIV: reason = ProcessMessage::eFloatDivideByZero; break; case FPE_FLTOVF: reason = ProcessMessage::eFloatOverflow; break; case FPE_FLTUND: reason = ProcessMessage::eFloatUnderflow; break; case FPE_FLTRES: reason = ProcessMessage::eFloatInexactResult; break; case FPE_FLTINV: reason = ProcessMessage::eFloatInvalidOperation; break; case FPE_FLTSUB: reason = ProcessMessage::eFloatSubscriptRange; break; } return reason; } #endif #if 0 ProcessMessage::CrashReason NativeProcessLinux::GetCrashReasonForSIGBUS(const siginfo_t *info) { ProcessMessage::CrashReason reason; assert(info->si_signo == SIGBUS); reason = ProcessMessage::eInvalidCrashReason; switch (info->si_code) { default: assert(false && "unexpected si_code for SIGBUS"); break; case BUS_ADRALN: reason = ProcessMessage::eIllegalAlignment; break; case BUS_ADRERR: reason = ProcessMessage::eIllegalAddress; break; case BUS_OBJERR: reason = ProcessMessage::eHardwareError; break; } return reason; } #endif Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { if (ProcessVmReadvSupported()) { // The process_vm_readv path is about 50 times faster than ptrace api. We // want to use // this syscall if it is supported. const ::pid_t pid = GetID(); struct iovec local_iov, remote_iov; local_iov.iov_base = buf; local_iov.iov_len = size; remote_iov.iov_base = reinterpret_cast(addr); remote_iov.iov_len = size; bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); const bool success = bytes_read == size; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); LLDB_LOG(log, "using process_vm_readv to read {0} bytes from inferior " "address {1:x}: {2}", - size, addr, success ? "Success" : strerror(errno)); + size, addr, success ? "Success" : llvm::sys::StrError(errno)); if (success) return Status(); // else the call failed for some reason, let's retry the read using ptrace // api. } unsigned char *dst = static_cast(buf); size_t remainder; long data; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); for (bytes_read = 0; bytes_read < size; bytes_read += remainder) { Status error = NativeProcessLinux::PtraceWrapper( PTRACE_PEEKDATA, GetID(), (void *)addr, nullptr, 0, &data); if (error.Fail()) return error; remainder = size - bytes_read; remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; // Copy the data into our buffer memcpy(dst, &data, remainder); LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); addr += k_ptrace_word_size; dst += k_ptrace_word_size; } return Status(); } Status NativeProcessLinux::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size, size_t &bytes_read) { Status error = ReadMemory(addr, buf, size, bytes_read); if (error.Fail()) return error; return m_breakpoint_list.RemoveTrapsFromBuffer(addr, buf, size); } Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size, size_t &bytes_written) { const unsigned char *src = static_cast(buf); size_t remainder; Status error; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_MEMORY)); LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size); for (bytes_written = 0; bytes_written < size; bytes_written += remainder) { remainder = size - bytes_written; remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder; if (remainder == k_ptrace_word_size) { unsigned long data = 0; memcpy(&data, src, k_ptrace_word_size); LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data); error = NativeProcessLinux::PtraceWrapper(PTRACE_POKEDATA, GetID(), (void *)addr, (void *)data); if (error.Fail()) return error; } else { unsigned char buff[8]; size_t bytes_read; error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read); if (error.Fail()) return error; memcpy(buff, src, remainder); size_t bytes_written_rec; error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec); if (error.Fail()) return error; LLDB_LOG(log, "[{0:x}]:{1:x} ({2:x})", addr, *(const unsigned long *)src, *(unsigned long *)buff); } addr += k_ptrace_word_size; src += k_ptrace_word_size; } return error; } Status NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); } Status NativeProcessLinux::GetEventMessage(lldb::tid_t tid, unsigned long *message) { return PtraceWrapper(PTRACE_GETEVENTMSG, tid, nullptr, message); } Status NativeProcessLinux::Detach(lldb::tid_t tid) { if (tid == LLDB_INVALID_THREAD_ID) return Status(); return PtraceWrapper(PTRACE_DETACH, tid); } bool NativeProcessLinux::HasThreadNoLock(lldb::tid_t thread_id) { for (auto thread_sp : m_threads) { assert(thread_sp && "thread list should not contain NULL threads"); if (thread_sp->GetID() == thread_id) { // We have this thread. return true; } } // We don't have this thread. return false; } bool NativeProcessLinux::StopTrackingThread(lldb::tid_t thread_id) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0})", thread_id); bool found = false; for (auto it = m_threads.begin(); it != m_threads.end(); ++it) { if (*it && ((*it)->GetID() == thread_id)) { m_threads.erase(it); found = true; break; } } SignalIfAllThreadsStopped(); return found; } NativeThreadLinuxSP NativeProcessLinux::AddThread(lldb::tid_t thread_id) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD)); LLDB_LOG(log, "pid {0} adding thread with tid {1}", GetID(), thread_id); assert(!HasThreadNoLock(thread_id) && "attempted to add a thread by id that already exists"); // If this is the first thread, save it as the current thread if (m_threads.empty()) SetCurrentThreadID(thread_id); auto thread_sp = std::make_shared(this, thread_id); m_threads.push_back(thread_sp); return thread_sp; } Status NativeProcessLinux::FixupBreakpointPCAsNeeded(NativeThreadLinux &thread) { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_BREAKPOINTS)); Status error; // Find out the size of a breakpoint (might depend on where we are in the // code). NativeRegisterContextSP context_sp = thread.GetRegisterContext(); if (!context_sp) { error.SetErrorString("cannot get a NativeRegisterContext for the thread"); LLDB_LOG(log, "failed: {0}", error); return error; } uint32_t breakpoint_size = 0; error = GetSoftwareBreakpointPCOffset(breakpoint_size); if (error.Fail()) { LLDB_LOG(log, "GetBreakpointSize() failed: {0}", error); return error; } else LLDB_LOG(log, "breakpoint size: {0}", breakpoint_size); // First try probing for a breakpoint at a software breakpoint location: PC - // breakpoint size. const lldb::addr_t initial_pc_addr = context_sp->GetPCfromBreakpointLocation(); lldb::addr_t breakpoint_addr = initial_pc_addr; if (breakpoint_size > 0) { // Do not allow breakpoint probe to wrap around. if (breakpoint_addr >= breakpoint_size) breakpoint_addr -= breakpoint_size; } // Check if we stopped because of a breakpoint. NativeBreakpointSP breakpoint_sp; error = m_breakpoint_list.GetBreakpoint(breakpoint_addr, breakpoint_sp); if (!error.Success() || !breakpoint_sp) { // We didn't find one at a software probe location. Nothing to do. LLDB_LOG(log, "pid {0} no lldb breakpoint found at current pc with " "adjustment: {1}", GetID(), breakpoint_addr); return Status(); } // If the breakpoint is not a software breakpoint, nothing to do. if (!breakpoint_sp->IsSoftwareBreakpoint()) { LLDB_LOG( log, "pid {0} breakpoint found at {1:x}, not software, nothing to adjust", GetID(), breakpoint_addr); return Status(); } // // We have a software breakpoint and need to adjust the PC. // // Sanity check. if (breakpoint_size == 0) { // Nothing to do! How did we get here? LLDB_LOG(log, "pid {0} breakpoint found at {1:x}, it is software, but the " "size is zero, nothing to do (unexpected)", GetID(), breakpoint_addr); return Status(); } // Change the program counter. LLDB_LOG(log, "pid {0} tid {1}: changing PC from {2:x} to {3:x}", GetID(), thread.GetID(), initial_pc_addr, breakpoint_addr); error = context_sp->SetPC(breakpoint_addr); if (error.Fail()) { LLDB_LOG(log, "pid {0} tid {1}: failed to set PC: {2}", GetID(), thread.GetID(), error); return error; } return error; } Status NativeProcessLinux::GetLoadedModuleFileSpec(const char *module_path, FileSpec &file_spec) { Status error = PopulateMemoryRegionCache(); if (error.Fail()) return error; FileSpec module_file_spec(module_path, true); file_spec.Clear(); for (const auto &it : m_mem_region_cache) { if (it.second.GetFilename() == module_file_spec.GetFilename()) { file_spec = it.second; return Status(); } } return Status("Module file (%s) not found in /proc/%" PRIu64 "/maps file!", module_file_spec.GetFilename().AsCString(), GetID()); } Status NativeProcessLinux::GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) { load_addr = LLDB_INVALID_ADDRESS; Status error = PopulateMemoryRegionCache(); if (error.Fail()) return error; FileSpec file(file_name, false); for (const auto &it : m_mem_region_cache) { if (it.second == file) { load_addr = it.first.GetRange().GetRangeBase(); return Status(); } } return Status("No load address found for specified file."); } NativeThreadLinuxSP NativeProcessLinux::GetThreadByID(lldb::tid_t tid) { return std::static_pointer_cast( NativeProcessProtocol::GetThreadByID(tid)); } Status NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType state, int signo) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0}", thread.GetID()); // Before we do the resume below, first check if we have a pending // stop notification that is currently waiting for // all threads to stop. This is potentially a buggy situation since // we're ostensibly waiting for threads to stop before we send out the // pending notification, and here we are resuming one before we send // out the pending stop notification. if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID) { LLDB_LOG(log, "about to resume tid {0} per explicit request but we have a " "pending stop notification (tid {1}) that is actively " "waiting for this thread to stop. Valid sequence of events?", thread.GetID(), m_pending_notification_tid); } // Request a resume. We expect this to be synchronous and the system // to reflect it is running after this completes. switch (state) { case eStateRunning: { const auto resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { const auto step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; } default: LLDB_LOG(log, "Unhandled state {0}.", state); llvm_unreachable("Unhandled state for resume"); } } //===----------------------------------------------------------------------===// void NativeProcessLinux::StopRunningThreads(const lldb::tid_t triggering_tid) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "about to process event: (triggering_tid: {0})", triggering_tid); m_pending_notification_tid = triggering_tid; // Request a stop for all the thread stops that need to be stopped // and are not already known to be stopped. for (const auto &thread_sp : m_threads) { if (StateIsRunningState(thread_sp->GetState())) static_pointer_cast(thread_sp)->RequestStop(); } SignalIfAllThreadsStopped(); LLDB_LOG(log, "event processing done"); } void NativeProcessLinux::SignalIfAllThreadsStopped() { if (m_pending_notification_tid == LLDB_INVALID_THREAD_ID) return; // No pending notification. Nothing to do. for (const auto &thread_sp : m_threads) { if (StateIsRunningState(thread_sp->GetState())) return; // Some threads are still running. Don't signal yet. } // We have a pending notification and all threads have stopped. Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_BREAKPOINTS)); // Clear any temporary breakpoints we used to implement software single // stepping. for (const auto &thread_info : m_threads_stepping_with_breakpoint) { Status error = RemoveBreakpoint(thread_info.second); if (error.Fail()) LLDB_LOG(log, "pid = {0} remove stepping breakpoint: {1}", thread_info.first, error); } m_threads_stepping_with_breakpoint.clear(); // Notify the delegate about the stop SetCurrentThreadID(m_pending_notification_tid); SetState(StateType::eStateStopped, true); m_pending_notification_tid = LLDB_INVALID_THREAD_ID; } void NativeProcessLinux::ThreadWasCreated(NativeThreadLinux &thread) { Log *const log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); LLDB_LOG(log, "tid: {0}", thread.GetID()); if (m_pending_notification_tid != LLDB_INVALID_THREAD_ID && StateIsRunningState(thread.GetState())) { // We will need to wait for this new thread to stop as well before firing // the // notification. thread.RequestStop(); } } void NativeProcessLinux::SigchldHandler() { Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS)); // Process all pending waitpid notifications. while (true) { int status = -1; ::pid_t wait_pid = waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG); if (wait_pid == 0) break; // We are done. if (wait_pid == -1) { if (errno == EINTR) continue; Status error(errno, eErrorTypePOSIX); LLDB_LOG(log, "waitpid (-1, &status, _) failed: {0}", error); break; } bool exited = false; int signal = 0; int exit_status = 0; const char *status_cstr = nullptr; if (WIFSTOPPED(status)) { signal = WSTOPSIG(status); status_cstr = "STOPPED"; } else if (WIFEXITED(status)) { exit_status = WEXITSTATUS(status); status_cstr = "EXITED"; exited = true; } else if (WIFSIGNALED(status)) { signal = WTERMSIG(status); status_cstr = "SIGNALED"; if (wait_pid == static_cast<::pid_t>(GetID())) { exited = true; exit_status = -1; } } else status_cstr = "(\?\?\?)"; LLDB_LOG(log, "waitpid (-1, &status, _) => pid = {0}, status = {1:x} " "({2}), signal = {3}, exit_state = {4}", wait_pid, status, status_cstr, signal, exit_status); MonitorCallback(wait_pid, exited, signal, exit_status); } } // Wrapper for ptrace to catch errors and log calls. // Note that ptrace sets errno on error because -1 can be a valid result (i.e. // for PTRACE_PEEK*) Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr, void *data, size_t data_size, long *result) { Status error; long int ret; Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); PtraceDisplayBytes(req, data, data_size); errno = 0; if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET) ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), *(unsigned int *)addr, data); else ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid), addr, data); if (ret == -1) error.SetErrorToErrno(); if (result) *result = ret; LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data, data_size, ret); PtraceDisplayBytes(req, data, data_size); if (error.Fail()) LLDB_LOG(log, "ptrace() failed: {0}", error); return error; } Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (revision 319790) @@ -1,2113 +1,2115 @@ //===-- RegisterContextLLDB.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/Address.h" #include "lldb/Core/AddressRange.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Expression/DWARFExpression.h" #include "lldb/Symbol/ArmUnwindInfo.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" #include "lldb/lldb-private.h" #include "RegisterContextLLDB.h" using namespace lldb; using namespace lldb_private; static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) { if (sym_ctx.symbol) return sym_ctx.symbol->GetName(); else if (sym_ctx.function) return sym_ctx.function->GetName(); return ConstString(); } RegisterContextLLDB::RegisterContextLLDB(Thread &thread, const SharedPtr &next_frame, SymbolContext &sym_ctx, uint32_t frame_number, UnwindLLDB &unwind_lldb) : RegisterContext(thread, frame_number), m_thread(thread), m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(), m_fallback_unwind_plan_sp(), m_all_registers_available(false), m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0), m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(), m_parent_unwind(unwind_lldb) { m_sym_ctx.Clear(false); m_sym_ctx_valid = false; if (IsFrameZero()) { InitializeZerothFrame(); } else { InitializeNonZerothFrame(); } // This same code exists over in the GetFullUnwindPlanForFrame() but it may // not have been executed yet if (IsFrameZero() || next_frame->m_frame_type == eTrapHandlerFrame || next_frame->m_frame_type == eDebuggerFrame) { m_all_registers_available = true; } } bool RegisterContextLLDB::IsUnwindPlanValidForCurrentPC( lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset) { if (!unwind_plan_sp) return false; // check if m_current_pc is valid if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { // yes - current offset can be used as is valid_pc_offset = m_current_offset; return true; } // if m_current_offset <= 0, we've got nothing else to try if (m_current_offset <= 0) return false; // check pc - 1 to see if it's valid Address pc_minus_one(m_current_pc); pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1); if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one)) { // *valid_pc_offset = m_current_offset - 1; valid_pc_offset = m_current_pc.GetOffset() - 1; return true; } return false; } // Initialize a RegisterContextLLDB which is the first frame of a stack -- the // zeroth frame or currently // executing frame. void RegisterContextLLDB::InitializeZerothFrame() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); ExecutionContext exe_ctx(m_thread.shared_from_this()); RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext(); if (reg_ctx_sp.get() == NULL) { m_frame_type = eNotAValidFrame; UnwindLogMsg("frame does not have a register context"); return; } addr_t current_pc = reg_ctx_sp->GetPC(); if (current_pc == LLDB_INVALID_ADDRESS) { m_frame_type = eNotAValidFrame; UnwindLogMsg("frame does not have a pc"); return; } Process *process = exe_ctx.GetProcessPtr(); // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs // this will strip bit zero in case we read a PC from memory or from the LR. // (which would be a no-op in frame 0 where we get it from the register set, // but still a good idea to make the call here for other ABIs that may exist.) ABI *abi = process->GetABI().get(); if (abi) current_pc = abi->FixCodeAddress(current_pc); // Initialize m_current_pc, an Address object, based on current_pc, an addr_t. m_current_pc.SetLoadAddress(current_pc, &process->GetTarget()); // If we don't have a Module for some reason, we're not going to find // symbol/function information - just // stick in some reasonable defaults and hope we can unwind past this frame. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp) { UnwindLogMsg("using architectural default unwind method"); } // We require either a symbol or function in the symbols context to be // successfully // filled in or this context is of no use to us. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; if (pc_module_sp.get() && (pc_module_sp->ResolveSymbolContextForAddress( m_current_pc, resolve_scope, m_sym_ctx) & resolve_scope)) { m_sym_ctx_valid = true; } if (m_sym_ctx.symbol) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { UnwindLogMsg("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", current_pc); } AddressRange addr_range; m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range); if (IsTrapHandlerSymbol(process, m_sym_ctx)) { m_frame_type = eTrapHandlerFrame; } else { // FIXME: Detect eDebuggerFrame here. m_frame_type = eNormalFrame; } // If we were able to find a symbol/function, set addr_range to the bounds of // that symbol/function. // else treat the current pc value as the start_pc and record no offset. if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); if (m_current_pc.GetSection() == m_start_pc.GetSection()) { m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); } else if (m_current_pc.GetModule() == m_start_pc.GetModule()) { // This means that whatever symbol we kicked up isn't really correct // --- we should not cross section boundaries ... We really should NULL // out // the function/symbol in this case unless there is a bad assumption // here due to inlined functions? m_current_offset = m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress(); } m_current_offset_backed_up_one = m_current_offset; } else { m_start_pc = m_current_pc; m_current_offset = -1; m_current_offset_backed_up_one = -1; } // We've set m_frame_type and m_sym_ctx before these calls. m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); UnwindPlan::RowSP active_row; lldb::RegisterKind row_register_kind = eRegisterKindGeneric; if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("%s", active_row_strm.GetData()); } } if (!active_row.get()) { UnwindLogMsg("could not find an unwindplan row for this frame's pc"); m_frame_type = eNotAValidFrame; return; } if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { // Try the fall back unwind plan since the // full unwind plan failed. FuncUnwindersSP func_unwinders_sp; UnwindPlanSP call_site_unwind_plan; bool cfa_status = false; if (m_sym_ctx_valid) { func_unwinders_sp = pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx); } if (func_unwinders_sp.get() != nullptr) call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan.get() != nullptr) { m_fallback_unwind_plan_sp = call_site_unwind_plan; if (TryFallbackUnwindPlan()) cfa_status = true; } if (!cfa_status) { UnwindLogMsg("could not read CFA value for first frame."); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), (uint64_t)m_cfa, m_full_unwind_plan_sp->GetSourceName().GetCString()); } // Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the // RegisterContextLLDB "below" it // to provide things like its current pc value. void RegisterContextLLDB::InitializeNonZerothFrame() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (IsFrameZero()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("non-zeroth frame tests positive for IsFrameZero -- that " "shouldn't happen."); return; } if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("Could not get next frame, marking this frame as invalid."); return; } if (!m_thread.GetRegisterContext()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("Could not get register context for this thread, marking this " "frame as invalid."); return; } addr_t pc; if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { UnwindLogMsg("could not get pc value"); m_frame_type = eNotAValidFrame; return; } if (log) { UnwindLogMsg("pc = 0x%" PRIx64, pc); addr_t reg_val; if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val)) UnwindLogMsg("fp = 0x%" PRIx64, reg_val); if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val)) UnwindLogMsg("sp = 0x%" PRIx64, reg_val); } // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap // handler function bool above_trap_handler = false; if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) above_trap_handler = true; if (pc == 0 || pc == 0x1) { if (above_trap_handler == false) { m_frame_type = eNotAValidFrame; UnwindLogMsg("this frame has a pc of 0x0"); return; } } ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs // this will strip bit zero in case we read a PC from memory or from the LR. ABI *abi = process->GetABI().get(); if (abi) pc = abi->FixCodeAddress(pc); - m_current_pc.SetLoadAddress(pc, &process->GetTarget()); + const bool allow_section_end = true; + m_current_pc.SetLoadAddress(pc, &process->GetTarget(), allow_section_end); // If we don't have a Module for some reason, we're not going to find // symbol/function information - just // stick in some reasonable defaults and hope we can unwind past this frame. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp) { UnwindLogMsg("using architectural default unwind method"); // Test the pc value to see if we know it's in an unmapped/non-executable // region of memory. uint32_t permissions; if (process->GetLoadAddressPermissions(pc, permissions) && (permissions & ePermissionsExecutable) == 0) { // If this is the second frame off the stack, we may have unwound the // first frame // incorrectly. But using the architecture default unwind plan may get us // back on // track -- albeit possibly skipping a real frame. Give this frame a // clearly-invalid // pc and see if we can get any further. if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero()) { UnwindLogMsg("had a pc of 0x%" PRIx64 " which is not in executable " "memory but on frame 1 -- " "allowing it once.", (uint64_t)pc); m_frame_type = eSkipFrame; } else { // anywhere other than the second frame, a non-executable pc means we're // off in the weeds -- stop now. m_frame_type = eNotAValidFrame; UnwindLogMsg("pc is in a non-executable section of memory and this " "isn't the 2nd frame in the stack walk."); return; } } if (abi) { m_fast_unwind_plan_sp.reset(); m_full_unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNormalFrame; } m_all_registers_available = false; m_current_offset = -1; m_current_offset_backed_up_one = -1; RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0); if (row.get()) { if (!ReadCFAValueForRow(row_register_kind, row, m_cfa)) { UnwindLogMsg("failed to get cfa value"); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNotAValidFrame; } return; } // A couple of sanity checks.. if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) { UnwindLogMsg("could not find a valid cfa address"); m_frame_type = eNotAValidFrame; return; } // m_cfa should point into the stack memory; if we can query memory // region permissions, // see if the memory is allocated & readable. if (process->GetLoadAddressPermissions(m_cfa, permissions) && (permissions & ePermissionsReadable) == 0) { m_frame_type = eNotAValidFrame; UnwindLogMsg( "the CFA points to a region of memory that is not readable"); return; } } else { UnwindLogMsg("could not find a row for function offset zero"); m_frame_type = eNotAValidFrame; return; } if (CheckIfLoopingStack()) { TryFallbackUnwindPlan(); if (CheckIfLoopingStack()) { UnwindLogMsg("same CFA address as next frame, assuming the unwind is " "looping - stopping"); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame cfa is 0x%" PRIx64, (uint64_t)m_cfa); return; } m_frame_type = eNotAValidFrame; UnwindLogMsg("could not find any symbol for this pc, or a default unwind " "plan, to continue unwind."); return; } bool resolve_tail_call_address = false; // m_current_pc can be one past the // address range of the function... // If the saved pc does not point to a function/symbol because it is // beyond the bounds of the correct function and there's no symbol there, // we do *not* want ResolveSymbolContextForAddress to back up the pc by 1, // because then we might not find the correct unwind information later. // Instead, let ResolveSymbolContextForAddress fail, and handle the case // via decr_pc_and_recompute_addr_range below. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress( m_current_pc, resolve_scope, m_sym_ctx, resolve_tail_call_address); // We require either a symbol or function in the symbols context to be // successfully // filled in or this context is of no use to us. if (resolve_scope & resolved_scope) { m_sym_ctx_valid = true; } if (m_sym_ctx.symbol) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { UnwindLogMsg("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", pc); } AddressRange addr_range; if (!m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) { m_sym_ctx_valid = false; } bool decr_pc_and_recompute_addr_range = false; // If the symbol lookup failed... if (m_sym_ctx_valid == false) decr_pc_and_recompute_addr_range = true; // Or if we're in the middle of the stack (and not "above" an asynchronous // event like sigtramp), // and our "current" pc is the start of a function... - if (m_sym_ctx_valid && GetNextFrame()->m_frame_type != eTrapHandlerFrame && + if (GetNextFrame()->m_frame_type != eTrapHandlerFrame && GetNextFrame()->m_frame_type != eDebuggerFrame && - addr_range.GetBaseAddress().IsValid() && - addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() && - addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()) { + (!m_sym_ctx_valid || + (addr_range.GetBaseAddress().IsValid() && + addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() && + addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()))) { decr_pc_and_recompute_addr_range = true; } // We need to back up the pc by 1 byte and re-search for the Symbol to handle // the case where the "saved pc" // value is pointing to the next function, e.g. if a function ends with a CALL // instruction. // FIXME this may need to be an architectural-dependent behavior; if so we'll // need to add a member function // to the ABI plugin and consult that. if (decr_pc_and_recompute_addr_range) { UnwindLogMsg("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); Address temporary_pc; temporary_pc.SetLoadAddress(pc - 1, &process->GetTarget()); m_sym_ctx.Clear(false); m_sym_ctx_valid = false; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; ModuleSP temporary_module_sp = temporary_pc.GetModule(); if (temporary_module_sp && temporary_module_sp->ResolveSymbolContextForAddress( temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope) { if (m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) m_sym_ctx_valid = true; } UnwindLogMsg("Symbol is now %s", GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } // If we were able to find a symbol/function, set addr_range_ptr to the bounds // of that symbol/function. // else treat the current pc value as the start_pc and record no offset. if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); m_current_offset = pc - m_start_pc.GetLoadAddress(&process->GetTarget()); m_current_offset_backed_up_one = m_current_offset; if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0) { m_current_offset_backed_up_one--; if (m_sym_ctx_valid) { m_current_pc.SetLoadAddress(pc - 1, &process->GetTarget()); } } } else { m_start_pc = m_current_pc; m_current_offset = -1; m_current_offset_backed_up_one = -1; } if (IsTrapHandlerSymbol(process, m_sym_ctx)) { m_frame_type = eTrapHandlerFrame; } else { // FIXME: Detect eDebuggerFrame here. if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNormalFrame; } } // We've set m_frame_type and m_sym_ctx before this call. m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); UnwindPlan::RowSP active_row; RegisterKind row_register_kind = eRegisterKindGeneric; // Try to get by with just the fast UnwindPlan if possible - the full // UnwindPlan may be expensive to get // (e.g. if we have to parse the entire eh_frame section of an ObjectFile for // the first time.) if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("active row: %s", active_row_strm.GetData()); } } else { m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); int valid_offset = -1; if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp, valid_offset)) { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(valid_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("active row: %s", active_row_strm.GetData()); } } } if (!active_row.get()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("could not find unwind row for this pc"); return; } if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { UnwindLogMsg("failed to get cfa"); m_frame_type = eNotAValidFrame; return; } UnwindLogMsg("m_cfa = 0x%" PRIx64, m_cfa); if (CheckIfLoopingStack()) { TryFallbackUnwindPlan(); if (CheckIfLoopingStack()) { UnwindLogMsg("same CFA address as next frame, assuming the unwind is " "looping - stopping"); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64, (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), (uint64_t)m_cfa); } bool RegisterContextLLDB::CheckIfLoopingStack() { // If we have a bad stack setup, we can get the same CFA value multiple times // -- or even // more devious, we can actually oscillate between two CFA values. Detect that // here and // break out to avoid a possible infinite loop in lldb trying to unwind the // stack. // To detect when we have the same CFA value multiple times, we compare the // CFA of the current // frame with the 2nd next frame because in some specail case (e.g. signal // hanlders, hand // written assembly without ABI compiance) we can have 2 frames with the same // CFA (in theory we // can have arbitrary number of frames with the same CFA, but more then 2 is // very very unlikely) RegisterContextLLDB::SharedPtr next_frame = GetNextFrame(); if (next_frame) { RegisterContextLLDB::SharedPtr next_next_frame = next_frame->GetNextFrame(); addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS; if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa)) { if (next_next_frame_cfa == m_cfa) { // We have a loop in the stack unwind return true; } } } return false; } bool RegisterContextLLDB::IsFrameZero() const { return m_frame_number == 0; } // Find a fast unwind plan for this frame, if possible. // // On entry to this method, // // 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame // if either of those are correct, // 2. m_sym_ctx should already be filled in, and // 3. m_current_pc should have the current pc value for this frame // 4. m_current_offset_backed_up_one should have the current byte offset into // the function, maybe backed up by 1, -1 if unknown UnwindPlanSP RegisterContextLLDB::GetFastUnwindPlanForFrame() { UnwindPlanSP unwind_plan_sp; ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) return unwind_plan_sp; if (IsFrameZero()) return unwind_plan_sp; FuncUnwindersSP func_unwinders_sp( pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx)); if (!func_unwinders_sp) return unwind_plan_sp; // If we're in _sigtramp(), unwinding past this frame requires special // knowledge. if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame) return unwind_plan_sp; unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind( *m_thread.CalculateTarget(), m_thread); if (unwind_plan_sp) { if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { if (m_fast_unwind_plan_sp) UnwindLogMsgVerbose("frame, and has a fast UnwindPlan"); else UnwindLogMsgVerbose("frame"); } m_frame_type = eNormalFrame; return unwind_plan_sp; } else { unwind_plan_sp.reset(); } } return unwind_plan_sp; } // On entry to this method, // // 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame // if either of those are correct, // 2. m_sym_ctx should already be filled in, and // 3. m_current_pc should have the current pc value for this frame // 4. m_current_offset_backed_up_one should have the current byte offset into // the function, maybe backed up by 1, -1 if unknown UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() { UnwindPlanSP unwind_plan_sp; UnwindPlanSP arch_default_unwind_plan_sp; ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { arch_default_unwind_plan_sp.reset( new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp); } else { UnwindLogMsg( "unable to get architectural default UnwindPlan from ABI plugin"); } bool behaves_like_zeroth_frame = false; if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame || GetNextFrame()->m_frame_type == eDebuggerFrame) { behaves_like_zeroth_frame = true; // If this frame behaves like a 0th frame (currently executing or // interrupted asynchronously), all registers can be retrieved. m_all_registers_available = true; } // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) // so the pc is 0x0 // in the zeroth frame, we need to use the "unwind at first instruction" arch // default UnwindPlan // Also, if this Process can report on memory region attributes, any // non-executable region means // we jumped through a bad function pointer - handle the same way as 0x0. // Note, if we have a symbol context & a symbol, we don't want to follow this // code path. This is // for jumping to memory regions without any information available. if ((!m_sym_ctx_valid || (m_sym_ctx.function == NULL && m_sym_ctx.symbol == NULL)) && behaves_like_zeroth_frame && m_current_pc.IsValid()) { uint32_t permissions; addr_t current_pc_addr = m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()); if (current_pc_addr == 0 || (process && process->GetLoadAddressPermissions(current_pc_addr, permissions) && (permissions & ePermissionsExecutable) == 0)) { if (abi) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp); m_frame_type = eNormalFrame; return unwind_plan_sp; } } } // No Module for the current pc, try using the architecture default unwind. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) { m_frame_type = eNormalFrame; return arch_default_unwind_plan_sp; } FuncUnwindersSP func_unwinders_sp; if (m_sym_ctx_valid) { func_unwinders_sp = pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx); } // No FuncUnwinders available for this pc (stripped function symbols, lldb // could not augment its // function table with another source, like LC_FUNCTION_STARTS or eh_frame in // ObjectFileMachO). // See if eh_frame or the .ARM.exidx tables have unwind information for this // address, else fall // back to the architectural default unwind. if (!func_unwinders_sp) { m_frame_type = eNormalFrame; if (!pc_module_sp || !pc_module_sp->GetObjectFile() || !m_current_pc.IsValid()) return arch_default_unwind_plan_sp; // Even with -fomit-frame-pointer, we can try eh_frame to get back on track. DWARFCallFrameInfo *eh_frame = pc_module_sp->GetObjectFile()->GetUnwindTable().GetEHFrameInfo(); if (eh_frame) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); if (eh_frame->GetUnwindPlan(m_current_pc, *unwind_plan_sp)) return unwind_plan_sp; else unwind_plan_sp.reset(); } ArmUnwindInfo *arm_exidx = pc_module_sp->GetObjectFile()->GetUnwindTable().GetArmUnwindInfo(); if (arm_exidx) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); if (arm_exidx->GetUnwindPlan(exe_ctx.GetTargetRef(), m_current_pc, *unwind_plan_sp)) return unwind_plan_sp; else unwind_plan_sp.reset(); } return arch_default_unwind_plan_sp; } // If we're in _sigtramp(), unwinding past this frame requires special // knowledge. On Mac OS X this knowledge // is properly encoded in the eh_frame section, so prefer that if available. // On other platforms we may need to provide a platform-specific UnwindPlan // which encodes the details of // how to unwind out of sigtramp. if (m_frame_type == eTrapHandlerFrame && process) { m_fast_unwind_plan_sp.reset(); unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan( process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) { return unwind_plan_sp; } } // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame // even when it's frame zero // This comes up if we have hand-written functions in a Module and // hand-written eh_frame. The assembly // instruction inspection may fail and the eh_frame CFI were probably written // with some care to do the // right thing. It'd be nice if there was a way to ask the eh_frame directly // if it is asynchronous // (can be trusted at every instruction point) or synchronous (the normal case // - only at call sites). // But there is not. if (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo(m_sym_ctx)) { // We must specifically call the GetEHFrameUnwindPlan() method here -- // normally we would // call GetUnwindPlanAtCallSite() -- because CallSite may return an unwind // plan sourced from // either eh_frame (that's what we intend) or compact unwind (this won't // work) unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan( process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the " "DynamicLoader suggested we prefer it", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // Typically the NonCallSite UnwindPlan is the unwind created by inspecting // the assembly language instructions if (behaves_like_zeroth_frame && process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread, m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly // instructions. The // assembly profilers work really well with compiler-generated functions // but hand- // written assembly can be problematic. We set the eh_frame based unwind // plan as our // fallback unwind plan if instruction emulation doesn't work out even // for non call // sites if it is available and use the architecture default unwind plan // if it is // not available. The eh_frame unwind plan is more reliable even on non // call sites // then the architecture default plan and for hand written assembly code // it is often // written in a way that it valid at all location what helps in the most // common // cases when the instruction emulation fails. UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan && call_site_unwind_plan.get() != unwind_plan_sp.get() && call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) { m_fallback_unwind_plan_sp = call_site_unwind_plan; } else { m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; } } UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // Typically this is unwind info from an eh_frame section intended for // exception handling; only valid at call sites if (process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); } int valid_offset = -1; if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } // We'd prefer to use an UnwindPlan intended for call sites when we're at a // call site but if we've // struck out on that, fall back to using the non-call-site assembly // inspection UnwindPlan if possible. if (process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread, m_current_offset_backed_up_one); } if (unwind_plan_sp && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly // instructions. The assembly // profilers work really well with compiler-generated functions but hand- // written assembly // can be problematic. We set the eh_frame based unwind plan as our fallback // unwind plan if // instruction emulation doesn't work out even for non call sites if it is // available and use // the architecture default unwind plan if it is not available. The eh_frame // unwind plan is // more reliable even on non call sites then the architecture default plan // and for hand // written assembly code it is often written in a way that it valid at all // location what // helps in the most common cases when the instruction emulation fails. UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan && call_site_unwind_plan.get() != unwind_plan_sp.get() && call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) { m_fallback_unwind_plan_sp = call_site_unwind_plan; } else { m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; } } if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } // If we're on the first instruction of a function, and we have an // architectural default UnwindPlan // for the initial instruction of a function, use that. if (m_current_offset_backed_up_one == 0) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry( m_thread); if (unwind_plan_sp) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // If nothing else, use the architectural default UnwindPlan and hope that // does the job. if (arch_default_unwind_plan_sp) UnwindLogMsgVerbose( "frame uses %s for full UnwindPlan", arch_default_unwind_plan_sp->GetSourceName().GetCString()); else UnwindLogMsg( "Unable to find any UnwindPlan for full unwind of this frame."); return arch_default_unwind_plan_sp; } void RegisterContextLLDB::InvalidateAllRegisters() { m_frame_type = eNotAValidFrame; } size_t RegisterContextLLDB::GetRegisterCount() { return m_thread.GetRegisterContext()->GetRegisterCount(); } const RegisterInfo *RegisterContextLLDB::GetRegisterInfoAtIndex(size_t reg) { return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); } size_t RegisterContextLLDB::GetRegisterSetCount() { return m_thread.GetRegisterContext()->GetRegisterSetCount(); } const RegisterSet *RegisterContextLLDB::GetRegisterSet(size_t reg_set) { return m_thread.GetRegisterContext()->GetRegisterSet(reg_set); } uint32_t RegisterContextLLDB::ConvertRegisterKindToRegisterNumber( lldb::RegisterKind kind, uint32_t num) { return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber( kind, num); } bool RegisterContextLLDB::ReadRegisterValueFromRegisterLocation( lldb_private::UnwindLLDB::RegisterLocation regloc, const RegisterInfo *reg_info, RegisterValue &value) { if (!IsValid()) return false; bool success = false; switch (regloc.type) { case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (!other_reg_info) return false; success = m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value); } break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (!other_reg_info) return false; if (IsFrameZero()) { success = m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value); } else { success = GetNextFrame()->ReadRegister(other_reg_info, value); } } break; case UnwindLLDB::RegisterLocation::eRegisterValueInferred: success = value.SetUInt(regloc.location.inferred_value, reg_info->byte_size); break; case UnwindLLDB::RegisterLocation::eRegisterNotSaved: break; case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: llvm_unreachable("FIXME debugger inferior function call unwind"); case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: { Status error(ReadRegisterValueFromMemory( reg_info, regloc.location.target_memory_location, reg_info->byte_size, value)); success = error.Success(); } break; default: llvm_unreachable("Unknown RegisterLocation type."); } return success; } bool RegisterContextLLDB::WriteRegisterValueToRegisterLocation( lldb_private::UnwindLLDB::RegisterLocation regloc, const RegisterInfo *reg_info, const RegisterValue &value) { if (!IsValid()) return false; bool success = false; switch (regloc.type) { case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); success = m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value); } break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (IsFrameZero()) { success = m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value); } else { success = GetNextFrame()->WriteRegister(other_reg_info, value); } } break; case UnwindLLDB::RegisterLocation::eRegisterValueInferred: case UnwindLLDB::RegisterLocation::eRegisterNotSaved: break; case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: llvm_unreachable("FIXME debugger inferior function call unwind"); case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: { Status error(WriteRegisterValueToMemory( reg_info, regloc.location.target_memory_location, reg_info->byte_size, value)); success = error.Success(); } break; default: llvm_unreachable("Unknown RegisterLocation type."); } return success; } bool RegisterContextLLDB::IsValid() const { return m_frame_type != eNotAValidFrame; } // After the final stack frame in a stack walk we'll get one invalid // (eNotAValidFrame) stack frame -- // one past the end of the stack walk. But higher-level code will need to tell // the differnece between // "the unwind plan below this frame failed" versus "we successfully completed // the stack walk" so // this method helps to disambiguate that. bool RegisterContextLLDB::IsTrapHandlerFrame() const { return m_frame_type == eTrapHandlerFrame; } // A skip frame is a bogus frame on the stack -- but one where we're likely to // find a real frame farther // up the stack if we keep looking. It's always the second frame in an unwind // (i.e. the first frame after // frame zero) where unwinding can be the trickiest. Ideally we'll mark up this // frame in some way so the // user knows we're displaying bad data and we may have skipped one frame of // their real program in the // process of getting back on track. bool RegisterContextLLDB::IsSkipFrame() const { return m_frame_type == eSkipFrame; } bool RegisterContextLLDB::IsTrapHandlerSymbol( lldb_private::Process *process, const lldb_private::SymbolContext &m_sym_ctx) const { PlatformSP platform_sp(process->GetTarget().GetPlatform()); if (platform_sp) { const std::vector trap_handler_names( platform_sp->GetTrapHandlerSymbolNames()); for (ConstString name : trap_handler_names) { if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } } } const std::vector user_specified_trap_handler_names( m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames()); for (ConstString name : user_specified_trap_handler_names) { if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } } return false; } // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? enum UnwindLLDB::RegisterSearchResult RegisterContextLLDB::SavedLocationForRegister( uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) { RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum); // Have we already found this register location? if (!m_registers.empty()) { std::map::const_iterator iterator; iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB)); if (iterator != m_registers.end()) { regloc = iterator->second; UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } // Look through the available UnwindPlans for the register location. UnwindPlan::Row::RegisterLocation unwindplan_regloc; bool have_unwindplan_regloc = false; RegisterKind unwindplan_registerkind = kNumRegisterKinds; if (m_fast_unwind_plan_sp) { UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind(); if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) { UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind " "reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), (int)unwindplan_registerkind); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { UnwindLogMsg( "supplying caller's saved %s (%d)'s location using FastUnwindPlan", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); have_unwindplan_regloc = true; } } if (!have_unwindplan_regloc) { // m_full_unwind_plan_sp being NULL means that we haven't tried to find a // full UnwindPlan yet if (!m_full_unwind_plan_sp) m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); if (m_full_unwind_plan_sp) { RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind(); RegisterNumber return_address_reg; // If we're fetching the saved pc and this UnwindPlan defines a // ReturnAddress register (e.g. lr on arm), // look for the return address register number in the UnwindPlan's row. if (pc_regnum.IsValid() && pc_regnum == regnum && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) { return_address_reg.init( m_thread, m_full_unwind_plan_sp->GetRegisterKind(), m_full_unwind_plan_sp->GetReturnAddressRegister()); regnum = return_address_reg; UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a " "RA reg; getting %s (%d) instead", return_address_reg.GetName(), return_address_reg.GetAsKind(eRegisterKindLLDB)); } else { if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) { if (unwindplan_registerkind == eRegisterKindGeneric) { UnwindLogMsg("could not convert lldb regnum %s (%d) into " "eRegisterKindGeneric reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); } else { UnwindLogMsg("could not convert lldb regnum %s (%d) into %d " "RegisterKind reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), (int)unwindplan_registerkind); } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } } if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { have_unwindplan_regloc = true; UnwindLogMsg( "supplying caller's saved %s (%d)'s location using %s UnwindPlan", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), m_full_unwind_plan_sp->GetSourceName().GetCString()); } // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and // it hasn't been saved anywhere yet -- that is, it's still live in the // actual register. // Handle this specially. if (have_unwindplan_regloc == false && return_address_reg.IsValid() && IsFrameZero()) { if (return_address_reg.GetAsKind(eRegisterKindLLDB) != LLDB_INVALID_REGNUM) { lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = return_address_reg.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " "RegisterContext at frame 0, saved in %d", return_address_reg.GetName(), return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } // If this architecture stores the return address in a register (it // defines a Return Address register) // and we're on a non-zero stack frame and the Full UnwindPlan says that // the pc is stored in the // RA registers (e.g. lr on arm), then we know that the full unwindplan is // not trustworthy -- this // is an impossible situation and the instruction emulation code has // likely been misled. // If this stack frame meets those criteria, we need to throw away the // Full UnwindPlan that the // instruction emulation came up with and fall back to the architecture's // Default UnwindPlan so // the stack walk can get past this point. // Special note: If the Full UnwindPlan was generated from the compiler, // don't second-guess it // when we're at a call site location. // arch_default_ra_regnum is the return address register # in the Full // UnwindPlan register numbering RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); if (arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) != LLDB_INVALID_REGNUM && pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) && m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes && !m_all_registers_available) { UnwindLogMsg("%s UnwindPlan tried to restore the pc from the link " "register but this is a non-zero frame", m_full_unwind_plan_sp->GetSourceName().GetCString()); // Throw away the full unwindplan; install the arch default unwindplan if (ForceSwitchToFallbackUnwindPlan()) { // Update for the possibly new unwind plan unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind(); UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); // Sanity check: Verify that we can fetch a pc value and CFA value // with this unwind plan RegisterNumber arch_default_pc_reg(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); bool can_fetch_pc_value = false; bool can_fetch_cfa = false; addr_t cfa_value; if (active_row) { if (arch_default_pc_reg.GetAsKind(unwindplan_registerkind) != LLDB_INVALID_REGNUM && active_row->GetRegisterInfo( arch_default_pc_reg.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { can_fetch_pc_value = true; } if (ReadCFAValueForRow(unwindplan_registerkind, active_row, cfa_value)) { can_fetch_cfa = true; } } if (can_fetch_pc_value && can_fetch_cfa) { have_unwindplan_regloc = true; } else { have_unwindplan_regloc = false; } } else { // We were unable to fall back to another unwind plan have_unwindplan_regloc = false; } } } } ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); if (have_unwindplan_regloc == false) { // If the UnwindPlan failed to give us an unwind location for this register, // we may be able to fall back // to some ABI-defined default. For example, some ABIs allow to determine // the caller's SP via the CFA. // Also, the ABI may set volatile registers to the undefined state. ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info && abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) { UnwindLogMsg( "supplying caller's saved %s (%d)'s location using ABI default", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); have_unwindplan_regloc = true; } } } if (have_unwindplan_regloc == false) { if (IsFrameZero()) { // This is frame 0 - we should return the actual live register context // value lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " "RegisterContext at frame 0", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else { std::string unwindplan_name(""); if (m_full_unwind_plan_sp) { unwindplan_name += "via '"; unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString(); unwindplan_name += "'"; } UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), unwindplan_name.c_str()); } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } // unwindplan_regloc has valid contents about where to retrieve the register if (unwindplan_regloc.IsUnspecified()) { lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; UnwindLogMsg("save location for %s (%d) is unspecified, continue searching", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } if (unwindplan_regloc.IsUndefined()) { UnwindLogMsg( "did not supply reg location for %s (%d) because it is volatile", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; } if (unwindplan_regloc.IsSame()) { if (IsFrameZero() == false && (regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC || regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) { UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or " "return address reg on a non-zero frame -- treat as if we " "have no information", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } else { regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg( "supplying caller's register %s (%d), saved in register %s (%d)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } if (unwindplan_regloc.IsCFAPlusOffset()) { int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = m_cfa + offset; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d), value is CFA plus " "offset %d [value is 0x%" PRIx64 "]", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, regloc.location.inferred_value); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsAtCFAPlusOffset()) { int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = m_cfa + offset; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at " "CFA plus offset %d [saved at 0x%" PRIx64 "]", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, regloc.location.target_memory_location); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsInOtherRegister()) { uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber(); RegisterNumber row_regnum(m_thread, unwindplan_registerkind, unwindplan_regnum); if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) { UnwindLogMsg("could not supply caller's %s (%d) location - was saved in " "another reg but couldn't convert that regnum", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; regloc.location.register_number = row_regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg( "supplying caller's register %s (%d), saved in register %s (%d)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression()) { DataExtractor dwarfdata(unwindplan_regloc.GetDWARFExpressionBytes(), unwindplan_regloc.GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0, unwindplan_regloc.GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind(unwindplan_registerkind); Value result; Status error; if (dwarfexpr.Evaluate(&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { addr_t val; val = result.GetScalar().ULongLong(); if (unwindplan_regloc.IsDWARFExpression()) { regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = val; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression " "(IsDWARFExpression)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else { regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = val; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression " "(IsAtDWARFExpression)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } UnwindLogMsg("tried to use IsDWARFExpression or IsAtDWARFExpression for %s " "(%d) but failed", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } UnwindLogMsg("no save location for %s (%d) in this stack frame", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are // unsupported. return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } // TryFallbackUnwindPlan() -- this method is a little tricky. // // When this is called, the frame above -- the caller frame, the "previous" // frame -- // is invalid or bad. // // Instead of stopping the stack walk here, we'll try a different UnwindPlan and // see // if we can get a valid frame above us. // // This most often happens when an unwind plan based on assembly instruction // inspection // is not correct -- mostly with hand-written assembly functions or functions // where the // stack frame is set up "out of band", e.g. the kernel saved the register // context and // then called an asynchronous trap handler like _sigtramp. // // Often in these cases, if we just do a dumb stack walk we'll get past this // tricky // frame and our usual techniques can continue to be used. bool RegisterContextLLDB::TryFallbackUnwindPlan() { if (m_fallback_unwind_plan_sp.get() == nullptr) return false; if (m_full_unwind_plan_sp.get() == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) { return false; } // If a compiler generated unwind plan failed, trying the arch default // unwindplan // isn't going to do any better. if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) return false; // Get the caller's pc value and our own CFA value. // Swap in the fallback unwind plan, re-fetch the caller's pc value and CFA // value. // If they're the same, then the fallback unwind plan provides no benefit. RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS; addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS; addr_t old_this_frame_cfa_value = m_cfa; UnwindLLDB::RegisterLocation regloc; if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info) { RegisterValue reg_value; if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { old_caller_pc_value = reg_value.GetAsUInt64(); } } } // This is a tricky wrinkle! If SavedLocationForRegister() detects a really // impossible // register location for the full unwind plan, it may call // ForceSwitchToFallbackUnwindPlan() // which in turn replaces the full unwindplan with the fallback... in short, // we're done, // we're using the fallback UnwindPlan. // We checked if m_fallback_unwind_plan_sp was nullptr at the top -- the only // way it // became nullptr since then is via SavedLocationForRegister(). if (m_fallback_unwind_plan_sp.get() == nullptr) return true; // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide // this isn't // working, we need to restore. // We'll also need to save & restore the value of the m_cfa ivar. Save is // down below a bit in 'old_cfa'. UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp; addr_t old_cfa = m_cfa; m_registers.clear(); m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } m_cfa = new_cfa; if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info) { RegisterValue reg_value; if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { new_caller_pc_value = reg_value.GetAsUInt64(); } } } if (new_caller_pc_value == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get a pc value for the caller frame with the " "fallback unwind plan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } if (old_caller_pc_value != LLDB_INVALID_ADDRESS) { if (old_caller_pc_value == new_caller_pc_value && new_cfa == old_this_frame_cfa_value) { UnwindLogMsg("fallback unwind plan got the same values for this frame " "CFA and caller frame pc, not using"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } } UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' " "because UnwindPlan '%s' failed.", m_fallback_unwind_plan_sp->GetSourceName().GetCString(), original_full_unwind_plan_sp->GetSourceName().GetCString()); // We've copied the fallback unwind plan into the full - now clear the // fallback. m_fallback_unwind_plan_sp.reset(); } return true; } bool RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan() { if (m_fallback_unwind_plan_sp.get() == NULL) return false; if (m_full_unwind_plan_sp.get() == NULL) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) { return false; } UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); return false; } m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; m_fallback_unwind_plan_sp.reset(); m_registers.clear(); m_cfa = new_cfa; UnwindLogMsg("switched unconditionally to the fallback unwindplan %s", m_full_unwind_plan_sp->GetSourceName().GetCString()); return true; } return false; } bool RegisterContextLLDB::ReadCFAValueForRow( lldb::RegisterKind row_register_kind, const UnwindPlan::RowSP &row, addr_t &cfa_value) { RegisterValue reg_value; cfa_value = LLDB_INVALID_ADDRESS; addr_t cfa_reg_contents; switch (row->GetCFAValue().GetValueType()) { case UnwindPlan::Row::CFAValue::isRegisterDereferenced: { RegisterNumber cfa_reg(m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB)); RegisterValue reg_value; if (reg_info) { Status error = ReadRegisterValueFromMemory( reg_info, cfa_reg_contents, reg_info->byte_size, reg_value); if (error.Success()) { cfa_value = reg_value.GetAsUInt64(); UnwindLogMsg( "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents, cfa_value); return true; } else { UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.", cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents); } } } break; } case UnwindPlan::Row::CFAValue::isRegisterPlusOffset: { RegisterNumber cfa_reg(m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) { UnwindLogMsg( "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents); cfa_reg_contents = LLDB_INVALID_ADDRESS; return false; } cfa_value = cfa_reg_contents + row->GetCFAValue().GetOffset(); UnwindLogMsg( "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", cfa_value, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents, row->GetCFAValue().GetOffset()); return true; } break; } case UnwindPlan::Row::CFAValue::isDWARFExpression: { ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); DataExtractor dwarfdata(row->GetCFAValue().GetDWARFExpressionBytes(), row->GetCFAValue().GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0, row->GetCFAValue().GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind(row_register_kind); Value result; Status error; if (dwarfexpr.Evaluate(&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { cfa_value = result.GetScalar().ULongLong(); UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64, cfa_value); return true; } UnwindLogMsg("Failed to set CFA value via DWARF expression: %s", error.AsCString()); break; } default: return false; } return false; } // Retrieve a general purpose register value for THIS frame, as saved by the // NEXT frame, i.e. the frame that // this frame called. e.g. // // foo () { } // bar () { foo (); } // main () { bar (); } // // stopped in foo() so // frame 0 - foo // frame 1 - bar // frame 2 - main // and this RegisterContext is for frame 1 (bar) - if we want to get the pc // value for frame 1, we need to ask // where frame 0 (the "next" frame) saved that and retrieve the value. bool RegisterContextLLDB::ReadGPRValue(lldb::RegisterKind register_kind, uint32_t regnum, addr_t &value) { if (!IsValid()) return false; uint32_t lldb_regnum; if (register_kind == eRegisterKindLLDB) { lldb_regnum = regnum; } else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds( register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) { return false; } const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum); RegisterValue reg_value; // if this is frame 0 (currently executing frame), get the requested reg // contents from the actual thread registers if (IsFrameZero()) { if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) { value = reg_value.GetAsUInt64(); return true; } return false; } bool pc_register = false; uint32_t generic_regnum; if (register_kind == eRegisterKindGeneric && (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds( register_kind, regnum, eRegisterKindGeneric, generic_regnum) && (generic_regnum == LLDB_REGNUM_GENERIC_PC || generic_regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } lldb_private::UnwindLLDB::RegisterLocation regloc; if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, pc_register)) { return false; } if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { value = reg_value.GetAsUInt64(); return true; } return false; } bool RegisterContextLLDB::ReadGPRValue(const RegisterNumber ®num, addr_t &value) { return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(), value); } // Find the value of a register in THIS frame bool RegisterContextLLDB::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) { if (!IsValid()) return false; const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; UnwindLogMsgVerbose("looking for register saved location for reg %d", lldb_regnum); // If this is the 0th frame, hand this over to the live register context if (IsFrameZero()) { UnwindLogMsgVerbose("passing along to the live register context for reg %d", lldb_regnum); return m_thread.GetRegisterContext()->ReadRegister(reg_info, value); } bool is_pc_regnum = false; if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC || reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) { is_pc_regnum = true; } lldb_private::UnwindLLDB::RegisterLocation regloc; // Find out where the NEXT frame saved THIS frame's register contents if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum)) return false; return ReadRegisterValueFromRegisterLocation(regloc, reg_info, value); } bool RegisterContextLLDB::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) { if (!IsValid()) return false; const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; UnwindLogMsgVerbose("looking for register saved location for reg %d", lldb_regnum); // If this is the 0th frame, hand this over to the live register context if (IsFrameZero()) { UnwindLogMsgVerbose("passing along to the live register context for reg %d", lldb_regnum); return m_thread.GetRegisterContext()->WriteRegister(reg_info, value); } lldb_private::UnwindLLDB::RegisterLocation regloc; // Find out where the NEXT frame saved THIS frame's register contents if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, false)) return false; return WriteRegisterValueToRegisterLocation(regloc, reg_info, value); } // Don't need to implement this one bool RegisterContextLLDB::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) { return false; } // Don't need to implement this one bool RegisterContextLLDB::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { return false; } // Retrieve the pc value for THIS from bool RegisterContextLLDB::GetCFA(addr_t &cfa) { if (!IsValid()) { return false; } if (m_cfa == LLDB_INVALID_ADDRESS) { return false; } cfa = m_cfa; return true; } RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetNextFrame() const { RegisterContextLLDB::SharedPtr regctx; if (m_frame_number == 0) return regctx; return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1); } RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetPrevFrame() const { RegisterContextLLDB::SharedPtr regctx; return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1); } // Retrieve the address of the start of the function of THIS frame bool RegisterContextLLDB::GetStartPC(addr_t &start_pc) { if (!IsValid()) return false; if (!m_start_pc.IsValid()) { bool read_successfully = ReadPC (start_pc); if (read_successfully) { ProcessSP process_sp (m_thread.GetProcess()); if (process_sp) { ABI *abi = process_sp->GetABI().get(); if (abi) start_pc = abi->FixCodeAddress(start_pc); } } return read_successfully; } start_pc = m_start_pc.GetLoadAddress(CalculateTarget().get()); return true; } // Retrieve the current pc value for THIS frame, as saved by the NEXT frame. bool RegisterContextLLDB::ReadPC(addr_t &pc) { if (!IsValid()) return false; bool above_trap_handler = false; if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) above_trap_handler = true; if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { // A pc value of 0 or 1 is impossible in the middle of the stack -- it // indicates the end of a stack walk. // On the currently executing frame (or such a frame interrupted // asynchronously by sigtramp et al) this may // occur if code has jumped through a NULL pointer -- we want to be able to // unwind past that frame to help // find the bug. if (m_all_registers_available == false && above_trap_handler == false && (pc == 0 || pc == 1)) { return false; } ProcessSP process_sp (m_thread.GetProcess()); if (process_sp) { ABI *abi = process_sp->GetABI().get(); if (abi) pc = abi->FixCodeAddress(pc); } return true; } else { return false; } } void RegisterContextLLDB::UnwindLogMsg(const char *fmt, ...) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log) { va_list args; va_start(args, fmt); char *logmsg; if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == NULL) { if (logmsg) free(logmsg); va_end(args); return; } va_end(args); log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, logmsg); free(logmsg); } } void RegisterContextLLDB::UnwindLogMsgVerbose(const char *fmt, ...) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { va_list args; va_start(args, fmt); char *logmsg; if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == NULL) { if (logmsg) free(logmsg); va_end(args); return; } va_end(args); log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, logmsg); free(logmsg); } } Index: vendor/lldb/dist/source/Plugins/Process/elf-core/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Plugins/Process/elf-core/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/elf-core/CMakeLists.txt (revision 319790) @@ -1,21 +1,22 @@ include_directories(../Utility) add_lldb_library(lldbPluginProcessElfCore PLUGIN ProcessElfCore.cpp ThreadElfCore.cpp RegisterContextPOSIXCore_arm.cpp RegisterContextPOSIXCore_arm64.cpp RegisterContextPOSIXCore_mips64.cpp RegisterContextPOSIXCore_powerpc.cpp RegisterContextPOSIXCore_s390x.cpp RegisterContextPOSIXCore_x86_64.cpp LINK_LIBS lldbCore lldbTarget lldbPluginDynamicLoaderPosixDYLD lldbPluginObjectFileELF lldbPluginProcessUtility LINK_COMPONENTS + BinaryFormat Support ) Index: vendor/lldb/dist/source/Plugins/Process/elf-core/ProcessElfCore.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/elf-core/ProcessElfCore.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/Process/elf-core/ProcessElfCore.cpp (revision 319790) @@ -1,758 +1,758 @@ //===-- ProcessElfCore.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 #include // C++ Includes #include // Other libraries and framework includes #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataBufferLLVM.h" #include "lldb/Utility/Log.h" -#include "llvm/Support/ELF.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Threading.h" #include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" // Project includes #include "ProcessElfCore.h" #include "ThreadElfCore.h" using namespace lldb_private; ConstString ProcessElfCore::GetPluginNameStatic() { static ConstString g_name("elf-core"); return g_name; } const char *ProcessElfCore::GetPluginDescriptionStatic() { return "ELF core dump plug-in."; } void ProcessElfCore::Terminate() { PluginManager::UnregisterPlugin(ProcessElfCore::CreateInstance); } lldb::ProcessSP ProcessElfCore::CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec *crash_file) { lldb::ProcessSP process_sp; if (crash_file) { // Read enough data for a ELF32 header or ELF64 header // Note: Here we care about e_type field only, so it is safe // to ignore possible presence of the header extension. const size_t header_size = sizeof(llvm::ELF::Elf64_Ehdr); auto data_sp = DataBufferLLVM::CreateSliceFromPath(crash_file->GetPath(), header_size, 0); if (data_sp && data_sp->GetByteSize() == header_size && elf::ELFHeader::MagicBytesMatch(data_sp->GetBytes())) { elf::ELFHeader elf_header; DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); lldb::offset_t data_offset = 0; if (elf_header.Parse(data, &data_offset)) { if (elf_header.e_type == llvm::ELF::ET_CORE) process_sp.reset( new ProcessElfCore(target_sp, listener_sp, *crash_file)); } } } return process_sp; } bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp, bool plugin_specified_by_name) { // For now we are just making sure the file exists for a given module if (!m_core_module_sp && m_core_file.Exists()) { ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, NULL, NULL, NULL)); if (m_core_module_sp) { ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) return true; } } return false; } //---------------------------------------------------------------------- // ProcessElfCore constructor //---------------------------------------------------------------------- ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, const FileSpec &core_file) : Process(target_sp, listener_sp), m_core_module_sp(), m_core_file(core_file), m_dyld_plugin_name(), m_os(llvm::Triple::UnknownOS), m_thread_data_valid(false), m_thread_data(), m_core_aranges() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessElfCore::~ProcessElfCore() { Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessElfCore::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessElfCore::GetPluginVersion() { return 1; } lldb::addr_t ProcessElfCore::AddAddressRangeFromLoadSegment( const elf::ELFProgramHeader *header) { const lldb::addr_t addr = header->p_vaddr; FileRange file_range(header->p_offset, header->p_filesz); VMRangeToFileOffset::Entry range_entry(addr, header->p_memsz, file_range); VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); if (last_entry && last_entry->GetRangeEnd() == range_entry.GetRangeBase() && last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase() && last_entry->GetByteSize() == last_entry->data.GetByteSize()) { last_entry->SetRangeEnd(range_entry.GetRangeEnd()); last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); } else { m_core_aranges.Append(range_entry); } // Keep a separate map of permissions that that isn't coalesced so all ranges // are maintained. const uint32_t permissions = ((header->p_flags & llvm::ELF::PF_R) ? lldb::ePermissionsReadable : 0u) | ((header->p_flags & llvm::ELF::PF_W) ? lldb::ePermissionsWritable : 0u) | ((header->p_flags & llvm::ELF::PF_X) ? lldb::ePermissionsExecutable : 0u); m_core_range_infos.Append( VMRangeToPermissions::Entry(addr, header->p_memsz, permissions)); return addr; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Status ProcessElfCore::DoLoadCore() { Status error; if (!m_core_module_sp) { error.SetErrorString("invalid core module"); return error; } ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile()); if (core == NULL) { error.SetErrorString("invalid core object file"); return error; } const uint32_t num_segments = core->GetProgramHeaderCount(); if (num_segments == 0) { error.SetErrorString("core file has no segments"); return error; } SetCanJIT(false); m_thread_data_valid = true; bool ranges_are_sorted = true; lldb::addr_t vm_addr = 0; /// Walk through segments and Thread and Address Map information. /// PT_NOTE - Contains Thread and Register information /// PT_LOAD - Contains a contiguous range of Process Address Space for (uint32_t i = 1; i <= num_segments; i++) { const elf::ELFProgramHeader *header = core->GetProgramHeaderByIndex(i); assert(header != NULL); DataExtractor data = core->GetSegmentDataByIndex(i); // Parse thread contexts and auxv structure if (header->p_type == llvm::ELF::PT_NOTE) { error = ParseThreadContextsFromNoteSegment(header, data); if (error.Fail()) return error; } // PT_LOAD segments contains address map if (header->p_type == llvm::ELF::PT_LOAD) { lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(header); if (vm_addr > last_addr) ranges_are_sorted = false; vm_addr = last_addr; } } if (!ranges_are_sorted) { m_core_aranges.Sort(); m_core_range_infos.Sort(); } // Even if the architecture is set in the target, we need to override // it to match the core file which is always single arch. ArchSpec arch(m_core_module_sp->GetArchitecture()); ArchSpec target_arch = GetTarget().GetArchitecture(); ArchSpec core_arch(m_core_module_sp->GetArchitecture()); target_arch.MergeFrom(core_arch); GetTarget().SetArchitecture(target_arch); SetUnixSignals(UnixSignals::Create(GetArchitecture())); // Ensure we found at least one thread that was stopped on a signal. bool siginfo_signal_found = false; bool prstatus_signal_found = false; // Check we found a signal in a SIGINFO note. for (const auto &thread_data : m_thread_data) { if (thread_data.signo != 0) siginfo_signal_found = true; if (thread_data.prstatus_sig != 0) prstatus_signal_found = true; } if (!siginfo_signal_found) { // If we don't have signal from SIGINFO use the signal from each threads // PRSTATUS note. if (prstatus_signal_found) { for (auto &thread_data : m_thread_data) thread_data.signo = thread_data.prstatus_sig; } else if (m_thread_data.size() > 0) { // If all else fails force the first thread to be SIGSTOP m_thread_data.begin()->signo = GetUnixSignals()->GetSignalNumberFromName("SIGSTOP"); } } // Core files are useless without the main executable. See if we can locate // the main // executable using data we found in the core file notes. lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); if (!exe_module_sp) { // The first entry in the NT_FILE might be our executable if (!m_nt_file_entries.empty()) { ModuleSpec exe_module_spec; exe_module_spec.GetArchitecture() = arch; exe_module_spec.GetFileSpec().SetFile( m_nt_file_entries[0].path.GetCString(), false); if (exe_module_spec.GetFileSpec()) { exe_module_sp = GetTarget().GetSharedModule(exe_module_spec); if (exe_module_sp) GetTarget().SetExecutableModule(exe_module_sp, false); } } } return error; } lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset(DynamicLoader::FindPlugin( this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic().GetCString())); return m_dyld_ap.get(); } bool ProcessElfCore::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { const uint32_t num_threads = GetNumThreadContexts(); if (!m_thread_data_valid) return false; for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { const ThreadData &td = m_thread_data[tid]; lldb::ThreadSP thread_sp(new ThreadElfCore(*this, td)); new_thread_list.AddThread(thread_sp); } return new_thread_list.GetSize(false) > 0; } void ProcessElfCore::RefreshStateAfterStop() {} Status ProcessElfCore::DoDestroy() { return Status(); } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessElfCore::IsAlive() { return true; } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) { // Don't allow the caching that lldb_private::Process::ReadMemory does // since in core files we have it all cached our our core file anyway. return DoReadMemory(addr, buf, size, error); } Status ProcessElfCore::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo ®ion_info) { region_info.Clear(); const VMRangeToPermissions::Entry *permission_entry = m_core_range_infos.FindEntryThatContainsOrFollows(load_addr); if (permission_entry) { if (permission_entry->Contains(load_addr)) { region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); const Flags permissions(permission_entry->data); region_info.SetReadable(permissions.Test(lldb::ePermissionsReadable) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); region_info.SetWritable(permissions.Test(lldb::ePermissionsWritable) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); region_info.SetExecutable(permissions.Test(lldb::ePermissionsExecutable) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eYes); } else if (load_addr < permission_entry->GetRangeBase()) { region_info.GetRange().SetRangeBase(load_addr); region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); } return Status(); } region_info.GetRange().SetRangeBase(load_addr); region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); return Status(); } size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) { ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); if (core_objfile == NULL) return 0; // Get the address range const VMRangeToFileOffset::Entry *address_range = m_core_aranges.FindEntryThatContains(addr); if (address_range == NULL || address_range->GetRangeEnd() < addr) { error.SetErrorStringWithFormat("core file does not contain 0x%" PRIx64, addr); return 0; } // Convert the address into core file offset const lldb::addr_t offset = addr - address_range->GetRangeBase(); const lldb::addr_t file_start = address_range->data.GetRangeBase(); const lldb::addr_t file_end = address_range->data.GetRangeEnd(); size_t bytes_to_read = size; // Number of bytes to read from the core file size_t bytes_copied = 0; // Number of bytes actually read from the core file size_t zero_fill_size = 0; // Padding lldb::addr_t bytes_left = 0; // Number of bytes available in the core file from the given address // Don't proceed if core file doesn't contain the actual data for this address range. if (file_start == file_end) return 0; // Figure out how many on-disk bytes remain in this segment // starting at the given offset if (file_end > file_start + offset) bytes_left = file_end - (file_start + offset); // Figure out how many bytes we need to zero-fill if we are // reading more bytes than available in the on-disk segment if (bytes_to_read > bytes_left) { zero_fill_size = bytes_to_read - bytes_left; bytes_to_read = bytes_left; } // If there is data available on the core file read it if (bytes_to_read) bytes_copied = core_objfile->CopyData(offset + file_start, bytes_to_read, buf); assert(zero_fill_size <= size); // Pad remaining bytes if (zero_fill_size) memset(((char *)buf) + bytes_copied, 0, zero_fill_size); return bytes_copied + zero_fill_size; } void ProcessElfCore::Clear() { m_thread_list.Clear(); m_os = llvm::Triple::UnknownOS; SetUnixSignals(std::make_shared()); } void ProcessElfCore::Initialize() { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); }); } lldb::addr_t ProcessElfCore::GetImageInfoAddress() { ObjectFile *obj_file = GetTarget().GetExecutableModule()->GetObjectFile(); Address addr = obj_file->GetImageInfoAddress(&GetTarget()); if (addr.IsValid()) return addr.GetLoadAddress(&GetTarget()); return LLDB_INVALID_ADDRESS; } /// Core files PT_NOTE segment descriptor types enum { NT_PRSTATUS = 1, NT_FPREGSET, NT_PRPSINFO, NT_TASKSTRUCT, NT_PLATFORM, NT_AUXV, NT_FILE = 0x46494c45, NT_PRXFPREG = 0x46e62b7f, NT_SIGINFO = 0x53494749, NT_OPENBSD_PROCINFO = 10, NT_OPENBSD_AUXV = 11, NT_OPENBSD_REGS = 20, NT_OPENBSD_FPREGS = 21, }; namespace FREEBSD { enum { NT_PRSTATUS = 1, NT_FPREGSET, NT_PRPSINFO, NT_THRMISC = 7, NT_PROCSTAT_AUXV = 16, NT_PPC_VMX = 0x100 }; } namespace NETBSD { enum { NT_PROCINFO = 1, NT_AUXV, NT_AMD64_REGS = 33, NT_AMD64_FPREGS = 35 }; } // Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details. static void ParseFreeBSDPrStatus(ThreadData &thread_data, DataExtractor &data, ArchSpec &arch) { lldb::offset_t offset = 0; bool lp64 = (arch.GetMachine() == llvm::Triple::aarch64 || arch.GetMachine() == llvm::Triple::mips64 || arch.GetMachine() == llvm::Triple::ppc64 || arch.GetMachine() == llvm::Triple::x86_64); int pr_version = data.GetU32(&offset); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) { if (pr_version > 1) log->Printf("FreeBSD PRSTATUS unexpected version %d", pr_version); } // Skip padding, pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate if (lp64) offset += 32; else offset += 16; thread_data.signo = data.GetU32(&offset); // pr_cursig thread_data.tid = data.GetU32(&offset); // pr_pid if (lp64) offset += 4; size_t len = data.GetByteSize() - offset; thread_data.gpregset = DataExtractor(data, offset, len); } static void ParseFreeBSDThrMisc(ThreadData &thread_data, DataExtractor &data) { lldb::offset_t offset = 0; thread_data.name = data.GetCStr(&offset, 20); } static void ParseNetBSDProcInfo(ThreadData &thread_data, DataExtractor &data) { lldb::offset_t offset = 0; int version = data.GetU32(&offset); if (version != 1) return; offset += 4; thread_data.signo = data.GetU32(&offset); } static void ParseOpenBSDProcInfo(ThreadData &thread_data, DataExtractor &data) { lldb::offset_t offset = 0; int version = data.GetU32(&offset); if (version != 1) return; offset += 4; thread_data.signo = data.GetU32(&offset); } /// Parse Thread context from PT_NOTE segment and store it in the thread list /// Notes: /// 1) A PT_NOTE segment is composed of one or more NOTE entries. /// 2) NOTE Entry contains a standard header followed by variable size data. /// (see ELFNote structure) /// 3) A Thread Context in a core file usually described by 3 NOTE entries. /// a) NT_PRSTATUS - Register context /// b) NT_PRPSINFO - Process info(pid..) /// c) NT_FPREGSET - Floating point registers /// 4) The NOTE entries can be in any order /// 5) If a core file contains multiple thread contexts then there is two data /// forms /// a) Each thread context(2 or more NOTE entries) contained in its own /// segment (PT_NOTE) /// b) All thread context is stored in a single segment(PT_NOTE). /// This case is little tricker since while parsing we have to find where /// the /// new thread starts. The current implementation marks beginning of /// new thread when it finds NT_PRSTATUS or NT_PRPSINFO NOTE entry. /// For case (b) there may be either one NT_PRPSINFO per thread, or a single /// one that applies to all threads (depending on the platform type). Status ProcessElfCore::ParseThreadContextsFromNoteSegment( const elf::ELFProgramHeader *segment_header, DataExtractor segment_data) { assert(segment_header && segment_header->p_type == llvm::ELF::PT_NOTE); lldb::offset_t offset = 0; std::unique_ptr thread_data(new ThreadData); bool have_prstatus = false; bool have_prpsinfo = false; ArchSpec arch = GetArchitecture(); ELFLinuxPrPsInfo prpsinfo; ELFLinuxPrStatus prstatus; ELFLinuxSigInfo siginfo; size_t header_size; size_t len; Status error; // Loop through the NOTE entires in the segment while (offset < segment_header->p_filesz) { ELFNote note = ELFNote(); note.Parse(segment_data, &offset); // Beginning of new thread if ((note.n_type == NT_PRSTATUS && have_prstatus) || (note.n_type == NT_PRPSINFO && have_prpsinfo)) { assert(thread_data->gpregset.GetByteSize() > 0); // Add the new thread to thread list m_thread_data.push_back(*thread_data); *thread_data = ThreadData(); have_prstatus = false; have_prpsinfo = false; } size_t note_start, note_size; note_start = offset; note_size = llvm::alignTo(note.n_descsz, 4); // Store the NOTE information in the current thread DataExtractor note_data(segment_data, note_start, note_size); note_data.SetAddressByteSize( m_core_module_sp->GetArchitecture().GetAddressByteSize()); if (note.n_name == "FreeBSD") { m_os = llvm::Triple::FreeBSD; switch (note.n_type) { case FREEBSD::NT_PRSTATUS: have_prstatus = true; ParseFreeBSDPrStatus(*thread_data, note_data, arch); break; case FREEBSD::NT_FPREGSET: thread_data->fpregset = note_data; break; case FREEBSD::NT_PRPSINFO: have_prpsinfo = true; break; case FREEBSD::NT_THRMISC: ParseFreeBSDThrMisc(*thread_data, note_data); break; case FREEBSD::NT_PROCSTAT_AUXV: // FIXME: FreeBSD sticks an int at the beginning of the note m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4); break; case FREEBSD::NT_PPC_VMX: thread_data->vregset = note_data; break; default: break; } } else if (note.n_name.substr(0, 11) == "NetBSD-CORE") { // NetBSD per-thread information is stored in notes named // "NetBSD-CORE@nnn" so match on the initial part of the string. m_os = llvm::Triple::NetBSD; if (note.n_type == NETBSD::NT_PROCINFO) { ParseNetBSDProcInfo(*thread_data, note_data); } else if (note.n_type == NETBSD::NT_AUXV) { m_auxv = DataExtractor(note_data); } else if (arch.GetMachine() == llvm::Triple::x86_64 && note.n_type == NETBSD::NT_AMD64_REGS) { thread_data->gpregset = note_data; } else if (arch.GetMachine() == llvm::Triple::x86_64 && note.n_type == NETBSD::NT_AMD64_FPREGS) { thread_data->fpregset = note_data; } } else if (note.n_name.substr(0, 7) == "OpenBSD") { // OpenBSD per-thread information is stored in notes named // "OpenBSD@nnn" so match on the initial part of the string. m_os = llvm::Triple::OpenBSD; switch (note.n_type) { case NT_OPENBSD_PROCINFO: ParseOpenBSDProcInfo(*thread_data, note_data); break; case NT_OPENBSD_AUXV: m_auxv = DataExtractor(note_data); break; case NT_OPENBSD_REGS: thread_data->gpregset = note_data; break; case NT_OPENBSD_FPREGS: thread_data->fpregset = note_data; break; } } else if (note.n_name == "CORE") { switch (note.n_type) { case NT_PRSTATUS: have_prstatus = true; error = prstatus.Parse(note_data, arch); if (error.Fail()) return error; thread_data->prstatus_sig = prstatus.pr_cursig; thread_data->tid = prstatus.pr_pid; header_size = ELFLinuxPrStatus::GetSize(arch); len = note_data.GetByteSize() - header_size; thread_data->gpregset = DataExtractor(note_data, header_size, len); break; case NT_FPREGSET: // In a i386 core file NT_FPREGSET is present, but it's not the result // of the FXSAVE instruction like in 64 bit files. // The result from FXSAVE is in NT_PRXFPREG for i386 core files if (arch.GetCore() == ArchSpec::eCore_x86_64_x86_64) thread_data->fpregset = note_data; else if(arch.IsMIPS()) thread_data->fpregset = note_data; break; case NT_PRPSINFO: have_prpsinfo = true; error = prpsinfo.Parse(note_data, arch); if (error.Fail()) return error; thread_data->name = prpsinfo.pr_fname; SetID(prpsinfo.pr_pid); break; case NT_AUXV: m_auxv = DataExtractor(note_data); break; case NT_FILE: { m_nt_file_entries.clear(); lldb::offset_t offset = 0; const uint64_t count = note_data.GetAddress(&offset); note_data.GetAddress(&offset); // Skip page size for (uint64_t i = 0; i < count; ++i) { NT_FILE_Entry entry; entry.start = note_data.GetAddress(&offset); entry.end = note_data.GetAddress(&offset); entry.file_ofs = note_data.GetAddress(&offset); m_nt_file_entries.push_back(entry); } for (uint64_t i = 0; i < count; ++i) { const char *path = note_data.GetCStr(&offset); if (path && path[0]) m_nt_file_entries[i].path.SetCString(path); } } break; case NT_SIGINFO: { error = siginfo.Parse(note_data, arch); if (error.Fail()) return error; thread_data->signo = siginfo.si_signo; } break; default: break; } } else if (note.n_name == "LINUX") { switch (note.n_type) { case NT_PRXFPREG: thread_data->fpregset = note_data; } } offset += note_size; } // Add last entry in the note section if (thread_data && thread_data->gpregset.GetByteSize() > 0) { m_thread_data.push_back(*thread_data); } return error; } uint32_t ProcessElfCore::GetNumThreadContexts() { if (!m_thread_data_valid) DoLoadCore(); return m_thread_data.size(); } ArchSpec ProcessElfCore::GetArchitecture() { ObjectFileELF *core_file = (ObjectFileELF *)(m_core_module_sp->GetObjectFile()); ArchSpec arch; core_file->GetArchitecture(arch); ArchSpec target_arch = GetTarget().GetArchitecture(); if (target_arch.IsMIPS()) return target_arch; return arch; } const lldb::DataBufferSP ProcessElfCore::GetAuxvData() { const uint8_t *start = m_auxv.GetDataStart(); size_t len = m_auxv.GetByteSize(); lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len)); return buffer; } bool ProcessElfCore::GetProcessInfo(ProcessInstanceInfo &info) { info.Clear(); info.SetProcessID(GetID()); info.SetArchitecture(GetArchitecture()); lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); if (module_sp) { const bool add_exe_file_as_first_arg = false; info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), add_exe_file_as_first_arg); } return true; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp (revision 319790) @@ -1,4157 +1,4157 @@ //===-- DWARFASTParserClang.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 "DWARFASTParserClang.h" #include "DWARFCompileUnit.h" #include "DWARFDIE.h" #include "DWARFDIECollection.h" #include "DWARFDebugInfo.h" #include "DWARFDeclContext.h" #include "DWARFDefines.h" #include "SymbolFileDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "UniqueDWARFASTType.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/Args.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Target/Language.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include #include //#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN #ifdef ENABLE_DEBUG_PRINTF #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif using namespace lldb; using namespace lldb_private; DWARFASTParserClang::DWARFASTParserClang(ClangASTContext &ast) : m_ast(ast), m_die_to_decl_ctx(), m_decl_ctx_to_die() {} DWARFASTParserClang::~DWARFASTParserClang() {} static AccessType DW_ACCESS_to_AccessType(uint32_t dwarf_accessibility) { switch (dwarf_accessibility) { case DW_ACCESS_public: return eAccessPublic; case DW_ACCESS_private: return eAccessPrivate; case DW_ACCESS_protected: return eAccessProtected; default: break; } return eAccessNone; } static bool DeclKindIsCXXClass(clang::Decl::Kind decl_kind) { switch (decl_kind) { case clang::Decl::CXXRecord: case clang::Decl::ClassTemplateSpecialization: return true; default: break; } return false; } struct BitfieldInfo { uint64_t bit_size; uint64_t bit_offset; BitfieldInfo() : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {} void Clear() { bit_size = LLDB_INVALID_ADDRESS; bit_offset = LLDB_INVALID_ADDRESS; } bool IsValid() const { return (bit_size != LLDB_INVALID_ADDRESS) && (bit_offset != LLDB_INVALID_ADDRESS); } bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const { if (IsValid()) { // This bitfield info is valid, so any subsequent bitfields // must not overlap and must be at a higher bit offset than // any previous bitfield + size. return (bit_size + bit_offset) <= next_bit_offset; } else { // If the this BitfieldInfo is not valid, then any offset isOK return true; } } }; ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() { if (!m_clang_ast_importer_ap) { m_clang_ast_importer_ap.reset(new ClangASTImporter); } return *m_clang_ast_importer_ap; } TypeSP DWARFASTParserClang::ParseTypeFromDWO(const DWARFDIE &die, Log *log) { ModuleSP dwo_module_sp = die.GetContainingDWOModule(); if (dwo_module_sp) { // This type comes from an external DWO module std::vector dwo_context; die.GetDWOContext(dwo_context); TypeMap dwo_types; if (dwo_module_sp->GetSymbolVendor()->FindTypes(dwo_context, true, dwo_types)) { const size_t num_dwo_types = dwo_types.GetSize(); if (num_dwo_types == 1) { // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die TypeSP dwo_type_sp = dwo_types.GetTypeAtIndex(0); if (dwo_type_sp) { lldb_private::CompilerType dwo_type = dwo_type_sp->GetForwardCompilerType(); lldb_private::CompilerType type = GetClangASTImporter().CopyType(m_ast, dwo_type); // printf ("copied_qual_type: ast = %p, clang_type = %p, name = // '%s'\n", m_ast, copied_qual_type.getAsOpaquePtr(), // external_type->GetName().GetCString()); if (type) { SymbolFileDWARF *dwarf = die.GetDWARF(); TypeSP type_sp(new Type(die.GetID(), dwarf, dwo_type_sp->GetName(), dwo_type_sp->GetByteSize(), NULL, LLDB_INVALID_UID, Type::eEncodingInvalid, &dwo_type_sp->GetDeclaration(), type, Type::eResolveStateForward)); dwarf->GetTypeList()->Insert(type_sp); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::TagDecl *tag_decl = ClangASTContext::GetAsTagDecl(type); if (tag_decl) LinkDeclContextToDIE(tag_decl, die); else { clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(die); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); } return type_sp; } } } } } return TypeSP(); } TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, const DWARFDIE &die, Log *log, bool *type_is_new_ptr) { TypeSP type_sp; if (type_is_new_ptr) *type_is_new_ptr = false; AccessType accessibility = eAccessNone; if (die) { SymbolFileDWARF *dwarf = die.GetDWARF(); if (log) { DWARFDIE context_die; clang::DeclContext *context = GetClangDeclContextContainingDIE(die, &context_die); dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die " "0x%8.8x)) %s name = '%s')", die.GetOffset(), static_cast(context), context_die.GetOffset(), die.GetTagAsCString(), die.GetName()); } // // Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); // if (log && dwarf_cu) // { // StreamString s; // die->DumpLocation (this, dwarf_cu, s); // dwarf->GetObjectFile()->GetModule()->LogMessage (log, // "SymbolFileDwarf::%s %s", __FUNCTION__, s.GetData()); // // } Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); TypeList *type_list = dwarf->GetTypeList(); if (type_ptr == NULL) { if (type_is_new_ptr) *type_is_new_ptr = true; const dw_tag_t tag = die.Tag(); bool is_forward_declaration = false; DWARFAttributes attributes; const char *type_name_cstr = NULL; ConstString type_name_const_str; Type::ResolveState resolve_state = Type::eResolveStateUnresolved; uint64_t byte_size = 0; Declaration decl; Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; CompilerType clang_type; DWARFFormValue form_value; dw_attr_t attr; switch (tag) { case DW_TAG_typedef: case DW_TAG_base_type: case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: case DW_TAG_const_type: case DW_TAG_restrict_type: case DW_TAG_volatile_type: case DW_TAG_unspecified_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; const size_t num_attributes = die.GetAttributes(attributes); uint32_t encoding = 0; DWARFFormValue encoding_uid; if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); // Work around a bug in llvm-gcc where they give a name to a // reference type which doesn't // include the "&"... if (tag == DW_TAG_reference_type) { if (strchr(type_name_cstr, '&') == NULL) type_name_cstr = NULL; } if (type_name_cstr) type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_encoding: encoding = form_value.Unsigned(); break; case DW_AT_type: encoding_uid = form_value; break; default: case DW_AT_sibling: break; } } } } if (tag == DW_TAG_typedef && encoding_uid.IsValid()) { // Try to parse a typedef from the DWO file first as modules // can contain typedef'ed structures that have no names like: // // typedef struct { int a; } Foo; // // In this case we will have a structure with no name and a // typedef named "Foo" that points to this unnamed structure. // The name in the typedef is the only identifier for the struct, // so always try to get typedefs from DWO files if possible. // // The type_sp returned will be empty if the typedef doesn't exist // in a DWO file, so it is cheap to call this function just to check. // // If we don't do this we end up creating a TypeSP that says this // is a typedef to type 0x123 (the DW_AT_type value would be 0x123 // in the DW_TAG_typedef), and this is the unnamed structure type. // We will have a hard time tracking down an unnammed structure // type in the module DWO file, so we make sure we don't get into // this situation by always resolving typedefs from the DWO file. const DWARFDIE encoding_die = dwarf->GetDIE(DIERef(encoding_uid)); // First make sure that the die that this is typedef'ed to _is_ // just a declaration (DW_AT_declaration == 1), not a full definition // since template types can't be represented in modules since only // concrete instances of templates are ever emitted and modules // won't contain those if (encoding_die && encoding_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8lx\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr, encoding_uid.Reference()); switch (tag) { default: break; case DW_TAG_unspecified_type: if (strcmp(type_name_cstr, "nullptr_t") == 0 || strcmp(type_name_cstr, "decltype(nullptr)") == 0) { resolve_state = Type::eResolveStateFull; clang_type = m_ast.GetBasicType(eBasicTypeNullPtr); break; } // Fall through to base type below in case we can handle the type // there... LLVM_FALLTHROUGH; case DW_TAG_base_type: resolve_state = Type::eResolveStateFull; clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( type_name_cstr, encoding, byte_size * 8); break; case DW_TAG_pointer_type: encoding_data_type = Type::eEncodingIsPointerUID; break; case DW_TAG_reference_type: encoding_data_type = Type::eEncodingIsLValueReferenceUID; break; case DW_TAG_rvalue_reference_type: encoding_data_type = Type::eEncodingIsRValueReferenceUID; break; case DW_TAG_typedef: encoding_data_type = Type::eEncodingIsTypedefUID; break; case DW_TAG_const_type: encoding_data_type = Type::eEncodingIsConstUID; break; case DW_TAG_restrict_type: encoding_data_type = Type::eEncodingIsRestrictUID; break; case DW_TAG_volatile_type: encoding_data_type = Type::eEncodingIsVolatileUID; break; } if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID || encoding_data_type == Type::eEncodingIsTypedefUID) && sc.comp_unit != NULL) { if (tag == DW_TAG_pointer_type) { DWARFDIE target_die = die.GetReferencedDIE(DW_AT_type); if (target_die.GetAttributeValueAsUnsigned(DW_AT_APPLE_block, 0)) { // Blocks have a __FuncPtr inside them which is a pointer to a // function of the proper type. for (DWARFDIE child_die = target_die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { if (!strcmp(child_die.GetAttributeValueAsString(DW_AT_name, ""), "__FuncPtr")) { DWARFDIE function_pointer_type = child_die.GetReferencedDIE(DW_AT_type); if (function_pointer_type) { DWARFDIE function_type = function_pointer_type.GetReferencedDIE(DW_AT_type); bool function_type_is_new_pointer; TypeSP lldb_function_type_sp = ParseTypeFromDWARF( sc, function_type, log, &function_type_is_new_pointer); if (lldb_function_type_sp) { clang_type = m_ast.CreateBlockPointerType( lldb_function_type_sp->GetForwardCompilerType()); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } break; } } } } bool translation_unit_is_objc = (sc.comp_unit->GetLanguage() == eLanguageTypeObjC || sc.comp_unit->GetLanguage() == eLanguageTypeObjC_plus_plus); if (translation_unit_is_objc) { if (type_name_cstr != NULL) { static ConstString g_objc_type_name_id("id"); static ConstString g_objc_type_name_Class("Class"); static ConstString g_objc_type_name_selector("SEL"); if (type_name_const_str == g_objc_type_name_id) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'id' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCID); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } else if (type_name_const_str == g_objc_type_name_Class) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'Class' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCClass); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } else if (type_name_const_str == g_objc_type_name_selector) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' " "is Objective C 'selector' built-in type.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCSel); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } else if (encoding_data_type == Type::eEncodingIsPointerUID && encoding_uid.IsValid()) { // Clang sometimes erroneously emits id as objc_object*. In that // case we fix up the type to "id". const DWARFDIE encoding_die = dwarf->GetDIE(DIERef(encoding_uid)); if (encoding_die && encoding_die.Tag() == DW_TAG_structure_type) { if (const char *struct_name = encoding_die.GetName()) { if (!strcmp(struct_name, "objc_object")) { if (log) dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s " "'%s' is 'objc_object*', which we overrode to " "'id'.", die.GetOffset(), die.GetTagAsCString(), die.GetName()); clang_type = m_ast.GetBasicType(eBasicTypeObjCID); encoding_data_type = Type::eEncodingIsUID; encoding_uid.Clear(); resolve_state = Type::eResolveStateFull; } } } } } } type_sp.reset( new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, DIERef(encoding_uid).GetUID(dwarf), encoding_data_type, &decl, clang_type, resolve_state)); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); // Type* encoding_type = // GetUniquedTypeForDIEOffset(encoding_uid, type_sp, // NULL, 0, 0, false); // if (encoding_type != NULL) // { // if (encoding_type != DIE_IS_BEING_PARSED) // type_sp->SetEncodingType(encoding_type); // else // m_indirect_fixups.push_back(type_sp.get()); // } } break; case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; bool byte_size_valid = false; LanguageType class_language = eLanguageTypeUnknown; bool is_complete_objc_class = false; // bool struct_is_class = false; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: if (die.GetCU()->DW_AT_decl_file_attributes_are_invalid()) { // llvm-gcc outputs invalid DW_AT_decl_file attributes that // always // point to the compile unit file, so we clear this invalid // value // so that we can still unique types efficiently. decl.SetFile(FileSpec("", false)); } else decl.SetFile( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); byte_size_valid = true; break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: is_forward_declaration = form_value.Boolean(); break; case DW_AT_APPLE_runtime_class: class_language = (LanguageType)form_value.Signed(); break; case DW_AT_APPLE_objc_complete_type: is_complete_objc_class = form_value.Signed(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_description: case DW_AT_start_scope: case DW_AT_visibility: default: case DW_AT_sibling: break; } } } } // UniqueDWARFASTType is large, so don't create a local variables on the // stack, put it on the heap. This function is often called recursively // and clang isn't good and sharing the stack space for variables in // different blocks. std::unique_ptr unique_ast_entry_ap( new UniqueDWARFASTType()); ConstString unique_typename(type_name_const_str); Declaration unique_decl(decl); if (type_name_const_str) { LanguageType die_language = die.GetLanguage(); if (Language::LanguageIsCPlusPlus(die_language)) { // For C++, we rely solely upon the one definition rule that says // only // one thing can exist at a given decl context. We ignore the file // and // line that things are declared on. std::string qualified_name; if (die.GetQualifiedName(qualified_name)) unique_typename = ConstString(qualified_name); unique_decl.Clear(); } if (dwarf->GetUniqueDWARFASTTypeMap().Find( unique_typename, die, unique_decl, byte_size_valid ? byte_size : -1, *unique_ast_entry_ap)) { type_sp = unique_ast_entry_ap->m_type_sp; if (type_sp) { dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); int tag_decl_kind = -1; AccessType default_accessibility = eAccessNone; if (tag == DW_TAG_structure_type) { tag_decl_kind = clang::TTK_Struct; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_union_type) { tag_decl_kind = clang::TTK_Union; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_class_type) { tag_decl_kind = clang::TTK_Class; default_accessibility = eAccessPrivate; } if (byte_size_valid && byte_size == 0 && type_name_cstr && die.HasChildren() == false && sc.comp_unit->GetLanguage() == eLanguageTypeObjC) { // Work around an issue with clang at the moment where // forward declarations for objective C classes are emitted // as: // DW_TAG_structure_type [2] // DW_AT_name( "ForwardObjcClass" ) // DW_AT_byte_size( 0x00 ) // DW_AT_decl_file( "..." ) // DW_AT_decl_line( 1 ) // // Note that there is no DW_AT_declaration and there are // no children, and the byte size is zero. is_forward_declaration = true; } if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) { if (!is_complete_objc_class && die.Supports_DW_AT_APPLE_objc_complete_type()) { // We have a valid eSymbolTypeObjCClass class symbol whose // name matches the current objective C class that we // are trying to find and this DIE isn't the complete // definition (we checked is_complete_objc_class above and // know it is false), so the real definition is in here somewhere type_sp = dwarf->FindCompleteObjCDefinitionTypeForDIE( die, type_name_const_str, true); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE( die, type_name_const_str, true); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an " "incomplete objc type, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } } } if (is_forward_declaration) { // We have a forward declaration to a type and we need // to try and find a full declaration. We look in the // current type index just in case we have a forward // declaration followed by an actual declarations in the // DWARF. If this fails, we need to look elsewhere... if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, trying to find complete type", static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr); } // See if the type comes from a DWO module and if so, track down that // type. type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; DWARFDeclContext die_decl_ctx; die.GetDWARFDeclContext(die_decl_ctx); // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die, // type_name_const_str); type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( die_decl_ctx); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE( dwarf->DebugInfo()->GetDIE(DIERef(type_sp->GetID(), dwarf))); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); return type_sp; } } assert(tag_decl_kind != -1); bool clang_type_was_created = false; clang_type.SetCompilerType( &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); if (!clang_type) { clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); if (accessibility == eAccessNone && decl_ctx) { // Check the decl context that contains this class/struct/union. // If it is a class we must give it an accessibility. const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind(); if (DeclKindIsCXXClass(containing_decl_kind)) accessibility = default_accessibility; } ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die)); if (type_name_cstr && strchr(type_name_cstr, '<')) { ClangASTContext::TemplateParameterInfos template_param_infos; if (ParseTemplateParameterInfos(die, template_param_infos)) { clang::ClassTemplateDecl *class_template_decl = m_ast.ParseClassTemplateDecl(decl_ctx, accessibility, type_name_cstr, tag_decl_kind, template_param_infos); clang::ClassTemplateSpecializationDecl *class_specialization_decl = m_ast.CreateClassTemplateSpecializationDecl( decl_ctx, class_template_decl, tag_decl_kind, template_param_infos); clang_type = m_ast.CreateClassTemplateSpecializationType( class_specialization_decl); clang_type_was_created = true; m_ast.SetMetadata(class_template_decl, metadata); m_ast.SetMetadata(class_specialization_decl, metadata); } } if (!clang_type_was_created) { clang_type_was_created = true; clang_type = m_ast.CreateRecordType(decl_ctx, accessibility, type_name_cstr, tag_decl_kind, class_language, &metadata); } } // Store a forward declaration to this class type in case any // parameters in any class methods need it for the clang // types for function prototypes. LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateForward)); type_sp->SetIsCompleteObjCClass(is_complete_objc_class); // Add our type to the unique type map so we don't // end up creating many copies of the same type over // and over in the ASTContext for our module unique_ast_entry_ap->m_type_sp = type_sp; unique_ast_entry_ap->m_die = die; unique_ast_entry_ap->m_declaration = unique_decl; unique_ast_entry_ap->m_byte_size = byte_size; dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename, *unique_ast_entry_ap); if (is_forward_declaration && die.HasChildren()) { // Check to see if the DIE actually has a definition, some version of // GCC will // emit DIEs with DW_AT_declaration set to true, but yet still have // subprogram, // members, or inheritance, so we can't trust it DWARFDIE child_die = die.GetFirstChild(); while (child_die) { switch (child_die.Tag()) { case DW_TAG_inheritance: case DW_TAG_subprogram: case DW_TAG_member: case DW_TAG_APPLE_property: case DW_TAG_class_type: case DW_TAG_structure_type: case DW_TAG_enumeration_type: case DW_TAG_typedef: case DW_TAG_union_type: child_die.Clear(); is_forward_declaration = false; break; default: child_die = child_die.GetSibling(); break; } } } if (!is_forward_declaration) { // Always start the definition for a class type so that // if the class has child classes or types that require // the class to be created for use as their decl contexts // the class will be ready to accept these child definitions. if (die.HasChildren() == false) { // No children for this struct/union/class, lets finish it if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", die.GetOffset(), type_name_cstr); } if (tag == DW_TAG_structure_type) // this only applies in C { clang::RecordDecl *record_decl = ClangASTContext::GetAsRecordDecl(clang_type); if (record_decl) { GetClangASTImporter().InsertRecordDecl( record_decl, ClangASTImporter::LayoutInfo()); } } } else if (clang_type_was_created) { // Start the definition if the class is not objective C since // the underlying decls respond to isCompleteDefinition(). Objective // C decls don't respond to isCompleteDefinition() so we can't // start the declaration definition right away. For C++ // class/union/structs // we want to start the definition in case the class is needed as // the // declaration context for a contained class or type without the // need // to complete that type.. if (class_language != eLanguageTypeObjC && class_language != eLanguageTypeObjC_plus_plus) ClangASTContext::StartTagDeclarationDefinition(clang_type); // Leave this as a forward declaration until we need // to know the details of the type. lldb_private::Type // will automatically call the SymbolFile virtual function // "SymbolFileDWARF::CompleteType(Type *)" // When the definition needs to be defined. assert(!dwarf->GetForwardDeclClangTypeToDie().count( ClangUtil::RemoveFastQualifiers(clang_type) .GetOpaqueQualType()) && "Type already in the forward declaration map!"); // Can't assume m_ast.GetSymbolFile() is actually a SymbolFileDWARF, // it can be a // SymbolFileDWARFDebugMap for Apple binaries. dwarf->GetForwardDeclDieToClangType()[die.GetDIE()] = clang_type.GetOpaqueQualType(); dwarf->GetForwardDeclClangTypeToDie() [ClangUtil::RemoveFastQualifiers(clang_type) .GetOpaqueQualType()] = die.GetDIERef(); m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } } break; case DW_TAG_enumeration_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue encoding_form; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: is_forward_declaration = form_value.Boolean(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_bit_stride: case DW_AT_byte_stride: case DW_AT_data_location: case DW_AT_description: case DW_AT_start_scope: case DW_AT_visibility: case DW_AT_specification: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } if (is_forward_declaration) { type_sp = ParseTypeFromDWO(die, log); if (type_sp) return type_sp; DWARFDeclContext die_decl_ctx; die.GetDWARFDeclContext(die_decl_ctx); type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in // this DWARF, see if we have a declaration anywhere // else... type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext( die_decl_ctx); } } if (type_sp) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a " "forward declaration, complete type is 0x%8.8" PRIx64, static_cast(this), die.GetOffset(), DW_TAG_value_to_name(tag), type_name_cstr, type_sp->GetID()); } // We found a real definition for this type elsewhere // so lets use it and cache the fact that we found // a complete type for this die dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::DeclContext *defn_decl_ctx = GetCachedClangDeclContextForDIE(dwarf->DebugInfo()->GetDIE( DIERef(type_sp->GetID(), dwarf))); if (defn_decl_ctx) LinkDeclContextToDIE(defn_decl_ctx, die); return type_sp; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); CompilerType enumerator_clang_type; clang_type.SetCompilerType( &m_ast, dwarf->GetForwardDeclDieToClangType().lookup(die.GetDIE())); if (!clang_type) { if (encoding_form.IsValid()) { Type *enumerator_type = dwarf->ResolveTypeUID(DIERef(encoding_form)); if (enumerator_type) enumerator_clang_type = enumerator_type->GetFullCompilerType(); } if (!enumerator_clang_type) { if (byte_size > 0) { enumerator_clang_type = m_ast.GetBuiltinTypeForDWARFEncodingAndBitSize( NULL, DW_ATE_signed, byte_size * 8); } else { enumerator_clang_type = m_ast.GetBasicType(eBasicTypeInt); } } clang_type = m_ast.CreateEnumerationType( type_name_cstr, GetClangDeclContextContainingDIE(die, nullptr), decl, enumerator_clang_type); } else { enumerator_clang_type = m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType()); } LinkDeclContextToDIE( ClangASTContext::GetDeclContextForType(clang_type), die); type_sp.reset(new Type( die.GetID(), dwarf, type_name_const_str, byte_size, NULL, DIERef(encoding_form).GetUID(dwarf), Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateForward)); if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { if (die.HasChildren()) { SymbolContext cu_sc(die.GetLLDBCompileUnit()); bool is_signed = false; enumerator_clang_type.IsIntegerType(is_signed); ParseChildEnumerators(cu_sc, clang_type, is_signed, type_sp->GetByteSize(), die); } ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "DWARF DIE at 0x%8.8x named \"%s\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", die.GetOffset(), type_name_cstr); } } } break; case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: case DW_TAG_subroutine_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue type_die_form; bool is_variadic = false; bool is_inline = false; bool is_static = false; bool is_virtual = false; bool is_explicit = false; bool is_artificial = false; bool has_template_params = false; DWARFFormValue specification_die_form; DWARFFormValue abstract_origin_die_form; dw_offset_t object_pointer_die_offset = DW_INVALID_OFFSET; unsigned type_quals = 0; clang::StorageClass storage = clang::SC_None; //, Extern, Static, PrivateExtern const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: break; // mangled = // form_value.AsCString(&dwarf->get_debug_str_data()); // break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; case DW_AT_inline: is_inline = form_value.Boolean(); break; case DW_AT_virtuality: is_virtual = form_value.Boolean(); break; case DW_AT_explicit: is_explicit = form_value.Boolean(); break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_external: if (form_value.Unsigned()) { if (storage == clang::SC_None) storage = clang::SC_Extern; else storage = clang::SC_PrivateExtern; } break; case DW_AT_specification: specification_die_form = form_value; break; case DW_AT_abstract_origin: abstract_origin_die_form = form_value; break; case DW_AT_object_pointer: object_pointer_die_offset = form_value.Reference(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_address_class: case DW_AT_calling_convention: case DW_AT_data_location: case DW_AT_elemental: case DW_AT_entry_pc: case DW_AT_frame_base: case DW_AT_high_pc: case DW_AT_low_pc: case DW_AT_prototyped: case DW_AT_pure: case DW_AT_ranges: case DW_AT_recursive: case DW_AT_return_addr: case DW_AT_segment: case DW_AT_start_scope: case DW_AT_static_link: case DW_AT_trampoline: case DW_AT_visibility: case DW_AT_vtable_elem_location: case DW_AT_description: case DW_AT_sibling: break; } } } } std::string object_pointer_name; if (object_pointer_die_offset != DW_INVALID_OFFSET) { DWARFDIE object_pointer_die = die.GetDIE(object_pointer_die_offset); if (object_pointer_die) { const char *object_pointer_name_cstr = object_pointer_die.GetName(); if (object_pointer_name_cstr) object_pointer_name = object_pointer_name_cstr; } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); CompilerType return_clang_type; Type *func_type = NULL; if (type_die_form.IsValid()) func_type = dwarf->ResolveTypeUID(DIERef(type_die_form)); if (func_type) return_clang_type = func_type->GetForwardCompilerType(); else return_clang_type = m_ast.GetBasicType(eBasicTypeVoid); std::vector function_param_types; std::vector function_param_decls; // Parse the function children for the parameters DWARFDIE decl_ctx_die; clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, &decl_ctx_die); const clang::Decl::Kind containing_decl_kind = containing_decl_ctx->getDeclKind(); bool is_cxx_method = DeclKindIsCXXClass(containing_decl_kind); // Start off static. This will be set to false in // ParseChildParameters(...) // if we find a "this" parameters as the first parameter if (is_cxx_method) { is_static = true; } if (die.HasChildren()) { bool skip_artificial = true; ParseChildParameters(sc, containing_decl_ctx, die, skip_artificial, is_static, is_variadic, has_template_params, function_param_types, function_param_decls, type_quals); } bool ignore_containing_context = false; // Check for templatized class member functions. If we had any // DW_TAG_template_type_parameter // or DW_TAG_template_value_parameter the DW_TAG_subprogram DIE, then we // can't let this become // a method in a class. Why? Because templatized functions are only // emitted if one of the // templatized methods is used in the current compile unit and we will // end up with classes // that may or may not include these member functions and this means one // class won't match another // class definition and it affects our ability to use a class in the // clang expression parser. So // for the greater good, we currently must not allow any template member // functions in a class definition. if (is_cxx_method && has_template_params) { ignore_containing_context = true; is_cxx_method = false; } // clang_type will get the function prototype clang type after this call clang_type = m_ast.CreateFunctionType( return_clang_type, function_param_types.data(), function_param_types.size(), is_variadic, type_quals); if (type_name_cstr) { bool type_handled = false; if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) { ObjCLanguage::MethodName objc_method(type_name_cstr, true); if (objc_method.IsValid(true)) { CompilerType class_opaque_type; ConstString class_name(objc_method.GetClassName()); if (class_name) { TypeSP complete_objc_class_type_sp( dwarf->FindCompleteObjCDefinitionTypeForDIE( DWARFDIE(), class_name, false)); if (complete_objc_class_type_sp) { CompilerType type_clang_forward_type = complete_objc_class_type_sp->GetForwardCompilerType(); if (ClangASTContext::IsObjCObjectOrInterfaceType( type_clang_forward_type)) class_opaque_type = type_clang_forward_type; } } if (class_opaque_type) { // If accessibility isn't set to anything valid, assume public // for // now... if (accessibility == eAccessNone) accessibility = eAccessPublic; clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( class_opaque_type, type_name_cstr, clang_type, accessibility, is_artificial, is_variadic); type_handled = objc_method_decl != NULL; if (type_handled) { LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext(objc_method_decl), die); m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); } else { dwarf->GetObjectFile()->GetModule()->ReportError( "{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), " "please file a bug and attach the file at the start of " "this error message", die.GetOffset(), tag, DW_TAG_value_to_name(tag)); } } } else if (is_cxx_method) { // Look at the parent of this DIE and see if is is // a class or struct and see if this is actually a // C++ method Type *class_type = dwarf->ResolveType(decl_ctx_die); if (class_type) { bool alternate_defn = false; if (class_type->GetID() != decl_ctx_die.GetID() || decl_ctx_die.GetContainingDWOModuleDIE()) { alternate_defn = true; // We uniqued the parent class of this function to another // class // so we now need to associate all dies under "decl_ctx_die" // to // DIEs in the DIE for "class_type"... SymbolFileDWARF *class_symfile = NULL; DWARFDIE class_type_die; SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { class_symfile = debug_map_symfile->GetSymbolFileByOSOIndex( SymbolFileDWARFDebugMap::GetOSOIndexFromUserID( class_type->GetID())); class_type_die = class_symfile->DebugInfo()->GetDIE( DIERef(class_type->GetID(), dwarf)); } else { class_symfile = dwarf; class_type_die = dwarf->DebugInfo()->GetDIE( DIERef(class_type->GetID(), dwarf)); } if (class_type_die) { DWARFDIECollection failures; CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die, class_type, failures); // FIXME do something with these failures that's smarter // than // just dropping them on the ground. Unfortunately classes // don't // like having stuff added to them after their definitions // are // complete... type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); break; } } } if (specification_die_form.IsValid()) { // We have a specification which we are going to base our // function // prototype off of, so we need this type to be completed so // that the // m_die_to_decl_ctx for the method in the specification has a // valid // clang decl context. class_type->GetForwardCompilerType(); // If we have a specification, then the function type should // have been // made with the specification and not with this die. DWARFDIE spec_die = dwarf->DebugInfo()->GetDIE( DIERef(specification_die_form)); clang::DeclContext *spec_clang_decl_ctx = GetClangDeclContextForDIE(spec_die); if (spec_clang_decl_ctx) { LinkDeclContextToDIE(spec_clang_decl_ctx, die); } else { dwarf->GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8" PRIx64 ") has no decl\n", die.GetID(), specification_die_form.Reference()); } type_handled = true; } else if (abstract_origin_die_form.IsValid()) { // We have a specification which we are going to base our // function // prototype off of, so we need this type to be completed so // that the // m_die_to_decl_ctx for the method in the abstract origin has // a valid // clang decl context. class_type->GetForwardCompilerType(); DWARFDIE abs_die = dwarf->DebugInfo()->GetDIE( DIERef(abstract_origin_die_form)); clang::DeclContext *abs_clang_decl_ctx = GetClangDeclContextForDIE(abs_die); if (abs_clang_decl_ctx) { LinkDeclContextToDIE(abs_clang_decl_ctx, die); } else { dwarf->GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8" PRIx64 ") has no decl\n", die.GetID(), abstract_origin_die_form.Reference()); } type_handled = true; } else { CompilerType class_opaque_type = class_type->GetForwardCompilerType(); if (ClangASTContext::IsCXXClassType(class_opaque_type)) { if (class_opaque_type.IsBeingDefined() || alternate_defn) { if (!is_static && !die.HasChildren()) { // We have a C++ member function with no children (this // pointer!) // and clang will get mad if we try and make a function // that isn't // well formed in the DWARF, so we will just skip it... type_handled = true; } else { bool add_method = true; if (alternate_defn) { // If an alternate definition for the class exists, // then add the method only if an // equivalent is not already present. clang::CXXRecordDecl *record_decl = m_ast.GetAsCXXRecordDecl( class_opaque_type.GetOpaqueQualType()); if (record_decl) { for (auto method_iter = record_decl->method_begin(); method_iter != record_decl->method_end(); method_iter++) { clang::CXXMethodDecl *method_decl = *method_iter; if (method_decl->getNameInfo().getAsString() == std::string(type_name_cstr)) { if (method_decl->getType() == ClangUtil::GetQualType(clang_type)) { add_method = false; LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext( method_decl), die); type_handled = true; break; } } } } } if (add_method) { llvm::PrettyStackTraceFormat stack_trace( "SymbolFileDWARF::ParseType() is adding a method " "%s to class %s in DIE 0x%8.8" PRIx64 " from %s", type_name_cstr, class_type->GetName().GetCString(), die.GetID(), dwarf->GetObjectFile() ->GetFileSpec() .GetPath() .c_str()); const bool is_attr_used = false; // Neither GCC 4.2 nor clang++ currently set a valid // accessibility // in the DWARF for C++ methods... Default to public // for now... if (accessibility == eAccessNone) accessibility = eAccessPublic; clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( class_opaque_type.GetOpaqueQualType(), type_name_cstr, clang_type, accessibility, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); type_handled = cxx_method_decl != NULL; if (type_handled) { LinkDeclContextToDIE( ClangASTContext::GetAsDeclContext( cxx_method_decl), die); ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); if (!object_pointer_name.empty()) { metadata.SetObjectPtrName( object_pointer_name.c_str()); if (log) log->Printf( "Setting object pointer name: %s on method " "object %p.\n", object_pointer_name.c_str(), static_cast(cxx_method_decl)); } m_ast.SetMetadata(cxx_method_decl, metadata); } else { ignore_containing_context = true; } } } } else { // We were asked to parse the type for a method in a // class, yet the // class hasn't been asked to complete itself through the // clang::ExternalASTSource protocol, so we need to just // have the // class complete itself and do things the right way, then // our // DIE should then have an entry in the // dwarf->GetDIEToType() map. First // we need to modify the dwarf->GetDIEToType() so it // doesn't think we are // trying to parse this DIE anymore... dwarf->GetDIEToType()[die.GetDIE()] = NULL; // Now we get the full type to force our class type to // complete itself // using the clang::ExternalASTSource protocol which will // parse all // base classes and all methods (including the method for // this DIE). class_type->GetFullCompilerType(); // The type for this DIE should have been filled in the // function call above type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); break; } // FIXME This is fixing some even uglier behavior but we // really need to // uniq the methods of each class as well as the class // itself. // type_handled = true; } } } } } } if (!type_handled) { clang::FunctionDecl *function_decl = nullptr; if (abstract_origin_die_form.IsValid()) { DWARFDIE abs_die = dwarf->DebugInfo()->GetDIE(DIERef(abstract_origin_die_form)); SymbolContext sc; if (dwarf->ResolveType(abs_die)) { function_decl = llvm::dyn_cast_or_null( GetCachedClangDeclContextForDIE(abs_die)); if (function_decl) { LinkDeclContextToDIE(function_decl, die); } } } if (!function_decl) { // We just have a function that isn't part of a class function_decl = m_ast.CreateFunctionDeclaration( ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, type_name_cstr, clang_type, storage, is_inline); if (has_template_params) { ClangASTContext::TemplateParameterInfos template_param_infos; ParseTemplateParameterInfos(die, template_param_infos); clang::FunctionTemplateDecl *func_template_decl = m_ast.CreateFunctionTemplateDecl( containing_decl_ctx, function_decl, type_name_cstr, template_param_infos); m_ast.CreateFunctionTemplateSpecializationInfo( function_decl, func_template_decl, template_param_infos); } lldbassert(function_decl); if (function_decl) { LinkDeclContextToDIE(function_decl, die); if (!function_param_decls.empty()) m_ast.SetFunctionParameters(function_decl, &function_param_decls.front(), function_param_decls.size()); ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); if (!object_pointer_name.empty()) { metadata.SetObjectPtrName(object_pointer_name.c_str()); if (log) log->Printf("Setting object pointer name: %s on function " "object %p.", object_pointer_name.c_str(), static_cast(function_decl)); } m_ast.SetMetadata(function_decl, metadata); } } } } type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, 0, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateFull)); assert(type_sp.get()); } break; case DW_TAG_array_type: { // Set a bit that lets us know that we are currently parsing this dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; DWARFFormValue type_die_form; int64_t first_index = 0; uint32_t byte_stride = 0; uint32_t bit_stride = 0; bool is_vector = false; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: type_name_cstr = form_value.AsCString(); type_name_const_str.SetCString(type_name_cstr); break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_byte_size: break; // byte_size = form_value.Unsigned(); break; case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; case DW_AT_GNU_vector: is_vector = form_value.Boolean(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_description: case DW_AT_ordering: case DW_AT_start_scope: case DW_AT_visibility: case DW_AT_specification: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); DIERef type_die_ref(type_die_form); Type *element_type = dwarf->ResolveTypeUID(type_die_ref); if (element_type) { std::vector element_orders; ParseChildArrayInfo(sc, die, first_index, element_orders, byte_stride, bit_stride); if (byte_stride == 0 && bit_stride == 0) byte_stride = element_type->GetByteSize(); CompilerType array_element_type = element_type->GetForwardCompilerType(); if (ClangASTContext::IsCXXClassType(array_element_type) && array_element_type.GetCompleteType() == false) { ModuleSP module_sp = die.GetModule(); if (module_sp) { if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module_sp->ReportError( "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " "class/union/struct element type DIE 0x%8.8x that is a " "forward declaration, not a complete definition.\nTry " "compiling the source file with -fno-limit-debug-info or " "disable -gmodule", die.GetOffset(), type_die_ref.die_offset); else module_sp->ReportError( "DWARF DW_TAG_array_type DIE at 0x%8.8x has a " "class/union/struct element type DIE 0x%8.8x that is a " "forward declaration, not a complete definition.\nPlease " "file a bug against the compiler and include the " "preprocessed output for %s", die.GetOffset(), type_die_ref.die_offset, die.GetLLDBCompileUnit() ? die.GetLLDBCompileUnit()->GetPath().c_str() : "the source file"); } // We have no choice other than to pretend that the element class // type // is complete. If we don't do this, clang will crash when trying // to layout the class. Since we provide layout assistance, all // ivars in this class and other classes will be fine, this is // the best we can do short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( array_element_type)) { ClangASTContext::CompleteTagDeclarationDefinition( array_element_type); } else { module_sp->ReportError("DWARF DIE at 0x%8.8x was not able to " "start its definition.\nPlease file a " "bug and attach the file at the start " "of this error message", type_die_ref.die_offset); } } uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; if (element_orders.size() > 0) { uint64_t num_elements = 0; std::vector::const_reverse_iterator pos; std::vector::const_reverse_iterator end = element_orders.rend(); for (pos = element_orders.rbegin(); pos != end; ++pos) { num_elements = *pos; clang_type = m_ast.CreateArrayType(array_element_type, num_elements, is_vector); array_element_type = clang_type; array_element_bit_stride = num_elements ? array_element_bit_stride * num_elements : array_element_bit_stride; } } else { clang_type = m_ast.CreateArrayType(array_element_type, 0, is_vector); } ConstString empty_name; type_sp.reset(new Type( die.GetID(), dwarf, empty_name, array_element_bit_stride / 8, NULL, DIERef(type_die_form).GetUID(dwarf), Type::eEncodingIsUID, &decl, clang_type, Type::eResolveStateFull)); type_sp->SetEncodingType(element_type); } } } break; case DW_TAG_ptr_to_member_type: { DWARFFormValue type_die_form; DWARFFormValue containing_type_die_form; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { uint32_t i; for (i = 0; i < num_attributes; ++i) { attr = attributes.AttributeAtIndex(i); if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_type: type_die_form = form_value; break; case DW_AT_containing_type: containing_type_die_form = form_value; break; } } } Type *pointee_type = dwarf->ResolveTypeUID(DIERef(type_die_form)); Type *class_type = dwarf->ResolveTypeUID(DIERef(containing_type_die_form)); CompilerType pointee_clang_type = pointee_type->GetForwardCompilerType(); CompilerType class_clang_type = class_type->GetLayoutCompilerType(); clang_type = ClangASTContext::CreateMemberPointerType( class_clang_type, pointee_clang_type); byte_size = clang_type.GetByteSize(nullptr); type_sp.reset(new Type(die.GetID(), dwarf, type_name_const_str, byte_size, NULL, LLDB_INVALID_UID, Type::eEncodingIsUID, NULL, clang_type, Type::eResolveStateForward)); } break; } default: dwarf->GetObjectFile()->GetModule()->ReportError( "{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and " "attach the file at the start of this error message", die.GetOffset(), tag, DW_TAG_value_to_name(tag)); break; } if (type_sp.get()) { DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die); dw_tag_t sc_parent_tag = sc_parent_die.Tag(); SymbolContextScope *symbol_context_scope = NULL; if (sc_parent_tag == DW_TAG_compile_unit) { symbol_context_scope = sc.comp_unit; } else if (sc.function != NULL && sc_parent_die) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(sc_parent_die.GetID()); if (symbol_context_scope == NULL) symbol_context_scope = sc.function; } if (symbol_context_scope != NULL) { type_sp->SetSymbolContextScope(symbol_context_scope); } // We are ready to put this type into the uniqued list up at the module // level type_list->Insert(type_sp); dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); } } else if (type_ptr != DIE_IS_BEING_PARSED) { type_sp = type_ptr->shared_from_this(); } } return type_sp; } // DWARF parsing functions class DWARFASTParserClang::DelayedAddObjCClassProperty { public: DelayedAddObjCClassProperty( const CompilerType &class_opaque_type, const char *property_name, const CompilerType &property_opaque_type, // The property type is only // required if you don't have an // ivar decl clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name, const char *property_getter_name, uint32_t property_attributes, const ClangASTMetadata *metadata) : m_class_opaque_type(class_opaque_type), m_property_name(property_name), m_property_opaque_type(property_opaque_type), m_ivar_decl(ivar_decl), m_property_setter_name(property_setter_name), m_property_getter_name(property_getter_name), m_property_attributes(property_attributes) { if (metadata != NULL) { m_metadata_ap.reset(new ClangASTMetadata()); *m_metadata_ap = *metadata; } } DelayedAddObjCClassProperty(const DelayedAddObjCClassProperty &rhs) { *this = rhs; } DelayedAddObjCClassProperty & operator=(const DelayedAddObjCClassProperty &rhs) { m_class_opaque_type = rhs.m_class_opaque_type; m_property_name = rhs.m_property_name; m_property_opaque_type = rhs.m_property_opaque_type; m_ivar_decl = rhs.m_ivar_decl; m_property_setter_name = rhs.m_property_setter_name; m_property_getter_name = rhs.m_property_getter_name; m_property_attributes = rhs.m_property_attributes; if (rhs.m_metadata_ap.get()) { m_metadata_ap.reset(new ClangASTMetadata()); *m_metadata_ap = *rhs.m_metadata_ap; } return *this; } bool Finalize() { return ClangASTContext::AddObjCClassProperty( m_class_opaque_type, m_property_name, m_property_opaque_type, m_ivar_decl, m_property_setter_name, m_property_getter_name, m_property_attributes, m_metadata_ap.get()); } private: CompilerType m_class_opaque_type; const char *m_property_name; CompilerType m_property_opaque_type; clang::ObjCIvarDecl *m_ivar_decl; const char *m_property_setter_name; const char *m_property_getter_name; uint32_t m_property_attributes; std::unique_ptr m_metadata_ap; }; bool DWARFASTParserClang::ParseTemplateDIE( const DWARFDIE &die, ClangASTContext::TemplateParameterInfos &template_param_infos) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_GNU_template_parameter_pack: { template_param_infos.packed_args.reset( new ClangASTContext::TemplateParameterInfos); for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { if (!ParseTemplateDIE(child_die, *template_param_infos.packed_args)) return false; } if (const char *name = die.GetName()) { template_param_infos.pack_name = name; } return true; } case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); const char *name = nullptr; CompilerType clang_type; uint64_t uval64 = 0; bool uval64_valid = false; if (num_attributes > 0) { DWARFFormValue form_value; for (size_t i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); switch (attr) { case DW_AT_name: if (attributes.ExtractFormValueAtIndex(i, form_value)) name = form_value.AsCString(); break; case DW_AT_type: if (attributes.ExtractFormValueAtIndex(i, form_value)) { Type *lldb_type = die.ResolveTypeUID(DIERef(form_value)); if (lldb_type) clang_type = lldb_type->GetForwardCompilerType(); } break; case DW_AT_const_value: if (attributes.ExtractFormValueAtIndex(i, form_value)) { uval64_valid = true; uval64 = form_value.Unsigned(); } break; default: break; } } clang::ASTContext *ast = m_ast.getASTContext(); if (!clang_type) clang_type = m_ast.GetBasicType(eBasicTypeVoid); if (clang_type) { bool is_signed = false; if (name && name[0]) template_param_infos.names.push_back(name); else template_param_infos.names.push_back(NULL); // Get the signed value for any integer or enumeration if available clang_type.IsIntegerOrEnumerationType(is_signed); if (tag == DW_TAG_template_value_parameter && uval64_valid) { llvm::APInt apint(clang_type.GetBitSize(nullptr), uval64, is_signed); template_param_infos.args.push_back( clang::TemplateArgument(*ast, llvm::APSInt(apint, !is_signed), ClangUtil::GetQualType(clang_type))); } else { template_param_infos.args.push_back( clang::TemplateArgument(ClangUtil::GetQualType(clang_type))); } } else { return false; } } } return true; default: break; } return false; } bool DWARFASTParserClang::ParseTemplateParameterInfos( const DWARFDIE &parent_die, ClangASTContext::TemplateParameterInfos &template_param_infos) { if (!parent_die) return false; Args template_parameter_names; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: case DW_TAG_GNU_template_parameter_pack: ParseTemplateDIE(die, template_param_infos); break; default: break; } } if (template_param_infos.args.empty()) return false; return template_param_infos.args.size() == template_param_infos.names.size(); } bool DWARFASTParserClang::CompleteTypeFromDWARF(const DWARFDIE &die, lldb_private::Type *type, CompilerType &clang_type) { SymbolFileDWARF *dwarf = die.GetDWARF(); std::lock_guard guard( dwarf->GetObjectFile()->GetModule()->GetMutex()); // Disable external storage for this type so we don't get anymore // clang::ExternalASTSource queries for this type. m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), false); if (!die) return false; #if defined LLDB_CONFIGURATION_DEBUG //---------------------------------------------------------------------- // For debugging purposes, the LLDB_DWARF_DONT_COMPLETE_TYPENAMES // environment variable can be set with one or more typenames separated // by ';' characters. This will cause this function to not complete any // types whose names match. // // Examples of setting this environment variable: // // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo // LLDB_DWARF_DONT_COMPLETE_TYPENAMES=Foo;Bar;Baz //---------------------------------------------------------------------- const char *dont_complete_typenames_cstr = getenv("LLDB_DWARF_DONT_COMPLETE_TYPENAMES"); if (dont_complete_typenames_cstr && dont_complete_typenames_cstr[0]) { const char *die_name = die.GetName(); if (die_name && die_name[0]) { const char *match = strstr(dont_complete_typenames_cstr, die_name); if (match) { size_t die_name_length = strlen(die_name); while (match) { const char separator_char = ';'; const char next_char = match[die_name_length]; if (next_char == '\0' || next_char == separator_char) { if (match == dont_complete_typenames_cstr || match[-1] == separator_char) return false; } match = strstr(match + 1, die_name); } } } } #endif const dw_tag_t tag = die.Tag(); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION)); if (log) dwarf->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", die.GetID(), die.GetTagAsCString(), type->GetName().AsCString()); assert(clang_type); DWARFAttributes attributes; switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { ClangASTImporter::LayoutInfo layout_info; { if (die.HasChildren()) { LanguageType class_language = eLanguageTypeUnknown; if (ClangASTContext::IsObjCObjectOrInterfaceType(clang_type)) { class_language = eLanguageTypeObjC; // For objective C we don't start the definition when // the class is created. ClangASTContext::StartTagDeclarationDefinition(clang_type); } int tag_decl_kind = -1; AccessType default_accessibility = eAccessNone; if (tag == DW_TAG_structure_type) { tag_decl_kind = clang::TTK_Struct; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_union_type) { tag_decl_kind = clang::TTK_Union; default_accessibility = eAccessPublic; } else if (tag == DW_TAG_class_type) { tag_decl_kind = clang::TTK_Class; default_accessibility = eAccessPrivate; } SymbolContext sc(die.GetLLDBCompileUnit()); std::vector base_classes; std::vector member_accessibilities; bool is_a_class = false; // Parse members and base classes first DWARFDIECollection member_function_dies; DelayedPropertyList delayed_properties; ParseChildMembers(sc, die, clang_type, class_language, base_classes, member_accessibilities, member_function_dies, delayed_properties, default_accessibility, is_a_class, layout_info); // Now parse any methods if there were any... size_t num_functions = member_function_dies.Size(); if (num_functions > 0) { for (size_t i = 0; i < num_functions; ++i) { dwarf->ResolveType(member_function_dies.GetDIEAtIndex(i)); } } if (class_language == eLanguageTypeObjC) { ConstString class_name(clang_type.GetTypeName()); if (class_name) { DIEArray method_die_offsets; dwarf->GetObjCMethodDIEOffsets(class_name, method_die_offsets); if (!method_die_offsets.empty()) { DWARFDebugInfo *debug_info = dwarf->DebugInfo(); const size_t num_matches = method_die_offsets.size(); for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = method_die_offsets[i]; DWARFDIE method_die = debug_info->GetDIE(die_ref); if (method_die) method_die.ResolveType(); } } for (DelayedPropertyList::iterator pi = delayed_properties.begin(), pe = delayed_properties.end(); pi != pe; ++pi) pi->Finalize(); } } // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we // need to tell the clang type it is actually a class. if (class_language != eLanguageTypeObjC) { if (is_a_class && tag_decl_kind != clang::TTK_Class) m_ast.SetTagTypeKind(ClangUtil::GetQualType(clang_type), clang::TTK_Class); } // Since DW_TAG_structure_type gets used for both classes // and structures, we may need to set any DW_TAG_member // fields to have a "private" access if none was specified. // When we parsed the child members we tracked that actual // accessibility value for each DW_TAG_member in the // "member_accessibilities" array. If the value for the // member is zero, then it was set to the "default_accessibility" // which for structs was "public". Below we correct this // by setting any fields to "private" that weren't correctly // set. if (is_a_class && !member_accessibilities.empty()) { // This is a class and all members that didn't have // their access specified are private. m_ast.SetDefaultAccessForRecordFields( m_ast.GetAsRecordDecl(clang_type), eAccessPrivate, &member_accessibilities.front(), member_accessibilities.size()); } if (!base_classes.empty()) { // Make sure all base classes refer to complete types and not // forward declarations. If we don't do this, clang will crash // with an assertion in the call to // clang_type.SetBaseClassesForClassType() for (auto &base_class : base_classes) { clang::TypeSourceInfo *type_source_info = base_class->getTypeSourceInfo(); if (type_source_info) { CompilerType base_class_type( &m_ast, type_source_info->getType().getAsOpaquePtr()); if (base_class_type.GetCompleteType() == false) { auto module = dwarf->GetObjectFile()->GetModule(); module->ReportError(":: Class '%s' has a base class '%s' which " "does not have a complete definition.", die.GetName(), base_class_type.GetTypeName().GetCString()); if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module->ReportError(":: Try compiling the source file with " "-fno-limit-debug-info."); // We have no choice other than to pretend that the base class // is complete. If we don't do this, clang will crash when we // call setBases() inside of // "clang_type.SetBaseClassesForClassType()" // below. Since we provide layout assistance, all ivars in this // class and other classes will be fine, this is the best we can // do // short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( base_class_type)) { ClangASTContext::CompleteTagDeclarationDefinition( base_class_type); } } } } m_ast.SetBaseClassesForClassType(clang_type.GetOpaqueQualType(), &base_classes.front(), base_classes.size()); // Clang will copy each CXXBaseSpecifier in "base_classes" // so we have to free them all. ClangASTContext::DeleteBaseClassSpecifiers(&base_classes.front(), base_classes.size()); } } } ClangASTContext::BuildIndirectFields(clang_type); ClangASTContext::CompleteTagDeclarationDefinition(clang_type); if (!layout_info.field_offsets.empty() || !layout_info.base_offsets.empty() || !layout_info.vbase_offsets.empty()) { if (type) layout_info.bit_size = type->GetByteSize() * 8; if (layout_info.bit_size == 0) layout_info.bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; clang::CXXRecordDecl *record_decl = m_ast.GetAsCXXRecordDecl(clang_type.GetOpaqueQualType()); if (record_decl) { if (log) { ModuleSP module_sp = dwarf->GetObjectFile()->GetModule(); if (module_sp) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = %p) " "caching layout info for record_decl = %p, bit_size = %" PRIu64 ", alignment = %" PRIu64 ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])", static_cast(clang_type.GetOpaqueQualType()), static_cast(record_decl), layout_info.bit_size, layout_info.alignment, static_cast(layout_info.field_offsets.size()), static_cast(layout_info.base_offsets.size()), static_cast(layout_info.vbase_offsets.size())); uint32_t idx; { llvm::DenseMap::const_iterator pos, end = layout_info.field_offsets.end(); for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end; ++pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) field[%u] = { bit_offset=%u, name='%s' }", static_cast(clang_type.GetOpaqueQualType()), idx, static_cast(pos->second), pos->first->getNameAsString().c_str()); } } { llvm::DenseMap::const_iterator base_pos, base_end = layout_info.base_offsets.end(); for (idx = 0, base_pos = layout_info.base_offsets.begin(); base_pos != base_end; ++base_pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) base[%u] = { byte_offset=%u, name='%s' }", clang_type.GetOpaqueQualType(), idx, (uint32_t)base_pos->second.getQuantity(), base_pos->first->getNameAsString().c_str()); } } { llvm::DenseMap::const_iterator vbase_pos, vbase_end = layout_info.vbase_offsets.end(); for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin(); vbase_pos != vbase_end; ++vbase_pos, ++idx) { module_sp->LogMessage( log, "ClangASTContext::CompleteTypeFromDWARF (clang_type = " "%p) vbase[%u] = { byte_offset=%u, name='%s' }", static_cast(clang_type.GetOpaqueQualType()), idx, static_cast(vbase_pos->second.getQuantity()), vbase_pos->first->getNameAsString().c_str()); } } } } GetClangASTImporter().InsertRecordDecl(record_decl, layout_info); } } } return (bool)clang_type; case DW_TAG_enumeration_type: if (ClangASTContext::StartTagDeclarationDefinition(clang_type)) { if (die.HasChildren()) { SymbolContext sc(die.GetLLDBCompileUnit()); bool is_signed = false; clang_type.IsIntegerType(is_signed); ParseChildEnumerators(sc, clang_type, is_signed, type->GetByteSize(), die); } ClangASTContext::CompleteTagDeclarationDefinition(clang_type); } return (bool)clang_type; default: assert(false && "not a forward clang type decl!"); break; } return false; } std::vector DWARFASTParserClang::GetDIEForDeclContext( lldb_private::CompilerDeclContext decl_context) { std::vector result; for (auto it = m_decl_ctx_to_die.find( (clang::DeclContext *)decl_context.GetOpaqueDeclContext()); it != m_decl_ctx_to_die.end(); it++) result.push_back(it->second); return result; } CompilerDecl DWARFASTParserClang::GetDeclForUIDFromDWARF(const DWARFDIE &die) { clang::Decl *clang_decl = GetClangDeclForDIE(die); if (clang_decl != nullptr) return CompilerDecl(&m_ast, clang_decl); return CompilerDecl(); } CompilerDeclContext DWARFASTParserClang::GetDeclContextForUIDFromDWARF(const DWARFDIE &die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(die); if (clang_decl_ctx) return CompilerDeclContext(&m_ast, clang_decl_ctx); return CompilerDeclContext(); } CompilerDeclContext DWARFASTParserClang::GetDeclContextContainingUIDFromDWARF(const DWARFDIE &die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); if (clang_decl_ctx) return CompilerDeclContext(&m_ast, clang_decl_ctx); return CompilerDeclContext(); } size_t DWARFASTParserClang::ParseChildEnumerators( const SymbolContext &sc, lldb_private::CompilerType &clang_type, bool is_signed, uint32_t enumerator_byte_size, const DWARFDIE &parent_die) { if (!parent_die) return 0; size_t enumerators_added = 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); if (tag == DW_TAG_enumerator) { DWARFAttributes attributes; const size_t num_child_attributes = die.GetAttributes(attributes); if (num_child_attributes > 0) { const char *name = NULL; bool got_value = false; int64_t enum_value = 0; Declaration decl; uint32_t i; for (i = 0; i < num_child_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_const_value: got_value = true; if (is_signed) enum_value = form_value.Signed(); else enum_value = form_value.Unsigned(); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_description: default: case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_sibling: break; } } } if (name && name[0] && got_value) { m_ast.AddEnumerationValueToEnumerationType( clang_type.GetOpaqueQualType(), m_ast.GetEnumerationIntegerType(clang_type.GetOpaqueQualType()), decl, name, enum_value, enumerator_byte_size * 8); ++enumerators_added; } } } } return enumerators_added; } #if defined(LLDB_CONFIGURATION_DEBUG) || defined(LLDB_CONFIGURATION_RELEASE) class DIEStack { public: void Push(const DWARFDIE &die) { m_dies.push_back(die); } void LogDIEs(Log *log) { StreamString log_strm; const size_t n = m_dies.size(); log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n); for (size_t i = 0; i < n; i++) { std::string qualified_name; const DWARFDIE &die = m_dies[i]; die.GetQualifiedName(qualified_name); log_strm.Printf("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", (uint64_t)i, die.GetOffset(), die.GetTagAsCString(), qualified_name.c_str()); } log->PutCString(log_strm.GetData()); } void Pop() { m_dies.pop_back(); } class ScopedPopper { public: ScopedPopper(DIEStack &die_stack) : m_die_stack(die_stack), m_valid(false) {} void Push(const DWARFDIE &die) { m_valid = true; m_die_stack.Push(die); } ~ScopedPopper() { if (m_valid) m_die_stack.Pop(); } protected: DIEStack &m_die_stack; bool m_valid; }; protected: typedef std::vector Stack; Stack m_dies; }; #endif Function *DWARFASTParserClang::ParseFunctionFromDWARF(const SymbolContext &sc, const DWARFDIE &die) { DWARFRangeList func_ranges; const char *name = NULL; const char *mangled = NULL; int decl_file = 0; int decl_line = 0; int decl_column = 0; int call_file = 0; int call_line = 0; int call_column = 0; DWARFExpression frame_base(die.GetCU()); const dw_tag_t tag = die.Tag(); if (tag != DW_TAG_subprogram) return NULL; if (die.GetDIENamesAndRanges(name, mangled, func_ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, &frame_base)) { // Union of all ranges in the function DIE (if the function is // discontiguous) AddressRange func_range; lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase(0); lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd(0); if (lowest_func_addr != LLDB_INVALID_ADDRESS && lowest_func_addr <= highest_func_addr) { ModuleSP module_sp(die.GetModule()); func_range.GetBaseAddress().ResolveAddressUsingFileSections( lowest_func_addr, module_sp->GetSectionList()); if (func_range.GetBaseAddress().IsValid()) func_range.SetByteSize(highest_func_addr - lowest_func_addr); } if (func_range.GetBaseAddress().IsValid()) { Mangled func_name; if (mangled) func_name.SetValue(ConstString(mangled), true); else if (die.GetParent().Tag() == DW_TAG_compile_unit && Language::LanguageIsCPlusPlus(die.GetLanguage()) && name && strcmp(name, "main") != 0) { // If the mangled name is not present in the DWARF, generate the // demangled name // using the decl context. We skip if the function is "main" as its name // is // never mangled. bool is_static = false; bool is_variadic = false; bool has_template_params = false; unsigned type_quals = 0; std::vector param_types; std::vector param_decls; DWARFDeclContext decl_ctx; StreamString sstr; die.GetDWARFDeclContext(decl_ctx); sstr << decl_ctx.GetQualifiedName(); clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); ParseChildParameters(sc, containing_decl_ctx, die, true, is_static, is_variadic, has_template_params, param_types, param_decls, type_quals); sstr << "("; for (size_t i = 0; i < param_types.size(); i++) { if (i > 0) sstr << ", "; sstr << param_types[i].GetTypeName(); } if (is_variadic) sstr << ", ..."; sstr << ")"; if (type_quals & clang::Qualifiers::Const) sstr << " const"; func_name.SetValue(ConstString(sstr.GetString()), false); } else func_name.SetValue(ConstString(name), false); FunctionSP func_sp; std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); SymbolFileDWARF *dwarf = die.GetDWARF(); // Supply the type _only_ if it has already been parsed Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE()); assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); if (dwarf->FixupAddress(func_range.GetBaseAddress())) { const user_id_t func_user_id = die.GetID(); func_sp.reset(new Function(sc.comp_unit, func_user_id, // UserID is the DIE offset func_user_id, func_name, func_type, func_range)); // first address range if (func_sp.get() != NULL) { if (frame_base.IsValid()) func_sp->GetFrameBaseExpression() = frame_base; sc.comp_unit->AddFunction(func_sp); return func_sp.get(); } } } } return NULL; } bool DWARFASTParserClang::ParseChildMembers( const SymbolContext &sc, const DWARFDIE &parent_die, CompilerType &class_clang_type, const LanguageType class_language, std::vector &base_classes, std::vector &member_accessibilities, DWARFDIECollection &member_function_dies, DelayedPropertyList &delayed_properties, AccessType &default_accessibility, bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) { if (!parent_die) return 0; // Get the parent byte size so we can verify any members will fit const uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); const uint64_t parent_bit_size = parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8; uint32_t member_idx = 0; BitfieldInfo last_field_info; ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); ClangASTContext *ast = llvm::dyn_cast_or_null(class_clang_type.GetTypeSystem()); if (ast == nullptr) return 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_member: case DW_TAG_APPLE_property: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { Declaration decl; // DWARFExpression location; const char *name = NULL; const char *prop_name = NULL; const char *prop_getter_name = NULL; const char *prop_setter_name = NULL; uint32_t prop_attributes = 0; bool is_artificial = false; DWARFFormValue encoding_form; AccessType accessibility = eAccessNone; uint32_t member_byte_offset = (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; size_t byte_size = 0; int64_t bit_offset = 0; uint64_t data_bit_offset = UINT64_MAX; size_t bit_size = 0; bool is_external = false; // On DW_TAG_members, this means the member is static uint32_t i; for (i = 0; i < num_attributes && !is_artificial; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_bit_offset: bit_offset = form_value.Signed(); break; case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_data_bit_offset: data_bit_offset = form_value.Unsigned(); break; case DW_AT_data_member_location: if (form_value.BlockData()) { Value initialValue(0); Value memberOffset(0); const DWARFDataExtractor &debug_info_data = die.GetDWARF()->get_debug_info_data(); uint32_t block_length = form_value.Unsigned(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); if (DWARFExpression::Evaluate( nullptr, // ExecutionContext * nullptr, // ClangExpressionVariableList * nullptr, // ClangExpressionDeclMap * nullptr, // RegisterContext * module_sp, debug_info_data, die.GetCU(), block_offset, block_length, eRegisterKindDWARF, &initialValue, nullptr, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); } } else { // With DWARF 3 and later, if the value is an integer constant, // this form value is the offset in bytes from the beginning // of the containing entity. member_byte_offset = form_value.Unsigned(); } break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_APPLE_property_name: prop_name = form_value.AsCString(); break; case DW_AT_APPLE_property_getter: prop_getter_name = form_value.AsCString(); break; case DW_AT_APPLE_property_setter: prop_setter_name = form_value.AsCString(); break; case DW_AT_APPLE_property_attribute: prop_attributes = form_value.Unsigned(); break; case DW_AT_external: is_external = form_value.Boolean(); break; default: case DW_AT_declaration: case DW_AT_description: case DW_AT_mutable: case DW_AT_visibility: case DW_AT_sibling: break; } } } if (prop_name) { ConstString fixed_getter; ConstString fixed_setter; // Check if the property getter/setter were provided as full // names. We want basenames, so we extract them. if (prop_getter_name && prop_getter_name[0] == '-') { ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true); prop_getter_name = prop_getter_method.GetSelector().GetCString(); } if (prop_setter_name && prop_setter_name[0] == '-') { ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true); prop_setter_name = prop_setter_method.GetSelector().GetCString(); } // If the names haven't been provided, they need to be // filled in. if (!prop_getter_name) { prop_getter_name = prop_name; } if (!prop_setter_name && prop_name[0] && !(prop_attributes & DW_APPLE_PROPERTY_readonly)) { StreamString ss; ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]); fixed_setter.SetString(ss.GetString()); prop_setter_name = fixed_setter.GetCString(); } } // Clang has a DWARF generation bug where sometimes it // represents fields that are references with bad byte size // and bit size/offset information such as: // // DW_AT_byte_size( 0x00 ) // DW_AT_bit_size( 0x40 ) // DW_AT_bit_offset( 0xffffffffffffffc0 ) // // So check the bit offset to make sure it is sane, and if // the values are not sane, remove them. If we don't do this // then we will end up with a crash if we try to use this // type in an expression when clang becomes unhappy with its // recycled debug info. if (byte_size == 0 && bit_offset < 0) { bit_size = 0; bit_offset = 0; } // FIXME: Make Clang ignore Objective-C accessibility for expressions if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) accessibility = eAccessNone; if (member_idx == 0 && !is_artificial && name && (strstr(name, "_vptr$") == name)) { // Not all compilers will mark the vtable pointer // member as artificial (llvm-gcc). We can't have // the virtual members in our classes otherwise it // throws off all child offsets since we end up // having and extra pointer sized member in our // class layouts. is_artificial = true; } // Handle static members if (is_external && member_byte_offset == UINT32_MAX) { Type *var_type = die.ResolveTypeUID(DIERef(encoding_form)); if (var_type) { if (accessibility == eAccessNone) accessibility = eAccessPublic; ClangASTContext::AddVariableToRecordType( class_clang_type, name, var_type->GetLayoutCompilerType(), accessibility); } break; } if (is_artificial == false) { Type *member_type = die.ResolveTypeUID(DIERef(encoding_form)); clang::FieldDecl *field_decl = NULL; if (tag == DW_TAG_member) { if (member_type) { if (accessibility == eAccessNone) accessibility = default_accessibility; member_accessibilities.push_back(accessibility); uint64_t field_bit_offset = (member_byte_offset == UINT32_MAX ? 0 : (member_byte_offset * 8)); if (bit_size > 0) { BitfieldInfo this_field_info; this_field_info.bit_offset = field_bit_offset; this_field_info.bit_size = bit_size; ///////////////////////////////////////////////////////////// // How to locate a field given the DWARF debug information // // AT_byte_size indicates the size of the word in which the // bit offset must be interpreted. // // AT_data_member_location indicates the byte offset of the // word from the base address of the structure. // // AT_bit_offset indicates how many bits into the word // (according to the host endianness) the low-order bit of // the field starts. AT_bit_offset can be negative. // // AT_bit_size indicates the size of the field in bits. ///////////////////////////////////////////////////////////// if (data_bit_offset != UINT64_MAX) { this_field_info.bit_offset = data_bit_offset; } else { if (byte_size == 0) byte_size = member_type->GetByteSize(); ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); if (objfile->GetByteOrder() == eByteOrderLittle) { this_field_info.bit_offset += byte_size * 8; this_field_info.bit_offset -= (bit_offset + bit_size); } else { this_field_info.bit_offset += bit_offset; } } if ((this_field_info.bit_offset >= parent_bit_size) || !last_field_info.NextBitfieldOffsetIsValid( this_field_info.bit_offset)) { ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); objfile->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid " "bit offset (0x%8.8" PRIx64 ") member will be ignored. Please file a bug against the " "compiler and include the preprocessed output for %s\n", die.GetID(), DW_TAG_value_to_name(tag), name, this_field_info.bit_offset, sc.comp_unit ? sc.comp_unit->GetPath().c_str() : "the source file"); this_field_info.Clear(); continue; } // Update the field bit offset we will report for layout field_bit_offset = this_field_info.bit_offset; // If the member to be emitted did not start on a character // boundary and there is // empty space between the last field and this one, then we need // to emit an // anonymous member filling up the space up to its start. There // are three cases // here: // // 1 If the previous member ended on a character boundary, then // we can emit an // anonymous member starting at the most recent character // boundary. // // 2 If the previous member did not end on a character boundary // and the distance // from the end of the previous member to the current member // is less than a // word width, then we can emit an anonymous member starting // right after the // previous member and right before this member. // // 3 If the previous member did not end on a character boundary // and the distance // from the end of the previous member to the current member // is greater than // or equal a word width, then we act as in Case 1. const uint64_t character_width = 8; const uint64_t word_width = 32; // Objective-C has invalid DW_AT_bit_offset values in older // versions // of clang, so we have to be careful and only insert unnamed // bitfields // if we have a new enough clang. bool detect_unnamed_bitfields = true; if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) detect_unnamed_bitfields = die.GetCU()->Supports_unnamed_objc_bitfields(); if (detect_unnamed_bitfields) { BitfieldInfo anon_field_info; if ((this_field_info.bit_offset % character_width) != 0) // not char aligned { uint64_t last_field_end = 0; if (last_field_info.IsValid()) last_field_end = last_field_info.bit_offset + last_field_info.bit_size; if (this_field_info.bit_offset != last_field_end) { if (((last_field_end % character_width) == 0) || // case 1 (this_field_info.bit_offset - last_field_end >= word_width)) // case 3 { anon_field_info.bit_size = this_field_info.bit_offset % character_width; anon_field_info.bit_offset = this_field_info.bit_offset - anon_field_info.bit_size; } else // case 2 { anon_field_info.bit_size = this_field_info.bit_offset - last_field_end; anon_field_info.bit_offset = last_field_end; } } } if (anon_field_info.IsValid()) { clang::FieldDecl *unnamed_bitfield_decl = ClangASTContext::AddFieldToRecordType( class_clang_type, NULL, m_ast.GetBuiltinTypeForEncodingAndBitSize( eEncodingSint, word_width), accessibility, anon_field_info.bit_size); layout_info.field_offsets.insert(std::make_pair( unnamed_bitfield_decl, anon_field_info.bit_offset)); } } last_field_info = this_field_info; } else { last_field_info.Clear(); } CompilerType member_clang_type = member_type->GetLayoutCompilerType(); if (!member_clang_type.IsCompleteType()) member_clang_type.GetCompleteType(); { // Older versions of clang emit array[0] and array[1] in the // same way (). // If the current field is at the end of the structure, then // there is definitely no room for extra // elements and we override the type to array[0]. CompilerType member_array_element_type; uint64_t member_array_size; bool member_array_is_incomplete; if (member_clang_type.IsArrayType( &member_array_element_type, &member_array_size, &member_array_is_incomplete) && !member_array_is_incomplete) { uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); if (member_byte_offset >= parent_byte_size) { if (member_array_size != 1 && (member_array_size != 0 || member_byte_offset > parent_byte_size)) { module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which extends beyond the bounds of 0x%8.8" PRIx64, die.GetID(), name, encoding_form.Reference(), parent_die.GetID()); } member_clang_type = m_ast.CreateArrayType( member_array_element_type, 0, false); } } } if (ClangASTContext::IsCXXClassType(member_clang_type) && member_clang_type.GetCompleteType() == false) { if (die.GetCU()->GetProducer() == DWARFCompileUnit::eProducerClang) module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type is a forward declaration, not a " "complete definition.\nTry compiling the source file " "with -fno-limit-debug-info", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name); else module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type is a forward declaration, not a " "complete definition.\nPlease file a bug against the " "compiler and include the preprocessed output for %s", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name, sc.comp_unit ? sc.comp_unit->GetPath().c_str() : "the source file"); // We have no choice other than to pretend that the member class // is complete. If we don't do this, clang will crash when // trying // to layout the class. Since we provide layout assistance, all // ivars in this class and other classes will be fine, this is // the best we can do short of crashing. if (ClangASTContext::StartTagDeclarationDefinition( member_clang_type)) { ClangASTContext::CompleteTagDeclarationDefinition( member_clang_type); } else { module_sp->ReportError( "DWARF DIE at 0x%8.8x (class %s) has a member variable " "0x%8.8x (%s) whose type claims to be a C++ class but we " "were not able to start its definition.\nPlease file a " "bug and attach the file at the start of this error " "message", parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), name); } } field_decl = ClangASTContext::AddFieldToRecordType( class_clang_type, name, member_clang_type, accessibility, bit_size); m_ast.SetMetadataAsUserID(field_decl, die.GetID()); layout_info.field_offsets.insert( std::make_pair(field_decl, field_bit_offset)); } else { if (name) module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which was unable to be parsed", die.GetID(), name, encoding_form.Reference()); else module_sp->ReportError( "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8" PRIx64 " which was unable to be parsed", die.GetID(), encoding_form.Reference()); } } if (prop_name != NULL && member_type) { clang::ObjCIvarDecl *ivar_decl = NULL; if (field_decl) { ivar_decl = clang::dyn_cast(field_decl); assert(ivar_decl != NULL); } ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); delayed_properties.push_back(DelayedAddObjCClassProperty( class_clang_type, prop_name, member_type->GetLayoutCompilerType(), ivar_decl, prop_setter_name, prop_getter_name, prop_attributes, &metadata)); if (ivar_decl) m_ast.SetMetadataAsUserID(ivar_decl, die.GetID()); } } } ++member_idx; } break; case DW_TAG_subprogram: // Let the type parsing code handle this one for us. member_function_dies.Append(die); break; case DW_TAG_inheritance: { is_a_class = true; if (default_accessibility == eAccessNone) default_accessibility = eAccessPrivate; // TODO: implement DW_TAG_inheritance type parsing DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { Declaration decl; DWARFExpression location(die.GetCU()); DWARFFormValue encoding_form; AccessType accessibility = default_accessibility; bool is_virtual = false; bool is_base_of_class = true; off_t member_byte_offset = 0; uint32_t i; for (i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_type: encoding_form = form_value; break; case DW_AT_data_member_location: if (form_value.BlockData()) { Value initialValue(0); Value memberOffset(0); const DWARFDataExtractor &debug_info_data = die.GetDWARF()->get_debug_info_data(); uint32_t block_length = form_value.Unsigned(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); if (DWARFExpression::Evaluate( nullptr, nullptr, nullptr, nullptr, module_sp, debug_info_data, die.GetCU(), block_offset, block_length, eRegisterKindDWARF, &initialValue, nullptr, memberOffset, nullptr)) { member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); } } else { // With DWARF 3 and later, if the value is an integer constant, // this form value is the offset in bytes from the beginning // of the containing entity. member_byte_offset = form_value.Unsigned(); } break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_virtuality: is_virtual = form_value.Boolean(); break; case DW_AT_sibling: break; default: break; } } } Type *base_class_type = die.ResolveTypeUID(DIERef(encoding_form)); if (base_class_type == NULL) { module_sp->ReportError("0x%8.8x: DW_TAG_inheritance failed to " "resolve the base class at 0x%8.8" PRIx64 " from enclosing type 0x%8.8x. \nPlease file " "a bug and attach the file at the start of " "this error message", die.GetOffset(), encoding_form.Reference(), parent_die.GetOffset()); break; } CompilerType base_class_clang_type = base_class_type->GetFullCompilerType(); assert(base_class_clang_type); if (class_language == eLanguageTypeObjC) { ast->SetObjCSuperClass(class_clang_type, base_class_clang_type); } else { base_classes.push_back(ast->CreateBaseClassSpecifier( base_class_clang_type.GetOpaqueQualType(), accessibility, is_virtual, is_base_of_class)); if (is_virtual) { // Do not specify any offset for virtual inheritance. The DWARF // produced by clang doesn't // give us a constant offset, but gives us a DWARF expressions that // requires an actual object // in memory. the DW_AT_data_member_location for a virtual base // class looks like: // DW_AT_data_member_location( DW_OP_dup, DW_OP_deref, // DW_OP_constu(0x00000018), DW_OP_minus, DW_OP_deref, // DW_OP_plus ) // Given this, there is really no valid response we can give to // clang for virtual base // class offsets, and this should eventually be removed from // LayoutRecordType() in the external // AST source in clang. } else { layout_info.base_offsets.insert(std::make_pair( ast->GetAsCXXRecordDecl( base_class_clang_type.GetOpaqueQualType()), clang::CharUnits::fromQuantity(member_byte_offset))); } } } } break; default: break; } } return true; } size_t DWARFASTParserClang::ParseChildParameters( const SymbolContext &sc, clang::DeclContext *containing_decl_ctx, const DWARFDIE &parent_die, bool skip_artificial, bool &is_static, bool &is_variadic, bool &has_template_params, std::vector &function_param_types, std::vector &function_param_decls, unsigned &type_quals) { if (!parent_die) return 0; size_t arg_idx = 0; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_formal_parameter: { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { const char *name = NULL; Declaration decl; DWARFFormValue param_type_die_form; bool is_artificial = false; // one of None, Auto, Register, Extern, Static, PrivateExtern clang::StorageClass storage = clang::SC_None; uint32_t i; for (i = 0; i < num_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_type: param_type_die_form = form_value; break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_location: // if (form_value.BlockData()) // { // const DWARFDataExtractor& // debug_info_data = debug_info(); // uint32_t block_length = // form_value.Unsigned(); // DWARFDataExtractor // location(debug_info_data, // form_value.BlockData() - // debug_info_data.GetDataStart(), // block_length); // } // else // { // } // break; case DW_AT_const_value: case DW_AT_default_value: case DW_AT_description: case DW_AT_endianity: case DW_AT_is_optional: case DW_AT_segment: case DW_AT_variable_parameter: default: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } bool skip = false; if (skip_artificial) { if (is_artificial) { // In order to determine if a C++ member function is // "const" we have to look at the const-ness of "this"... // Ugly, but that if (arg_idx == 0) { if (DeclKindIsCXXClass(containing_decl_ctx->getDeclKind())) { // Often times compilers omit the "this" name for the // specification DIEs, so we can't rely upon the name // being in the formal parameter DIE... if (name == NULL || ::strcmp(name, "this") == 0) { Type *this_type = die.ResolveTypeUID(DIERef(param_type_die_form)); if (this_type) { uint32_t encoding_mask = this_type->GetEncodingMask(); if (encoding_mask & Type::eEncodingIsPointerUID) { is_static = false; if (encoding_mask & (1u << Type::eEncodingIsConstUID)) type_quals |= clang::Qualifiers::Const; if (encoding_mask & (1u << Type::eEncodingIsVolatileUID)) type_quals |= clang::Qualifiers::Volatile; } } } } } skip = true; } else { // HACK: Objective C formal parameters "self" and "_cmd" // are not marked as artificial in the DWARF... CompileUnit *comp_unit = die.GetLLDBCompileUnit(); if (comp_unit) { switch (comp_unit->GetLanguage()) { case eLanguageTypeObjC: case eLanguageTypeObjC_plus_plus: if (name && name[0] && (strcmp(name, "self") == 0 || strcmp(name, "_cmd") == 0)) skip = true; break; default: break; } } } } if (!skip) { Type *type = die.ResolveTypeUID(DIERef(param_type_die_form)); if (type) { function_param_types.push_back(type->GetForwardCompilerType()); clang::ParmVarDecl *param_var_decl = m_ast.CreateParameterDeclaration( name, type->GetForwardCompilerType(), storage); assert(param_var_decl); function_param_decls.push_back(param_var_decl); m_ast.SetMetadataAsUserID(param_var_decl, die.GetID()); } } } arg_idx++; } break; case DW_TAG_unspecified_parameters: is_variadic = true; break; case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: case DW_TAG_GNU_template_parameter_pack: // The one caller of this was never using the template_param_infos, // and the local variable was taking up a large amount of stack space // in SymbolFileDWARF::ParseType() so this was removed. If we ever need // the template params back, we can add them back. // ParseTemplateDIE (dwarf_cu, die, template_param_infos); has_template_params = true; break; default: break; } } return arg_idx; } void DWARFASTParserClang::ParseChildArrayInfo( const SymbolContext &sc, const DWARFDIE &parent_die, int64_t &first_index, std::vector &element_orders, uint32_t &byte_stride, uint32_t &bit_stride) { if (!parent_die) return; for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); die = die.GetSibling()) { const dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_subrange_type: { DWARFAttributes attributes; const size_t num_child_attributes = die.GetAttributes(attributes); if (num_child_attributes > 0) { uint64_t num_elements = 0; uint64_t lower_bound = 0; uint64_t upper_bound = 0; bool upper_bound_valid = false; uint32_t i; for (i = 0; i < num_child_attributes; ++i) { const dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_name: break; case DW_AT_count: num_elements = form_value.Unsigned(); break; case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; case DW_AT_lower_bound: lower_bound = form_value.Unsigned(); break; case DW_AT_upper_bound: upper_bound_valid = true; upper_bound = form_value.Unsigned(); break; default: case DW_AT_abstract_origin: case DW_AT_accessibility: case DW_AT_allocated: case DW_AT_associated: case DW_AT_data_location: case DW_AT_declaration: case DW_AT_description: case DW_AT_sibling: case DW_AT_threads_scaled: case DW_AT_type: case DW_AT_visibility: break; } } } if (num_elements == 0) { if (upper_bound_valid && upper_bound >= lower_bound) num_elements = upper_bound - lower_bound + 1; } element_orders.push_back(num_elements); } } break; } } } Type *DWARFASTParserClang::GetTypeForDIE(const DWARFDIE &die) { if (die) { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); if (num_attributes > 0) { DWARFFormValue type_die_form; for (size_t i = 0; i < num_attributes; ++i) { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attr == DW_AT_type && attributes.ExtractFormValueAtIndex(i, form_value)) return dwarf->ResolveTypeUID(dwarf->GetDIE(DIERef(form_value)), true); } } } return nullptr; } clang::Decl *DWARFASTParserClang::GetClangDeclForDIE(const DWARFDIE &die) { if (!die) return nullptr; switch (die.Tag()) { case DW_TAG_variable: case DW_TAG_constant: case DW_TAG_formal_parameter: case DW_TAG_imported_declaration: case DW_TAG_imported_module: break; default: return nullptr; } DIEToDeclMap::iterator cache_pos = m_die_to_decl.find(die.GetDIE()); if (cache_pos != m_die_to_decl.end()) return cache_pos->second; if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) { clang::Decl *decl = GetClangDeclForDIE(spec_die); m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } if (DWARFDIE abstract_origin_die = die.GetReferencedDIE(DW_AT_abstract_origin)) { clang::Decl *decl = GetClangDeclForDIE(abstract_origin_die); m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } clang::Decl *decl = nullptr; switch (die.Tag()) { case DW_TAG_variable: case DW_TAG_constant: case DW_TAG_formal_parameter: { SymbolFileDWARF *dwarf = die.GetDWARF(); Type *type = GetTypeForDIE(die); if (dwarf && type) { const char *name = die.GetName(); clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); decl = m_ast.CreateVariableDeclaration( decl_context, name, ClangUtil::GetQualType(type->GetForwardCompilerType())); } break; } case DW_TAG_imported_declaration: { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); if (imported_uid) { CompilerDecl imported_decl = imported_uid.GetDecl(); if (imported_decl) { clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); if (clang::NamedDecl *clang_imported_decl = llvm::dyn_cast( (clang::Decl *)imported_decl.GetOpaqueDecl())) decl = m_ast.CreateUsingDeclaration(decl_context, clang_imported_decl); } } break; } case DW_TAG_imported_module: { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE imported_uid = die.GetAttributeValueAsReferenceDIE(DW_AT_import); if (imported_uid) { CompilerDeclContext imported_decl_ctx = imported_uid.GetDeclContext(); if (imported_decl_ctx) { clang::DeclContext *decl_context = ClangASTContext::DeclContextGetAsDeclContext( dwarf->GetDeclContextContainingUID(die.GetID())); if (clang::NamespaceDecl *ns_decl = ClangASTContext::DeclContextGetAsNamespaceDecl( imported_decl_ctx)) decl = m_ast.CreateUsingDirectiveDeclaration(decl_context, ns_decl); } } break; } default: break; } m_die_to_decl[die.GetDIE()] = decl; m_decl_to_die[decl].insert(die.GetDIE()); return decl; } clang::DeclContext * DWARFASTParserClang::GetClangDeclContextForDIE(const DWARFDIE &die) { if (die) { clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(die); if (decl_ctx) return decl_ctx; bool try_parsing_type = true; switch (die.Tag()) { case DW_TAG_compile_unit: decl_ctx = m_ast.GetTranslationUnitDecl(); try_parsing_type = false; break; case DW_TAG_namespace: decl_ctx = ResolveNamespaceDIE(die); try_parsing_type = false; break; case DW_TAG_lexical_block: decl_ctx = GetDeclContextForBlock(die); try_parsing_type = false; break; default: break; } if (decl_ctx == nullptr && try_parsing_type) { Type *type = die.GetDWARF()->ResolveType(die); if (type) decl_ctx = GetCachedClangDeclContextForDIE(die); } if (decl_ctx) { LinkDeclContextToDIE(decl_ctx, die); return decl_ctx; } } return nullptr; } static bool IsSubroutine(const DWARFDIE &die) { switch (die.Tag()) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: return true; default: return false; } } static DWARFDIE GetContainingFunctionWithAbstractOrigin(const DWARFDIE &die) { for (DWARFDIE candidate = die; candidate; candidate = candidate.GetParent()) { if (IsSubroutine(candidate)) { if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { return candidate; } else { return DWARFDIE(); } } } - assert(!"Shouldn't call GetContainingFunctionWithAbstractOrigin on something " - "not in a function"); + assert(0 && "Shouldn't call GetContainingFunctionWithAbstractOrigin on " + "something not in a function"); return DWARFDIE(); } static DWARFDIE FindAnyChildWithAbstractOrigin(const DWARFDIE &context) { for (DWARFDIE candidate = context.GetFirstChild(); candidate.IsValid(); candidate = candidate.GetSibling()) { if (candidate.GetReferencedDIE(DW_AT_abstract_origin)) { return candidate; } } return DWARFDIE(); } static DWARFDIE FindFirstChildWithAbstractOrigin(const DWARFDIE &block, const DWARFDIE &function) { assert(IsSubroutine(function)); for (DWARFDIE context = block; context != function.GetParent(); context = context.GetParent()) { assert(!IsSubroutine(context) || context == function); if (DWARFDIE child = FindAnyChildWithAbstractOrigin(context)) { return child; } } return DWARFDIE(); } clang::DeclContext * DWARFASTParserClang::GetDeclContextForBlock(const DWARFDIE &die) { assert(die.Tag() == DW_TAG_lexical_block); DWARFDIE containing_function_with_abstract_origin = GetContainingFunctionWithAbstractOrigin(die); if (!containing_function_with_abstract_origin) { return (clang::DeclContext *)ResolveBlockDIE(die); } DWARFDIE child = FindFirstChildWithAbstractOrigin( die, containing_function_with_abstract_origin); CompilerDeclContext decl_context = GetDeclContextContainingUIDFromDWARF(child); return (clang::DeclContext *)decl_context.GetOpaqueDeclContext(); } clang::BlockDecl *DWARFASTParserClang::ResolveBlockDIE(const DWARFDIE &die) { if (die && die.Tag() == DW_TAG_lexical_block) { clang::BlockDecl *decl = llvm::cast_or_null(m_die_to_decl_ctx[die.GetDIE()]); if (!decl) { DWARFDIE decl_context_die; clang::DeclContext *decl_context = GetClangDeclContextContainingDIE(die, &decl_context_die); decl = m_ast.CreateBlockDeclaration(decl_context); if (decl) LinkDeclContextToDIE((clang::DeclContext *)decl, die); } return decl; } return nullptr; } clang::NamespaceDecl * DWARFASTParserClang::ResolveNamespaceDIE(const DWARFDIE &die) { if (die && die.Tag() == DW_TAG_namespace) { // See if we already parsed this namespace DIE and associated it with a // uniqued namespace declaration clang::NamespaceDecl *namespace_decl = static_cast(m_die_to_decl_ctx[die.GetDIE()]); if (namespace_decl) return namespace_decl; else { const char *namespace_name = die.GetName(); clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); namespace_decl = m_ast.GetUniqueNamespaceDeclaration(namespace_name, containing_decl_ctx); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); if (log) { SymbolFileDWARF *dwarf = die.GetDWARF(); if (namespace_name) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace with DW_AT_name(\"%s\") => " "clang::NamespaceDecl *%p (original = %p)", static_cast(m_ast.getASTContext()), die.GetID(), namespace_name, static_cast(namespace_decl), static_cast(namespace_decl->getOriginalNamespace())); } else { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p " "(original = %p)", static_cast(m_ast.getASTContext()), die.GetID(), static_cast(namespace_decl), static_cast(namespace_decl->getOriginalNamespace())); } } if (namespace_decl) LinkDeclContextToDIE((clang::DeclContext *)namespace_decl, die); return namespace_decl; } } return nullptr; } clang::DeclContext *DWARFASTParserClang::GetClangDeclContextContainingDIE( const DWARFDIE &die, DWARFDIE *decl_ctx_die_copy) { SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE decl_ctx_die = dwarf->GetDeclContextDIEContainingDIE(die); if (decl_ctx_die_copy) *decl_ctx_die_copy = decl_ctx_die; if (decl_ctx_die) { clang::DeclContext *clang_decl_ctx = GetClangDeclContextForDIE(decl_ctx_die); if (clang_decl_ctx) return clang_decl_ctx; } return m_ast.GetTranslationUnitDecl(); } clang::DeclContext * DWARFASTParserClang::GetCachedClangDeclContextForDIE(const DWARFDIE &die) { if (die) { DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die.GetDIE()); if (pos != m_die_to_decl_ctx.end()) return pos->second; } return nullptr; } void DWARFASTParserClang::LinkDeclContextToDIE(clang::DeclContext *decl_ctx, const DWARFDIE &die) { m_die_to_decl_ctx[die.GetDIE()] = decl_ctx; // There can be many DIEs for a single decl context // m_decl_ctx_to_die[decl_ctx].insert(die.GetDIE()); m_decl_ctx_to_die.insert(std::make_pair(decl_ctx, die)); } bool DWARFASTParserClang::CopyUniqueClassMethodTypes( const DWARFDIE &src_class_die, const DWARFDIE &dst_class_die, lldb_private::Type *class_type, DWARFDIECollection &failures) { if (!class_type || !src_class_die || !dst_class_die) return false; if (src_class_die.Tag() != dst_class_die.Tag()) return false; // We need to complete the class type so we can get all of the method types // parsed so we can then unique those types to their equivalent counterparts // in "dst_cu" and "dst_class_die" class_type->GetFullCompilerType(); DWARFDIE src_die; DWARFDIE dst_die; UniqueCStringMap src_name_to_die; UniqueCStringMap dst_name_to_die; UniqueCStringMap src_name_to_die_artificial; UniqueCStringMap dst_name_to_die_artificial; for (src_die = src_class_die.GetFirstChild(); src_die.IsValid(); src_die = src_die.GetSibling()) { if (src_die.Tag() == DW_TAG_subprogram) { // Make sure this is a declaration and not a concrete instance by looking // for DW_AT_declaration set to 1. Sometimes concrete function instances // are placed inside the class definitions and shouldn't be included in // the list of things are are tracking here. if (src_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { const char *src_name = src_die.GetMangledName(); if (src_name) { ConstString src_const_name(src_name); if (src_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) src_name_to_die_artificial.Append(src_const_name, src_die); else src_name_to_die.Append(src_const_name, src_die); } } } } for (dst_die = dst_class_die.GetFirstChild(); dst_die.IsValid(); dst_die = dst_die.GetSibling()) { if (dst_die.Tag() == DW_TAG_subprogram) { // Make sure this is a declaration and not a concrete instance by looking // for DW_AT_declaration set to 1. Sometimes concrete function instances // are placed inside the class definitions and shouldn't be included in // the list of things are are tracking here. if (dst_die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0) == 1) { const char *dst_name = dst_die.GetMangledName(); if (dst_name) { ConstString dst_const_name(dst_name); if (dst_die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0)) dst_name_to_die_artificial.Append(dst_const_name, dst_die); else dst_name_to_die.Append(dst_const_name, dst_die); } } } } const uint32_t src_size = src_name_to_die.GetSize(); const uint32_t dst_size = dst_name_to_die.GetSize(); Log *log = nullptr; // (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | // DWARF_LOG_TYPE_COMPLETION)); // Is everything kosher so we can go through the members at top speed? bool fast_path = true; if (src_size != dst_size) { if (src_size != 0 && dst_size != 0) { if (log) log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, " "but they didn't have the same size (src=%d, dst=%d)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_size, dst_size); } fast_path = false; } uint32_t idx; if (fast_path) { for (idx = 0; idx < src_size; ++idx) { src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); if (src_die.Tag() != dst_die.Tag()) { if (log) log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " "but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_die.GetOffset(), src_die.GetTagAsCString(), dst_die.GetOffset(), dst_die.GetTagAsCString()); fast_path = false; } const char *src_name = src_die.GetMangledName(); const char *dst_name = dst_die.GetMangledName(); // Make sure the names match if (src_name == dst_name || (strcmp(src_name, dst_name) == 0)) continue; if (log) log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, " "but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)", src_class_die.GetOffset(), dst_class_die.GetOffset(), src_die.GetOffset(), src_name, dst_die.GetOffset(), dst_name); fast_path = false; } } DWARFASTParserClang *src_dwarf_ast_parser = (DWARFASTParserClang *)src_die.GetDWARFParser(); DWARFASTParserClang *dst_dwarf_ast_parser = (DWARFASTParserClang *)dst_die.GetDWARFParser(); // Now do the work of linking the DeclContexts and Types. if (fast_path) { // We can do this quickly. Just run across the tables index-for-index since // we know each node has matching names and tags. for (idx = 0; idx < src_size; ++idx) { src_die = src_name_to_die.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x for " "0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf( "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } } else { // We must do this slowly. For each member of the destination, look // up a member in the source with the same name, check its tag, and // unique them if everything matches up. Report failures. if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) { src_name_to_die.Sort(); for (idx = 0; idx < dst_size; ++idx) { ConstString dst_name = dst_name_to_die.GetCStringAtIndex(idx); dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); src_die = src_name_to_die.Find(dst_name, DWARFDIE()); if (src_die && (src_die.Tag() == dst_die.Tag())) { clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x " "for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf("uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } else { if (log) log->Printf("warning: couldn't find a match for 0x%8.8x", dst_die.GetOffset()); failures.Append(dst_die); } } } } const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize(); const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize(); if (src_size_artificial && dst_size_artificial) { dst_name_to_die_artificial.Sort(); for (idx = 0; idx < src_size_artificial; ++idx) { ConstString src_name_artificial = src_name_to_die_artificial.GetCStringAtIndex(idx); src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked(idx); dst_die = dst_name_to_die_artificial.Find(src_name_artificial, DWARFDIE()); if (dst_die) { // Both classes have the artificial types, link them clang::DeclContext *src_decl_ctx = src_dwarf_ast_parser->m_die_to_decl_ctx[src_die.GetDIE()]; if (src_decl_ctx) { if (log) log->Printf("uniquing decl context %p from 0x%8.8x for 0x%8.8x", static_cast(src_decl_ctx), src_die.GetOffset(), dst_die.GetOffset()); dst_dwarf_ast_parser->LinkDeclContextToDIE(src_decl_ctx, dst_die); } else { if (log) log->Printf("warning: tried to unique decl context from 0x%8.8x " "for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } Type *src_child_type = dst_die.GetDWARF()->GetDIEToType()[src_die.GetDIE()]; if (src_child_type) { if (log) log->Printf( "uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", static_cast(src_child_type), src_child_type->GetID(), src_die.GetOffset(), dst_die.GetOffset()); dst_die.GetDWARF()->GetDIEToType()[dst_die.GetDIE()] = src_child_type; } else { if (log) log->Printf("warning: tried to unique lldb_private::Type from " "0x%8.8x for 0x%8.8x, but none was found", src_die.GetOffset(), dst_die.GetOffset()); } } } } if (dst_size_artificial) { for (idx = 0; idx < dst_size_artificial; ++idx) { ConstString dst_name_artificial = dst_name_to_die_artificial.GetCStringAtIndex(idx); dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked(idx); if (log) log->Printf("warning: need to create artificial method for 0x%8.8x for " "method '%s'", dst_die.GetOffset(), dst_name_artificial.GetCString()); failures.Append(dst_die); } } return (failures.Size() != 0); } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 319789) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (revision 319790) @@ -1,4291 +1,4290 @@ //===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "SymbolFileDWARF.h" // Other libraries and framework includes #include "llvm/Support/Casting.h" #include "llvm/Support/Threading.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/StreamString.h" #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/DebugMacros.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Target/Language.h" #include "lldb/Utility/TaskPool.h" #include "DWARFASTParser.h" #include "DWARFASTParserClang.h" #include "DWARFCompileUnit.h" #include "DWARFDIECollection.h" #include "DWARFDebugAbbrev.h" #include "DWARFDebugAranges.h" #include "DWARFDebugInfo.h" #include "DWARFDebugLine.h" #include "DWARFDebugMacro.h" #include "DWARFDebugPubnames.h" #include "DWARFDebugRanges.h" #include "DWARFDeclContext.h" #include "DWARFFormValue.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" #include "llvm/Support/FileSystem.h" #include #include #include //#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN #ifdef ENABLE_DEBUG_PRINTF #include #define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DEBUG_PRINTF(fmt, ...) #endif using namespace lldb; using namespace lldb_private; // static inline bool // child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag) //{ // switch (tag) // { // default: // break; // case DW_TAG_subprogram: // case DW_TAG_inlined_subroutine: // case DW_TAG_class_type: // case DW_TAG_structure_type: // case DW_TAG_union_type: // return true; // } // return false; //} // namespace { PropertyDefinition g_properties[] = { {"comp-dir-symlink-paths", OptionValue::eTypeFileSpecList, true, 0, nullptr, nullptr, "If the DW_AT_comp_dir matches any of these paths the symbolic " "links will be resolved at DWARF parse time."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertySymLinkPaths }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return SymbolFileDWARF::GetPluginNameStatic(); } PluginProperties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } FileSpecList &GetSymLinkPaths() { OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList( nullptr, true, ePropertySymLinkPaths); assert(option_value); return option_value->GetCurrentValue(); } }; typedef std::shared_ptr SymbolFileDWARFPropertiesSP; static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() { static const auto g_settings_sp(std::make_shared()); return g_settings_sp; } } // anonymous namespace end static const char *removeHostnameFromPathname(const char *path_from_dwarf) { if (!path_from_dwarf || !path_from_dwarf[0]) { return path_from_dwarf; } const char *colon_pos = strchr(path_from_dwarf, ':'); if (nullptr == colon_pos) { return path_from_dwarf; } const char *slash_pos = strchr(path_from_dwarf, '/'); if (slash_pos && (slash_pos < colon_pos)) { return path_from_dwarf; } // check whether we have a windows path, and so the first character // is a drive-letter not a hostname. if (colon_pos == path_from_dwarf + 1 && isalpha(*path_from_dwarf) && strlen(path_from_dwarf) > 2 && '\\' == path_from_dwarf[2]) { return path_from_dwarf; } return colon_pos + 1; } static const char *resolveCompDir(const char *path_from_dwarf) { if (!path_from_dwarf) return nullptr; // DWARF2/3 suggests the form hostname:pathname for compilation directory. // Remove the host part if present. const char *local_path = removeHostnameFromPathname(path_from_dwarf); if (!local_path) return nullptr; bool is_symlink = false; FileSpec local_path_spec(local_path, false); const auto &file_specs = GetGlobalPluginProperties()->GetSymLinkPaths(); for (size_t i = 0; i < file_specs.GetSize() && !is_symlink; ++i) is_symlink = FileSpec::Equal(file_specs.GetFileSpecAtIndex(i), local_path_spec, true); if (!is_symlink) return local_path; namespace fs = llvm::sys::fs; if (fs::get_file_type(local_path_spec.GetPath(), false) != fs::file_type::symlink_file) return local_path; FileSpec resolved_local_path_spec; const auto error = FileSystem::Readlink(local_path_spec, resolved_local_path_spec); if (error.Success()) return resolved_local_path_spec.GetCString(); return nullptr; } void SymbolFileDWARF::Initialize() { LogChannelDWARF::Initialize(); PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); } void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) { if (!PluginManager::GetSettingForSymbolFilePlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForSymbolFilePlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the dwarf symbol-file plug-in."), is_global_setting); } } void SymbolFileDWARF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); LogChannelDWARF::Terminate(); } lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() { static ConstString g_name("dwarf"); return g_name; } const char *SymbolFileDWARF::GetPluginDescriptionStatic() { return "DWARF and DWARF3 debug symbol file reader."; } SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFile *obj_file) { return new SymbolFileDWARF(obj_file); } TypeList *SymbolFileDWARF::GetTypeList() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetTypeList(); else return m_obj_file->GetModule()->GetTypeList(); } void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset, dw_offset_t max_die_offset, uint32_t type_mask, TypeSet &type_set) { if (die) { const dw_offset_t die_offset = die.GetOffset(); if (die_offset >= max_die_offset) return; if (die_offset >= min_die_offset) { const dw_tag_t tag = die.Tag(); bool add_type = false; switch (tag) { case DW_TAG_array_type: add_type = (type_mask & eTypeClassArray) != 0; break; case DW_TAG_unspecified_type: case DW_TAG_base_type: add_type = (type_mask & eTypeClassBuiltin) != 0; break; case DW_TAG_class_type: add_type = (type_mask & eTypeClassClass) != 0; break; case DW_TAG_structure_type: add_type = (type_mask & eTypeClassStruct) != 0; break; case DW_TAG_union_type: add_type = (type_mask & eTypeClassUnion) != 0; break; case DW_TAG_enumeration_type: add_type = (type_mask & eTypeClassEnumeration) != 0; break; case DW_TAG_subroutine_type: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: add_type = (type_mask & eTypeClassFunction) != 0; break; case DW_TAG_pointer_type: add_type = (type_mask & eTypeClassPointer) != 0; break; case DW_TAG_rvalue_reference_type: case DW_TAG_reference_type: add_type = (type_mask & eTypeClassReference) != 0; break; case DW_TAG_typedef: add_type = (type_mask & eTypeClassTypedef) != 0; break; case DW_TAG_ptr_to_member_type: add_type = (type_mask & eTypeClassMemberPointer) != 0; break; } if (add_type) { const bool assert_not_being_parsed = true; Type *type = ResolveTypeUID(die, assert_not_being_parsed); if (type) { if (type_set.find(type) == type_set.end()) type_set.insert(type); } } } for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid(); child_die = child_die.GetSibling()) { GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set); } } } size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope, uint32_t type_mask, TypeList &type_list) { TypeSet type_set; CompileUnit *comp_unit = NULL; DWARFCompileUnit *dwarf_cu = NULL; if (sc_scope) comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); if (comp_unit) { dwarf_cu = GetDWARFCompileUnit(comp_unit); if (dwarf_cu == 0) return 0; GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), type_mask, type_set); } else { DWARFDebugInfo *info = DebugInfo(); if (info) { const size_t num_cus = info->GetNumCompileUnits(); for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) { dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set); } } } } std::set compiler_type_set; size_t num_types_added = 0; for (Type *type : type_set) { CompilerType compiler_type = type->GetForwardCompilerType(); if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) { compiler_type_set.insert(compiler_type); type_list.Insert(type->shared_from_this()); ++num_types_added; } } return num_types_added; } //---------------------------------------------------------------------- // Gets the first parent that is a lexical block, function or inlined // subroutine, or compile unit. //---------------------------------------------------------------------- DWARFDIE SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) { DWARFDIE die; for (die = child_die.GetParent(); die; die = die.GetParent()) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_compile_unit: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: return die; } } return DWARFDIE(); } SymbolFileDWARF::SymbolFileDWARF(ObjectFile *objfile) : SymbolFile(objfile), UserID(0), // Used by SymbolFileDWARFDebugMap to when // this class parses .o files to contain // the .o file index/ID m_debug_map_module_wp(), m_debug_map_symfile(NULL), m_data_debug_abbrev(), m_data_debug_aranges(), m_data_debug_frame(), m_data_debug_info(), m_data_debug_line(), m_data_debug_macro(), m_data_debug_loc(), m_data_debug_ranges(), m_data_debug_str(), m_data_apple_names(), m_data_apple_types(), m_data_apple_namespaces(), m_abbr(), m_info(), m_line(), m_apple_names_ap(), m_apple_types_ap(), m_apple_namespaces_ap(), m_apple_objc_ap(), m_function_basename_index(), m_function_fullname_index(), m_function_method_index(), m_function_selector_index(), m_objc_class_selectors_index(), m_global_index(), m_type_index(), m_namespace_index(), m_indexed(false), m_using_apple_tables(false), m_fetched_external_modules(false), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate), m_ranges(), m_unique_ast_type_map() {} SymbolFileDWARF::~SymbolFileDWARF() {} static const ConstString &GetDWARFMachOSegmentName() { static ConstString g_dwarf_section_name("__DWARF"); return g_dwarf_section_name; } UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) return debug_map_symfile->GetUniqueDWARFASTTypeMap(); else return m_unique_ast_type_map; } TypeSystem *SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); TypeSystem *type_system; if (debug_map_symfile) { type_system = debug_map_symfile->GetTypeSystemForLanguage(language); } else { type_system = m_obj_file->GetModule()->GetTypeSystemForLanguage(language); if (type_system) type_system->SetSymbolFile(this); } return type_system; } void SymbolFileDWARF::InitializeObject() { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const SectionList *section_list = module_sp->GetSectionList(); const Section *section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); // Memory map the DWARF mach-o segment so we have everything mmap'ed // to keep our heap memory usage down. if (section) m_obj_file->MemoryMapSectionData(section, m_dwarf_data); } get_apple_names_data(); if (m_data_apple_names.m_data.GetByteSize() > 0) { m_apple_names_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_names.m_data, get_debug_str_data(), ".apple_names")); if (m_apple_names_ap->IsValid()) m_using_apple_tables = true; else m_apple_names_ap.reset(); } get_apple_types_data(); if (m_data_apple_types.m_data.GetByteSize() > 0) { m_apple_types_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_types.m_data, get_debug_str_data(), ".apple_types")); if (m_apple_types_ap->IsValid()) m_using_apple_tables = true; else m_apple_types_ap.reset(); } get_apple_namespaces_data(); if (m_data_apple_namespaces.m_data.GetByteSize() > 0) { m_apple_namespaces_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_namespaces.m_data, get_debug_str_data(), ".apple_namespaces")); if (m_apple_namespaces_ap->IsValid()) m_using_apple_tables = true; else m_apple_namespaces_ap.reset(); } get_apple_objc_data(); if (m_data_apple_objc.m_data.GetByteSize() > 0) { m_apple_objc_ap.reset(new DWARFMappedHash::MemoryTable( m_data_apple_objc.m_data, get_debug_str_data(), ".apple_objc")); if (m_apple_objc_ap->IsValid()) m_using_apple_tables = true; else m_apple_objc_ap.reset(); } } bool SymbolFileDWARF::SupportedVersion(uint16_t version) { return version == 2 || version == 3 || version == 4; } uint32_t SymbolFileDWARF::CalculateAbilities() { uint32_t abilities = 0; if (m_obj_file != NULL) { const Section *section = NULL; const SectionList *section_list = m_obj_file->GetSectionList(); if (section_list == NULL) return 0; uint64_t debug_abbrev_file_size = 0; uint64_t debug_info_file_size = 0; uint64_t debug_line_file_size = 0; section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get(); if (section) section_list = §ion->GetChildren(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get(); if (section != NULL) { debug_info_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true) .get(); if (section) debug_abbrev_file_size = section->GetFileSize(); section = section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) .get(); if (section) debug_line_file_size = section->GetFileSize(); } else { const char *symfile_dir_cstr = m_obj_file->GetFileSpec().GetDirectory().GetCString(); if (symfile_dir_cstr) { if (strcasestr(symfile_dir_cstr, ".dsym")) { if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) { // We have a dSYM file that didn't have a any debug info. // If the string table has a size of 1, then it was made from // an executable with no debug info, or from an executable that // was stripped. section = section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true) .get(); if (section && section->GetFileSize() == 1) { m_obj_file->GetModule()->ReportWarning( "empty dSYM file detected, dSYM was created with an " "executable with no debug info."); } } } } } if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; if (debug_line_file_size > 0) abilities |= LineTables; } return abilities; } const DWARFDataExtractor & SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type, DWARFDataSegment &data_segment) { llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] { this->LoadSectionData(sect_type, std::ref(data_segment.m_data)); }); return data_segment.m_data; } void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type, DWARFDataExtractor &data) { ModuleSP module_sp(m_obj_file->GetModule()); const SectionList *section_list = module_sp->GetSectionList(); if (section_list) { SectionSP section_sp(section_list->FindSectionByType(sect_type, true)); if (section_sp) { // See if we memory mapped the DWARF segment? if (m_dwarf_data.GetByteSize()) { data.SetData(m_dwarf_data, section_sp->GetOffset(), section_sp->GetFileSize()); } else { if (m_obj_file->ReadSectionData(section_sp.get(), data) == 0) data.Clear(); } } } } const DWARFDataExtractor &SymbolFileDWARF::get_debug_abbrev_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAbbrev, m_data_debug_abbrev); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_addr_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAddr, m_data_debug_addr); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_aranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugAranges, m_data_debug_aranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_frame_data() { return GetCachedSectionData(eSectionTypeDWARFDebugFrame, m_data_debug_frame); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_info_data() { return GetCachedSectionData(eSectionTypeDWARFDebugInfo, m_data_debug_info); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_line_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLine, m_data_debug_line); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_macro_data() { return GetCachedSectionData(eSectionTypeDWARFDebugMacro, m_data_debug_macro); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() { return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_ranges_data() { return GetCachedSectionData(eSectionTypeDWARFDebugRanges, m_data_debug_ranges); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStr, m_data_debug_str); } const DWARFDataExtractor &SymbolFileDWARF::get_debug_str_offsets_data() { return GetCachedSectionData(eSectionTypeDWARFDebugStrOffsets, m_data_debug_str_offsets); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_names_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNames, m_data_apple_names); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_types_data() { return GetCachedSectionData(eSectionTypeDWARFAppleTypes, m_data_apple_types); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_namespaces_data() { return GetCachedSectionData(eSectionTypeDWARFAppleNamespaces, m_data_apple_namespaces); } const DWARFDataExtractor &SymbolFileDWARF::get_apple_objc_data() { return GetCachedSectionData(eSectionTypeDWARFAppleObjC, m_data_apple_objc); } DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() { if (m_abbr.get() == NULL) { const DWARFDataExtractor &debug_abbrev_data = get_debug_abbrev_data(); if (debug_abbrev_data.GetByteSize() > 0) { m_abbr.reset(new DWARFDebugAbbrev()); if (m_abbr.get()) m_abbr->Parse(debug_abbrev_data); } } return m_abbr.get(); } const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const { return m_abbr.get(); } DWARFDebugInfo *SymbolFileDWARF::DebugInfo() { if (m_info.get() == NULL) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_info_data().GetByteSize() > 0) { m_info.reset(new DWARFDebugInfo()); if (m_info.get()) { m_info->SetDwarfData(this); } } } return m_info.get(); } const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const { return m_info.get(); } DWARFCompileUnit * SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) { if (!comp_unit) return nullptr; DWARFDebugInfo *info = DebugInfo(); if (info) { // Just a normal DWARF file whose user ID for the compile unit is // the DWARF offset itself DWARFCompileUnit *dwarf_cu = info->GetCompileUnit((dw_offset_t)comp_unit->GetID()); if (dwarf_cu && dwarf_cu->GetUserData() == NULL) dwarf_cu->SetUserData(comp_unit); return dwarf_cu; } return NULL; } DWARFDebugRanges *SymbolFileDWARF::DebugRanges() { if (m_ranges.get() == NULL) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, static_cast(this)); if (get_debug_ranges_data().GetByteSize() > 0) { m_ranges.reset(new DWARFDebugRanges()); if (m_ranges.get()) m_ranges->Extract(this); } } return m_ranges.get(); } const DWARFDebugRanges *SymbolFileDWARF::DebugRanges() const { return m_ranges.get(); } lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { CompUnitSP cu_sp; if (dwarf_cu) { CompileUnit *comp_unit = (CompileUnit *)dwarf_cu->GetUserData(); if (comp_unit) { // We already parsed this compile unit, had out a shared pointer to it cu_sp = comp_unit->shared_from_this(); } else { if (dwarf_cu->GetSymbolFileDWARF() != this) { return dwarf_cu->GetSymbolFileDWARF()->ParseCompileUnit(dwarf_cu, cu_idx); } else if (dwarf_cu->GetOffset() == 0 && GetDebugMapSymfile()) { // Let the debug map create the compile unit cu_sp = m_debug_map_symfile->GetCompileUnit(this); dwarf_cu->SetUserData(cu_sp.get()); } else { ModuleSP module_sp(m_obj_file->GetModule()); if (module_sp) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { FileSpec cu_file_spec{cu_die.GetName(), false}; if (cu_file_spec) { // If we have a full path to the compile unit, we don't need to // resolve // the file. This can be expensive e.g. when the source files are // NFS mounted. if (cu_file_spec.IsRelative()) { const char *cu_comp_dir{ cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)}; cu_file_spec.PrependPathComponent(resolveCompDir(cu_comp_dir)); } std::string remapped_file; if (module_sp->RemapSourceFile(cu_file_spec.GetPath(), remapped_file)) cu_file_spec.SetFile(remapped_file, false); } LanguageType cu_language = DWARFCompileUnit::LanguageTypeFromDWARF( cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0)); bool is_optimized = dwarf_cu->GetIsOptimized(); cu_sp.reset(new CompileUnit( module_sp, dwarf_cu, cu_file_spec, dwarf_cu->GetID(), cu_language, is_optimized ? eLazyBoolYes : eLazyBoolNo)); if (cu_sp) { // If we just created a compile unit with an invalid file spec, // try and get the // first entry in the supports files from the line table as that // should be the // compile unit. if (!cu_file_spec) { cu_file_spec = cu_sp->GetSupportFiles().GetFileSpecAtIndex(1); if (cu_file_spec) { (FileSpec &)(*cu_sp) = cu_file_spec; // Also fix the invalid file spec which was copied from the // compile unit. cu_sp->GetSupportFiles().Replace(0, cu_file_spec); } } dwarf_cu->SetUserData(cu_sp.get()); // Figure out the compile unit index if we weren't given one if (cu_idx == UINT32_MAX) DebugInfo()->GetCompileUnit(dwarf_cu->GetOffset(), &cu_idx); m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( cu_idx, cu_sp); } } } } } } return cu_sp; } uint32_t SymbolFileDWARF::GetNumCompileUnits() { DWARFDebugInfo *info = DebugInfo(); if (info) return info->GetNumCompileUnits(); return 0; } CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { CompUnitSP cu_sp; DWARFDebugInfo *info = DebugInfo(); if (info) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) cu_sp = ParseCompileUnit(dwarf_cu, cu_idx); } return cu_sp; } Function *SymbolFileDWARF::ParseCompileUnitFunction(const SymbolContext &sc, const DWARFDIE &die) { if (die.IsValid()) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) return dwarf_ast->ParseFunctionFromDWARF(sc, die); } } return nullptr; } bool SymbolFileDWARF::FixupAddress(Address &addr) { SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { return debug_map_symfile->LinkOSOAddress(addr); } // This is a normal DWARF file, no address fixups need to happen return true; } lldb::LanguageType SymbolFileDWARF::ParseCompileUnitLanguage(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetLanguageType(); else return eLanguageTypeUnknown; } size_t SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) { assert(sc.comp_unit); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { DWARFDIECollection function_dies; const size_t num_functions = dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies); size_t func_idx; for (func_idx = 0; func_idx < num_functions; ++func_idx) { DWARFDIE die = function_dies.GetDIEAtIndex(func_idx); if (sc.comp_unit->FindFunctionByUID(die.GetID()).get() == NULL) { if (ParseCompileUnitFunction(sc, die)) ++functions_added; } } // FixupTypes(); } return functions_added; } bool SymbolFileDWARF::ParseCompileUnitSupportFiles( const SymbolContext &sc, FileSpecList &support_files) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (cu_die) { const char *cu_comp_dir = resolveCompDir( cu_die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr)); const dw_offset_t stmt_list = cu_die.GetAttributeValueAsUnsigned( DW_AT_stmt_list, DW_INVALID_OFFSET); if (stmt_list != DW_INVALID_OFFSET) { // All file indexes in DWARF are one based and a file of index zero is // supposed to be the compile unit itself. support_files.Append(*sc.comp_unit); return DWARFDebugLine::ParseSupportFiles( sc.comp_unit->GetModule(), get_debug_line_data(), cu_comp_dir, stmt_list, support_files); } } } return false; } bool SymbolFileDWARF::ParseCompileUnitIsOptimized( const lldb_private::SymbolContext &sc) { DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) return dwarf_cu->GetIsOptimized(); return false; } bool SymbolFileDWARF::ParseImportedModules( const lldb_private::SymbolContext &sc, std::vector &imported_modules) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (ClangModulesDeclVendor::LanguageSupportsClangModules( sc.comp_unit->GetLanguage())) { UpdateExternalModuleListIfNeeded(); if (sc.comp_unit) { const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die) { for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { if (child_die.Tag() == DW_TAG_imported_declaration) { if (DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import)) { if (module_die.Tag() == DW_TAG_module) { if (const char *name = module_die.GetAttributeValueAsString( DW_AT_name, nullptr)) { ConstString const_name(name); imported_modules.push_back(const_name); } } } } } } } else { for (const auto &pair : m_external_type_modules) { imported_modules.push_back(pair.first); } } } } return false; } struct ParseDWARFLineTableCallbackInfo { LineTable *line_table; std::unique_ptr sequence_ap; lldb::addr_t addr_mask; }; //---------------------------------------------------------------------- // ParseStatementTableCallback //---------------------------------------------------------------------- static void ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State &state, void *userData) { if (state.row == DWARFDebugLine::State::StartParsingLineTable) { // Just started parsing the line table } else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) { // Done parsing line table, nothing to do for the cleanup } else { ParseDWARFLineTableCallbackInfo *info = (ParseDWARFLineTableCallbackInfo *)userData; LineTable *line_table = info->line_table; // If this is our first time here, we need to create a // sequence container. if (!info->sequence_ap.get()) { info->sequence_ap.reset(line_table->CreateLineSequenceContainer()); assert(info->sequence_ap.get()); } line_table->AppendLineEntryToSequence( info->sequence_ap.get(), state.address & info->addr_mask, state.line, state.column, state.file, state.is_stmt, state.basic_block, state.prologue_end, state.epilogue_begin, state.end_sequence); if (state.end_sequence) { // First, put the current sequence into the line table. line_table->InsertSequence(info->sequence_ap.get()); // Then, empty it to prepare for the next sequence. info->sequence_ap->Clear(); } } } bool SymbolFileDWARF::ParseCompileUnitLineTable(const SymbolContext &sc) { assert(sc.comp_unit); if (sc.comp_unit->GetLineTable() != NULL) return true; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (dwarf_cu_die) { const dw_offset_t cu_line_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_stmt_list, DW_INVALID_OFFSET); if (cu_line_offset != DW_INVALID_OFFSET) { std::unique_ptr line_table_ap(new LineTable(sc.comp_unit)); if (line_table_ap.get()) { ParseDWARFLineTableCallbackInfo info; info.line_table = line_table_ap.get(); /* * MIPS: * The SymbolContext may not have a valid target, thus we may not be * able * to call Address::GetOpcodeLoadAddress() which would clear the bit * #0 * for MIPS. Use ArchSpec to clear the bit #0. */ ArchSpec arch; GetObjectFile()->GetArchitecture(arch); switch (arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: info.addr_mask = ~((lldb::addr_t)1); break; default: info.addr_mask = ~((lldb::addr_t)0); break; } lldb::offset_t offset = cu_line_offset; DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (debug_map_symfile) { // We have an object file that has a line table with addresses // that are not linked. We need to link the line table and convert // the addresses that are relative to the .o file into addresses // for the main executable. sc.comp_unit->SetLineTable( debug_map_symfile->LinkOSOLineTable(this, line_table_ap.get())); } else { sc.comp_unit->SetLineTable(line_table_ap.release()); return true; } } } } } return false; } lldb_private::DebugMacrosSP SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) { auto iter = m_debug_macros_map.find(*offset); if (iter != m_debug_macros_map.end()) return iter->second; const DWARFDataExtractor &debug_macro_data = get_debug_macro_data(); if (debug_macro_data.GetByteSize() == 0) return DebugMacrosSP(); lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros()); m_debug_macros_map[*offset] = debug_macros_sp; const DWARFDebugMacroHeader &header = DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset); DWARFDebugMacroEntry::ReadMacroEntries(debug_macro_data, get_debug_str_data(), header.OffsetIs64Bit(), offset, this, debug_macros_sp); return debug_macros_sp; } bool SymbolFileDWARF::ParseCompileUnitDebugMacros(const SymbolContext &sc) { assert(sc.comp_unit); DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu == nullptr) return false; const DWARFDIE dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); if (!dwarf_cu_die) return false; lldb::offset_t sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros, DW_INVALID_OFFSET); if (sect_offset == DW_INVALID_OFFSET) return false; sc.comp_unit->SetDebugMacros(ParseDebugMacros(§_offset)); return true; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc, Block *parent_block, const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) { size_t blocks_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); switch (tag) { case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: case DW_TAG_lexical_block: { Block *block = NULL; if (tag == DW_TAG_subprogram) { // Skip any DW_TAG_subprogram DIEs that are inside // of a normal or inlined functions. These will be // parsed on their own as separate entities. if (depth > 0) break; block = parent_block; } else { BlockSP block_sp(new Block(die.GetID())); parent_block->AddChild(block_sp); block = block_sp.get(); } DWARFRangeList ranges; const char *name = NULL; const char *mangled_name = NULL; int decl_file = 0; int decl_line = 0; int decl_column = 0; int call_file = 0; int call_line = 0; int call_column = 0; if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column, nullptr)) { if (tag == DW_TAG_subprogram) { assert(subprogram_low_pc == LLDB_INVALID_ADDRESS); subprogram_low_pc = ranges.GetMinRangeBase(0); } else if (tag == DW_TAG_inlined_subroutine) { // We get called here for inlined subroutines in two ways. // The first time is when we are making the Function object // for this inlined concrete instance. Since we're creating a top // level block at // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we // need to // adjust the containing address. // The second time is when we are parsing the blocks inside the // function that contains // the inlined concrete instance. Since these will be blocks inside // the containing "real" // function the offset will be for that function. if (subprogram_low_pc == LLDB_INVALID_ADDRESS) { subprogram_low_pc = ranges.GetMinRangeBase(0); } } const size_t num_ranges = ranges.GetSize(); for (size_t i = 0; i < num_ranges; ++i) { const DWARFRangeList::Entry &range = ranges.GetEntryRef(i); const addr_t range_base = range.GetRangeBase(); if (range_base >= subprogram_low_pc) block->AddRange(Block::Range(range_base - subprogram_low_pc, range.GetByteSize())); else { GetObjectFile()->GetModule()->ReportError( "0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64 ") which has a base that is less than the function's low PC " "0x%" PRIx64 ". Please file a bug and attach the file at the " "start of this error message", block->GetID(), range_base, range.GetRangeEnd(), subprogram_low_pc); } } block->FinalizeRanges(); if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) { std::unique_ptr decl_ap; if (decl_file != 0 || decl_line != 0 || decl_column != 0) decl_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), decl_line, decl_column)); std::unique_ptr call_ap; if (call_file != 0 || call_line != 0 || call_column != 0) call_ap.reset(new Declaration( sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), call_line, call_column)); block->SetInlinedFunctionInfo(name, mangled_name, decl_ap.get(), call_ap.get()); } ++blocks_added; if (die.HasChildren()) { blocks_added += ParseFunctionBlocks(sc, block, die.GetFirstChild(), subprogram_low_pc, depth + 1); } } } break; default: break; } // Only parse siblings of the block if we are not at depth zero. A depth // of zero indicates we are currently parsing the top level // DW_TAG_subprogram DIE if (depth == 0) die.Clear(); else die = die.GetSibling(); } return blocks_added; } bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) { if (parent_die) { for (DWARFDIE die = parent_die.GetFirstChild(); die; die = die.GetSibling()) { dw_tag_t tag = die.Tag(); bool check_virtuality = false; switch (tag) { case DW_TAG_inheritance: case DW_TAG_subprogram: check_virtuality = true; break; default: break; } if (check_virtuality) { if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0) return true; } } } return false; } void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) { TypeSystem *type_system = decl_ctx.GetTypeSystem(); DWARFASTParser *ast_parser = type_system->GetDWARFParser(); std::vector decl_ctx_die_list = ast_parser->GetDIEForDeclContext(decl_ctx); for (DWARFDIE decl_ctx_die : decl_ctx_die_list) for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl; decl = decl.GetSibling()) ast_parser->GetDeclForUIDFromDWARF(decl); } SymbolFileDWARF *SymbolFileDWARF::GetDWARFForUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile(); if (debug_map) return debug_map->GetSymbolFileByOSOIndex( debug_map->GetOSOIndexFromUserID(uid)); return this; } DWARFDIE SymbolFileDWARF::GetDIEFromUID(lldb::user_id_t uid) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API // we must make sure we use the correct DWARF file when resolving things. // On MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple // SymbolFileDWARF classes, one for each .o file. We can often end up // with references to other DWARF objects and we must be ready to receive // a "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. SymbolFileDWARF *dwarf = GetDWARFForUID(uid); if (dwarf) return dwarf->GetDIE(DIERef(uid, dwarf)); return DWARFDIE(); } CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDecl(); return CompilerDecl(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetDeclContext(); return CompilerDeclContext(); } CompilerDeclContext SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE die = GetDIEFromUID(type_uid); if (die) return die.GetContainingDeclContext(); return CompilerDeclContext(); } Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) { // Anytime we have a lldb::user_id_t, we must get the DIE by // calling SymbolFileDWARF::GetDIEFromUID(). See comments inside // the SymbolFileDWARF::GetDIEFromUID() for details. DWARFDIE type_die = GetDIEFromUID(type_uid); if (type_die) return type_die.ResolveType(); else return nullptr; } Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) { return ResolveType(GetDIE(die_ref), true); } Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die, bool assert_not_being_parsed) { if (die) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'", die.GetOffset(), die.GetTagAsCString(), die.GetName()); // We might be coming in in the middle of a type tree (a class // within a class, an enum within a class), so parse any needed // parent DIEs before we get to this one... DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die); if (decl_ctx_die) { if (log) { switch (decl_ctx_die.Tag()) { case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: { // Get the type, which could be a forward declaration if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' " "resolve parent forward type for 0x%8.8x", die.GetOffset(), die.GetTagAsCString(), die.GetName(), decl_ctx_die.GetOffset()); } break; default: break; } } } return ResolveType(die); } return NULL; } // This function is used when SymbolFileDWARFDebugMap owns a bunch of // SymbolFileDWARF objects to detect if this DWARF file is the one that // can resolve a compiler_type. bool SymbolFileDWARF::HasForwardDeclForClangType( const CompilerType &compiler_type) { CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); if (GetForwardDeclClangTypeToDie().count( compiler_type_no_qualifiers.GetOpaqueQualType())) { return true; } TypeSystem *type_system = compiler_type.GetTypeSystem(); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(type_system); if (!clang_type_system) return false; DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); return ast_parser->GetClangASTImporter().CanImport(compiler_type); } bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { std::lock_guard guard( GetObjectFile()->GetModule()->GetMutex()); ClangASTContext *clang_type_system = llvm::dyn_cast_or_null(compiler_type.GetTypeSystem()); if (clang_type_system) { DWARFASTParserClang *ast_parser = static_cast(clang_type_system->GetDWARFParser()); if (ast_parser && ast_parser->GetClangASTImporter().CanImport(compiler_type)) return ast_parser->GetClangASTImporter().CompleteType(compiler_type); } // We have a struct/union/class/enum that needs to be fully resolved. CompilerType compiler_type_no_qualifiers = ClangUtil::RemoveFastQualifiers(compiler_type); auto die_it = GetForwardDeclClangTypeToDie().find( compiler_type_no_qualifiers.GetOpaqueQualType()); if (die_it == GetForwardDeclClangTypeToDie().end()) { // We have already resolved this type... return true; } DWARFDIE dwarf_die = GetDIE(die_it->getSecond()); if (dwarf_die) { // Once we start resolving this type, remove it from the forward declaration // map in case anyone child members or other types require this type to get // resolved. // The type will get resolved when all of the calls to // SymbolFileDWARF::ResolveClangOpaqueTypeDefinition // are done. GetForwardDeclClangTypeToDie().erase(die_it); Type *type = GetDIEToType().lookup(dwarf_die.GetDIE()); Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | DWARF_LOG_TYPE_COMPLETION)); if (log) GetObjectFile()->GetModule()->LogMessageVerboseBacktrace( log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", dwarf_die.GetID(), dwarf_die.GetTagAsCString(), type->GetName().AsCString()); assert(compiler_type); DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser(); if (dwarf_ast) return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type); } return false; } Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die, bool assert_not_being_parsed, bool resolve_function_context) { if (die) { Type *type = GetTypeForDIE(die, resolve_function_context).get(); if (assert_not_being_parsed) { if (type != DIE_IS_BEING_PARSED) return type; GetObjectFile()->GetModule()->ReportError( "Parsing a die that is being parsed die: 0x%8.8x: %s %s", die.GetOffset(), die.GetTagAsCString(), die.GetName()); } else return type; } return nullptr; } CompileUnit * SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit *dwarf_cu, uint32_t cu_idx) { // Check if the symbol vendor already knows about this compile unit? if (dwarf_cu->GetUserData() == NULL) { // The symbol vendor doesn't know about this compile unit, we // need to parse and add it to the symbol vendor object. return ParseCompileUnit(dwarf_cu, cu_idx).get(); } return (CompileUnit *)dwarf_cu->GetUserData(); } size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name, DIEArray &method_die_offsets) { method_die_offsets.clear(); if (m_using_apple_tables) { if (m_apple_objc_ap.get()) m_apple_objc_ap->FindByName(class_name.GetCString(), method_die_offsets); } else { if (!m_indexed) Index(); m_objc_class_selectors_index.Find(class_name, method_die_offsets); } return method_die_offsets.size(); } bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) { sc.Clear(false); if (die) { // Check if the symbol vendor already knows about this compile unit? sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, die); if (sc.function) { sc.module_sp = sc.function->CalculateSymbolContextModule(); return true; } } return false; } lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) { UpdateExternalModuleListIfNeeded(); const auto &pos = m_external_type_modules.find(name); if (pos != m_external_type_modules.end()) return pos->second; else return lldb::ModuleSP(); } DWARFDIE SymbolFileDWARF::GetDIE(const DIERef &die_ref) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) return debug_info->GetDIE(die_ref); else return DWARFDIE(); } std::unique_ptr SymbolFileDWARF::GetDwoSymbolFileForCompileUnit( DWARFCompileUnit &dwarf_cu, const DWARFDebugInfoEntry &cu_die) { // If we are using a dSYM file, we never want the standard DWO files since // the -gmodule support uses the same DWO machanism to specify full debug // info files for modules. if (GetDebugMapSymfile()) return nullptr; const char *dwo_name = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_GNU_dwo_name, nullptr); if (!dwo_name) return nullptr; FileSpec dwo_file(dwo_name, true); if (dwo_file.IsRelative()) { const char *comp_dir = cu_die.GetAttributeValueAsString( this, &dwarf_cu, DW_AT_comp_dir, nullptr); if (!comp_dir) return nullptr; dwo_file.SetFile(comp_dir, true); dwo_file.AppendPathComponent(dwo_name); } if (!dwo_file.Exists()) return nullptr; const lldb::offset_t file_offset = 0; DataBufferSP dwo_file_data_sp; lldb::offset_t dwo_file_data_offset = 0; ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin( GetObjectFile()->GetModule(), &dwo_file, file_offset, dwo_file.GetByteSize(), dwo_file_data_sp, dwo_file_data_offset); if (dwo_obj_file == nullptr) return nullptr; return llvm::make_unique(dwo_obj_file, &dwarf_cu); } void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { if (m_fetched_external_modules) return; m_fetched_external_modules = true; DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); const DWARFDIE die = dwarf_cu->GetCompileUnitDIEOnly(); if (die && die.HasChildren() == false) { const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr); if (name) { ConstString const_name(name); if (m_external_type_modules.find(const_name) == m_external_type_modules.end()) { ModuleSP module_sp; const char *dwo_path = die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr); if (dwo_path) { ModuleSpec dwo_module_spec; dwo_module_spec.GetFileSpec().SetFile(dwo_path, false); if (dwo_module_spec.GetFileSpec().IsRelative()) { const char *comp_dir = die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr); if (comp_dir) { dwo_module_spec.GetFileSpec().SetFile(comp_dir, true); dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path); } } dwo_module_spec.GetArchitecture() = m_obj_file->GetModule()->GetArchitecture(); // printf ("Loading dwo = '%s'\n", dwo_path); Status error = ModuleList::GetSharedModule( dwo_module_spec, module_sp, NULL, NULL, NULL); if (!module_sp) { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: unable to locate module needed for external types: " "%s\nerror: %s\nDebugging will be degraded due to missing " "types. Rebuilding your project will regenerate the needed " "module files.", die.GetOffset(), dwo_module_spec.GetFileSpec().GetPath().c_str(), error.AsCString("unknown error")); } } m_external_type_modules[const_name] = module_sp; } } } } } SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() { if (!m_global_aranges_ap) { m_global_aranges_ap.reset(new GlobalVariableMap()); ModuleSP module_sp = GetObjectFile()->GetModule(); if (module_sp) { const size_t num_cus = module_sp->GetNumCompileUnits(); for (size_t i = 0; i < num_cus; ++i) { CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i); if (cu_sp) { VariableListSP globals_sp = cu_sp->GetVariableList(true); if (globals_sp) { const size_t num_globals = globals_sp->GetSize(); for (size_t g = 0; g < num_globals; ++g) { VariableSP var_sp = globals_sp->GetVariableAtIndex(g); if (var_sp && !var_sp->GetLocationIsConstantValueData()) { const DWARFExpression &location = var_sp->LocationExpression(); Value location_result; Status error; if (location.Evaluate(nullptr, nullptr, nullptr, LLDB_INVALID_ADDRESS, nullptr, nullptr, location_result, &error)) { if (location_result.GetValueType() == Value::eValueTypeFileAddress) { lldb::addr_t file_addr = location_result.GetScalar().ULongLong(); lldb::addr_t byte_size = 1; if (var_sp->GetType()) byte_size = var_sp->GetType()->GetByteSize(); m_global_aranges_ap->Append(GlobalVariableMap::Entry( file_addr, byte_size, var_sp.get())); } } } } } } } } m_global_aranges_ap->Sort(); } return *m_global_aranges_ap; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, uint32_t resolve_scope, SymbolContext &sc) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "SymbolFileDWARF::" "ResolveSymbolContext (so_addr = { " "section = %p, offset = 0x%" PRIx64 " }, resolve_scope = 0x%8.8x)", static_cast(so_addr.GetSection().get()), so_addr.GetOffset(), resolve_scope); uint32_t resolved = 0; if (resolve_scope & (eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock | eSymbolContextLineEntry | eSymbolContextVariable)) { lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const dw_offset_t cu_offset = debug_info->GetCompileUnitAranges().FindAddress(file_vm_addr); if (cu_offset == DW_INVALID_OFFSET) { // Global variables are not in the compile unit address ranges. The only // way to // currently find global variables is to iterate over the // .debug_pubnames or the // __apple_names table and find all items in there that point to // DW_TAG_variable // DIEs and then find the address that matches. if (resolve_scope & eSymbolContextVariable) { GlobalVariableMap &map = GetGlobalAranges(); const GlobalVariableMap::Entry *entry = map.FindEntryThatContains(file_vm_addr); if (entry && entry->data) { Variable *variable = entry->data; SymbolContextScope *scc = variable->GetSymbolContextScope(); if (scc) { scc->CalculateSymbolContext(&sc); sc.variable = variable; } return sc.GetResolvedMask(); } } } else { uint32_t cu_idx = DW_INVALID_INDEX; DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnit(cu_offset, &cu_idx); if (dwarf_cu) { sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { resolved |= eSymbolContextCompUnit; bool force_check_line_table = false; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit->FindFunctionByUID(function_die.GetID()).get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } else { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. force_check_line_table = true; } if (sc.function != NULL) { resolved |= eSymbolContextFunction; if (resolve_scope & eSymbolContextBlock) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else sc.block = block.FindBlockByID(function_die.GetID()); if (sc.block) resolved |= eSymbolContextBlock; } } } if ((resolve_scope & eSymbolContextLineEntry) || force_check_line_table) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL) { // And address that makes it into this function should be in // terms // of this debug file if there is no debug map, or it will be an // address in the .o file which needs to be fixed up to be in // terms // of the debug map executable. Either way, calling // FixupAddress() // will work for us. Address exe_so_addr(so_addr); if (FixupAddress(exe_so_addr)) { if (line_table->FindLineEntryByAddress(exe_so_addr, sc.line_entry)) { resolved |= eSymbolContextLineEntry; } } } } if (force_check_line_table && !(resolved & eSymbolContextLineEntry)) { // We might have had a compile unit that had discontiguous // address ranges where the gaps are symbols that don't have // any debug info. Discontiguous compile unit address ranges // should only happen when there aren't other functions from // other compile units in these gaps. This helps keep the size // of the aranges down. sc.comp_unit = NULL; resolved &= ~eSymbolContextCompUnit; } } else { GetObjectFile()->GetModule()->ReportWarning( "0x%8.8x: compile unit %u failed to create a valid " "lldb_private::CompileUnit class.", cu_offset, cu_idx); } } } } } return resolved; } uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { const uint32_t prev_size = sc_list.GetSize(); if (resolve_scope & eSymbolContextCompUnit) { DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { uint32_t cu_idx; DWARFCompileUnit *dwarf_cu = NULL; for (cu_idx = 0; (dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) { CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); const bool full_match = (bool)file_spec.GetDirectory(); bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Equal(file_spec, *dc_cu, full_match); if (check_inlines || file_spec_matches_cu_file_spec) { SymbolContext sc(m_obj_file->GetModule()); sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); if (sc.comp_unit) { uint32_t file_idx = UINT32_MAX; // If we are looking for inline functions only and we don't // find it in the support files, we are done. if (check_inlines) { file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx == UINT32_MAX) continue; } if (line != 0) { LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table != NULL && line != 0) { // We will have already looked up the file index if // we are searching for inline entries. if (!check_inlines) file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex( 1, file_spec, true); if (file_idx != UINT32_MAX) { uint32_t found_line; uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex( 0, file_idx, line, false, &sc.line_entry); found_line = sc.line_entry.line; while (line_idx != UINT32_MAX) { sc.function = NULL; sc.block = NULL; if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) { const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); if (file_vm_addr != LLDB_INVALID_ADDRESS) { DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr); DWARFDIE block_die; if (function_die) { sc.function = sc.comp_unit ->FindFunctionByUID(function_die.GetID()) .get(); if (sc.function == NULL) sc.function = ParseCompileUnitFunction(sc, function_die); if (sc.function && (resolve_scope & eSymbolContextBlock)) block_die = function_die.LookupDeepestBlock(file_vm_addr); } if (sc.function != NULL) { Block &block = sc.function->GetBlock(true); if (block_die) sc.block = block.FindBlockByID(block_die.GetID()); else if (function_die) sc.block = block.FindBlockByID(function_die.GetID()); } } } sc_list.Append(sc); line_idx = line_table->FindLineEntryIndexByFileIndex( line_idx + 1, file_idx, found_line, true, &sc.line_entry); } } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } } else if (file_spec_matches_cu_file_spec && !check_inlines) { // only append the context if we aren't looking for inline call // sites // by file and line and if the file spec matches that of the // compile unit sc_list.Append(sc); } if (!check_inlines) break; } } } } } return sc_list.GetSize() - prev_size; } void SymbolFileDWARF::PreloadSymbols() { std::lock_guard guard( GetObjectFile()->GetModule()->GetMutex()); Index(); } void SymbolFileDWARF::Index() { if (m_indexed) return; m_indexed = true; static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer( func_cat, "SymbolFileDWARF::Index (%s)", GetObjectFile()->GetFileSpec().GetFilename().AsCString("")); DWARFDebugInfo *debug_info = DebugInfo(); if (debug_info) { const uint32_t num_compile_units = GetNumCompileUnits(); if (num_compile_units == 0) return; std::vector function_basename_index(num_compile_units); std::vector function_fullname_index(num_compile_units); std::vector function_method_index(num_compile_units); std::vector function_selector_index(num_compile_units); std::vector objc_class_selectors_index(num_compile_units); std::vector global_index(num_compile_units); std::vector type_index(num_compile_units); std::vector namespace_index(num_compile_units); // std::vector might be implemented using bit test-and-set, so use // uint8_t instead. std::vector clear_cu_dies(num_compile_units, false); auto parser_fn = [debug_info, &function_basename_index, &function_fullname_index, &function_method_index, &function_selector_index, &objc_class_selectors_index, &global_index, &type_index, - &namespace_index](uint32_t cu_idx) { + &namespace_index](size_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { dwarf_cu->Index( function_basename_index[cu_idx], function_fullname_index[cu_idx], function_method_index[cu_idx], function_selector_index[cu_idx], objc_class_selectors_index[cu_idx], global_index[cu_idx], type_index[cu_idx], namespace_index[cu_idx]); } - return cu_idx; }; - auto extract_fn = [debug_info, &clear_cu_dies](uint32_t cu_idx) { + auto extract_fn = [debug_info, &clear_cu_dies](size_t cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu) { // dwarf_cu->ExtractDIEsIfNeeded(false) will return zero if the // DIEs for a compile unit have already been parsed. if (dwarf_cu->ExtractDIEsIfNeeded(false) > 1) clear_cu_dies[cu_idx] = true; } }; // Create a task runner that extracts dies for each DWARF compile unit in a // separate thread //---------------------------------------------------------------------- // First figure out which compile units didn't have their DIEs already // parsed and remember this. If no DIEs were parsed prior to this index // function call, we are going to want to clear the CU dies after we // are done indexing to make sure we don't pull in all DWARF dies, but // we need to wait until all compile units have been indexed in case // a DIE in one compile unit refers to another and the indexes accesses // those DIEs. //---------------------------------------------------------------------- TaskMapOverInt(0, num_compile_units, extract_fn); // Now create a task runner that can index each DWARF compile unit in a // separate // thread so we can index quickly. TaskMapOverInt(0, num_compile_units, parser_fn); auto finalize_fn = [](NameToDIE &index, std::vector &srcs) { for (auto &src : srcs) index.Append(src); index.Finalize(); }; TaskPool::RunTasks( [&]() { finalize_fn(m_function_basename_index, function_basename_index); }, [&]() { finalize_fn(m_function_fullname_index, function_fullname_index); }, [&]() { finalize_fn(m_function_method_index, function_method_index); }, [&]() { finalize_fn(m_function_selector_index, function_selector_index); }, [&]() { finalize_fn(m_objc_class_selectors_index, objc_class_selectors_index); }, [&]() { finalize_fn(m_global_index, global_index); }, [&]() { finalize_fn(m_type_index, type_index); }, [&]() { finalize_fn(m_namespace_index, namespace_index); }); //---------------------------------------------------------------------- // Keep memory down by clearing DIEs for any compile units if indexing // caused us to load the compile unit's DIEs. //---------------------------------------------------------------------- for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { if (clear_cu_dies[cu_idx]) debug_info->GetCompileUnitAtIndex(cu_idx)->ClearDIEs(true); } #if defined(ENABLE_DEBUG_PRINTF) StreamFile s(stdout, false); s.Printf("DWARF index for '%s':", GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); #endif } } bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile( const lldb_private::CompilerDeclContext *decl_ctx) { if (decl_ctx == nullptr || !decl_ctx->IsValid()) { // Invalid namespace decl which means we aren't matching only things // in this symbol file, so return true to indicate it matches this // symbol file. return true; } TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem(); TypeSystem *type_system = GetTypeSystemForLanguage( decl_ctx_type_system->GetMinimumLanguage(nullptr)); if (decl_ctx_type_system == type_system) return true; // The type systems match, return true // The namespace AST was valid, and it does not match... Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "Valid namespace does not match symbol file"); return false; } uint32_t SymbolFileDWARF::FindGlobalVariables( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables)", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { const char *name_cstr = name.GetCString(); llvm::StringRef basename; llvm::StringRef context; if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename)) basename = name_cstr; m_apple_names_ap->FindByName(basename.data(), die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); bool done = false; for (size_t i = 0; i < num_die_matches && !done; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { switch (die.Tag()) { default: case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_try_block: case DW_TAG_catch_block: break; case DW_TAG_variable: { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); if (parent_decl_ctx) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_parent_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (!actual_parent_decl_ctx || actual_parent_decl_ctx != *parent_decl_ctx) continue; } } ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) done = true; } break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } } // Return the number of variable that were appended to the list const uint32_t num_matches = variables.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", " "parent_decl_ctx=%p, append=%u, max_matches=%u, variables) => %u", name.GetCString(), static_cast(parent_decl_ctx), append, max_matches, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex, bool append, uint32_t max_matches, VariableList &variables) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", append=%u, " "max_matches=%u, variables)", regex.GetText().str().c_str(), append, max_matches); } DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); m_global_index.Find(regex, die_offsets); } SymbolContext sc; sc.module_sp = m_obj_file->GetModule(); assert(sc.module_sp); const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { sc.comp_unit = GetCompUnitForDWARFCompUnit(die.GetCU(), UINT32_MAX); ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables); if (variables.GetSize() - original_size >= max_matches) break; } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for regex '%s')\n", die_ref.die_offset, regex.GetText().str().c_str()); } } } } // Return the number of variable that were appended to the list return variables.GetSize() - original_size; } bool SymbolFileDWARF::ResolveFunction(const DIERef &die_ref, bool include_inlines, SymbolContextList &sc_list) { DWARFDIE die = DebugInfo()->GetDIE(die_ref); return ResolveFunction(die, include_inlines, sc_list); } bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, bool include_inlines, SymbolContextList &sc_list) { SymbolContext sc; if (!orig_die) return false; // If we were passed a die that is not a function, just return false... if (!(orig_die.Tag() == DW_TAG_subprogram || (include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine))) return false; DWARFDIE die = orig_die; DWARFDIE inlined_die; if (die.Tag() == DW_TAG_inlined_subroutine) { inlined_die = die; while (1) { die = die.GetParent(); if (die) { if (die.Tag() == DW_TAG_subprogram) break; } else break; } } assert(die && die.Tag() == DW_TAG_subprogram); if (GetFunction(die, sc)) { Address addr; // Parse all blocks if needed if (inlined_die) { Block &function_block = sc.function->GetBlock(true); sc.block = function_block.FindBlockByID(inlined_die.GetID()); if (sc.block == NULL) sc.block = function_block.FindBlockByID(inlined_die.GetOffset()); if (sc.block == NULL || sc.block->GetStartAddress(addr) == false) addr.Clear(); } else { sc.block = NULL; addr = sc.function->GetAddressRange().GetBaseAddress(); } if (addr.IsValid()) { sc_list.Append(sc); return true; } } return false; } void SymbolFileDWARF::FindFunctions(const ConstString &name, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(name, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, const NameToDIE &name_to_die, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; if (name_to_die.Find(regex, die_offsets)) { ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::FindFunctions( const RegularExpression ®ex, const DWARFMappedHash::MemoryTable &memory_table, bool include_inlines, SymbolContextList &sc_list) { DIEArray die_offsets; DWARFMappedHash::DIEInfoArray hash_data_array; if (memory_table.AppendAllDIEsThatMatchingRegex(regex, hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); ParseFunctions(die_offsets, include_inlines, sc_list); } } void SymbolFileDWARF::ParseFunctions(const DIEArray &die_offsets, bool include_inlines, SymbolContextList &sc_list) { const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) ResolveFunction(die_offsets[i], include_inlines, sc_list); } } bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx, const DWARFDIE &die) { // If we have no parent decl context to match this DIE matches, and if the // parent // decl context isn't valid, we aren't trying to look for any particular decl // context so any die matches. if (decl_ctx == nullptr || !decl_ctx->IsValid()) return true; if (die) { DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { CompilerDeclContext actual_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); if (actual_decl_ctx) return actual_decl_ctx == *decl_ctx; } } return false; } uint32_t SymbolFileDWARF::FindFunctions(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList &sc_list) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (name = '%s')", name.AsCString()); // eFunctionNameTypeAuto should be pre-resolved by a call to // Module::LookupInfo::LookupInfo() assert((name_type_mask & eFunctionNameTypeAuto) == 0); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, append=%u, sc_list)", name.GetCString(), name_type_mask, append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; // If name is empty then we won't find anything. if (name.IsEmpty()) return 0; // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. const char *name_cstr = name.GetCString(); const uint32_t original_size = sc_list.GetSize(); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; std::set resolved_dies; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DIEArray die_offsets; uint32_t num_matches = 0; if (name_type_mask & eFunctionNameTypeFull) { // If they asked for the full name, match what they typed. At some // point we may // want to canonicalize this (strip double spaces, etc. For now, we // just add all the // dies that we find by exact match. num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } } if (name_type_mask & eFunctionNameTypeSelector) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no selectors in namespaces num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); // Now make sure these are actually ObjC methods. In this case we can // simply look up the name, // and if it is an ObjC method name, we're good. for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { const char *die_name = die.GetName(); if (ObjCLanguage::IsPossibleObjCMethodName(die_name)) { if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } else { GetObjectFile()->GetModule()->ReportError( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } if (((name_type_mask & eFunctionNameTypeMethod) && !parent_decl_ctx) || name_type_mask & eFunctionNameTypeBase) { // The apple_names table stores just the "base name" of C++ methods in // the table. So we have to // extract the base name, look that up, and if there is any other // information in the name we were // passed in we have to post-filter based on that. // FIXME: Arrange the logic above so that we don't calculate the base // name twice: num_matches = m_apple_names_ap->FindByName(name_cstr, die_offsets); for (uint32_t i = 0; i < num_matches; i++) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = info->GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end() && ResolveFunction(die, include_inlines, sc_list)) { bool keep_die = true; if ((name_type_mask & (eFunctionNameTypeBase | eFunctionNameTypeMethod)) != (eFunctionNameTypeBase | eFunctionNameTypeMethod)) { // We are looking for either basenames or methods, so we need to // trim out the ones we won't want by looking at the type SymbolContext sc; if (sc_list.GetLastContext(sc)) { if (sc.block) { // We have an inlined function } else if (sc.function) { Type *type = sc.function->GetType(); if (type) { CompilerDeclContext decl_ctx = GetDeclContextContainingUID(type->GetID()); if (decl_ctx.IsStructUnionOrClass()) { if (name_type_mask & eFunctionNameTypeBase) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } else { if (name_type_mask & eFunctionNameTypeMethod) { sc_list.RemoveContextAtIndex(sc_list.GetSize() - 1); keep_die = false; } } } else { GetObjectFile()->GetModule()->ReportWarning( "function at die offset 0x%8.8x had no function type", die_ref.die_offset); } } } } if (keep_die) resolved_dies.insert(die.GetDIE()); } } else { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_names " "accelerator table had bad die 0x%8.8x for '%s')", die_ref.die_offset, name_cstr); } } die_offsets.clear(); } } } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); if (name_type_mask & eFunctionNameTypeFull) { FindFunctions(name, m_function_fullname_index, include_inlines, sc_list); // FIXME Temporary workaround for global/anonymous namespace // functions debugging FreeBSD and Linux binaries. // If we didn't find any functions in the global namespace try // looking in the basename index but ignore any returned // functions that have a namespace but keep functions which // have an anonymous namespace // TODO: The arch in the object file isn't correct for MSVC // binaries on windows, we should find a way to make it // correct and handle those symbols as well. if (sc_list.GetSize() == original_size) { ArchSpec arch; if (!parent_decl_ctx && GetObjectFile()->GetArchitecture(arch) && arch.GetTriple().isOSBinFormatELF()) { SymbolContextList temp_sc_list; FindFunctions(name, m_function_basename_index, include_inlines, temp_sc_list); SymbolContext sc; for (uint32_t i = 0; i < temp_sc_list.GetSize(); i++) { if (temp_sc_list.GetContextAtIndex(i, sc)) { ConstString mangled_name = sc.GetFunctionName(Mangled::ePreferMangled); ConstString demangled_name = sc.GetFunctionName(Mangled::ePreferDemangled); // Mangled names on Linux and FreeBSD are of the form: // _ZN18function_namespace13function_nameEv. if (strncmp(mangled_name.GetCString(), "_ZN", 3) || !strncmp(demangled_name.GetCString(), "(anonymous namespace)", 21)) { sc_list.Append(sc); } } } } } } DIEArray die_offsets; if (name_type_mask & eFunctionNameTypeBase) { uint32_t num_base = m_function_basename_index.Find(name, die_offsets); for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } die_offsets.clear(); } if (name_type_mask & eFunctionNameTypeMethod) { if (parent_decl_ctx && parent_decl_ctx->IsValid()) return 0; // no methods in namespaces uint32_t num_base = m_function_method_index.Find(name, die_offsets); { for (uint32_t i = 0; i < num_base; i++) { DWARFDIE die = info->GetDIE(die_offsets[i]); if (die) { // If we get to here, the die is good, and we should add it: if (resolved_dies.find(die.GetDIE()) == resolved_dies.end()) { if (ResolveFunction(die, include_inlines, sc_list)) resolved_dies.insert(die.GetDIE()); } } } } die_offsets.clear(); } if ((name_type_mask & eFunctionNameTypeSelector) && (!parent_decl_ctx || !parent_decl_ctx->IsValid())) { FindFunctions(name, m_function_selector_index, include_inlines, sc_list); } } // Return the number of variable that were appended to the list const uint32_t num_matches = sc_list.GetSize() - original_size; if (log && num_matches > 0) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (name=\"%s\", " "name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => " "%u", name.GetCString(), name_type_mask, include_inlines, append, num_matches); } return num_matches; } uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, bool include_inlines, bool append, SymbolContextList &sc_list) { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (regex = '%s')", regex.GetText().str().c_str()); Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)", regex.GetText().str().c_str(), append); } // If we aren't appending the results to this list, then clear the list if (!append) sc_list.Clear(); // Remember how many sc_list are in the list before we search in case // we are appending the results to a variable list. uint32_t original_size = sc_list.GetSize(); if (m_using_apple_tables) { if (m_apple_names_ap.get()) FindFunctions(regex, *m_apple_names_ap, include_inlines, sc_list); } else { // Index the DWARF if we haven't already if (!m_indexed) Index(); FindFunctions(regex, m_function_basename_index, include_inlines, sc_list); FindFunctions(regex, m_function_fullname_index, include_inlines, sc_list); } // Return the number of variable that were appended to the list return sc_list.GetSize() - original_size; } void SymbolFileDWARF::GetMangledNamesForFunction( const std::string &scope_qualified_name, std::vector &mangled_names) { DWARFDebugInfo *info = DebugInfo(); uint32_t num_comp_units = 0; if (info) num_comp_units = info->GetNumCompileUnits(); for (uint32_t i = 0; i < num_comp_units; i++) { DWARFCompileUnit *cu = info->GetCompileUnitAtIndex(i); if (cu == nullptr) continue; SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile(); if (dwo) dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names); } NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find(scope_qualified_name); if (iter == m_function_scope_qualified_name_map.end()) return; DIERefSetSP set_sp = (*iter).second; std::set::iterator set_iter; for (set_iter = set_sp->begin(); set_iter != set_sp->end(); set_iter++) { DWARFDIE die = DebugInfo()->GetDIE(*set_iter); mangled_names.push_back(ConstString(die.GetMangledName())); } } uint32_t SymbolFileDWARF::FindTypes( const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { // If we aren't appending the results to this list, then clear the list if (!append) types.Clear(); // Make sure we haven't already searched this SymbolFile before... if (searched_symbol_files.count(this)) return 0; else searched_symbol_files.insert(this); DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { if (parent_decl_ctx) GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "%p (\"%s\"), append=%u, max_matches=%u, type_list)", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches); else GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = " "NULL, append=%u, max_matches=%u, type_list)", name.GetCString(), append, max_matches); } if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return 0; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { const uint32_t initial_types_size = types.GetSize(); for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); if (types.GetSize() >= max_matches) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } const uint32_t num_matches = types.GetSize() - initial_types_size; if (log && num_matches) { if (parent_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u", name.GetCString(), static_cast(parent_decl_ctx), parent_decl_ctx->GetName().AsCString(""), append, max_matches, num_matches); } else { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx " "= NULL, append=%u, max_matches=%u, type_list) => %u", name.GetCString(), append, max_matches, num_matches); } } return num_matches; } else { UpdateExternalModuleListIfNeeded(); for (const auto &pair : m_external_type_modules) { ModuleSP external_module_sp = pair.second; if (external_module_sp) { SymbolVendor *sym_vendor = external_module_sp->GetSymbolVendor(); if (sym_vendor) { const uint32_t num_external_matches = sym_vendor->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); if (num_external_matches) return num_external_matches; } } } } return 0; } size_t SymbolFileDWARF::FindTypes(const std::vector &context, bool append, TypeMap &types) { if (!append) types.Clear(); if (context.empty()) return 0; DIEArray die_offsets; ConstString name = context.back().name; if (!name) return 0; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_types_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_type_index.Find(name, die_offsets); } const size_t num_die_matches = die_offsets.size(); if (num_die_matches) { size_t num_matches = 0; for (size_t i = 0; i < num_die_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { std::vector die_context; die.GetDWOContext(die_context); if (die_context != context) continue; Type *matching_type = ResolveType(die, true, true); if (matching_type) { // We found a type pointer, now find the shared pointer form our type // list types.InsertUnique(matching_type->shared_from_this()); ++num_matches; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, name.GetCString()); } } } return num_matches; } return 0; } CompilerDeclContext SymbolFileDWARF::FindNamespace(const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")", name.GetCString()); } CompilerDeclContext namespace_decl_ctx; if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx)) return namespace_decl_ctx; DWARFDebugInfo *info = DebugInfo(); if (info) { DIEArray die_offsets; // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (m_using_apple_tables) { if (m_apple_namespaces_ap.get()) { const char *name_cstr = name.GetCString(); m_apple_namespaces_ap->FindByName(name_cstr, die_offsets); } } else { if (!m_indexed) Index(); m_namespace_index.Find(name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { if (!DIEInDeclContext(parent_decl_ctx, die)) continue; // The containing decl contexts don't match DWARFASTParser *dwarf_ast = die.GetDWARFParser(); if (dwarf_ast) { namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die); if (namespace_decl_ctx) break; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_namespaces accelerator table had bad die 0x%8.8x for " "'%s')\n", die_ref.die_offset, name.GetCString()); } } } } } if (log && namespace_decl_ctx) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => " "CompilerDeclContext(%p/%p) \"%s\"", name.GetCString(), static_cast(namespace_decl_ctx.GetTypeSystem()), static_cast(namespace_decl_ctx.GetOpaqueDeclContext()), namespace_decl_ctx.GetName().AsCString("")); } return namespace_decl_ctx; } TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die, bool resolve_function_context) { TypeSP type_sp; if (die) { Type *type_ptr = GetDIEToType().lookup(die.GetDIE()); if (type_ptr == NULL) { CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(die.GetCU()); assert(lldb_cu); SymbolContext sc(lldb_cu); const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE(); while (parent_die != nullptr) { if (parent_die->Tag() == DW_TAG_subprogram) break; parent_die = parent_die->GetParent(); } SymbolContext sc_backup = sc; if (resolve_function_context && parent_die != nullptr && !GetFunction(DWARFDIE(die.GetCU(), parent_die), sc)) sc = sc_backup; type_sp = ParseType(sc, die, NULL); } else if (type_ptr != DIE_IS_BEING_PARSED) { // Grab the existing type from the master types lists type_sp = type_ptr->shared_from_this(); } } return type_sp; } DWARFDIE SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) { if (orig_die) { DWARFDIE die = orig_die; while (die) { // If this is the original DIE that we are searching for a declaration // for, then don't look in the cache as we don't want our own decl // context to be our decl context... if (orig_die != die) { switch (die.Tag()) { case DW_TAG_compile_unit: case DW_TAG_namespace: case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_class_type: case DW_TAG_lexical_block: case DW_TAG_subprogram: return die; case DW_TAG_inlined_subroutine: { DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); if (abs_die) { return abs_die; } break; } default: break; } } DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification); if (spec_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die); if (decl_ctx_die) return decl_ctx_die; } DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin); if (abs_die) { DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die); if (decl_ctx_die) return decl_ctx_die; } die = die.GetParent(); } } return DWARFDIE(); } Symbol * SymbolFileDWARF::GetObjCClassSymbol(const ConstString &objc_class_name) { Symbol *objc_class_symbol = NULL; if (m_obj_file) { Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo, Symtab::eVisibilityAny); } } return objc_class_symbol; } // Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If // they don't // then we can end up looking through all class types for a complete type and // never find // the full definition. We need to know if this attribute is supported, so we // determine // this here and cache th result. We also need to worry about the debug map // DWARF file // if we are doing darwin DWARF in .o file debugging. bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type( DWARFCompileUnit *cu) { if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type()) m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; else { DWARFDebugInfo *debug_info = DebugInfo(); const uint32_t num_compile_units = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) { DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); if (dwarf_cu != cu && dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; break; } } } if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo && GetDebugMapSymfile()) return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this); } return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; } // This function can be used when a DIE is found that is a forward declaration // DIE and we want to try and find a type that has the complete definition. TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE( const DWARFDIE &die, const ConstString &type_name, bool must_be_implementation) { TypeSP type_sp; if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name))) return type_sp; DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const char *name_cstr = type_name.GetCString(); m_apple_types_ap->FindCompleteObjCClassByName(name_cstr, die_offsets, must_be_implementation); } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE itself! if (type_die != die) { switch (type_die.Tag()) { case DW_TAG_class_type: case DW_TAG_structure_type: try_resolving_type = true; break; default: break; } } if (try_resolving_type) { if (must_be_implementation && type_die.Supports_DW_AT_APPLE_objc_complete_type()) try_resolving_type = type_die.GetAttributeValueAsUnsigned( DW_AT_APPLE_objc_complete_type, 0); if (try_resolving_type) { Type *resolved_type = ResolveType(type_die, false, true); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", die.GetID(), m_obj_file->GetFileSpec().GetFilename().AsCString( ""), type_die.GetID(), type_cu->GetID()); if (die) GetDIEToType()[die.GetDIE()] = resolved_type; type_sp = resolved_type->shared_from_this(); break; } } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } return type_sp; } //---------------------------------------------------------------------- // This function helps to ensure that the declaration contexts match for // two different DIEs. Often times debug information will refer to a // forward declaration of a type (the equivalent of "struct my_struct;". // There will often be a declaration of that type elsewhere that has the // full definition. When we go looking for the full type "my_struct", we // will find one or more matches in the accelerator tables and we will // then need to make sure the type was in the same declaration context // as the original DIE. This function can efficiently compare two DIEs // and will return true when the declaration context matches, and false // when they don't. //---------------------------------------------------------------------- bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2) { if (die1 == die2) return true; DWARFDIECollection decl_ctx_1; DWARFDIECollection decl_ctx_2; // The declaration DIE stack is a stack of the declaration context // DIEs all the way back to the compile unit. If a type "T" is // declared inside a class "B", and class "B" is declared inside // a class "A" and class "A" is in a namespace "lldb", and the // namespace is in a compile unit, there will be a stack of DIEs: // // [0] DW_TAG_class_type for "B" // [1] DW_TAG_class_type for "A" // [2] DW_TAG_namespace for "lldb" // [3] DW_TAG_compile_unit for the source file. // // We grab both contexts and make sure that everything matches // all the way back to the compiler unit. // First lets grab the decl contexts for both DIEs die1.GetDeclContextDIEs(decl_ctx_1); die2.GetDeclContextDIEs(decl_ctx_2); // Make sure the context arrays have the same size, otherwise // we are done const size_t count1 = decl_ctx_1.Size(); const size_t count2 = decl_ctx_2.Size(); if (count1 != count2) return false; // Make sure the DW_TAG values match all the way back up the // compile unit. If they don't, then we are done. DWARFDIE decl_ctx_die1; DWARFDIE decl_ctx_die2; size_t i; for (i = 0; i < count1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag()) return false; } #if defined LLDB_CONFIGURATION_DEBUG // Make sure the top item in the decl context die array is always // DW_TAG_compile_unit. If it isn't then something went wrong in // the DWARFDIE::GetDeclContextDIEs() function... assert(decl_ctx_1.GetDIEAtIndex(count1 - 1).Tag() == DW_TAG_compile_unit); #endif // Always skip the compile unit when comparing by only iterating up to // "count - 1". Here we compare the names as we go. for (i = 0; i < count1 - 1; i++) { decl_ctx_die1 = decl_ctx_1.GetDIEAtIndex(i); decl_ctx_die2 = decl_ctx_2.GetDIEAtIndex(i); const char *name1 = decl_ctx_die1.GetName(); const char *name2 = decl_ctx_die2.GetName(); // If the string was from a DW_FORM_strp, then the pointer will often // be the same! if (name1 == name2) continue; // Name pointers are not equal, so only compare the strings // if both are not NULL. if (name1 && name2) { // If the strings don't compare, we are done... if (strcmp(name1, name2) != 0) return false; } else { // One name was NULL while the other wasn't return false; } } // We made it through all of the checks and the declaration contexts // are equal. return true; } TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext( const DWARFDeclContext &dwarf_decl_ctx) { TypeSP type_sp; const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize(); if (dwarf_decl_ctx_count > 0) { const ConstString type_name(dwarf_decl_ctx[0].name); const dw_tag_t tag = dwarf_decl_ctx[0].tag; if (type_name) { Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION | DWARF_LOG_LOOKUPS)); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%" "s, qualified-name='%s')", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName()); } DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_types_ap.get()) { const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeTag); const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom( DWARFMappedHash::eAtomTypeQualNameHash); if (has_tag && has_qualified_name_hash) { const char *qualified_name = dwarf_decl_ctx.GetQualifiedName(); const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB(qualified_name); if (log) GetObjectFile()->GetModule()->LogMessage( log, "FindByNameAndTagAndQualifiedNameHash()"); m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash( type_name.GetCString(), tag, qualified_name_hash, die_offsets); } else if (has_tag) { if (log) GetObjectFile()->GetModule()->LogMessage(log, "FindByNameAndTag()"); m_apple_types_ap->FindByNameAndTag(type_name.GetCString(), tag, die_offsets); } else { m_apple_types_ap->FindByName(type_name.GetCString(), die_offsets); } } } else { if (!m_indexed) Index(); m_type_index.Find(type_name, die_offsets); } const size_t num_matches = die_offsets.size(); // Get the type system that we are looking to find a type for. We will use // this // to ensure any matches we find are in a language that this type system // supports const LanguageType language = dwarf_decl_ctx.GetLanguage(); TypeSystem *type_system = (language == eLanguageTypeUnknown) ? nullptr : GetTypeSystemForLanguage(language); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE type_die = GetDIE(die_ref); if (type_die) { // Make sure type_die's langauge matches the type system we are // looking for. // We don't want to find a "Foo" type from Java if we are looking // for a "Foo" // type for C, C++, ObjC, or ObjC++. if (type_system && !type_system->SupportsLanguage(type_die.GetLanguage())) continue; bool try_resolving_type = false; // Don't try and resolve the DIE we are looking for with the DIE // itself! const dw_tag_t type_tag = type_die.Tag(); // Make sure the tags match if (type_tag == tag) { // The tags match, lets try resolving this type try_resolving_type = true; } else { // The tags don't match, but we need to watch our for a // forward declaration for a struct and ("struct foo") // ends up being a class ("class foo { ... };") or // vice versa. switch (type_tag) { case DW_TAG_class_type: // We had a "class foo", see if we ended up with a "struct foo { // ... };" try_resolving_type = (tag == DW_TAG_structure_type); break; case DW_TAG_structure_type: // We had a "struct foo", see if we ended up with a "class foo { // ... };" try_resolving_type = (tag == DW_TAG_class_type); break; default: // Tags don't match, don't event try to resolve // using this type whose name matches.... break; } } if (try_resolving_type) { DWARFDeclContext type_dwarf_decl_ctx; type_die.GetDWARFDeclContext(type_dwarf_decl_ctx); if (log) { GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') trying die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), type_dwarf_decl_ctx.GetQualifiedName()); } // Make sure the decl contexts match all the way up if (dwarf_decl_ctx == type_dwarf_decl_ctx) { Type *resolved_type = ResolveType(type_die, false); if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) { type_sp = resolved_type->shared_from_this(); break; } } } else { if (log) { std::string qualified_name; type_die.GetQualifiedName(qualified_name); GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF::" "FindDefinitionTypeForDWARFDeclContext(tag=%s, " "qualified-name='%s') ignoring die=0x%8.8x (%s)", DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(), qualified_name.c_str()); } } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified (.apple_types " "accelerator table had bad die 0x%8.8x for '%s')\n", die_ref.die_offset, type_name.GetCString()); } } } } } } return type_sp; } TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, bool *type_is_new_ptr) { TypeSP type_sp; if (die) { TypeSystem *type_system = GetTypeSystemForLanguage(die.GetCU()->GetLanguageType()); if (type_system) { DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); if (dwarf_ast) { Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO); type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr); if (type_sp) { TypeList *type_list = GetTypeList(); if (type_list) type_list->Insert(type_sp); if (die.Tag() == DW_TAG_subprogram) { DIERef die_ref = die.GetDIERef(); std::string scope_qualified_name(GetDeclContextForUID(die.GetID()) .GetScopeQualifiedName() .AsCString("")); if (scope_qualified_name.size()) { NameToOffsetMap::iterator iter = m_function_scope_qualified_name_map.find( scope_qualified_name); if (iter != m_function_scope_qualified_name_map.end()) (*iter).second->insert(die_ref); else { DIERefSetSP new_set(new std::set); new_set->insert(die_ref); m_function_scope_qualified_name_map.emplace( std::make_pair(scope_qualified_name, new_set)); } } } } } } } return type_sp; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc, const DWARFDIE &orig_die, bool parse_siblings, bool parse_children) { size_t types_added = 0; DWARFDIE die = orig_die; while (die) { bool type_is_new = false; if (ParseType(sc, die, &type_is_new).get()) { if (type_is_new) ++types_added; } if (parse_children && die.HasChildren()) { if (die.Tag() == DW_TAG_subprogram) { SymbolContext child_sc(sc); child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get(); types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true); } else types_added += ParseTypes(sc, die.GetFirstChild(), true, true); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return types_added; } size_t SymbolFileDWARF::ParseFunctionBlocks(const SymbolContext &sc) { assert(sc.comp_unit && sc.function); size_t functions_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { const dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset); if (function_die) { ParseFunctionBlocks(sc, &sc.function->GetBlock(false), function_die, LLDB_INVALID_ADDRESS, 0); } } return functions_added; } size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc) { // At least a compile unit must be valid assert(sc.comp_unit); size_t types_added = 0; DWARFCompileUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); if (dwarf_cu) { if (sc.function) { dw_offset_t function_die_offset = sc.function->GetID(); DWARFDIE func_die = dwarf_cu->GetDIE(function_die_offset); if (func_die && func_die.HasChildren()) { types_added = ParseTypes(sc, func_die.GetFirstChild(), true, true); } } else { DWARFDIE dwarf_cu_die = dwarf_cu->DIE(); if (dwarf_cu_die && dwarf_cu_die.HasChildren()) { types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true); } } } return types_added; } size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) { if (sc.comp_unit != NULL) { DWARFDebugInfo *info = DebugInfo(); if (info == NULL) return 0; if (sc.function) { DWARFDIE function_die = info->GetDIE(DIERef(sc.function->GetID(), this)); const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress( DW_AT_low_pc, LLDB_INVALID_ADDRESS); if (func_lo_pc != LLDB_INVALID_ADDRESS) { const size_t num_variables = ParseVariables( sc, function_die.GetFirstChild(), func_lo_pc, true, true); // Let all blocks know they have parse all their variables sc.function->GetBlock(false).SetDidParseVariables(true, true); return num_variables; } } else if (sc.comp_unit) { DWARFCompileUnit *dwarf_cu = info->GetCompileUnit(sc.comp_unit->GetID()); if (dwarf_cu == NULL) return 0; uint32_t vars_added = 0; VariableListSP variables(sc.comp_unit->GetVariableList(false)); if (variables.get() == NULL) { variables.reset(new VariableList()); sc.comp_unit->SetVariableList(variables); DIEArray die_offsets; if (m_using_apple_tables) { if (m_apple_names_ap.get()) { DWARFMappedHash::DIEInfoArray hash_data_array; if (m_apple_names_ap->AppendAllDIEsInRange( dwarf_cu->GetOffset(), dwarf_cu->GetNextCompileUnitOffset(), hash_data_array)) { DWARFMappedHash::ExtractDIEArray(hash_data_array, die_offsets); } } } else { // Index if we already haven't to make sure the compile units // get indexed and make their global DIE index list if (!m_indexed) Index(); m_global_index.FindAllEntriesForCompileUnit(dwarf_cu->GetOffset(), die_offsets); } const size_t num_matches = die_offsets.size(); if (num_matches) { for (size_t i = 0; i < num_matches; ++i) { const DIERef &die_ref = die_offsets[i]; DWARFDIE die = GetDIE(die_ref); if (die) { VariableSP var_sp( ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS)); if (var_sp) { variables->AddVariableIfUnique(var_sp); ++vars_added; } } else { if (m_using_apple_tables) { GetObjectFile()->GetModule()->ReportErrorIfModifyDetected( "the DWARF debug information has been modified " "(.apple_names accelerator table had bad die 0x%8.8x)\n", die_ref.die_offset); } } } } } return vars_added; } } return 0; } VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc) { if (die.GetDWARF() != this) return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc); VariableSP var_sp; if (!die) return var_sp; var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) return var_sp; // Already been parsed! const dw_tag_t tag = die.Tag(); ModuleSP module = GetObjectFile()->GetModule(); if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); DWARFDIE spec_die; if (num_attributes > 0) { const char *name = NULL; const char *mangled = NULL; Declaration decl; uint32_t i; DWARFFormValue type_die_form; DWARFExpression location(die.GetCU()); bool is_external = false; bool is_artificial = false; bool location_is_const_value_data = false; bool has_explicit_location = false; DWARFFormValue const_value; Variable::RangeList scope_ranges; // AccessType accessibility = eAccessNone; for (i = 0; i < num_attributes; ++i) { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; if (attributes.ExtractFormValueAtIndex(i, form_value)) { switch (attr) { case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex( form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(); break; case DW_AT_type: type_die_form = form_value; break; case DW_AT_external: is_external = form_value.Boolean(); break; case DW_AT_const_value: // If we have already found a DW_AT_location attribute, ignore this // attribute. if (!has_explicit_location) { location_is_const_value_data = true; // The constant value will be either a block, a data value or a // string. const DWARFDataExtractor &debug_info_data = get_debug_info_data(); if (DWARFFormValue::IsBlockForm(form_value.Form())) { // Retrieve the value as a block expression. uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, debug_info_data, block_offset, block_length); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // Retrieve the value as a data expression. DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i)->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); if (data_length == 0) { const uint8_t *data_pointer = form_value.BlockData(); if (data_pointer) { form_value.Unsigned(); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { // we need to get the byte size of the type later after we // create the variable const_value = form_value; } } else location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { // Retrieve the value as a string expression. if (form_value.Form() == DW_FORM_strp) { DWARFFormValue::FixedFormSizes fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize( attributes.CompileUnitAtIndex(i) ->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes.GetSize(form_value.Form()); location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { const char *str = form_value.AsCString(); uint32_t string_offset = str - (const char *)debug_info_data.GetDataStart(); uint32_t string_length = strlen(str) + 1; location.CopyOpcodeData(module, debug_info_data, string_offset, string_length); } } } break; case DW_AT_location: { location_is_const_value_data = false; has_explicit_location = true; if (DWARFFormValue::IsBlockForm(form_value.Form())) { const DWARFDataExtractor &debug_info_data = get_debug_info_data(); uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); location.CopyOpcodeData(module, get_debug_info_data(), block_offset, block_length); } else { const DWARFDataExtractor &debug_loc_data = get_debug_loc_data(); const dw_offset_t debug_loc_offset = form_value.Unsigned(); size_t loc_list_length = DWARFExpression::LocationListSize( die.GetCU(), debug_loc_data, debug_loc_offset); if (loc_list_length > 0) { location.CopyOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); assert(func_low_pc != LLDB_INVALID_ADDRESS); location.SetLocationListSlide( func_low_pc - attributes.CompileUnitAtIndex(i)->GetBaseAddress()); } } } break; case DW_AT_specification: spec_die = GetDIE(DIERef(form_value)); break; case DW_AT_start_scope: { if (form_value.Form() == DW_FORM_sec_offset) { DWARFRangeList dwarf_scope_ranges; const DWARFDebugRanges *debug_ranges = DebugRanges(); debug_ranges->FindRanges(die.GetCU()->GetRangesBase(), form_value.Unsigned(), dwarf_scope_ranges); // All DW_AT_start_scope are relative to the base address of the // compile unit. We add the compile unit base address to make // sure all the addresses are properly fixed up. for (size_t i = 0, count = dwarf_scope_ranges.GetSize(); i < count; ++i) { const DWARFRangeList::Entry &range = dwarf_scope_ranges.GetEntryRef(i); scope_ranges.Append(range.GetRangeBase() + die.GetCU()->GetBaseAddress(), range.GetByteSize()); } } else { // TODO: Handle the case when DW_AT_start_scope have form // constant. The // dwarf spec is a bit ambiguous about what is the expected // behavior in // case the enclosing block have a non coninious address range and // the // DW_AT_start_scope entry have a form constant. GetObjectFile()->GetModule()->ReportWarning( "0x%8.8" PRIx64 ": DW_AT_start_scope has unsupported form type (0x%x)\n", die.GetID(), form_value.Form()); } scope_ranges.Sort(); scope_ranges.CombineConsecutiveRanges(); } break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_accessibility: break; // accessibility = // DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: case DW_AT_description: case DW_AT_endianity: case DW_AT_segment: case DW_AT_visibility: default: case DW_AT_abstract_origin: case DW_AT_sibling: break; } } } const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die); const dw_tag_t parent_tag = die.GetParent().Tag(); bool is_static_member = parent_tag == DW_TAG_compile_unit && (parent_context_die.Tag() == DW_TAG_class_type || parent_context_die.Tag() == DW_TAG_structure_type); ValueType scope = eValueTypeInvalid; const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); SymbolContextScope *symbol_context_scope = NULL; bool has_explicit_mangled = mangled != nullptr; if (!mangled) { // LLDB relies on the mangled name (DW_TAG_linkage_name or // DW_AT_MIPS_linkage_name) to // generate fully qualified names of global variables with commands like // "frame var j". // For example, if j were an int variable holding a value 4 and declared // in a namespace // B which in turn is contained in a namespace A, the command "frame var // j" returns // "(int) A::B::j = 4". If the compiler does not emit a linkage name, we // should be able // to generate a fully qualified name from the declaration context. if (parent_tag == DW_TAG_compile_unit && Language::LanguageIsCPlusPlus(die.GetLanguage())) { DWARFDeclContext decl_ctx; die.GetDWARFDeclContext(decl_ctx); mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString(); } } if (tag == DW_TAG_formal_parameter) scope = eValueTypeVariableArgument; else { // DWARF doesn't specify if a DW_TAG_variable is a local, global // or static variable, so we have to do a little digging: // 1) DW_AT_linkage_name implies static lifetime (but may be missing) // 2) An empty DW_AT_location is an (optimized-out) static lifetime var. // 3) DW_AT_location containing a DW_OP_addr implies static lifetime. // Clang likes to combine small global variables into the same symbol // with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus // so we need to look through the whole expression. bool is_static_lifetime = has_explicit_mangled || (has_explicit_location && !location.IsValid()); // Check if the location has a DW_OP_addr with any address value... lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; if (!location_is_const_value_data) { bool op_error = false; location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error); if (op_error) { StreamString strm; location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0, NULL); GetObjectFile()->GetModule()->ReportError( "0x%8.8x: %s has an invalid location: %s", die.GetOffset(), die.GetTagAsCString(), strm.GetData()); } if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) is_static_lifetime = true; } SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); if (is_static_lifetime) { if (is_external) scope = eValueTypeVariableGlobal; else scope = eValueTypeVariableStatic; if (debug_map_symfile) { // When leaving the DWARF in the .o files on darwin, // when we have a global variable that wasn't initialized, // the .o file might not have allocated a virtual // address for the global variable. In this case it will // have created a symbol for the global variable // that is undefined/data and external and the value will // be the byte size of the variable. When we do the // address map in SymbolFileDWARFDebugMap we rely on // having an address, we need to do some magic here // so we can get the correct address for our global // variable. The address for all of these entries // will be zero, and there will be an undefined symbol // in this object file, and the executable will have // a matching symbol with a good address. So here we // dig up the correct address and replace it in the // location for the variable, and set the variable's // symbol context scope to be that of the main executable // so the file address will resolve correctly. bool linked_oso_file_addr = false; if (is_external && location_DW_OP_addr == 0) { // we have a possible uninitialized extern global ConstString const_name(mangled ? mangled : name); ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); if (debug_map_objfile) { Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); if (debug_map_symtab) { Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType( const_name, eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityExtern); if (exe_symbol) { if (exe_symbol->ValueIsAddress()) { const addr_t exe_file_addr = exe_symbol->GetAddressRef().GetFileAddress(); if (exe_file_addr != LLDB_INVALID_ADDRESS) { if (location.Update_DW_OP_addr(exe_file_addr)) { linked_oso_file_addr = true; symbol_context_scope = exe_symbol; } } } } } } } if (!linked_oso_file_addr) { // The DW_OP_addr is not zero, but it contains a .o file address // which // needs to be linked up correctly. const lldb::addr_t exe_file_addr = debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); if (exe_file_addr != LLDB_INVALID_ADDRESS) { // Update the file address for this variable location.Update_DW_OP_addr(exe_file_addr); } else { // Variable didn't make it into the final executable return var_sp; } } } } else { if (location_is_const_value_data) scope = eValueTypeVariableStatic; else { scope = eValueTypeVariableLocal; if (debug_map_symfile) { // We need to check for TLS addresses that we need to fixup if (location.ContainsThreadLocalStorage()) { location.LinkThreadLocalStorage( debug_map_symfile->GetObjectFile()->GetModule(), [this, debug_map_symfile]( lldb::addr_t unlinked_file_addr) -> lldb::addr_t { return debug_map_symfile->LinkOSOFileAddress( this, unlinked_file_addr); }); scope = eValueTypeVariableThreadLocal; } } } } } if (symbol_context_scope == NULL) { switch (parent_tag) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (symbol_context_scope == NULL) symbol_context_scope = sc.function; } break; default: symbol_context_scope = sc.comp_unit; break; } } if (symbol_context_scope) { SymbolFileTypeSP type_sp( new SymbolFileType(*this, DIERef(type_die_form).GetUID(this))); if (const_value.Form() && type_sp && type_sp->GetType()) location.CopyOpcodeData(const_value.Unsigned(), type_sp->GetType()->GetByteSize(), die.GetCU()->GetAddressByteSize()); var_sp.reset(new Variable(die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, scope_ranges, &decl, location, is_external, is_artificial, is_static_member)); var_sp->SetLocationIsConstantValueData(location_is_const_value_data); } else { // Not ready to parse this variable yet. It might be a global // or static variable that is in a function scope and the function // in the symbol context wasn't filled in yet return var_sp; } } // Cache var_sp even if NULL (the variable was just a specification or // was missing vital information to be able to be displayed in the debugger // (missing location due to optimization, etc)) so we don't re-parse // this DIE over and over later... GetDIEToVariable()[die.GetDIE()] = var_sp; if (spec_die) GetDIEToVariable()[spec_die.GetDIE()] = var_sp; } return var_sp; } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) { // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref), spec_block_die_offset); } DWARFDIE SymbolFileDWARF::FindBlockContainingSpecification( const DWARFDIE &die, dw_offset_t spec_block_die_offset) { if (die) { switch (die.Tag()) { case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: { if (die.GetAttributeValueAsReference( DW_AT_specification, DW_INVALID_OFFSET) == spec_block_die_offset) return die; if (die.GetAttributeValueAsReference(DW_AT_abstract_origin, DW_INVALID_OFFSET) == spec_block_die_offset) return die; } break; } // Give the concrete function die specified by "func_die_offset", find the // concrete block whose DW_AT_specification or DW_AT_abstract_origin points // to "spec_block_die_offset" for (DWARFDIE child_die = die.GetFirstChild(); child_die; child_die = child_die.GetSibling()) { DWARFDIE result_die = FindBlockContainingSpecification(child_die, spec_block_die_offset); if (result_die) return result_die; } } return DWARFDIE(); } size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc, const DWARFDIE &orig_die, const lldb::addr_t func_low_pc, bool parse_siblings, bool parse_children, VariableList *cc_variable_list) { if (!orig_die) return 0; VariableListSP variable_list_sp; size_t vars_added = 0; DWARFDIE die = orig_die; while (die) { dw_tag_t tag = die.Tag(); // Check to see if we have already parsed this variable or constant? VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]; if (var_sp) { if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); } else { // We haven't already parsed it, lets do that now. if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter && sc.function)) { if (variable_list_sp.get() == NULL) { DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die); dw_tag_t parent_tag = sc_parent_die.Tag(); switch (parent_tag) { case DW_TAG_compile_unit: if (sc.comp_unit != NULL) { variable_list_sp = sc.comp_unit->GetVariableList(false); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); sc.comp_unit->SetVariableList(variable_list_sp); } } else { GetObjectFile()->GetModule()->ReportError( "parent 0x%8.8" PRIx64 " %s with no valid compile unit in " "symbol context for 0x%8.8" PRIx64 " %s.\n", sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(), orig_die.GetID(), orig_die.GetTagAsCString()); } break; case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_lexical_block: if (sc.function != NULL) { // Check to see if we already have parsed the variables for the // given scope Block *block = sc.function->GetBlock(true).FindBlockByID( sc_parent_die.GetID()); if (block == NULL) { // This must be a specification or abstract origin with // a concrete block counterpart in the current function. We need // to find the concrete block so we can correctly add the // variable to it const DWARFDIE concrete_block_die = FindBlockContainingSpecification( DIERef(sc.function->GetID(), this), sc_parent_die.GetOffset()); if (concrete_block_die) block = sc.function->GetBlock(true).FindBlockByID( concrete_block_die.GetID()); } if (block != NULL) { const bool can_create = false; variable_list_sp = block->GetBlockVariableList(can_create); if (variable_list_sp.get() == NULL) { variable_list_sp.reset(new VariableList()); block->SetVariableList(variable_list_sp); } } } break; default: GetObjectFile()->GetModule()->ReportError( "didn't find appropriate parent DIE for variable list for " "0x%8.8" PRIx64 " %s.\n", orig_die.GetID(), orig_die.GetTagAsCString()); break; } } if (variable_list_sp) { VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); if (var_sp) { variable_list_sp->AddVariableIfUnique(var_sp); if (cc_variable_list) cc_variable_list->AddVariableIfUnique(var_sp); ++vars_added; } } } } bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); if (!skip_children && parse_children && die.HasChildren()) { vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true, true, cc_variable_list); } if (parse_siblings) die = die.GetSibling(); else die.Clear(); } return vars_added; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); } uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; } void SymbolFileDWARF::DumpIndexes() { StreamFile s(stdout, false); s.Printf( "DWARF index for (%s) '%s':", GetObjectFile()->GetModule()->GetArchitecture().GetArchitectureName(), GetObjectFile()->GetFileSpec().GetPath().c_str()); s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump(&s); s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump(&s); s.Printf("\nFunction methods:\n"); m_function_method_index.Dump(&s); s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump(&s); s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump(&s); s.Printf("\nGlobals and statics:\n"); m_global_index.Dump(&s); s.Printf("\nTypes:\n"); m_type_index.Dump(&s); s.Printf("\nNamespaces:\n"); m_namespace_index.Dump(&s); } SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() { if (m_debug_map_symfile == NULL && !m_debug_map_module_wp.expired()) { lldb::ModuleSP module_sp(m_debug_map_module_wp.lock()); if (module_sp) { SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); if (sym_vendor) m_debug_map_symfile = (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile(); } } return m_debug_map_symfile; } DWARFExpression::LocationListFormat SymbolFileDWARF::GetLocationListFormat() const { return DWARFExpression::RegularLocationList; } Index: vendor/lldb/dist/source/Target/SectionLoadList.cpp =================================================================== --- vendor/lldb/dist/source/Target/SectionLoadList.cpp (revision 319789) +++ vendor/lldb/dist/source/Target/SectionLoadList.cpp (revision 319790) @@ -1,257 +1,260 @@ //===-- SectionLoadList.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/Target/SectionLoadList.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; SectionLoadList::SectionLoadList(const SectionLoadList &rhs) : m_addr_to_sect(), m_sect_to_addr(), m_mutex() { std::lock_guard guard(rhs.m_mutex); m_addr_to_sect = rhs.m_addr_to_sect; m_sect_to_addr = rhs.m_sect_to_addr; } void SectionLoadList::operator=(const SectionLoadList &rhs) { std::lock_guard lhs_guard(m_mutex); std::lock_guard rhs_guard(rhs.m_mutex); m_addr_to_sect = rhs.m_addr_to_sect; m_sect_to_addr = rhs.m_sect_to_addr; } bool SectionLoadList::IsEmpty() const { std::lock_guard guard(m_mutex); return m_addr_to_sect.empty(); } void SectionLoadList::Clear() { std::lock_guard guard(m_mutex); m_addr_to_sect.clear(); m_sect_to_addr.clear(); } addr_t SectionLoadList::GetSectionLoadAddress(const lldb::SectionSP §ion) const { // TODO: add support for the same section having multiple load addresses addr_t section_load_addr = LLDB_INVALID_ADDRESS; if (section) { std::lock_guard guard(m_mutex); sect_to_addr_collection::const_iterator pos = m_sect_to_addr.find(section.get()); if (pos != m_sect_to_addr.end()) section_load_addr = pos->second; } return section_load_addr; } bool SectionLoadList::SetSectionLoadAddress(const lldb::SectionSP §ion, addr_t load_addr, bool warn_multiple) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); ModuleSP module_sp(section->GetModule()); if (module_sp) { LLDB_LOGV(log, "(section = {0} ({1}.{2}), load_addr = {3:x}) module = {4}", section.get(), module_sp->GetFileSpec(), section->GetName(), load_addr, module_sp.get()); if (section->GetByteSize() == 0) return false; // No change // Fill in the section -> load_addr map std::lock_guard guard(m_mutex); sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section.get()); if (sta_pos != m_sect_to_addr.end()) { if (load_addr == sta_pos->second) return false; // No change... else sta_pos->second = load_addr; } else m_sect_to_addr[section.get()] = load_addr; // Fill in the load_addr -> section map addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr); if (ats_pos != m_addr_to_sect.end()) { // Some sections are ok to overlap, and for others we should warn. When // we have multiple load addresses that correspond to a section, we will // always attribute the section to the be last section that claims it // exists at that address. Sometimes it is ok for more that one section // to be loaded at a specific load address, and other times it isn't. // The "warn_multiple" parameter tells us if we should warn in this case // or not. The DynamicLoader plug-in subclasses should know which // sections should warn and which shouldn't (darwin shared cache modules // all shared the same "__LINKEDIT" sections, so the dynamic loader can // pass false for "warn_multiple"). if (warn_multiple && section != ats_pos->second) { ModuleSP module_sp(section->GetModule()); if (module_sp) { ModuleSP curr_module_sp(ats_pos->second->GetModule()); if (curr_module_sp) { module_sp->ReportWarning( "address 0x%16.16" PRIx64 " maps to more than one section: %s.%s and %s.%s", load_addr, module_sp->GetFileSpec().GetFilename().GetCString(), section->GetName().GetCString(), curr_module_sp->GetFileSpec().GetFilename().GetCString(), ats_pos->second->GetName().GetCString()); } } } ats_pos->second = section; } else m_addr_to_sect[load_addr] = section; return true; // Changed } else { if (log) { log->Printf( "SectionLoadList::%s (section = %p (%s), load_addr = 0x%16.16" PRIx64 ") error: module has been deleted", __FUNCTION__, static_cast(section.get()), section->GetName().AsCString(), load_addr); } } return false; } size_t SectionLoadList::SetSectionUnloaded(const lldb::SectionSP §ion_sp) { size_t unload_count = 0; if (section_sp) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); if (log && log->GetVerbose()) { ModuleSP module_sp = section_sp->GetModule(); std::string module_name(""); if (module_sp) { const FileSpec &module_file_spec( section_sp->GetModule()->GetFileSpec()); module_name = module_file_spec.GetPath(); } log->Printf("SectionLoadList::%s (section = %p (%s.%s))", __FUNCTION__, static_cast(section_sp.get()), module_name.c_str(), section_sp->GetName().AsCString()); } std::lock_guard guard(m_mutex); sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section_sp.get()); if (sta_pos != m_sect_to_addr.end()) { ++unload_count; addr_t load_addr = sta_pos->second; m_sect_to_addr.erase(sta_pos); addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr); if (ats_pos != m_addr_to_sect.end()) m_addr_to_sect.erase(ats_pos); } } return unload_count; } bool SectionLoadList::SetSectionUnloaded(const lldb::SectionSP §ion_sp, addr_t load_addr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); if (log && log->GetVerbose()) { ModuleSP module_sp = section_sp->GetModule(); std::string module_name(""); if (module_sp) { const FileSpec &module_file_spec(section_sp->GetModule()->GetFileSpec()); module_name = module_file_spec.GetPath(); } log->Printf( "SectionLoadList::%s (section = %p (%s.%s), load_addr = 0x%16.16" PRIx64 ")", __FUNCTION__, static_cast(section_sp.get()), module_name.c_str(), section_sp->GetName().AsCString(), load_addr); } bool erased = false; std::lock_guard guard(m_mutex); sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section_sp.get()); if (sta_pos != m_sect_to_addr.end()) { erased = true; m_sect_to_addr.erase(sta_pos); } addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr); if (ats_pos != m_addr_to_sect.end()) { erased = true; m_addr_to_sect.erase(ats_pos); } return erased; } -bool SectionLoadList::ResolveLoadAddress(addr_t load_addr, - Address &so_addr) const { +bool SectionLoadList::ResolveLoadAddress(addr_t load_addr, Address &so_addr, + bool allow_section_end) const { // First find the top level section that this load address exists in std::lock_guard guard(m_mutex); if (!m_addr_to_sect.empty()) { addr_to_sect_collection::const_iterator pos = m_addr_to_sect.lower_bound(load_addr); if (pos != m_addr_to_sect.end()) { if (load_addr != pos->first && pos != m_addr_to_sect.begin()) --pos; const addr_t pos_load_addr = pos->first; if (load_addr >= pos_load_addr) { addr_t offset = load_addr - pos_load_addr; - if (offset < pos->second->GetByteSize()) { + if (offset < pos->second->GetByteSize() + (allow_section_end ? 1 : 0)) { // We have found the top level section, now we need to find the // deepest child section. - return pos->second->ResolveContainedAddress(offset, so_addr); + return pos->second->ResolveContainedAddress(offset, so_addr, + allow_section_end); } } } else { // There are no entries that have an address that is >= load_addr, // so we need to check the last entry on our collection. addr_to_sect_collection::const_reverse_iterator rpos = m_addr_to_sect.rbegin(); if (load_addr >= rpos->first) { addr_t offset = load_addr - rpos->first; - if (offset < rpos->second->GetByteSize()) { + if (offset < + rpos->second->GetByteSize() + (allow_section_end ? 1 : 0)) { // We have found the top level section, now we need to find the // deepest child section. - return rpos->second->ResolveContainedAddress(offset, so_addr); + return rpos->second->ResolveContainedAddress(offset, so_addr, + allow_section_end); } } } } so_addr.Clear(); return false; } void SectionLoadList::Dump(Stream &s, Target *target) { std::lock_guard guard(m_mutex); addr_to_sect_collection::const_iterator pos, end; for (pos = m_addr_to_sect.begin(), end = m_addr_to_sect.end(); pos != end; ++pos) { s.Printf("addr = 0x%16.16" PRIx64 ", section = %p: ", pos->first, static_cast(pos->second.get())); pos->second->Dump(&s, target, 0); } } Index: vendor/lldb/dist/source/Target/StackFrame.cpp =================================================================== --- vendor/lldb/dist/source/Target/StackFrame.cpp (revision 319789) +++ vendor/lldb/dist/source/Target/StackFrame.cpp (revision 319790) @@ -1,1915 +1,1916 @@ //===-- StackFrame.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/StackFrame.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectMemory.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContextScope.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; // The first bits in the flags are reserved for the SymbolContext::Scope bits // so we know if we have tried to look up information in our internal symbol // context (m_sc) already. #define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1)) #define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1) #define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1) #define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1) #define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1) StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa, bool cfa_is_valid, addr_t pc, uint32_t stop_id, bool stop_id_is_valid, bool is_history_frame, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), m_stop_id(stop_id), m_stop_id_is_valid(stop_id_is_valid), m_is_history_frame(is_history_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { // If we don't have a CFA value, use the frame index for our StackID so that // recursive // functions properly aren't confused with one another on a history stack. if (m_is_history_frame && !m_cfa_is_valid) { m_id.SetCFA(m_frame_index); } if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } } StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, const RegisterContextSP ®_context_sp, addr_t cfa, addr_t pc, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), m_stop_id_is_valid(false), m_is_history_frame(false), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } if (reg_context_sp && !m_sc.target_sp) { m_sc.target_sp = reg_context_sp->CalculateTarget(); if (m_sc.target_sp) m_flags.Set(eSymbolContextTarget); } } StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, const RegisterContextSP ®_context_sp, addr_t cfa, const Address &pc_addr, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, nullptr), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), m_stop_id_is_valid(false), m_is_history_frame(false), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } if (!m_sc.target_sp && reg_context_sp) { m_sc.target_sp = reg_context_sp->CalculateTarget(); if (m_sc.target_sp) m_flags.Set(eSymbolContextTarget); } ModuleSP pc_module_sp(pc_addr.GetModule()); if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp) { if (pc_module_sp) { m_sc.module_sp = pc_module_sp; m_flags.Set(eSymbolContextModule); } else { m_sc.module_sp.reset(); } } } StackFrame::~StackFrame() = default; StackID &StackFrame::GetStackID() { std::lock_guard guard(m_mutex); // Make sure we have resolved the StackID object's symbol context scope if // we already haven't looked it up. if (m_flags.IsClear(RESOLVED_FRAME_ID_SYMBOL_SCOPE)) { if (m_id.GetSymbolContextScope()) { // We already have a symbol context scope, we just don't have our // flag bit set. m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); } else { // Calculate the frame block and use this for the stack ID symbol // context scope if we have one. SymbolContextScope *scope = GetFrameBlock(); if (scope == nullptr) { // We don't have a block, so use the symbol if (m_flags.IsClear(eSymbolContextSymbol)) GetSymbolContext(eSymbolContextSymbol); // It is ok if m_sc.symbol is nullptr here scope = m_sc.symbol; } // Set the symbol context scope (the accessor will set the // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags). SetSymbolContextScope(scope); } } return m_id; } uint32_t StackFrame::GetFrameIndex() const { ThreadSP thread_sp = GetThread(); if (thread_sp) return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex( m_frame_index); else return m_frame_index; } void StackFrame::SetSymbolContextScope(SymbolContextScope *symbol_scope) { std::lock_guard guard(m_mutex); m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); m_id.SetSymbolContextScope(symbol_scope); } const Address &StackFrame::GetFrameCodeAddress() { std::lock_guard guard(m_mutex); if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset()) { m_flags.Set(RESOLVED_FRAME_CODE_ADDR); // Resolve the PC into a temporary address because if ResolveLoadAddress // fails to resolve the address, it will clear the address object... ThreadSP thread_sp(GetThread()); if (thread_sp) { TargetSP target_sp(thread_sp->CalculateTarget()); if (target_sp) { + const bool allow_section_end = true; if (m_frame_code_addr.SetOpcodeLoadAddress( m_frame_code_addr.GetOffset(), target_sp.get(), - eAddressClassCode)) { + eAddressClassCode, allow_section_end)) { ModuleSP module_sp(m_frame_code_addr.GetModule()); if (module_sp) { m_sc.module_sp = module_sp; m_flags.Set(eSymbolContextModule); } } } } } return m_frame_code_addr; } bool StackFrame::ChangePC(addr_t pc) { std::lock_guard guard(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. if (m_is_history_frame) return false; m_frame_code_addr.SetRawAddress(pc); m_sc.Clear(false); m_flags.Reset(0); ThreadSP thread_sp(GetThread()); if (thread_sp) thread_sp->ClearStackFrames(); return true; } const char *StackFrame::Disassemble() { std::lock_guard guard(m_mutex); if (m_disassembly.Empty()) { ExecutionContext exe_ctx(shared_from_this()); Target *target = exe_ctx.GetTargetPtr(); if (target) { const char *plugin_name = nullptr; const char *flavor = nullptr; Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(), plugin_name, flavor, exe_ctx, 0, false, 0, 0, m_disassembly); } if (m_disassembly.Empty()) return nullptr; } return m_disassembly.GetData(); } Block *StackFrame::GetFrameBlock() { if (m_sc.block == nullptr && m_flags.IsClear(eSymbolContextBlock)) GetSymbolContext(eSymbolContextBlock); if (m_sc.block) { Block *inline_block = m_sc.block->GetContainingInlinedBlock(); if (inline_block) { // Use the block with the inlined function info // as the frame block we want this frame to have only the variables // for the inlined function and its non-inlined block child blocks. return inline_block; } else { // This block is not contained within any inlined function blocks // with so we want to use the top most function block. return &m_sc.function->GetBlock(false); } } return nullptr; } //---------------------------------------------------------------------- // Get the symbol context if we already haven't done so by resolving the // PC address as much as possible. This way when we pass around a // StackFrame object, everyone will have as much information as // possible and no one will ever have to look things up manually. //---------------------------------------------------------------------- const SymbolContext &StackFrame::GetSymbolContext(uint32_t resolve_scope) { std::lock_guard guard(m_mutex); // Copy our internal symbol context into "sc". if ((m_flags.Get() & resolve_scope) != resolve_scope) { uint32_t resolved = 0; // If the target was requested add that: if (!m_sc.target_sp) { m_sc.target_sp = CalculateTarget(); if (m_sc.target_sp) resolved |= eSymbolContextTarget; } // Resolve our PC to section offset if we haven't already done so // and if we don't have a module. The resolved address section will // contain the module to which it belongs if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR)) GetFrameCodeAddress(); // If this is not frame zero, then we need to subtract 1 from the PC // value when doing address lookups since the PC will be on the // instruction following the function call instruction... Address lookup_addr(GetFrameCodeAddress()); if (m_frame_index > 0 && lookup_addr.IsValid()) { addr_t offset = lookup_addr.GetOffset(); if (offset > 0) { lookup_addr.SetOffset(offset - 1); } else { // lookup_addr is the start of a section. We need // do the math on the actual load address and re-compute // the section. We're working with a 'noreturn' function // at the end of a section. ThreadSP thread_sp(GetThread()); if (thread_sp) { TargetSP target_sp(thread_sp->CalculateTarget()); if (target_sp) { addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1; lookup_addr.SetLoadAddress(addr_minus_one, target_sp.get()); } else { lookup_addr.SetOffset(offset - 1); } } } } if (m_sc.module_sp) { // We have something in our stack frame symbol context, lets check // if we haven't already tried to lookup one of those things. If we // haven't then we will do the query. uint32_t actual_resolve_scope = 0; if (resolve_scope & eSymbolContextCompUnit) { if (m_flags.IsClear(eSymbolContextCompUnit)) { if (m_sc.comp_unit) resolved |= eSymbolContextCompUnit; else actual_resolve_scope |= eSymbolContextCompUnit; } } if (resolve_scope & eSymbolContextFunction) { if (m_flags.IsClear(eSymbolContextFunction)) { if (m_sc.function) resolved |= eSymbolContextFunction; else actual_resolve_scope |= eSymbolContextFunction; } } if (resolve_scope & eSymbolContextBlock) { if (m_flags.IsClear(eSymbolContextBlock)) { if (m_sc.block) resolved |= eSymbolContextBlock; else actual_resolve_scope |= eSymbolContextBlock; } } if (resolve_scope & eSymbolContextSymbol) { if (m_flags.IsClear(eSymbolContextSymbol)) { if (m_sc.symbol) resolved |= eSymbolContextSymbol; else actual_resolve_scope |= eSymbolContextSymbol; } } if (resolve_scope & eSymbolContextLineEntry) { if (m_flags.IsClear(eSymbolContextLineEntry)) { if (m_sc.line_entry.IsValid()) resolved |= eSymbolContextLineEntry; else actual_resolve_scope |= eSymbolContextLineEntry; } } if (actual_resolve_scope) { // We might be resolving less information than what is already // in our current symbol context so resolve into a temporary // symbol context "sc" so we don't clear out data we have // already found in "m_sc" SymbolContext sc; // Set flags that indicate what we have tried to resolve resolved |= m_sc.module_sp->ResolveSymbolContextForAddress( lookup_addr, actual_resolve_scope, sc); // Only replace what we didn't already have as we may have // information for an inlined function scope that won't match // what a standard lookup by address would match if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == nullptr) m_sc.comp_unit = sc.comp_unit; if ((resolved & eSymbolContextFunction) && m_sc.function == nullptr) m_sc.function = sc.function; if ((resolved & eSymbolContextBlock) && m_sc.block == nullptr) m_sc.block = sc.block; if ((resolved & eSymbolContextSymbol) && m_sc.symbol == nullptr) m_sc.symbol = sc.symbol; if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid()) { m_sc.line_entry = sc.line_entry; m_sc.line_entry.ApplyFileMappings(m_sc.target_sp); } } } else { // If we don't have a module, then we can't have the compile unit, // function, block, line entry or symbol, so we can safely call // ResolveSymbolContextForAddress with our symbol context member m_sc. if (m_sc.target_sp) { resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress( lookup_addr, resolve_scope, m_sc); } } // Update our internal flags so we remember what we have tried to locate so // we don't have to keep trying when more calls to this function are made. // We might have dug up more information that was requested (for example // if we were asked to only get the block, we will have gotten the // compile unit, and function) so set any additional bits that we resolved m_flags.Set(resolve_scope | resolved); } // Return the symbol context with everything that was possible to resolve // resolved. return m_sc; } VariableList *StackFrame::GetVariableList(bool get_file_globals) { std::lock_guard guard(m_mutex); if (m_flags.IsClear(RESOLVED_VARIABLES)) { m_flags.Set(RESOLVED_VARIABLES); Block *frame_block = GetFrameBlock(); if (frame_block) { const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; m_variable_list_sp.reset(new VariableList()); frame_block->AppendBlockVariables(can_create, get_child_variables, stop_if_child_block_is_inlined_function, [](Variable *v) { return true; }, m_variable_list_sp.get()); } } if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) && get_file_globals) { m_flags.Set(RESOLVED_GLOBAL_VARIABLES); if (m_flags.IsClear(eSymbolContextCompUnit)) GetSymbolContext(eSymbolContextCompUnit); if (m_sc.comp_unit) { VariableListSP global_variable_list_sp( m_sc.comp_unit->GetVariableList(true)); if (m_variable_list_sp) m_variable_list_sp->AddVariables(global_variable_list_sp.get()); else m_variable_list_sp = global_variable_list_sp; } } return m_variable_list_sp.get(); } VariableListSP StackFrame::GetInScopeVariableList(bool get_file_globals, bool must_have_valid_location) { std::lock_guard guard(m_mutex); // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return VariableListSP(); VariableListSP var_list_sp(new VariableList); GetSymbolContext(eSymbolContextCompUnit | eSymbolContextBlock); if (m_sc.block) { const bool can_create = true; const bool get_parent_variables = true; const bool stop_if_block_is_inlined_function = true; m_sc.block->AppendVariables( can_create, get_parent_variables, stop_if_block_is_inlined_function, [this, must_have_valid_location](Variable *v) { return v->IsInScope(this) && (!must_have_valid_location || v->LocationIsValidForFrame(this)); }, var_list_sp.get()); } if (m_sc.comp_unit && get_file_globals) { VariableListSP global_variable_list_sp( m_sc.comp_unit->GetVariableList(true)); if (global_variable_list_sp) var_list_sp->AddVariables(global_variable_list_sp.get()); } return var_list_sp; } ValueObjectSP StackFrame::GetValueForVariableExpressionPath( llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, VariableSP &var_sp, Status &error) { llvm::StringRef original_var_expr = var_expr; // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return ValueObjectSP(); if (var_expr.empty()) { error.SetErrorStringWithFormat("invalid variable path '%s'", var_expr.str().c_str()); return ValueObjectSP(); } const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0; const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0; const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0; // const bool no_synth_array = (options & // eExpressionPathOptionsNoSyntheticArrayRange) != 0; error.Clear(); bool deref = false; bool address_of = false; ValueObjectSP valobj_sp; const bool get_file_globals = true; // When looking up a variable for an expression, we need only consider the // variables that are in scope. VariableListSP var_list_sp(GetInScopeVariableList(get_file_globals)); VariableList *variable_list = var_list_sp.get(); if (!variable_list) return ValueObjectSP(); // If first character is a '*', then show pointer contents std::string var_expr_storage; if (var_expr[0] == '*') { deref = true; var_expr = var_expr.drop_front(); // Skip the '*' } else if (var_expr[0] == '&') { address_of = true; var_expr = var_expr.drop_front(); // Skip the '&' } size_t separator_idx = var_expr.find_first_of(".-[=+~|&^%#@!/?,<>{}"); StreamString var_expr_path_strm; ConstString name_const_string(var_expr.substr(0, separator_idx)); var_sp = variable_list->FindVariable(name_const_string, false); bool synthetically_added_instance_object = false; if (var_sp) { var_expr = var_expr.drop_front(name_const_string.GetLength()); } if (!var_sp && (options & eExpressionPathOptionsAllowDirectIVarAccess)) { // Check for direct ivars access which helps us with implicit // access to ivars with the "this->" or "self->" GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock); lldb::LanguageType method_language = eLanguageTypeUnknown; bool is_instance_method = false; ConstString method_object_name; if (m_sc.GetFunctionMethodInfo(method_language, is_instance_method, method_object_name)) { if (is_instance_method && method_object_name) { var_sp = variable_list->FindVariable(method_object_name); if (var_sp) { separator_idx = 0; var_expr_storage = "->"; var_expr_storage += var_expr; var_expr = var_expr_storage; synthetically_added_instance_object = true; } } } } if (!var_sp && (options & eExpressionPathOptionsInspectAnonymousUnions)) { // Check if any anonymous unions are there which contain a variable with // the name we need for (size_t i = 0; i < variable_list->GetSize(); i++) { VariableSP variable_sp = variable_list->GetVariableAtIndex(i); if (!variable_sp) continue; if (!variable_sp->GetName().IsEmpty()) continue; Type *var_type = variable_sp->GetType(); if (!var_type) continue; if (!var_type->GetForwardCompilerType().IsAnonymousType()) continue; valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); if (!valobj_sp) return valobj_sp; valobj_sp = valobj_sp->GetChildMemberWithName(name_const_string, true); if (valobj_sp) break; } } if (var_sp && !valobj_sp) { valobj_sp = GetValueObjectForFrameVariable(var_sp, use_dynamic); if (!valobj_sp) return valobj_sp; } if (!valobj_sp) { error.SetErrorStringWithFormat("no variable named '%s' found in this frame", name_const_string.GetCString()); return ValueObjectSP(); } // We are dumping at least one child while (separator_idx != std::string::npos) { // Calculate the next separator index ahead of time ValueObjectSP child_valobj_sp; const char separator_type = var_expr[0]; bool expr_is_ptr = false; switch (separator_type) { case '-': expr_is_ptr = true; if (var_expr.size() >= 2 && var_expr[1] != '>') return ValueObjectSP(); if (no_fragile_ivar) { // Make sure we aren't trying to deref an objective // C ivar if this is not allowed const uint32_t pointer_type_flags = valobj_sp->GetCompilerType().GetTypeInfo(nullptr); if ((pointer_type_flags & eTypeIsObjC) && (pointer_type_flags & eTypeIsPointer)) { // This was an objective C object pointer and // it was requested we skip any fragile ivars // so return nothing here return ValueObjectSP(); } } // If we have a non pointer type with a sythetic value then lets check if // we have an sythetic dereference specified. if (!valobj_sp->IsPointerType() && valobj_sp->HasSyntheticValue()) { Status deref_error; if (valobj_sp->GetCompilerType().IsReferenceType()) { valobj_sp = valobj_sp->GetSyntheticValue()->Dereference(deref_error); if (error.Fail()) { error.SetErrorStringWithFormatv( "Failed to dereference reference type: %s", deref_error); return ValueObjectSP(); } } valobj_sp = valobj_sp->Dereference(deref_error); if (error.Fail()) { error.SetErrorStringWithFormatv( "Failed to dereference sythetic value: %s", deref_error); return ValueObjectSP(); } expr_is_ptr = false; } var_expr = var_expr.drop_front(); // Remove the '-' LLVM_FALLTHROUGH; case '.': { var_expr = var_expr.drop_front(); // Remove the '.' or '>' separator_idx = var_expr.find_first_of(".-["); ConstString child_name(var_expr.substr(0, var_expr.find_first_of(".-["))); if (check_ptr_vs_member) { // We either have a pointer type and need to verify // valobj_sp is a pointer, or we have a member of a // class/union/struct being accessed with the . syntax // and need to verify we don't have a pointer. const bool actual_is_ptr = valobj_sp->IsPointerType(); if (actual_is_ptr != expr_is_ptr) { // Incorrect use of "." with a pointer, or "->" with // a class/union/struct instance or reference. valobj_sp->GetExpressionPath(var_expr_path_strm, false); if (actual_is_ptr) error.SetErrorStringWithFormat( "\"%s\" is a pointer and . was used to attempt to access " "\"%s\". Did you mean \"%s->%s\"?", var_expr_path_strm.GetData(), child_name.GetCString(), var_expr_path_strm.GetData(), var_expr.str().c_str()); else error.SetErrorStringWithFormat( "\"%s\" is not a pointer and -> was used to attempt to " "access \"%s\". Did you mean \"%s.%s\"?", var_expr_path_strm.GetData(), child_name.GetCString(), var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } } child_valobj_sp = valobj_sp->GetChildMemberWithName(child_name, true); if (!child_valobj_sp) { if (!no_synth_child) { child_valobj_sp = valobj_sp->GetSyntheticValue(); if (child_valobj_sp) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } if (no_synth_child || !child_valobj_sp) { // No child member with name "child_name" if (synthetically_added_instance_object) { // We added a "this->" or "self->" to the beginning of the // expression // and this is the first pointer ivar access, so just return // the normal // error error.SetErrorStringWithFormat( "no variable or instance variable named '%s' found in " "this frame", name_const_string.GetCString()); } else { valobj_sp->GetExpressionPath(var_expr_path_strm, false); if (child_name) { error.SetErrorStringWithFormat( "\"%s\" is not a member of \"(%s) %s\"", child_name.GetCString(), valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { error.SetErrorStringWithFormat( "incomplete expression path after \"%s\" in \"%s\"", var_expr_path_strm.GetData(), original_var_expr.str().c_str()); } } return ValueObjectSP(); } } synthetically_added_instance_object = false; // Remove the child name from the path var_expr = var_expr.drop_front(child_name.GetLength()); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } } break; case '[': { // Array member access, or treating pointer as an array // Need at least two brackets and a number if (var_expr.size() <= 2) { error.SetErrorStringWithFormat( "invalid square bracket encountered after \"%s\" in \"%s\"", var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } // Drop the open brace. var_expr = var_expr.drop_front(); long child_index = 0; // If there's no closing brace, this is an invalid expression. size_t end_pos = var_expr.find_first_of(']'); if (end_pos == llvm::StringRef::npos) { error.SetErrorStringWithFormat( "missing closing square bracket in expression \"%s\"", var_expr_path_strm.GetData()); return ValueObjectSP(); } llvm::StringRef index_expr = var_expr.take_front(end_pos); llvm::StringRef original_index_expr = index_expr; // Drop all of "[index_expr]" var_expr = var_expr.drop_front(end_pos + 1); if (index_expr.consumeInteger(0, child_index)) { // If there was no integer anywhere in the index expression, this is // erroneous expression. error.SetErrorStringWithFormat("invalid index expression \"%s\"", index_expr.str().c_str()); return ValueObjectSP(); } if (index_expr.empty()) { // The entire index expression was a single integer. if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { // what we have is *ptr[low]. the most similar C++ syntax is to deref // ptr and extract bit low out of it. reading array item low would be // done by saying ptr[low], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->Dereference(error)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not dereference \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) { // what we have is *arr[low]. the most similar C++ syntax is // to get arr[0] // (an operation that is equivalent to deref-ing arr) // and extract bit low out of it. reading array item low // would be done by saying arr[low], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not get item 0 for \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } bool is_incomplete_array = false; if (valobj_sp->IsPointerType()) { bool is_objc_pointer = true; if (valobj_sp->GetCompilerType().GetMinimumLanguage() != eLanguageTypeObjC) is_objc_pointer = false; else if (!valobj_sp->GetCompilerType().IsPointerType()) is_objc_pointer = false; if (no_synth_child && is_objc_pointer) { error.SetErrorStringWithFormat( "\"(%s) %s\" is an Objective-C pointer, and cannot be " "subscripted", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } else if (is_objc_pointer) { // dereferencing ObjC variables is not valid.. so let's try // and recur to synthetic children ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); if (!synthetic /* no synthetic */ || synthetic == valobj_sp) /* synthetic is the same as the original object */ { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "\"(%s) %s\" is not an array type", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else if ( static_cast(child_index) >= synthetic ->GetNumChildren() /* synthetic does not have that many values */) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } else { child_valobj_sp = valobj_sp->GetSyntheticArrayMember(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "failed to use pointer as array for index %ld for " "\"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } else if (valobj_sp->GetCompilerType().IsArrayType( nullptr, nullptr, &is_incomplete_array)) { // Pass false to dynamic_value here so we can tell the // difference between // no dynamic value and no member of this type... child_valobj_sp = valobj_sp->GetChildAtIndex(child_index, true); if (!child_valobj_sp && (is_incomplete_array || !no_synth_child)) child_valobj_sp = valobj_sp->GetSyntheticArrayMember(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } else if (valobj_sp->GetCompilerType().IsScalarType()) { // this is a bitfield asking to display just one bit child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild( child_index, child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index, child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } else { ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); if (no_synth_child /* synthetic is forbidden */ || !synthetic /* no synthetic */ || synthetic == valobj_sp) /* synthetic is the same as the original object */ { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "\"(%s) %s\" is not an array type", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else if ( static_cast(child_index) >= synthetic ->GetNumChildren() /* synthetic does not have that many values */) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } if (!child_valobj_sp) { // Invalid array index... return ValueObjectSP(); } separator_idx = var_expr.find_first_of(".-["); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } // Break out early from the switch since we were able to find the child // member break; } // this is most probably a BitField, let's take a look if (index_expr.front() != '-') { error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", original_index_expr.str().c_str()); return ValueObjectSP(); } index_expr = index_expr.drop_front(); long final_index = 0; if (index_expr.getAsInteger(0, final_index)) { error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", original_index_expr.str().c_str()); return ValueObjectSP(); } // if the format given is [high-low], swap range if (child_index > final_index) { long temp = child_index; child_index = final_index; final_index = temp; } if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { // what we have is *ptr[low-high]. the most similar C++ syntax is to // deref ptr and extract bits low thru high out of it. reading array // items low thru high would be done by saying ptr[low-high], without // a deref * sign Status error; ValueObjectSP temp(valobj_sp->Dereference(error)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not dereference \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) { // what we have is *arr[low-high]. the most similar C++ syntax is to get // arr[0] (an operation that is equivalent to deref-ing arr) and extract // bits low thru high out of it. reading array items low thru high would // be done by saying arr[low-high], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not get item 0 for \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index, final_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } if (!child_valobj_sp) { // Invalid bitfield range... return ValueObjectSP(); } separator_idx = var_expr.find_first_of(".-["); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } // Break out early from the switch since we were able to find the child // member break; } default: // Failure... { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "unexpected char '%c' encountered after \"%s\" in \"%s\"", separator_type, var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } } if (child_valobj_sp) valobj_sp = child_valobj_sp; if (var_expr.empty()) break; } if (valobj_sp) { if (deref) { ValueObjectSP deref_valobj_sp(valobj_sp->Dereference(error)); valobj_sp = deref_valobj_sp; } else if (address_of) { ValueObjectSP address_of_valobj_sp(valobj_sp->AddressOf(error)); valobj_sp = address_of_valobj_sp; } } return valobj_sp; } bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { std::lock_guard guard(m_mutex); if (!m_cfa_is_valid) { m_frame_base_error.SetErrorString( "No frame base available for this historical stack frame."); return false; } if (m_flags.IsClear(GOT_FRAME_BASE)) { if (m_sc.function) { m_frame_base.Clear(); m_frame_base_error.Clear(); m_flags.Set(GOT_FRAME_BASE); ExecutionContext exe_ctx(shared_from_this()); Value expr_value; addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; if (m_sc.function->GetFrameBaseExpression().IsLocationList()) loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress( exe_ctx.GetTargetPtr()); if (m_sc.function->GetFrameBaseExpression().Evaluate( &exe_ctx, nullptr, nullptr, nullptr, loclist_base_addr, nullptr, nullptr, expr_value, &m_frame_base_error) == false) { // We should really have an error if evaluate returns, but in case // we don't, lets set the error to something at least. if (m_frame_base_error.Success()) m_frame_base_error.SetErrorString( "Evaluation of the frame base expression failed."); } else { m_frame_base = expr_value.ResolveValue(&exe_ctx); } } else { m_frame_base_error.SetErrorString("No function in symbol context."); } } if (m_frame_base_error.Success()) frame_base = m_frame_base; if (error_ptr) *error_ptr = m_frame_base_error; return m_frame_base_error.Success(); } DWARFExpression *StackFrame::GetFrameBaseExpression(Status *error_ptr) { if (!m_sc.function) { if (error_ptr) { error_ptr->SetErrorString("No function in symbol context."); } return nullptr; } return &m_sc.function->GetFrameBaseExpression(); } RegisterContextSP StackFrame::GetRegisterContext() { std::lock_guard guard(m_mutex); if (!m_reg_context_sp) { ThreadSP thread_sp(GetThread()); if (thread_sp) m_reg_context_sp = thread_sp->CreateRegisterContextForFrame(this); } return m_reg_context_sp; } bool StackFrame::HasDebugInformation() { GetSymbolContext(eSymbolContextLineEntry); return m_sc.line_entry.IsValid(); } ValueObjectSP StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard guard(m_mutex); ValueObjectSP valobj_sp; if (m_is_history_frame) { return valobj_sp; } VariableList *var_list = GetVariableList(true); if (var_list) { // Make sure the variable is a frame variable const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); valobj_sp = ValueObjectVariable::Create(this, variable_sp); m_variable_list_value_objects.SetValueObjectAtIndex(var_idx, valobj_sp); } } } if (use_dynamic != eNoDynamicValues && valobj_sp) { ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue(use_dynamic); if (dynamic_sp) return dynamic_sp; } return valobj_sp; } ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard guard(m_mutex); if (m_is_history_frame) return ValueObjectSP(); // Check to make sure we aren't already tracking this variable? ValueObjectSP valobj_sp( GetValueObjectForFrameVariable(variable_sp, use_dynamic)); if (!valobj_sp) { // We aren't already tracking this global VariableList *var_list = GetVariableList(true); // If this frame has no variables, create a new list if (var_list == nullptr) m_variable_list_sp.reset(new VariableList()); // Add the global/static variable to this frame m_variable_list_sp->AddVariable(variable_sp); // Now make a value object for it so we can track its changes valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); } return valobj_sp; } bool StackFrame::IsInlined() { if (m_sc.block == nullptr) GetSymbolContext(eSymbolContextBlock); if (m_sc.block) return m_sc.block->GetContainingInlinedBlock() != nullptr; return false; } lldb::LanguageType StackFrame::GetLanguage() { CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit; if (cu) return cu->GetLanguage(); return lldb::eLanguageTypeUnknown; } lldb::LanguageType StackFrame::GuessLanguage() { LanguageType lang_type = GetLanguage(); if (lang_type == eLanguageTypeUnknown) { SymbolContext sc = GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol); if (sc.function) { lang_type = sc.function->GetMangled().GuessLanguage(); } else if (sc.symbol) { lang_type = sc.symbol->GetMangled().GuessLanguage(); } } return lang_type; } namespace { std::pair GetBaseExplainingValue(const Instruction::Operand &operand, RegisterContext ®ister_context, lldb::addr_t value) { switch (operand.m_type) { case Instruction::Operand::Type::Dereference: case Instruction::Operand::Type::Immediate: case Instruction::Operand::Type::Invalid: case Instruction::Operand::Type::Product: // These are not currently interesting return std::make_pair(nullptr, 0); case Instruction::Operand::Type::Sum: { const Instruction::Operand *immediate_child = nullptr; const Instruction::Operand *variable_child = nullptr; if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate) { immediate_child = &operand.m_children[0]; variable_child = &operand.m_children[1]; } else if (operand.m_children[1].m_type == Instruction::Operand::Type::Immediate) { immediate_child = &operand.m_children[1]; variable_child = &operand.m_children[0]; } if (!immediate_child) { return std::make_pair(nullptr, 0); } lldb::addr_t adjusted_value = value; if (immediate_child->m_negative) { adjusted_value += immediate_child->m_immediate; } else { adjusted_value -= immediate_child->m_immediate; } std::pair base_and_offset = GetBaseExplainingValue(*variable_child, register_context, adjusted_value); if (!base_and_offset.first) { return std::make_pair(nullptr, 0); } if (immediate_child->m_negative) { base_and_offset.second -= immediate_child->m_immediate; } else { base_and_offset.second += immediate_child->m_immediate; } return base_and_offset; } case Instruction::Operand::Type::Register: { const RegisterInfo *info = register_context.GetRegisterInfoByName(operand.m_register.AsCString()); if (!info) { return std::make_pair(nullptr, 0); } RegisterValue reg_value; if (!register_context.ReadRegister(info, reg_value)) { return std::make_pair(nullptr, 0); } if (reg_value.GetAsUInt64() == value) { return std::make_pair(&operand, 0); } else { return std::make_pair(nullptr, 0); } } } return std::make_pair(nullptr, 0); } std::pair GetBaseExplainingDereference(const Instruction::Operand &operand, RegisterContext ®ister_context, lldb::addr_t addr) { if (operand.m_type == Instruction::Operand::Type::Dereference) { return GetBaseExplainingValue(operand.m_children[0], register_context, addr); } return std::make_pair(nullptr, 0); } } lldb::ValueObjectSP StackFrame::GuessValueForAddress(lldb::addr_t addr) { TargetSP target_sp = CalculateTarget(); const ArchSpec &target_arch = target_sp->GetArchitecture(); AddressRange pc_range; pc_range.GetBaseAddress() = GetFrameCodeAddress(); pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize()); ExecutionContext exe_ctx(shared_from_this()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool prefer_file_cache = false; DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { return ValueObjectSP(); } InstructionSP instruction_sp = disassembler_sp->GetInstructionList().GetInstructionAtIndex(0); llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands)) { return ValueObjectSP(); } RegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) { return ValueObjectSP(); } for (const Instruction::Operand &operand : operands) { std::pair base_and_offset = GetBaseExplainingDereference(operand, *register_context_sp, addr); if (!base_and_offset.first) { continue; } switch (base_and_offset.first->m_type) { case Instruction::Operand::Type::Immediate: { lldb_private::Address addr; if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate + base_and_offset.second, addr)) { TypeSystem *c_type_system = target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC); if (!c_type_system) { return ValueObjectSP(); } else { CompilerType void_ptr_type = c_type_system ->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar) .GetPointerType(); return ValueObjectMemory::Create(this, "", addr, void_ptr_type); } } else { return ValueObjectSP(); } break; } case Instruction::Operand::Type::Register: { return GuessValueForRegisterAndOffset(base_and_offset.first->m_register, base_and_offset.second); } default: return ValueObjectSP(); } } return ValueObjectSP(); } namespace { ValueObjectSP GetValueForOffset(StackFrame &frame, ValueObjectSP &parent, int64_t offset) { if (offset < 0 || uint64_t(offset) >= parent->GetByteSize()) { return ValueObjectSP(); } if (parent->IsPointerOrReferenceType()) { return parent; } for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci) { const bool can_create = true; ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create); if (!child_sp) { return ValueObjectSP(); } int64_t child_offset = child_sp->GetByteOffset(); int64_t child_size = child_sp->GetByteSize(); if (offset >= child_offset && offset < (child_offset + child_size)) { return GetValueForOffset(frame, child_sp, offset - child_offset); } } if (offset == 0) { return parent; } else { return ValueObjectSP(); } } ValueObjectSP GetValueForDereferincingOffset(StackFrame &frame, ValueObjectSP &base, int64_t offset) { // base is a pointer to something // offset is the thing to add to the pointer // We return the most sensible ValueObject for the result of *(base+offset) if (!base->IsPointerOrReferenceType()) { return ValueObjectSP(); } Status error; ValueObjectSP pointee = base->Dereference(error); if (!pointee) { return ValueObjectSP(); } if (offset >= 0 && uint64_t(offset) >= pointee->GetByteSize()) { int64_t index = offset / pointee->GetByteSize(); offset = offset % pointee->GetByteSize(); const bool can_create = true; pointee = base->GetSyntheticArrayMember(index, can_create); } if (!pointee || error.Fail()) { return ValueObjectSP(); } return GetValueForOffset(frame, pointee, offset); } //------------------------------------------------------------------ /// Attempt to reconstruct the ValueObject for the address contained in a /// given register plus an offset. /// /// @params [in] frame /// The current stack frame. /// /// @params [in] reg /// The register. /// /// @params [in] offset /// The offset from the register. /// /// @param [in] disassembler /// A disassembler containing instructions valid up to the current PC. /// /// @param [in] variables /// The variable list from the current frame, /// /// @param [in] pc /// The program counter for the instruction considered the 'user'. /// /// @return /// A string describing the base for the ExpressionPath. This could be a /// variable, a register value, an argument, or a function return value. /// The ValueObject if found. If valid, it has a valid ExpressionPath. //------------------------------------------------------------------ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, int64_t offset, Disassembler &disassembler, VariableList &variables, const Address &pc) { // Example of operation for Intel: // // +14: movq -0x8(%rbp), %rdi // +18: movq 0x8(%rdi), %rdi // +22: addl 0x4(%rdi), %eax // // f, a pointer to a struct, is known to be at -0x8(%rbp). // // DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at +18 // that assigns to rdi, and calls itself recursively for that dereference // DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at // +14 that assigns to rdi, and calls itself recursively for that // derefernece // DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the // variable list. // Returns a ValueObject for f. (That's what was stored at rbp-8 at +14) // Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8 // at +18) // Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at // rdi+4 at +22) // First, check the variable list to see if anything is at the specified // location. using namespace OperandMatchers; const RegisterInfo *reg_info = frame.GetRegisterContext()->GetRegisterInfoByName(reg.AsCString()); if (!reg_info) { return ValueObjectSP(); } Instruction::Operand op = offset ? Instruction::Operand::BuildDereference( Instruction::Operand::BuildSum( Instruction::Operand::BuildRegister(reg), Instruction::Operand::BuildImmediate(offset))) : Instruction::Operand::BuildDereference( Instruction::Operand::BuildRegister(reg)); for (size_t vi = 0, ve = variables.GetSize(); vi != ve; ++vi) { VariableSP var_sp = variables.GetVariableAtIndex(vi); if (var_sp->LocationExpression().MatchesOperand(frame, op)) { return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); } } const uint32_t current_inst = disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc); if (current_inst == UINT32_MAX) { return ValueObjectSP(); } for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii) { // This is not an exact algorithm, and it sacrifices accuracy for // generality. Recognizing "mov" and "ld" instructions –– and which are // their source and destination operands -- is something the disassembler // should do for us. InstructionSP instruction_sp = disassembler.GetInstructionList().GetInstructionAtIndex(ii); if (instruction_sp->IsCall()) { ABISP abi_sp = frame.CalculateProcess()->GetABI(); if (!abi_sp) { continue; } const char *return_register_name; if (!abi_sp->GetPointerReturnRegister(return_register_name)) { continue; } const RegisterInfo *return_register_info = frame.GetRegisterContext()->GetRegisterInfoByName( return_register_name); if (!return_register_info) { continue; } int64_t offset = 0; if (!MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), MatchRegOp(*return_register_info))(op) && !MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), MatchRegOp(*return_register_info), FetchImmOp(offset)))(op)) { continue; } llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands) || operands.size() != 1) { continue; } switch (operands[0].m_type) { default: break; case Instruction::Operand::Type::Immediate: { SymbolContext sc; Address load_address; if (!frame.CalculateTarget()->ResolveLoadAddress( operands[0].m_immediate, load_address)) { break; } frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress( load_address, eSymbolContextFunction, sc); if (!sc.function) { break; } CompilerType function_type = sc.function->GetCompilerType(); if (!function_type.IsFunctionType()) { break; } CompilerType return_type = function_type.GetFunctionReturnType(); RegisterValue return_value; if (!frame.GetRegisterContext()->ReadRegister(return_register_info, return_value)) { break; } std::string name_str( sc.function->GetName().AsCString("")); name_str.append("()"); Address return_value_address(return_value.GetAsUInt64()); ValueObjectSP return_value_sp = ValueObjectMemory::Create( &frame, name_str, return_value_address, return_type); return GetValueForDereferincingOffset(frame, return_value_sp, offset); } } continue; } llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands) || operands.size() != 2) { continue; } Instruction::Operand *origin_operand = nullptr; auto clobbered_reg_matcher = [reg_info](const Instruction::Operand &op) { return MatchRegOp(*reg_info)(op) && op.m_clobbered; }; if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; } else { continue; } // We have an origin operand. Can we track its value down? ValueObjectSP source_path; ConstString origin_register; int64_t origin_offset = 0; if (FetchRegOp(origin_register)(*origin_operand)) { source_path = DoGuessValueAt(frame, origin_register, 0, disassembler, variables, instruction_sp->GetAddress()); } else if (MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), FetchRegOp(origin_register))(*origin_operand) || MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), FetchRegOp(origin_register), FetchImmOp(origin_offset)))(*origin_operand)) { source_path = DoGuessValueAt(frame, origin_register, origin_offset, disassembler, variables, instruction_sp->GetAddress()); if (!source_path) { continue; } source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { return source_path; } } return ValueObjectSP(); } } lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { TargetSP target_sp = CalculateTarget(); const ArchSpec &target_arch = target_sp->GetArchitecture(); Block *frame_block = GetFrameBlock(); if (!frame_block) { return ValueObjectSP(); } Function *function = frame_block->CalculateSymbolContextFunction(); if (!function) { return ValueObjectSP(); } AddressRange pc_range = function->GetAddressRange(); if (GetFrameCodeAddress().GetFileAddress() < pc_range.GetBaseAddress().GetFileAddress() || GetFrameCodeAddress().GetFileAddress() - pc_range.GetBaseAddress().GetFileAddress() >= pc_range.GetByteSize()) { return ValueObjectSP(); } ExecutionContext exe_ctx(shared_from_this()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool prefer_file_cache = false; DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { return ValueObjectSP(); } const bool get_file_globals = false; VariableList *variables = GetVariableList(get_file_globals); if (!variables) { return ValueObjectSP(); } return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables, GetFrameCodeAddress()); } TargetSP StackFrame::CalculateTarget() { TargetSP target_sp; ThreadSP thread_sp(GetThread()); if (thread_sp) { ProcessSP process_sp(thread_sp->CalculateProcess()); if (process_sp) target_sp = process_sp->CalculateTarget(); } return target_sp; } ProcessSP StackFrame::CalculateProcess() { ProcessSP process_sp; ThreadSP thread_sp(GetThread()); if (thread_sp) process_sp = thread_sp->CalculateProcess(); return process_sp; } ThreadSP StackFrame::CalculateThread() { return GetThread(); } StackFrameSP StackFrame::CalculateStackFrame() { return shared_from_this(); } void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.SetContext(shared_from_this()); } void StackFrame::DumpUsingSettingsFormat(Stream *strm, const char *frame_marker) { if (strm == nullptr) return; GetSymbolContext(eSymbolContextEverything); ExecutionContext exe_ctx(shared_from_this()); StreamString s; if (frame_marker) s.PutCString(frame_marker); const FormatEntity::Entry *frame_format = nullptr; Target *target = exe_ctx.GetTargetPtr(); if (target) frame_format = target->GetDebugger().GetFrameFormat(); if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx, nullptr, nullptr, false, false)) { strm->PutCString(s.GetString()); } else { Dump(strm, true, false); strm->EOL(); } } void StackFrame::Dump(Stream *strm, bool show_frame_index, bool show_fullpaths) { if (strm == nullptr) return; if (show_frame_index) strm->Printf("frame #%u: ", m_frame_index); ExecutionContext exe_ctx(shared_from_this()); Target *target = exe_ctx.GetTargetPtr(); strm->Printf("0x%0*" PRIx64 " ", target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16, GetFrameCodeAddress().GetLoadAddress(target)); GetSymbolContext(eSymbolContextEverything); const bool show_module = true; const bool show_inline = true; const bool show_function_arguments = true; const bool show_function_name = true; m_sc.DumpStopContext(strm, exe_ctx.GetBestExecutionContextScope(), GetFrameCodeAddress(), show_fullpaths, show_module, show_inline, show_function_arguments, show_function_name); } void StackFrame::UpdateCurrentFrameFromPreviousFrame(StackFrame &prev_frame) { std::lock_guard guard(m_mutex); assert(GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing m_variable_list_sp = prev_frame.m_variable_list_sp; m_variable_list_value_objects.Swap(prev_frame.m_variable_list_value_objects); if (!m_disassembly.GetString().empty()) { m_disassembly.Clear(); m_disassembly.PutCString(prev_frame.m_disassembly.GetString()); } } void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) { std::lock_guard guard(m_mutex); assert(GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value assert(GetThread() == curr_frame.GetThread()); m_frame_index = curr_frame.m_frame_index; m_concrete_frame_index = curr_frame.m_concrete_frame_index; m_reg_context_sp = curr_frame.m_reg_context_sp; m_frame_code_addr = curr_frame.m_frame_code_addr; assert(!m_sc.target_sp || !curr_frame.m_sc.target_sp || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get()); assert(!m_sc.module_sp || !curr_frame.m_sc.module_sp || m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get()); assert(m_sc.comp_unit == nullptr || curr_frame.m_sc.comp_unit == nullptr || m_sc.comp_unit == curr_frame.m_sc.comp_unit); assert(m_sc.function == nullptr || curr_frame.m_sc.function == nullptr || m_sc.function == curr_frame.m_sc.function); m_sc = curr_frame.m_sc; m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything); m_flags.Set(m_sc.GetResolvedMask()); m_frame_base.Clear(); m_frame_base_error.Clear(); } bool StackFrame::HasCachedData() const { if (m_variable_list_sp) return true; if (m_variable_list_value_objects.GetSize() > 0) return true; if (!m_disassembly.GetString().empty()) return true; return false; } bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, const char *frame_marker) { if (show_frame_info) { strm.Indent(); DumpUsingSettingsFormat(&strm, frame_marker); } if (show_source) { ExecutionContext exe_ctx(shared_from_this()); bool have_source = false, have_debuginfo = false; Debugger::StopDisassemblyType disasm_display = Debugger::eStopDisassemblyTypeNever; Target *target = exe_ctx.GetTargetPtr(); if (target) { Debugger &debugger = target->GetDebugger(); const uint32_t source_lines_before = debugger.GetStopSourceLineCount(true); const uint32_t source_lines_after = debugger.GetStopSourceLineCount(false); disasm_display = debugger.GetStopDisassemblyDisplay(); GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry); if (m_sc.comp_unit && m_sc.line_entry.IsValid()) { have_debuginfo = true; if (source_lines_before > 0 || source_lines_after > 0) { size_t num_lines = target->GetSourceManager().DisplaySourceLinesWithLineNumbers( m_sc.line_entry.file, m_sc.line_entry.line, m_sc.line_entry.column, source_lines_before, source_lines_after, "->", &strm); if (num_lines != 0) have_source = true; // TODO: Give here a one time warning if source file is missing. } } switch (disasm_display) { case Debugger::eStopDisassemblyTypeNever: break; case Debugger::eStopDisassemblyTypeNoDebugInfo: if (have_debuginfo) break; LLVM_FALLTHROUGH; case Debugger::eStopDisassemblyTypeNoSource: if (have_source) break; LLVM_FALLTHROUGH; case Debugger::eStopDisassemblyTypeAlways: if (target) { const uint32_t disasm_lines = debugger.GetDisassemblyLineCount(); if (disasm_lines > 0) { const ArchSpec &target_arch = target->GetArchitecture(); AddressRange pc_range; pc_range.GetBaseAddress() = GetFrameCodeAddress(); pc_range.SetByteSize(disasm_lines * target_arch.GetMaximumOpcodeByteSize()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool mixed_source_and_assembly = false; Disassembler::Disassemble( target->GetDebugger(), target_arch, plugin_name, flavor, exe_ctx, pc_range, disasm_lines, mixed_source_and_assembly, 0, Disassembler::eOptionMarkPCAddress, strm); } } break; } } } return true; } Index: vendor/lldb/dist/source/Utility/CMakeLists.txt =================================================================== --- vendor/lldb/dist/source/Utility/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/source/Utility/CMakeLists.txt (revision 319790) @@ -1,42 +1,43 @@ add_lldb_library(lldbUtility Baton.cpp ConstString.cpp DataBufferHeap.cpp DataBufferLLVM.cpp DataEncoder.cpp DataExtractor.cpp FastDemangle.cpp FileSpec.cpp History.cpp JSON.cpp LLDBAssert.cpp Log.cpp Logging.cpp NameMatches.cpp Range.cpp RegularExpression.cpp SelectHelper.cpp SharingPtr.cpp Status.cpp Stream.cpp StreamCallback.cpp StreamGDBRemote.cpp StreamString.cpp StringExtractor.cpp StringExtractorGDBRemote.cpp StringLexer.cpp StringList.cpp TaskPool.cpp TildeExpressionResolver.cpp UserID.cpp UriParser.cpp UUID.cpp VASprintf.cpp VMRange.cpp LINK_LIBS # lldbUtility cannot have any dependencies LINK_COMPONENTS + BinaryFormat Support ) Index: vendor/lldb/dist/source/Utility/Status.cpp =================================================================== --- vendor/lldb/dist/source/Utility/Status.cpp (revision 319789) +++ vendor/lldb/dist/source/Utility/Status.cpp (revision 319790) @@ -1,306 +1,304 @@ //===-- Status.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/Utility/Status.h" #include "lldb/Utility/VASPrintf.h" -#include "lldb/lldb-defines.h" // for LLDB_GENERIC_ERROR -#include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType::eErr... -#include "llvm/ADT/SmallString.h" // for SmallString -#include "llvm/ADT/StringRef.h" // for StringRef +#include "lldb/lldb-defines.h" // for LLDB_GENERIC_ERROR +#include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType::eErr... +#include "llvm/ADT/SmallString.h" // for SmallString +#include "llvm/ADT/StringRef.h" // for StringRef +#include "llvm/Support/Errno.h" #include "llvm/Support/FormatProviders.h" // for format_provider #include #include #include // for string #include #ifdef __APPLE__ #include #endif #include // for uint32_t -#include // for strerror namespace llvm { class raw_ostream; } using namespace lldb; using namespace lldb_private; Status::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {} Status::Status(ValueType err, ErrorType type) : m_code(err), m_type(type), m_string() {} Status::Status(std::error_code EC) : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric), m_string(EC.message()) {} Status::Status(const Status &rhs) = default; Status::Status(const char *format, ...) : m_code(0), m_type(eErrorTypeInvalid), m_string() { va_list args; va_start(args, format); SetErrorToGenericError(); SetErrorStringWithVarArg(format, args); va_end(args); } Status::Status(llvm::Error error) : m_code(0), m_type(ErrorType::eErrorTypeGeneric) { if (!error) return; // if the error happens to be a errno error, preserve the error code error = llvm::handleErrors( std::move(error), [&](std::unique_ptr e) -> llvm::Error { std::error_code ec = e->convertToErrorCode(); if (ec.category() == std::generic_category()) { m_code = ec.value(); m_type = ErrorType::eErrorTypePOSIX; return llvm::Error::success(); } return llvm::Error(std::move(e)); }); // Otherwise, just preserve the message if (error) SetErrorString(llvm::toString(std::move(error))); } llvm::Error Status::ToError() const { if (Success()) return llvm::Error::success(); if (m_type == ErrorType::eErrorTypePOSIX) return llvm::errorCodeToError(std::error_code(m_code, std::generic_category())); return llvm::make_error(AsCString(), llvm::inconvertibleErrorCode()); } //---------------------------------------------------------------------- // Assignment operator //---------------------------------------------------------------------- const Status &Status::operator=(const Status &rhs) { if (this != &rhs) { m_code = rhs.m_code; m_type = rhs.m_type; m_string = rhs.m_string; } return *this; } //---------------------------------------------------------------------- // Assignment operator //---------------------------------------------------------------------- const Status &Status::operator=(uint32_t err) { m_code = err; m_type = eErrorTypeMachKernel; m_string.clear(); return *this; } Status::~Status() = default; //---------------------------------------------------------------------- // Get the error value as a NULL C string. The error string will be // fetched and cached on demand. The cached error string value will // remain until the error value is changed or cleared. //---------------------------------------------------------------------- const char *Status::AsCString(const char *default_error_str) const { if (Success()) return nullptr; if (m_string.empty()) { - const char *s = nullptr; switch (m_type) { case eErrorTypeMachKernel: #if defined(__APPLE__) - s = ::mach_error_string(m_code); + if (const char *s = ::mach_error_string(m_code)) + m_string.assign(s); #endif break; case eErrorTypePOSIX: - s = ::strerror(m_code); + m_string = llvm::sys::StrError(m_code); break; default: break; } - if (s != nullptr) - m_string.assign(s); } if (m_string.empty()) { if (default_error_str) m_string.assign(default_error_str); else return nullptr; // User wanted a nullptr string back... } return m_string.c_str(); } //---------------------------------------------------------------------- // Clear the error and any cached error string that it might contain. //---------------------------------------------------------------------- void Status::Clear() { m_code = 0; m_type = eErrorTypeInvalid; m_string.clear(); } //---------------------------------------------------------------------- // Access the error value. //---------------------------------------------------------------------- Status::ValueType Status::GetError() const { return m_code; } //---------------------------------------------------------------------- // Access the error type. //---------------------------------------------------------------------- ErrorType Status::GetType() const { return m_type; } //---------------------------------------------------------------------- // Returns true if this object contains a value that describes an // error or otherwise non-success result. //---------------------------------------------------------------------- bool Status::Fail() const { return m_code != 0; } //---------------------------------------------------------------------- // Set accesssor for the error value to "err" and the type to // "eErrorTypeMachKernel" //---------------------------------------------------------------------- void Status::SetMachError(uint32_t err) { m_code = err; m_type = eErrorTypeMachKernel; m_string.clear(); } void Status::SetExpressionError(lldb::ExpressionResults result, const char *mssg) { m_code = result; m_type = eErrorTypeExpression; m_string = mssg; } int Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result, const char *format, ...) { int length = 0; if (format != nullptr && format[0]) { va_list args; va_start(args, format); length = SetErrorStringWithVarArg(format, args); va_end(args); } else { m_string.clear(); } m_code = result; m_type = eErrorTypeExpression; return length; } //---------------------------------------------------------------------- // Set accesssor for the error value and type. //---------------------------------------------------------------------- void Status::SetError(ValueType err, ErrorType type) { m_code = err; m_type = type; m_string.clear(); } //---------------------------------------------------------------------- // Update the error value to be "errno" and update the type to // be "POSIX". //---------------------------------------------------------------------- void Status::SetErrorToErrno() { m_code = errno; m_type = eErrorTypePOSIX; m_string.clear(); } //---------------------------------------------------------------------- // Update the error value to be LLDB_GENERIC_ERROR and update the type // to be "Generic". //---------------------------------------------------------------------- void Status::SetErrorToGenericError() { m_code = LLDB_GENERIC_ERROR; m_type = eErrorTypeGeneric; m_string.clear(); } //---------------------------------------------------------------------- // Set accessor for the error string value for a specific error. // This allows any string to be supplied as an error explanation. // The error string value will remain until the error value is // cleared or a new error value/type is assigned. //---------------------------------------------------------------------- void Status::SetErrorString(llvm::StringRef err_str) { if (!err_str.empty()) { // If we have an error string, we should always at least have an error // set to a generic value. if (Success()) SetErrorToGenericError(); } m_string = err_str; } //------------------------------------------------------------------ /// Set the current error string to a formatted error string. /// /// @param format /// A printf style format string //------------------------------------------------------------------ int Status::SetErrorStringWithFormat(const char *format, ...) { if (format != nullptr && format[0]) { va_list args; va_start(args, format); int length = SetErrorStringWithVarArg(format, args); va_end(args); return length; } else { m_string.clear(); } return 0; } int Status::SetErrorStringWithVarArg(const char *format, va_list args) { if (format != nullptr && format[0]) { // If we have an error string, we should always at least have // an error set to a generic value. if (Success()) SetErrorToGenericError(); llvm::SmallString<1024> buf; VASprintf(buf, format, args); m_string = buf.str(); return buf.size(); } else { m_string.clear(); } return 0; } //---------------------------------------------------------------------- // Returns true if the error code in this object is considered a // successful return value. //---------------------------------------------------------------------- bool Status::Success() const { return m_code == 0; } bool Status::WasInterrupted() const { return (m_type == eErrorTypePOSIX && m_code == EINTR); } void llvm::format_provider::format( const lldb_private::Status &error, llvm::raw_ostream &OS, llvm::StringRef Options) { llvm::format_provider::format(error.AsCString(), OS, Options); } Index: vendor/lldb/dist/source/Utility/TaskPool.cpp =================================================================== --- vendor/lldb/dist/source/Utility/TaskPool.cpp (revision 319789) +++ vendor/lldb/dist/source/Utility/TaskPool.cpp (revision 319790) @@ -1,98 +1,98 @@ //===--------------------- TaskPool.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/Utility/TaskPool.h" #include // for uint32_t #include // for queue #include // for thread namespace { class TaskPoolImpl { public: static TaskPoolImpl &GetInstance(); void AddTask(std::function &&task_fn); private: TaskPoolImpl(); static void Worker(TaskPoolImpl *pool); std::queue> m_tasks; std::mutex m_tasks_mutex; uint32_t m_thread_count; }; } // end of anonymous namespace TaskPoolImpl &TaskPoolImpl::GetInstance() { static TaskPoolImpl g_task_pool_impl; return g_task_pool_impl; } void TaskPool::AddTaskImpl(std::function &&task_fn) { TaskPoolImpl::GetInstance().AddTask(std::move(task_fn)); } TaskPoolImpl::TaskPoolImpl() : m_thread_count(0) {} void TaskPoolImpl::AddTask(std::function &&task_fn) { static const uint32_t max_threads = std::thread::hardware_concurrency(); std::unique_lock lock(m_tasks_mutex); m_tasks.emplace(std::move(task_fn)); if (m_thread_count < max_threads) { m_thread_count++; // Note that this detach call needs to happen with the m_tasks_mutex held. // This prevents the thread // from exiting prematurely and triggering a linux libc bug // (https://sourceware.org/bugzilla/show_bug.cgi?id=19951). std::thread(Worker, this).detach(); } } void TaskPoolImpl::Worker(TaskPoolImpl *pool) { while (true) { std::unique_lock lock(pool->m_tasks_mutex); if (pool->m_tasks.empty()) { pool->m_thread_count--; break; } std::function f = pool->m_tasks.front(); pool->m_tasks.pop(); lock.unlock(); f(); } } void TaskMapOverInt(size_t begin, size_t end, - std::function const &func) { + const llvm::function_ref &func) { std::atomic idx{begin}; size_t num_workers = std::min(end, std::thread::hardware_concurrency()); auto wrapper = [&idx, end, &func]() { while (true) { size_t i = idx.fetch_add(1); if (i >= end) break; func(i); } }; std::vector> futures; futures.reserve(num_workers); for (size_t i = 0; i < num_workers; i++) futures.push_back(TaskPool::AddTask(wrapper)); for (size_t i = 0; i < num_workers; i++) futures[i].wait(); } Index: vendor/lldb/dist/source/Utility/VMRange.cpp =================================================================== --- vendor/lldb/dist/source/Utility/VMRange.cpp (revision 319789) +++ vendor/lldb/dist/source/Utility/VMRange.cpp (revision 319790) @@ -1,103 +1,69 @@ //===-- VMRange.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/Utility/VMRange.h" #include "lldb/Utility/Stream.h" #include "lldb/lldb-types.h" // for addr_t #include #include // for distance #include // for const_iterator #include // for size_t #include // for UINT32_MAX, uint32_t using namespace lldb; using namespace lldb_private; bool VMRange::ContainsValue(const VMRange::collection &coll, lldb::addr_t value) { ValueInRangeUnaryPredicate in_range_predicate(value); - VMRange::const_iterator pos; - VMRange::const_iterator end = coll.end(); - pos = std::find_if(coll.begin(), end, in_range_predicate); - if (pos != end) - return true; - return false; + return llvm::find_if(coll, in_range_predicate) != coll.end(); } bool VMRange::ContainsRange(const VMRange::collection &coll, const VMRange &range) { RangeInRangeUnaryPredicate in_range_predicate(range); - VMRange::const_iterator pos; - VMRange::const_iterator end = coll.end(); - pos = std::find_if(coll.begin(), end, in_range_predicate); - if (pos != end) - return true; - return false; + return llvm::find_if(coll, in_range_predicate) != coll.end(); } -size_t VMRange::FindRangeIndexThatContainsValue(const VMRange::collection &coll, - lldb::addr_t value) { - ValueInRangeUnaryPredicate in_range_predicate(value); - VMRange::const_iterator begin = coll.begin(); - VMRange::const_iterator end = coll.end(); - VMRange::const_iterator pos = std::find_if(begin, end, in_range_predicate); - if (pos != end) - return std::distance(begin, pos); - return UINT32_MAX; -} - void VMRange::Dump(Stream *s, lldb::addr_t offset, uint32_t addr_width) const { s->AddressRange(offset + GetBaseAddress(), offset + GetEndAddress(), addr_width); } bool lldb_private::operator==(const VMRange &lhs, const VMRange &rhs) { return lhs.GetBaseAddress() == rhs.GetBaseAddress() && lhs.GetEndAddress() == rhs.GetEndAddress(); } bool lldb_private::operator!=(const VMRange &lhs, const VMRange &rhs) { - return lhs.GetBaseAddress() != rhs.GetBaseAddress() || - lhs.GetEndAddress() != rhs.GetEndAddress(); + return !(lhs == rhs); } bool lldb_private::operator<(const VMRange &lhs, const VMRange &rhs) { if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) return true; else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) return false; return lhs.GetEndAddress() < rhs.GetEndAddress(); } bool lldb_private::operator<=(const VMRange &lhs, const VMRange &rhs) { - if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) - return true; - else if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) - return false; - return lhs.GetEndAddress() <= rhs.GetEndAddress(); + return !(lhs > rhs); } bool lldb_private::operator>(const VMRange &lhs, const VMRange &rhs) { - if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) - return true; - else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) - return false; - return lhs.GetEndAddress() > rhs.GetEndAddress(); + return rhs < lhs; } bool lldb_private::operator>=(const VMRange &lhs, const VMRange &rhs) { - if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) - return true; - else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) - return false; - return lhs.GetEndAddress() >= rhs.GetEndAddress(); + return !(lhs < rhs); } Index: vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp (revision 319789) +++ vendor/lldb/dist/tools/lldb-server/lldb-gdbserver.cpp (revision 319790) @@ -1,485 +1,484 @@ //===-- lldb-gdbserver.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 #include #include #include #include #include #ifndef _WIN32 #include #include #endif // C++ Includes -// Other libraries and framework includes -#include "llvm/ADT/StringRef.h" #include "Acceptor.h" #include "LLDBServerUtilities.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/HostGetOpt.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/Socket.h" #include "lldb/Host/StringConvert.h" #include "lldb/Utility/Status.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errno.h" #ifndef LLGS_PROGRAM_NAME #define LLGS_PROGRAM_NAME "lldb-server" #endif #ifndef LLGS_VERSION_STR #define LLGS_VERSION_STR "local_build" #endif using namespace llvm; using namespace lldb; using namespace lldb_private; using namespace lldb_private::lldb_server; using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // option descriptors for getopt_long_only() //---------------------------------------------------------------------- static int g_debug = 0; static int g_verbose = 0; static struct option g_long_options[] = { {"debug", no_argument, &g_debug, 1}, {"verbose", no_argument, &g_verbose, 1}, {"log-file", required_argument, NULL, 'l'}, {"log-channels", required_argument, NULL, 'c'}, {"attach", required_argument, NULL, 'a'}, {"named-pipe", required_argument, NULL, 'N'}, {"pipe", required_argument, NULL, 'U'}, {"native-regs", no_argument, NULL, 'r'}, // Specify to use the native registers instead of the gdb defaults // for the architecture. NOTE: this is a do-nothing arg as it's // behavior is default now. FIXME remove call from lldb-platform. {"reverse-connect", no_argument, NULL, 'R'}, // Specifies that llgs attaches to the client address:port rather // than llgs listening for a connection from address on port. {"setsid", no_argument, NULL, 'S'}, // Call setsid() to make llgs run in its own session. {NULL, 0, NULL, 0}}; //---------------------------------------------------------------------- // Watch for signals //---------------------------------------------------------------------- static int g_sighup_received_count = 0; #ifndef _WIN32 static void sighup_handler(MainLoopBase &mainloop) { ++g_sighup_received_count; Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); if (log) log->Printf("lldb-server:%s swallowing SIGHUP (receive count=%d)", __FUNCTION__, g_sighup_received_count); if (g_sighup_received_count >= 2) mainloop.RequestTermination(); } #endif // #ifndef _WIN32 static void display_usage(const char *progname, const char *subcommand) { fprintf(stderr, "Usage:\n %s %s " "[--log-file log-file-name] " "[--log-channels log-channel-list] " "[--setsid] " "[--named-pipe named-pipe-path] " "[--native-regs] " "[--attach pid] " "[[HOST]:PORT] " "[-- PROGRAM ARG1 ARG2 ...]\n", progname, subcommand); exit(0); } void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server, lldb::pid_t pid) { Status error = gdb_server.AttachToProcess(pid); if (error.Fail()) { fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid, error.AsCString()); exit(1); } } void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server, const std::string &process_name) { // FIXME implement. } void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server, const std::string &attach_target) { assert(!attach_target.empty() && "attach_target cannot be empty"); // First check if the attach_target is convertible to a long. If so, we'll use // it as a pid. char *end_p = nullptr; const long int pid = strtol(attach_target.c_str(), &end_p, 10); // We'll call it a match if the entire argument is consumed. if (end_p && static_cast(end_p - attach_target.c_str()) == attach_target.size()) handle_attach_to_pid(gdb_server, static_cast(pid)); else handle_attach_to_process_name(gdb_server, attach_target); } void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, int argc, const char *const argv[]) { Status error; error = gdb_server.SetLaunchArguments(argv, argc); if (error.Fail()) { fprintf(stderr, "error: failed to set launch args for '%s': %s\n", argv[0], error.AsCString()); exit(1); } unsigned int launch_flags = eLaunchFlagStopAtEntry | eLaunchFlagDebug; error = gdb_server.SetLaunchFlags(launch_flags); if (error.Fail()) { fprintf(stderr, "error: failed to set launch flags for '%s': %s\n", argv[0], error.AsCString()); exit(1); } error = gdb_server.LaunchProcess(); if (error.Fail()) { fprintf(stderr, "error: failed to launch '%s': %s\n", argv[0], error.AsCString()); exit(1); } } Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &socket_id) { size_t bytes_written = 0; // Write the port number as a C string with the NULL terminator. return port_pipe.Write(socket_id.c_str(), socket_id.size() + 1, bytes_written); } Status writeSocketIdToPipe(const char *const named_pipe_path, const std::string &socket_id) { Pipe port_name_pipe; // Wait for 10 seconds for pipe to be opened. auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false, std::chrono::seconds{10}); if (error.Fail()) return error; return writeSocketIdToPipe(port_name_pipe, socket_id); } Status writeSocketIdToPipe(int unnamed_pipe_fd, const std::string &socket_id) { #if defined(_WIN32) return Status("Unnamed pipes are not supported on Windows."); #else Pipe port_pipe{Pipe::kInvalidDescriptor, unnamed_pipe_fd}; return writeSocketIdToPipe(port_pipe, socket_id); #endif } void ConnectToRemote(MainLoop &mainloop, GDBRemoteCommunicationServerLLGS &gdb_server, bool reverse_connect, const char *const host_and_port, const char *const progname, const char *const subcommand, const char *const named_pipe_path, int unnamed_pipe_fd) { Status error; if (host_and_port && host_and_port[0]) { // Parse out host and port. std::string final_host_and_port; std::string connection_host; std::string connection_port; uint32_t connection_portno = 0; // If host_and_port starts with ':', default the host to be "localhost" and // expect the remainder to be the port. if (host_and_port[0] == ':') final_host_and_port.append("localhost"); final_host_and_port.append(host_and_port); const std::string::size_type colon_pos = final_host_and_port.find(':'); if (colon_pos != std::string::npos) { connection_host = final_host_and_port.substr(0, colon_pos); connection_port = final_host_and_port.substr(colon_pos + 1); connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0); } std::unique_ptr connection_up; if (reverse_connect) { // llgs will connect to the gdb-remote client. // Ensure we have a port number for the connection. if (connection_portno == 0) { fprintf(stderr, "error: port number must be specified on when using " "reverse connect"); exit(1); } // Build the connection string. char connection_url[512]; snprintf(connection_url, sizeof(connection_url), "connect://%s", final_host_and_port.c_str()); // Create the connection. connection_up.reset(new ConnectionFileDescriptor); auto connection_result = connection_up->Connect(connection_url, &error); if (connection_result != eConnectionStatusSuccess) { fprintf(stderr, "error: failed to connect to client at '%s' " "(connection status: %d)", connection_url, static_cast(connection_result)); exit(-1); } if (error.Fail()) { fprintf(stderr, "error: failed to connect to client at '%s': %s", connection_url, error.AsCString()); exit(-1); } } else { std::unique_ptr acceptor_up( Acceptor::Create(final_host_and_port, false, error)); if (error.Fail()) { fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); exit(1); } error = acceptor_up->Listen(1); if (error.Fail()) { fprintf(stderr, "failed to listen: %s\n", error.AsCString()); exit(1); } const std::string socket_id = acceptor_up->GetLocalSocketId(); if (!socket_id.empty()) { // If we have a named pipe to write the socket id back to, do that now. if (named_pipe_path && named_pipe_path[0]) { error = writeSocketIdToPipe(named_pipe_path, socket_id); if (error.Fail()) fprintf(stderr, "failed to write to the named pipe \'%s\': %s", named_pipe_path, error.AsCString()); } // If we have an unnamed pipe to write the socket id back to, do that // now. else if (unnamed_pipe_fd >= 0) { error = writeSocketIdToPipe(unnamed_pipe_fd, socket_id); if (error.Fail()) fprintf(stderr, "failed to write to the unnamed pipe: %s", error.AsCString()); } } else { fprintf(stderr, "unable to get the socket id for the listening connection\n"); } Connection *conn = nullptr; error = acceptor_up->Accept(false, conn); if (error.Fail()) { printf("failed to accept new connection: %s\n", error.AsCString()); exit(1); } connection_up.reset(conn); } error = gdb_server.InitializeConnection(std::move(connection_up)); if (error.Fail()) { fprintf(stderr, "Failed to initialize connection: %s\n", error.AsCString()); exit(-1); } printf("Connection established.\n"); } } //---------------------------------------------------------------------- // main //---------------------------------------------------------------------- int main_gdbserver(int argc, char *argv[]) { Status error; MainLoop mainloop; #ifndef _WIN32 // Setup signal handlers first thing. signal(SIGPIPE, SIG_IGN); MainLoop::SignalHandleUP sighup_handle = mainloop.RegisterSignal(SIGHUP, sighup_handler, error); #endif const char *progname = argv[0]; const char *subcommand = argv[1]; argc--; argv++; int long_option_index = 0; int ch; std::string attach_target; std::string named_pipe_path; std::string log_file; StringRef log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" int unnamed_pipe_fd = -1; bool reverse_connect = false; // ProcessLaunchInfo launch_info; ProcessAttachInfo attach_info; bool show_usage = false; int option_error = 0; #if __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif std::string short_options(OptionParser::GetShortOptionString(g_long_options)); while ((ch = getopt_long_only(argc, argv, short_options.c_str(), g_long_options, &long_option_index)) != -1) { switch (ch) { case 0: // Any optional that auto set themselves will return 0 break; case 'l': // Set Log File if (optarg && optarg[0]) log_file.assign(optarg); break; case 'c': // Log Channels if (optarg && optarg[0]) log_channels = StringRef(optarg); break; case 'N': // named pipe if (optarg && optarg[0]) named_pipe_path = optarg; break; case 'U': // unnamed pipe if (optarg && optarg[0]) unnamed_pipe_fd = StringConvert::ToUInt32(optarg, -1); break; case 'r': // Do nothing, native regs is the default these days break; case 'R': reverse_connect = true; break; #ifndef _WIN32 case 'S': // Put llgs into a new session. Terminals group processes // into sessions and when a special terminal key sequences // (like control+c) are typed they can cause signals to go out to // all processes in a session. Using this --setsid (-S) option // will cause debugserver to run in its own sessions and be free // from such issues. // // This is useful when llgs is spawned from a command // line application that uses llgs to do the debugging, // yet that application doesn't want llgs receiving the // signals sent to the session (i.e. dying when anyone hits ^C). { const ::pid_t new_sid = setsid(); if (new_sid == -1) { - const char *errno_str = strerror(errno); - fprintf(stderr, "failed to set new session id for %s (%s)\n", - LLGS_PROGRAM_NAME, - errno_str ? errno_str : ""); + llvm::errs() << llvm::formatv( + "failed to set new session id for {0} ({1})\n", LLGS_PROGRAM_NAME, + llvm::sys::StrError()); } } break; #endif case 'a': // attach {pid|process_name} if (optarg && optarg[0]) attach_target = optarg; break; case 'h': /* fall-through is intentional */ case '?': show_usage = true; break; } } if (show_usage || option_error) { display_usage(progname, subcommand); exit(option_error); } if (!LLDBServerUtilities::SetupLogging( log_file, log_channels, LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) return -1; Log *log(lldb_private::GetLogIfAnyCategoriesSet(GDBR_LOG_PROCESS)); if (log) { log->Printf("lldb-server launch"); for (int i = 0; i < argc; i++) { log->Printf("argv[%i] = '%s'", i, argv[i]); } } // Skip any options we consumed with getopt_long_only. argc -= optind; argv += optind; if (argc == 0) { display_usage(progname, subcommand); exit(255); } GDBRemoteCommunicationServerLLGS gdb_server(mainloop); const char *const host_and_port = argv[0]; argc -= 1; argv += 1; // Any arguments left over are for the program that we need to launch. If // there // are no arguments, then the GDB server will start up and wait for an 'A' // packet // to launch a program, or a vAttach packet to attach to an existing process, // unless // explicitly asked to attach with the --attach={pid|program_name} form. if (!attach_target.empty()) handle_attach(gdb_server, attach_target); else if (argc > 0) handle_launch(gdb_server, argc, argv); // Print version info. printf("%s-%s", LLGS_PROGRAM_NAME, LLGS_VERSION_STR); ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, progname, subcommand, named_pipe_path.c_str(), unnamed_pipe_fd); if (!gdb_server.IsConnected()) { fprintf(stderr, "no connection information provided, unable to run\n"); display_usage(progname, subcommand); return 1; } mainloop.Run(); fprintf(stderr, "lldb-server exiting...\n"); return 0; } Index: vendor/lldb/dist/unittests/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/CMakeLists.txt (revision 319789) +++ vendor/lldb/dist/unittests/CMakeLists.txt (revision 319790) @@ -1,76 +1,77 @@ add_custom_target(LLDBUnitTests) set_target_properties(LLDBUnitTests PROPERTIES FOLDER "LLDB tests") include_directories(${LLDB_SOURCE_ROOT}) include_directories(${LLDB_PROJECT_ROOT}) set(LLDB_GTEST_COMMON_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/gtest_common.h) if (MSVC) list(APPEND LLVM_COMPILE_FLAGS /FI ${LLDB_GTEST_COMMON_INCLUDE}) else () list(APPEND LLVM_COMPILE_FLAGS -include ${LLDB_GTEST_COMMON_INCLUDE}) endif () include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) if (LLDB_BUILT_STANDALONE) # Build the gtest library needed for unittests, if we have LLVM sources # handy. if (EXISTS ${LLVM_MAIN_SRC_DIR}/utils/unittest AND NOT TARGET gtest) add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/unittest utils/unittest) endif() endif() function(add_lldb_unittest test_name) cmake_parse_arguments(ARG "" "" "LINK_LIBS;LINK_COMPONENTS" ${ARGN}) list(APPEND LLVM_LINK_COMPONENTS ${ARG_LINK_COMPONENTS}) add_unittest(LLDBUnitTests ${test_name} ${ARG_UNPARSED_ARGUMENTS} ) add_custom_command( TARGET ${test_name} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/Inputs) target_link_libraries(${test_name} ${ARG_LINK_LIBS} ${LLDB_SYSTEM_LIBS}) endfunction() function(add_unittest_inputs test_name inputs) foreach (INPUT ${inputs}) add_custom_command( TARGET ${test_name} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Inputs/${INPUT} ${CMAKE_CURRENT_BINARY_DIR}/Inputs COMMENT "Copying ${INPUT} to binary directory.") endforeach() endfunction() add_subdirectory(Breakpoint) add_subdirectory(Core) add_subdirectory(Editline) add_subdirectory(Expression) add_subdirectory(Host) add_subdirectory(Interpreter) add_subdirectory(Language) add_subdirectory(ObjectFile) add_subdirectory(Platform) add_subdirectory(Process) add_subdirectory(ScriptInterpreter) add_subdirectory(Signals) add_subdirectory(Symbol) add_subdirectory(SymbolFile) add_subdirectory(Target) +add_subdirectory(tools) add_subdirectory(UnwindAssembly) add_subdirectory(Utility) if(LLDB_CAN_USE_DEBUGSERVER) add_subdirectory(debugserver) -endif() \ No newline at end of file +endif() Index: vendor/lldb/dist/unittests/Core/ArchSpecTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Core/ArchSpecTest.cpp (revision 319789) +++ vendor/lldb/dist/unittests/Core/ArchSpecTest.cpp (revision 319790) @@ -1,155 +1,155 @@ //===-- ArchSpecTest.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "lldb/Core/ArchSpec.h" -#include "llvm/Support/MachO.h" +#include "llvm/BinaryFormat/MachO.h" using namespace lldb; using namespace lldb_private; TEST(ArchSpecTest, TestParseMachCPUDashSubtypeTripleSimple) { // Success conditions. Valid cpu/subtype combinations using both - and . ArchSpec AS; EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(10u, AS.GetMachOCPUSubType()); AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(15u, AS.GetMachOCPUSubType()); AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12.15", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(15u, AS.GetMachOCPUSubType()); // Failure conditions. // Valid string, unknown cpu/subtype. AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("13.11", AS)); EXPECT_EQ(0u, AS.GetMachOCPUType()); EXPECT_EQ(0u, AS.GetMachOCPUSubType()); // Missing / invalid cpu or subtype AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("13", AS)); AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("13.A", AS)); AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("A.13", AS)); // Empty string. AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("", AS)); } TEST(ArchSpecTest, TestParseMachCPUDashSubtypeTripleExtra) { ArchSpec AS; EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15-vendor-os", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(15u, AS.GetMachOCPUSubType()); EXPECT_EQ("vendor", AS.GetTriple().getVendorName()); EXPECT_EQ("os", AS.GetTriple().getOSName()); AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10-vendor-os-name", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(10u, AS.GetMachOCPUSubType()); EXPECT_EQ("vendor", AS.GetTriple().getVendorName()); EXPECT_EQ("os", AS.GetTriple().getOSName()); AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-15-vendor.os-name", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(15u, AS.GetMachOCPUSubType()); EXPECT_EQ("vendor.os", AS.GetTriple().getVendorName()); EXPECT_EQ("name", AS.GetTriple().getOSName()); // These there should parse correctly, but the vendor / OS should be defaulted // since they are unrecognized. AS = ArchSpec(); EXPECT_TRUE(ParseMachCPUDashSubtypeTriple("12-10-vendor", AS)); EXPECT_EQ(12u, AS.GetMachOCPUType()); EXPECT_EQ(10u, AS.GetMachOCPUSubType()); EXPECT_EQ("apple", AS.GetTriple().getVendorName()); EXPECT_EQ("", AS.GetTriple().getOSName()); AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("12.10.10", AS)); AS = ArchSpec(); EXPECT_FALSE(ParseMachCPUDashSubtypeTriple("12-10.10", AS)); } TEST(ArchSpecTest, TestSetTriple) { ArchSpec AS; // Various flavors of valid triples. EXPECT_TRUE(AS.SetTriple("12-10-apple-darwin")); EXPECT_EQ(uint32_t(llvm::MachO::CPU_TYPE_ARM), AS.GetMachOCPUType()); EXPECT_EQ(10u, AS.GetMachOCPUSubType()); EXPECT_TRUE(llvm::StringRef(AS.GetTriple().str()) .consume_front("armv7f-apple-darwin")); EXPECT_EQ(ArchSpec::eCore_arm_armv7f, AS.GetCore()); AS = ArchSpec(); EXPECT_TRUE(AS.SetTriple("18.100-apple-darwin")); EXPECT_EQ(uint32_t(llvm::MachO::CPU_TYPE_POWERPC), AS.GetMachOCPUType()); EXPECT_EQ(100u, AS.GetMachOCPUSubType()); EXPECT_TRUE(llvm::StringRef(AS.GetTriple().str()) .consume_front("powerpc-apple-darwin")); EXPECT_EQ(ArchSpec::eCore_ppc_ppc970, AS.GetCore()); AS = ArchSpec(); EXPECT_TRUE(AS.SetTriple("i686-pc-windows")); EXPECT_EQ(llvm::Triple::x86, AS.GetTriple().getArch()); EXPECT_EQ(llvm::Triple::PC, AS.GetTriple().getVendor()); EXPECT_EQ(llvm::Triple::Win32, AS.GetTriple().getOS()); EXPECT_TRUE( llvm::StringRef(AS.GetTriple().str()).consume_front("i686-pc-windows")); EXPECT_STREQ("i686", AS.GetArchitectureName()); EXPECT_EQ(ArchSpec::eCore_x86_32_i686, AS.GetCore()); // Various flavors of invalid triples. AS = ArchSpec(); EXPECT_FALSE(AS.SetTriple("unknown-unknown-unknown")); AS = ArchSpec(); EXPECT_FALSE(AS.SetTriple("unknown")); AS = ArchSpec(); EXPECT_FALSE(AS.SetTriple("")); } TEST(ArchSpecTest, MergeFrom) { ArchSpec A; ArchSpec B("x86_64-pc-linux"); EXPECT_FALSE(A.IsValid()); ASSERT_TRUE(B.IsValid()); EXPECT_EQ(llvm::Triple::ArchType::x86_64, B.GetTriple().getArch()); EXPECT_EQ(llvm::Triple::VendorType::PC, B.GetTriple().getVendor()); EXPECT_EQ(llvm::Triple::OSType::Linux, B.GetTriple().getOS()); EXPECT_EQ(ArchSpec::eCore_x86_64_x86_64, B.GetCore()); A.MergeFrom(B); ASSERT_TRUE(A.IsValid()); EXPECT_EQ(llvm::Triple::ArchType::x86_64, A.GetTriple().getArch()); EXPECT_EQ(llvm::Triple::VendorType::PC, A.GetTriple().getVendor()); EXPECT_EQ(llvm::Triple::OSType::Linux, A.GetTriple().getOS()); EXPECT_EQ(ArchSpec::eCore_x86_64_x86_64, A.GetCore()); } Index: vendor/lldb/dist/unittests/Core/StructuredDataTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Core/StructuredDataTest.cpp (revision 319789) +++ vendor/lldb/dist/unittests/Core/StructuredDataTest.cpp (revision 319790) @@ -1,32 +1,32 @@ //===-- StructuredDataTest.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "lldb/Core/StructuredData.h" #include "lldb/Utility/StreamString.h" -#include "llvm/Support/MachO.h" +#include "llvm/BinaryFormat/MachO.h" using namespace lldb; using namespace lldb_private; TEST(StructuredDataTest, StringDump) { std::pair TestCases[] = { { R"(asdfg)", R"("asdfg")" }, { R"(as"df)", R"("as\"df")" }, { R"(as\df)", R"("as\\df")" }, }; for(auto P : TestCases) { StreamString S; const bool pretty_print = false; StructuredData::String(P.first).Dump(S, pretty_print); EXPECT_EQ(P.second, S.GetString()); } } Index: vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 319789) +++ vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 319790) @@ -1,598 +1,595 @@ //===-- GDBRemoteCommunicationClientTest.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 "GDBRemoteTestUtils.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" #include "lldb/lldb-enumerations.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/TraceOptions.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Utility/DataBuffer.h" #include "llvm/ADT/ArrayRef.h" using namespace lldb_private::process_gdb_remote; using namespace lldb_private; using namespace lldb; using namespace llvm; namespace { typedef GDBRemoteCommunication::PacketResult PacketResult; struct TestClient : public GDBRemoteCommunicationClient { TestClient() { m_send_acks = false; } }; void Handle_QThreadSuffixSupported(MockServer &server, bool supported) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef()); if (supported) ASSERT_EQ(PacketResult::Success, server.SendOKResponse()); else ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr)); } void HandlePacket(MockServer &server, StringRef expected, StringRef response) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ(expected, request.GetStringRef()); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'}; std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f"; uint8_t one_register[] = {'A', 'B', 'C', 'D'}; std::string one_register_hex = "41424344"; } // end anonymous namespace class GDBRemoteCommunicationClientTest : public GDBRemoteTest {}; TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "P4=" + one_register_hex, "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex, "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future async_result = std::async( std::launch::async, [&] { return client.GetpPacketSupported(tid); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "p0;thread:0047;", one_register_hex); ASSERT_TRUE(async_result.get()); std::future read_result = std::async( std::launch::async, [&] { return client.ReadRegister(tid, reg_num); }); HandlePacket(server, "p4;thread:0047;", "41424344"); auto buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register)); read_result = std::async(std::launch::async, [&] { return client.ReadAllRegisters(tid); }); HandlePacket(server, "g;thread:0047;", all_registers_hex); buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); } TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; uint32_t save_id; std::future async_result = std::async(std::launch::async, [&] { return client.SaveRegisterState(tid, save_id); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "QSaveRegisterState", "1"); ASSERT_TRUE(async_result.get()); EXPECT_EQ(1u, save_id); async_result = std::async(std::launch::async, [&] { return client.RestoreRegisterState(tid, save_id); }); HandlePacket(server, "QRestoreRegisterState:1", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; std::future async_result = std::async( std::launch::async, [&] { return client.SyncThreadState(tid); }); HandlePacket(server, "qSyncThreadStateSupported", "OK"); HandlePacket(server, "QSyncThreadState:0047;", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_specs[] = { FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix), FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix), // This is a bit dodgy but we currently depend on GetModulesInfo not // performing denormalization. It can go away once the users // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for // the FileSpecs they create. FileSpec("/foo/baw.so", false, FileSpec::ePathSyntaxWindows), }; std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_specs, triple); }); HandlePacket( server, "jModulesInfo:[" R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)" R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)" R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])", R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); auto result = async_result.get(); ASSERT_TRUE(result.hasValue()); ASSERT_EQ(1u, result->size()); EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID()); EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix); const char *invalid_responses[] = { "OK", "E47", "[]", // no UUID R"([{"triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no triple R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no file_path R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_offset":0,"file_size":1234}])", // no file_offset R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_size":1234}])", // no file_size R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0}])", }; for (const char *response : invalid_responses) { std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_spec, triple); }); HandlePacket( server, R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])", response); ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response; } } TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; std::thread server_thread([&server] { for (;;) { StringExtractorGDBRemote request; PacketResult result = server.GetPacket(request); if (result == PacketResult::ErrorDisconnected) return; ASSERT_EQ(PacketResult::Success, result); StringRef ref = request.GetStringRef(); ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:")); int size; ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref; std::string response(size, 'X'); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } }); StreamString ss; client.TestPacketSpeed(10, 32, 32, 4096, true, ss); client.Disconnect(); server_thread.join(); GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData(); auto object_sp = StructuredData::ParseJSON(ss.GetString()); ASSERT_TRUE(bool(object_sp)); auto dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); object_sp = dict_sp->GetValueForKey("packet_speeds"); ASSERT_TRUE(bool(object_sp)); dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); int num_packets; ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets)) << ss.GetString(); ASSERT_EQ(10, num_packets); } TEST_F(GDBRemoteCommunicationClientTest, SendSignalsToIgnore) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; - const lldb::tid_t tid = 0x47; - const uint32_t reg_num = 4; std::future result = std::async(std::launch::async, [&] { return client.SendSignalsToIgnore({2, 3, 5, 7, 0xB, 0xD, 0x11}); }); HandlePacket(server, "QPassSignals:02;03;05;07;0b;0d;11", "OK"); EXPECT_TRUE(result.get().Success()); result = std::async(std::launch::async, [&] { return client.SendSignalsToIgnore(std::vector()); }); HandlePacket(server, "QPassSignals:", "OK"); EXPECT_TRUE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::addr_t addr = 0xa000; MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); // name is: /foo/bar.so HandlePacket(server, "qMemoryRegionInfo:a000", "start:a000;size:2000;permissions:rx;name:2f666f6f2f6261722e736f;"); EXPECT_TRUE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::addr_t addr = 0x4000; MemoryRegionInfo region_info; std::future result = std::async(std::launch::async, [&] { return client.GetMemoryRegionInfo(addr, region_info); }); HandlePacket(server, "qMemoryRegionInfo:4000", "start:4000;size:0000;"); EXPECT_FALSE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, SendStartTracePacket) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; TraceOptions options; Status error; options.setType(lldb::TraceType::eTraceTypeProcessorTrace); options.setMetaDataBufferSize(8192); options.setTraceBufferSize(8192); options.setThreadID(0x23); StructuredData::DictionarySP custom_params = std::make_shared(); custom_params->AddStringItem("tracetech", "intel-pt"); custom_params->AddIntegerItem("psb", 0x01); options.setTraceParams(custom_params); std::future result = std::async(std::launch::async, [&] { return client.SendStartTracePacket(options, error); }); // Since the line is exceeding 80 characters. std::string expected_packet1 = R"(jTraceStart:{"buffersize" : 8192,"metabuffersize" : 8192,"params" :)"; std::string expected_packet2 = R"( {"psb" : 1,"tracetech" : "intel-pt"},"threadid" : 35,"type" : 1})"; HandlePacket(server, (expected_packet1 + expected_packet2), "1"); ASSERT_TRUE(error.Success()); - ASSERT_EQ(result.get(), 1); + ASSERT_EQ(result.get(), 1u); error.Clear(); result = std::async(std::launch::async, [&] { return client.SendStartTracePacket(options, error); }); HandlePacket(server, (expected_packet1 + expected_packet2), "E23"); ASSERT_EQ(result.get(), LLDB_INVALID_UID); ASSERT_FALSE(error.Success()); } TEST_F(GDBRemoteCommunicationClientTest, SendStopTracePacket) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; std::future result = std::async(std::launch::async, [&] { return client.SendStopTracePacket(trace_id, thread_id); }); const char *expected_packet = R"(jTraceStop:{"threadid" : 35,"traceid" : 3})"; HandlePacket(server, expected_packet, "OK"); ASSERT_TRUE(result.get().Success()); result = std::async(std::launch::async, [&] { return client.SendStopTracePacket(trace_id, thread_id); }); HandlePacket(server, expected_packet, "E23"); ASSERT_FALSE(result.get().Success()); } TEST_F(GDBRemoteCommunicationClientTest, SendGetDataPacket) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; uint8_t buf[32] = {}; llvm::MutableArrayRef buffer(buf, 32); size_t offset = 0; std::future result = std::async(std::launch::async, [&] { return client.SendGetDataPacket(trace_id, thread_id, buffer, offset); }); std::string expected_packet1 = R"(jTraceBufferRead:{"buffersize" : 32,"offset" : 0,"threadid" : 35,)"; std::string expected_packet2 = R"("traceid" : 3})"; HandlePacket(server, expected_packet1+expected_packet2, "123456"); ASSERT_TRUE(result.get().Success()); - ASSERT_EQ(buffer.size(), 3); + ASSERT_EQ(buffer.size(), 3u); ASSERT_EQ(buf[0], 0x12); ASSERT_EQ(buf[1], 0x34); ASSERT_EQ(buf[2], 0x56); llvm::MutableArrayRef buffer2(buf, 32); result = std::async(std::launch::async, [&] { return client.SendGetDataPacket(trace_id, thread_id, buffer2, offset); }); HandlePacket(server, expected_packet1+expected_packet2, "E23"); ASSERT_FALSE(result.get().Success()); - ASSERT_EQ(buffer2.size(), 0); + ASSERT_EQ(buffer2.size(), 0u); } TEST_F(GDBRemoteCommunicationClientTest, SendGetMetaDataPacket) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; uint8_t buf[32] = {}; llvm::MutableArrayRef buffer(buf, 32); size_t offset = 0; std::future result = std::async(std::launch::async, [&] { return client.SendGetMetaDataPacket(trace_id, thread_id, buffer, offset); }); std::string expected_packet1 = R"(jTraceMetaRead:{"buffersize" : 32,"offset" : 0,"threadid" : 35,)"; std::string expected_packet2 = R"("traceid" : 3})"; HandlePacket(server, expected_packet1+expected_packet2, "123456"); ASSERT_TRUE(result.get().Success()); - ASSERT_EQ(buffer.size(), 3); + ASSERT_EQ(buffer.size(), 3u); ASSERT_EQ(buf[0], 0x12); ASSERT_EQ(buf[1], 0x34); ASSERT_EQ(buf[2], 0x56); llvm::MutableArrayRef buffer2(buf, 32); result = std::async(std::launch::async, [&] { return client.SendGetMetaDataPacket(trace_id, thread_id, buffer2, offset); }); HandlePacket(server, expected_packet1+expected_packet2, "E23"); ASSERT_FALSE(result.get().Success()); - ASSERT_EQ(buffer2.size(), 0); + ASSERT_EQ(buffer2.size(), 0u); } TEST_F(GDBRemoteCommunicationClientTest, SendGetTraceConfigPacket) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; lldb::tid_t thread_id = 0x23; lldb::user_id_t trace_id = 3; TraceOptions options; options.setThreadID(thread_id); std::future result = std::async(std::launch::async, [&] { return client.SendGetTraceConfigPacket(trace_id, options); }); const char *expected_packet = R"(jTraceConfigRead:{"threadid" : 35,"traceid" : 3})"; std::string response1 = R"({"buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})"; std::string response2 = R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; HandlePacket(server, expected_packet, response1+response2); ASSERT_TRUE(result.get().Success()); - ASSERT_EQ(options.getTraceBufferSize(), 8192); - ASSERT_EQ(options.getMetaDataBufferSize(), 8192); + ASSERT_EQ(options.getTraceBufferSize(), 8192u); + ASSERT_EQ(options.getMetaDataBufferSize(), 8192u); ASSERT_EQ(options.getType(), 1); auto custom_params = options.getTraceParams(); uint64_t psb_value; llvm::StringRef trace_tech_value; ASSERT_TRUE(custom_params); ASSERT_EQ(custom_params->GetType(), eStructuredDataTypeDictionary); - ASSERT_TRUE( - custom_params->GetValueForKeyAsInteger("psb", psb_value)); - ASSERT_EQ(psb_value, 1); + ASSERT_TRUE(custom_params->GetValueForKeyAsInteger("psb", psb_value)); + ASSERT_EQ(psb_value, 1u); ASSERT_TRUE( custom_params->GetValueForKeyAsString("tracetech", trace_tech_value)); ASSERT_STREQ(trace_tech_value.data(), "intel-pt"); // Checking error response. std::future result2 = std::async(std::launch::async, [&] { return client.SendGetTraceConfigPacket(trace_id, options); }); HandlePacket(server, expected_packet, "E23"); ASSERT_FALSE(result2.get().Success()); // Wrong JSON as response. std::future result3 = std::async(std::launch::async, [&] { return client.SendGetTraceConfigPacket(trace_id, options); }); std::string incorrect_json1 = R"("buffersize" : 8192,"params" : {"psb" : 1,"tracetech" : "intel-pt"})"; std::string incorrect_json2 = R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; HandlePacket(server, expected_packet, incorrect_json1+incorrect_json2); ASSERT_FALSE(result3.get().Success()); // Wrong JSON as custom_params. std::future result4 = std::async(std::launch::async, [&] { return client.SendGetTraceConfigPacket(trace_id, options); }); std::string incorrect_custom_params1 = R"({"buffersize" : 8192,"params" : "psb" : 1,"tracetech" : "intel-pt"})"; std::string incorrect_custom_params2 = R"(],"metabuffersize" : 8192,"threadid" : 35,"type" : 1}])"; HandlePacket(server, expected_packet, incorrect_custom_params1+ incorrect_custom_params2); ASSERT_FALSE(result4.get().Success()); } Index: vendor/lldb/dist/unittests/tools/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/tools/CMakeLists.txt (nonexistent) +++ vendor/lldb/dist/unittests/tools/CMakeLists.txt (revision 319790) @@ -0,0 +1,3 @@ +if(CMAKE_SYSTEM_NAME MATCHES "Android|Linux|NetBSD") + add_subdirectory(lldb-server) +endif() Property changes on: vendor/lldb/dist/unittests/tools/CMakeLists.txt ___________________________________________________________________ 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/unittests/tools/lldb-server/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/CMakeLists.txt (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/CMakeLists.txt (revision 319790) @@ -0,0 +1,13 @@ +function(add_lldb_test_executable test_name) + set(EXCLUDE_FROM_ALL ON) + add_llvm_executable(${test_name} NO_INSTALL_RPATH ${ARGN}) + set(outdir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}) + set_output_directory(${test_name} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir}) +endfunction() + +add_lldb_test_executable(thread_inferior inferior/thread_inferior.cpp) + +add_definitions(-DLLDB_SERVER="$") +add_definitions(-DTHREAD_INFERIOR="${CMAKE_CURRENT_BINARY_DIR}/thread_inferior") +add_subdirectory(tests) +add_dependencies(LLDBServerTests thread_inferior) Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/CMakeLists.txt ___________________________________________________________________ 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/unittests/tools/lldb-server/inferior/thread_inferior.cpp =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/inferior/thread_inferior.cpp (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/inferior/thread_inferior.cpp (revision 319790) @@ -0,0 +1,41 @@ +//===-- thread_inferior.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 +#include + +int main(int argc, char* argv[]) { + int thread_count = 2; + if (argc > 1) { + thread_count = std::stoi(argv[1], nullptr, 10); + } + + std::atomic delay(true); + std::vector threads; + for (int i = 0; i < thread_count; i++) { + threads.push_back(std::thread([&delay] { + while (delay.load()) + std::this_thread::sleep_for(std::chrono::seconds(1)); + })); + } + + // Cause a break. + volatile char *p = NULL; + *p = 'a'; + + delay.store(false); + for (std::thread& t : threads) { + t.join(); + } + + return 0; +} Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/inferior/thread_inferior.cpp ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/CMakeLists.txt (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/CMakeLists.txt (revision 319790) @@ -0,0 +1,15 @@ +add_lldb_unittest(LLDBServerTests + TestClient.cpp + MessageObjects.cpp + ThreadIdsInJstopinfoTest.cpp + + LINK_LIBS + lldbHost + lldbCore + lldbInterpreter + lldbTarget + lldbPluginPlatformLinux + lldbPluginProcessGDBRemote + LINK_COMPONENTS + Support + ) Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/CMakeLists.txt ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/MessageObjects.cpp =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.cpp (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.cpp (revision 319790) @@ -0,0 +1,207 @@ +//===-- MessageObjects.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MessageObjects.h" +#include "lldb/Core/StructuredData.h" +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace llvm; +using namespace llvm::support; +namespace llgs_tests { + +Expected ProcessInfo::Create(StringRef response) { + ProcessInfo process_info; + auto elements_or_error = SplitPairList("ProcessInfo", response); + if (!elements_or_error) + return elements_or_error.takeError(); + + auto &elements = *elements_or_error; + if (elements["pid"].getAsInteger(16, process_info.m_pid)) + return make_parsing_error("ProcessInfo: pid"); + if (elements["parent-pid"].getAsInteger(16, process_info.m_parent_pid)) + return make_parsing_error("ProcessInfo: parent-pid"); + if (elements["real-uid"].getAsInteger(16, process_info.m_real_uid)) + return make_parsing_error("ProcessInfo: real-uid"); + if (elements["real-gid"].getAsInteger(16, process_info.m_real_gid)) + return make_parsing_error("ProcessInfo: real-uid"); + if (elements["effective-uid"].getAsInteger(16, process_info.m_effective_uid)) + return make_parsing_error("ProcessInfo: effective-uid"); + if (elements["effective-gid"].getAsInteger(16, process_info.m_effective_gid)) + return make_parsing_error("ProcessInfo: effective-gid"); + if (elements["ptrsize"].getAsInteger(10, process_info.m_ptrsize)) + return make_parsing_error("ProcessInfo: ptrsize"); + + process_info.m_triple = fromHex(elements["triple"]); + StringRef endian_str = elements["endian"]; + if (endian_str == "little") + process_info.m_endian = support::little; + else if (endian_str == "big") + process_info.m_endian = support::big; + else + return make_parsing_error("ProcessInfo: endian"); + + return process_info; +} + +lldb::pid_t ProcessInfo::GetPid() const { return m_pid; } + +endianness ProcessInfo::GetEndian() const { return m_endian; } + +//====== ThreadInfo ============================================================ +ThreadInfo::ThreadInfo(StringRef name, StringRef reason, + const RegisterMap ®isters, unsigned int signal) + : m_name(name.str()), m_reason(reason.str()), m_registers(registers), + m_signal(signal) {} + +StringRef ThreadInfo::ReadRegister(unsigned int register_id) const { + return m_registers.lookup(register_id); +} + +bool ThreadInfo::ReadRegisterAsUint64(unsigned int register_id, + uint64_t &value) const { + StringRef value_str(m_registers.lookup(register_id)); + if (value_str.getAsInteger(16, value)) { + GTEST_LOG_(ERROR) + << formatv("ThreadInfo: Unable to parse register value at {0}.", + register_id) + .str(); + return false; + } + + sys::swapByteOrder(value); + return true; +} + +//====== JThreadsInfo ========================================================== +Expected JThreadsInfo::Create(StringRef response, + endianness endian) { + JThreadsInfo jthreads_info; + + StructuredData::ObjectSP json = StructuredData::ParseJSON(response); + StructuredData::Array *array = json->GetAsArray(); + if (!array) + return make_parsing_error("JThreadsInfo: JSON array"); + + for (size_t i = 0; i < array->GetSize(); i++) { + StructuredData::Dictionary *thread_info; + array->GetItemAtIndexAsDictionary(i, thread_info); + if (!thread_info) + return make_parsing_error("JThreadsInfo: JSON obj at {0}", i); + + StringRef name, reason; + thread_info->GetValueForKeyAsString("name", name); + thread_info->GetValueForKeyAsString("reason", reason); + uint64_t signal; + thread_info->GetValueForKeyAsInteger("signal", signal); + uint64_t tid; + thread_info->GetValueForKeyAsInteger("tid", tid); + + StructuredData::Dictionary *register_dict; + thread_info->GetValueForKeyAsDictionary("registers", register_dict); + if (!register_dict) + return make_parsing_error("JThreadsInfo: registers JSON obj"); + + RegisterMap registers; + + auto keys_obj = register_dict->GetKeys(); + auto keys = keys_obj->GetAsArray(); + for (size_t i = 0; i < keys->GetSize(); i++) { + StringRef key_str, value_str; + keys->GetItemAtIndexAsString(i, key_str); + register_dict->GetValueForKeyAsString(key_str, value_str); + unsigned int register_id; + if (key_str.getAsInteger(10, register_id)) + return make_parsing_error("JThreadsInfo: register key[{0}]", i); + + registers[register_id] = value_str.str(); + } + + jthreads_info.m_thread_infos[tid] = + ThreadInfo(name, reason, registers, signal); + } + + return jthreads_info; +} + +const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const { + return m_thread_infos; +} + +//====== StopReply ============================================================= +const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; } + +Expected StopReply::Create(StringRef response, + llvm::support::endianness endian) { + StopReply stop_reply; + + auto elements_or_error = SplitPairList("StopReply", response); + if (auto split_error = elements_or_error.takeError()) { + return std::move(split_error); + } + + auto elements = *elements_or_error; + stop_reply.m_name = elements["name"]; + stop_reply.m_reason = elements["reason"]; + + SmallVector threads; + SmallVector pcs; + elements["threads"].split(threads, ','); + elements["thread-pcs"].split(pcs, ','); + if (threads.size() != pcs.size()) + return make_parsing_error("StopReply: thread/PC count mismatch"); + + for (size_t i = 0; i < threads.size(); i++) { + lldb::tid_t thread_id; + uint64_t pc; + if (threads[i].getAsInteger(16, thread_id)) + return make_parsing_error("StopReply: thread ID at [{0}].", i); + if (pcs[i].getAsInteger(16, pc)) + return make_parsing_error("StopReply: thread PC at [{0}].", i); + + stop_reply.m_thread_pcs[thread_id] = pc; + } + + for (auto i = elements.begin(); i != elements.end(); i++) { + StringRef key = i->getKey(); + StringRef val = i->getValue(); + if (key.size() >= 9 && key[0] == 'T' && key.substr(3, 6) == "thread") { + if (val.getAsInteger(16, stop_reply.m_thread)) + return make_parsing_error("StopReply: thread id"); + if (key.substr(1, 2).getAsInteger(16, stop_reply.m_signal)) + return make_parsing_error("StopReply: stop signal"); + } else if (key.size() == 2) { + unsigned int reg; + if (!key.getAsInteger(16, reg)) { + stop_reply.m_registers[reg] = val.str(); + } + } + } + + return stop_reply; +} + +//====== Globals =============================================================== +Expected> SplitPairList(StringRef caller, StringRef str) { + SmallVector elements; + str.split(elements, ';'); + + StringMap pairs; + for (StringRef s : elements) { + std::pair pair = s.split(':'); + if (pairs.count(pair.first)) + return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first); + + pairs.insert(s.split(':')); + } + + return pairs; +} +} // namespace llgs_tests Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.cpp ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/MessageObjects.h =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.h (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.h (revision 319790) @@ -0,0 +1,102 @@ +//===-- MessageObjects.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-types.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include + +namespace llgs_tests { +class ThreadInfo; +typedef llvm::DenseMap ThreadInfoMap; +typedef llvm::DenseMap U64Map; +typedef llvm::DenseMap RegisterMap; + +class ProcessInfo { +public: + static llvm::Expected Create(llvm::StringRef response); + lldb::pid_t GetPid() const; + llvm::support::endianness GetEndian() const; + +private: + ProcessInfo() = default; + lldb::pid_t m_pid; + lldb::pid_t m_parent_pid; + uint32_t m_real_uid; + uint32_t m_real_gid; + uint32_t m_effective_uid; + uint32_t m_effective_gid; + std::string m_triple; + llvm::SmallString<16> m_ostype; + llvm::support::endianness m_endian; + unsigned int m_ptrsize; +}; + +class ThreadInfo { +public: + ThreadInfo() = default; + ThreadInfo(llvm::StringRef name, llvm::StringRef reason, + const RegisterMap ®isters, unsigned int signal); + + llvm::StringRef ReadRegister(unsigned int register_id) const; + bool ReadRegisterAsUint64(unsigned int register_id, uint64_t &value) const; + +private: + std::string m_name; + std::string m_reason; + RegisterMap m_registers; + unsigned int m_signal; +}; + +class JThreadsInfo { +public: + static llvm::Expected Create(llvm::StringRef response, + llvm::support::endianness endian); + + const ThreadInfoMap &GetThreadInfos() const; + +private: + JThreadsInfo() = default; + ThreadInfoMap m_thread_infos; +}; + +class StopReply { +public: + static llvm::Expected Create(llvm::StringRef response, + llvm::support::endianness endian); + const U64Map &GetThreadPcs() const; + +private: + StopReply() = default; + void ParseResponse(llvm::StringRef response, + llvm::support::endianness endian); + unsigned int m_signal; + lldb::tid_t m_thread; + std::string m_name; + U64Map m_thread_pcs; + RegisterMap m_registers; + std::string m_reason; +}; + +// Common functions for parsing packet data. +llvm::Expected> +SplitPairList(llvm::StringRef caller, llvm::StringRef s); + +template +llvm::Error make_parsing_error(llvm::StringRef format, Args &&... args) { + std::string error = + "Unable to parse " + + llvm::formatv(format.data(), std::forward(args)...).str(); + return llvm::make_error(error, + llvm::inconvertibleErrorCode()); +} +} // namespace llgs_tests Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/MessageObjects.h ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.cpp (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.cpp (revision 319790) @@ -0,0 +1,287 @@ +//===-- TestClient.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestClient.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "lldb/Host/posix/ProcessLauncherPosix.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" +#include +#include +#include +#include + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace llgs_tests { +void TestClient::Initialize() { HostInfo::Initialize(); } + +TestClient::TestClient(const std::string &test_name, + const std::string &test_case_name) + : m_test_name(test_name), m_test_case_name(test_case_name), + m_pc_register(UINT_MAX) {} + +TestClient::~TestClient() {} + +bool TestClient::StartDebugger() { + const ArchSpec &arch_spec = HostInfo::GetArchitecture(); + Args args; + args.AppendArgument(LLDB_SERVER); + args.AppendArgument("gdbserver"); + args.AppendArgument("--log-channels=gdb-remote packets"); + args.AppendArgument("--reverse-connect"); + std::string log_file_name = GenerateLogFileName(arch_spec); + if (log_file_name.size()) { + args.AppendArgument("--log-file=" + log_file_name); + } + + Status error; + TCPSocket listen_socket(true, false); + error = listen_socket.Listen("127.0.0.1:0", 5); + if (error.Fail()) { + GTEST_LOG_(ERROR) << "Unable to open listen socket."; + return false; + } + + char connect_remote_address[64]; + snprintf(connect_remote_address, sizeof(connect_remote_address), + "localhost:%u", listen_socket.GetLocalPortNumber()); + + args.AppendArgument(connect_remote_address); + + m_server_process_info.SetArchitecture(arch_spec); + m_server_process_info.SetArguments(args, true); + Status status = Host::LaunchProcess(m_server_process_info); + if (status.Fail()) { + GTEST_LOG_(ERROR) + << formatv("Failure to launch lldb server: {0}.", status).str(); + return false; + } + + char connect_remote_uri[64]; + snprintf(connect_remote_uri, sizeof(connect_remote_uri), "connect://%s", + connect_remote_address); + Socket *accept_socket; + listen_socket.Accept(accept_socket); + SetConnection(new ConnectionFileDescriptor(accept_socket)); + + SendAck(); // Send this as a handshake. + return true; +} + +bool TestClient::StopDebugger() { + std::string response; + return SendMessage("k", response, PacketResult::ErrorDisconnected); +} + +bool TestClient::SetInferior(llvm::ArrayRef inferior_args) { + std::stringstream command; + command << "A"; + for (size_t i = 0; i < inferior_args.size(); i++) { + if (i > 0) + command << ','; + std::string hex_encoded = toHex(inferior_args[i]); + command << hex_encoded.size() << ',' << i << ',' << hex_encoded; + } + + if (!SendMessage(command.str())) + return false; + if (!SendMessage("qLaunchSuccess")) + return false; + std::string response; + if (!SendMessage("qProcessInfo", response)) + return false; + auto create_or_error = ProcessInfo::Create(response); + if (auto create_error = create_or_error.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return false; + } + + m_process_info = *create_or_error; + return true; +} + +bool TestClient::ListThreadsInStopReply() { + return SendMessage("QListThreadsInStopReply"); +} + +bool TestClient::SetBreakpoint(unsigned long address) { + std::stringstream command; + command << "Z0," << std::hex << address << ",1"; + return SendMessage(command.str()); +} + +bool TestClient::ContinueAll() { return Continue("vCont;c"); } + +bool TestClient::ContinueThread(unsigned long thread_id) { + return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); +} + +const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; } + +Optional TestClient::GetJThreadsInfo() { + std::string response; + if (!SendMessage("jThreadsInfo", response)) + return llvm::None; + auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); + if (auto create_error = creation.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return llvm::None; + } + + return std::move(*creation); +} + +const StopReply &TestClient::GetLatestStopReply() { + return m_stop_reply.getValue(); +} + +bool TestClient::SendMessage(StringRef message) { + std::string dummy_string; + return SendMessage(message, dummy_string); +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string) { + if (!SendMessage(message, response_string, PacketResult::Success)) + return false; + else if (response_string[0] == 'E') { + GTEST_LOG_(ERROR) << "Error " << response_string + << " while sending message: " << message.str(); + return false; + } + + return true; +} + +bool TestClient::SendMessage(StringRef message, std::string &response_string, + PacketResult expected_result) { + StringExtractorGDBRemote response; + GTEST_LOG_(INFO) << "Send Packet: " << message.str(); + PacketResult result = SendPacketAndWaitForResponse(message, response, false); + response.GetEscapedBinaryData(response_string); + GTEST_LOG_(INFO) << "Read Packet: " << response_string; + if (result != expected_result) { + GTEST_LOG_(ERROR) << FormatFailedResult(message, result); + return false; + } + + return true; +} + +unsigned int TestClient::GetPcRegisterId() { + if (m_pc_register != UINT_MAX) + return m_pc_register; + + for (unsigned int register_id = 0;; register_id++) { + std::string message = formatv("qRegisterInfo{0:x-}", register_id).str(); + std::string response; + if (!SendMessage(message, response)) { + GTEST_LOG_(ERROR) << "Unable to query register ID for PC register."; + return UINT_MAX; + } + + auto elements_or_error = SplitPairList("GetPcRegisterId", response); + if (auto split_error = elements_or_error.takeError()) { + GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " + << response; + return UINT_MAX; + } + + auto elements = *elements_or_error; + if (elements["alt-name"] == "pc" || elements["generic"] == "pc") { + m_pc_register = register_id; + break; + } + } + + return m_pc_register; +} + +bool TestClient::Continue(StringRef message) { + if (!m_process_info.hasValue()) { + GTEST_LOG_(ERROR) << "Continue() called before m_process_info initialized."; + return false; + } + + std::string response; + if (!SendMessage(message, response)) + return false; + auto creation = StopReply::Create(response, m_process_info->GetEndian()); + if (auto create_error = creation.takeError()) { + GTEST_LOG_(ERROR) << toString(std::move(create_error)); + return false; + } + + m_stop_reply = std::move(*creation); + return true; +} + +std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const { + char *log_directory = getenv("LOG_FILE_DIRECTORY"); + if (!log_directory) + return ""; + + if (!llvm::sys::fs::is_directory(log_directory)) { + GTEST_LOG_(WARNING) << "Cannot access log directory: " << log_directory; + return ""; + } + + std::string log_file_name; + raw_string_ostream log_file(log_file_name); + log_file << log_directory << "/lldb-" << m_test_case_name << '-' + << m_test_name << '-' << arch.GetArchitectureName() << ".log"; + return log_file.str(); +} + +std::string TestClient::FormatFailedResult(const std::string &message, + PacketResult result) { + std::string formatted_error; + raw_string_ostream error_stream(formatted_error); + error_stream << "Failure sending message: " << message << " Result: "; + + switch (result) { + case PacketResult::ErrorSendFailed: + error_stream << "ErrorSendFailed"; + break; + case PacketResult::ErrorSendAck: + error_stream << "ErrorSendAck"; + break; + case PacketResult::ErrorReplyFailed: + error_stream << "ErrorReplyFailed"; + break; + case PacketResult::ErrorReplyTimeout: + error_stream << "ErrorReplyTimeout"; + break; + case PacketResult::ErrorReplyInvalid: + error_stream << "ErrorReplyInvalid"; + break; + case PacketResult::ErrorReplyAck: + error_stream << "ErrorReplyAck"; + break; + case PacketResult::ErrorDisconnected: + error_stream << "ErrorDisconnected"; + break; + case PacketResult::ErrorNoSequenceLock: + error_stream << "ErrorNoSequenceLock"; + break; + default: + error_stream << "Unknown Error"; + } + + error_stream.str(); + return formatted_error; +} +} // namespace llgs_tests Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.cpp ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/TestClient.h =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.h (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.h (revision 319790) @@ -0,0 +1,61 @@ +//===-- TestClient.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MessageObjects.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "llvm/ADT/Optional.h" +#include +#include + +namespace llgs_tests { +// TODO: Make the test client an abstract base class, with different children +// for different types of connections: llgs v. debugserver +class TestClient + : public lldb_private::process_gdb_remote::GDBRemoteCommunicationClient { +public: + static void Initialize(); + TestClient(const std::string &test_name, const std::string &test_case_name); + virtual ~TestClient(); + LLVM_NODISCARD bool StartDebugger(); + LLVM_NODISCARD bool StopDebugger(); + LLVM_NODISCARD bool SetInferior(llvm::ArrayRef inferior_args); + LLVM_NODISCARD bool ListThreadsInStopReply(); + LLVM_NODISCARD bool SetBreakpoint(unsigned long address); + LLVM_NODISCARD bool ContinueAll(); + LLVM_NODISCARD bool ContinueThread(unsigned long thread_id); + const ProcessInfo &GetProcessInfo(); + llvm::Optional GetJThreadsInfo(); + const StopReply &GetLatestStopReply(); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message, + std::string &response_string); + LLVM_NODISCARD bool SendMessage(llvm::StringRef message, + std::string &response_string, + PacketResult expected_result); + unsigned int GetPcRegisterId(); + +private: + LLVM_NODISCARD bool Continue(llvm::StringRef message); + LLVM_NODISCARD bool GenerateConnectionAddress(std::string &address); + std::string GenerateLogFileName(const lldb_private::ArchSpec &arch) const; + std::string FormatFailedResult( + const std::string &message, + lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult + result); + + llvm::Optional m_process_info; + llvm::Optional m_stop_reply; + lldb_private::ProcessLaunchInfo m_server_process_info; + std::string m_test_name; + std::string m_test_case_name; + unsigned int m_pc_register; +}; +} // namespace llgs_tests Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/TestClient.h ___________________________________________________________________ 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/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp =================================================================== --- vendor/lldb/dist/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp (nonexistent) +++ vendor/lldb/dist/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp (revision 319790) @@ -0,0 +1,58 @@ +//===-- ThreadsInJstopinfoTest.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestClient.h" +#include "gtest/gtest.h" +#include + +using namespace llgs_tests; + +class ThreadsInJstopinfoTest : public ::testing::Test { +protected: + virtual void SetUp() { TestClient::Initialize(); } +}; + +TEST_F(ThreadsInJstopinfoTest, TestStopReplyContainsThreadPcsLlgs) { + std::vector inferior_args; + // This inferior spawns N threads, then forces a break. + inferior_args.push_back(THREAD_INFERIOR); + inferior_args.push_back("4"); + + auto test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + + TestClient client(test_info->name(), test_info->test_case_name()); + ASSERT_TRUE(client.StartDebugger()); + ASSERT_TRUE(client.SetInferior(inferior_args)); + ASSERT_TRUE(client.ListThreadsInStopReply()); + ASSERT_TRUE(client.ContinueAll()); + unsigned int pc_reg = client.GetPcRegisterId(); + ASSERT_NE(pc_reg, UINT_MAX); + + auto jthreads_info = client.GetJThreadsInfo(); + ASSERT_TRUE(jthreads_info); + + auto stop_reply = client.GetLatestStopReply(); + auto stop_reply_pcs = stop_reply.GetThreadPcs(); + auto thread_infos = jthreads_info->GetThreadInfos(); + ASSERT_EQ(stop_reply_pcs.size(), thread_infos.size()) + << "Thread count mismatch."; + + for (auto stop_reply_pc : stop_reply_pcs) { + unsigned long tid = stop_reply_pc.first; + ASSERT_TRUE(thread_infos.find(tid) != thread_infos.end()) + << "Thread ID: " << tid << " not in JThreadsInfo."; + uint64_t pc_value; + ASSERT_TRUE(thread_infos[tid].ReadRegisterAsUint64(pc_reg, pc_value)) + << "Failure reading ThreadInfo register " << pc_reg; + ASSERT_EQ(stop_reply_pcs[tid], pc_value) + << "Mismatched PC for thread: " << tid; + } + + ASSERT_TRUE(client.StopDebugger()); +} Property changes on: vendor/lldb/dist/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp ___________________________________________________________________ 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