Index: vendor/lldb/dist/include/lldb/Core/ArchSpec.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/ArchSpec.h (revision 287513) +++ vendor/lldb/dist/include/lldb/Core/ArchSpec.h (revision 287514) @@ -1,582 +1,618 @@ //===-- ArchSpec.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_ArchSpec_h_ #define liblldb_ArchSpec_h_ #if defined(__cplusplus) #include "lldb/lldb-forward.h" #include "lldb/Core/ConstString.h" #include "llvm/ADT/Triple.h" namespace lldb_private { struct CoreDefinition; //---------------------------------------------------------------------- /// @class ArchSpec ArchSpec.h "lldb/Core/ArchSpec.h" /// @brief An architecture specification class. /// /// A class designed to be created from a cpu type and subtype, a /// string representation, or an llvm::Triple. Keeping all of the /// conversions of strings to architecture enumeration values confined /// to this class allows new architecture support to be added easily. //---------------------------------------------------------------------- class ArchSpec { public: enum MIPSSubType { eMIPSSubType_unknown, eMIPSSubType_mips32, eMIPSSubType_mips32r2, eMIPSSubType_mips32r6, eMIPSSubType_mips32el, eMIPSSubType_mips32r2el, eMIPSSubType_mips32r6el, eMIPSSubType_mips64, eMIPSSubType_mips64r2, eMIPSSubType_mips64r6, eMIPSSubType_mips64el, eMIPSSubType_mips64r2el, eMIPSSubType_mips64r6el, }; - + + // Masks for the ases word of an ABI flags structure. + enum MIPSASE + { + eMIPSAse_dsp = 0x00000001, // DSP ASE + eMIPSAse_dspr2 = 0x00000002, // DSP R2 ASE + eMIPSAse_eva = 0x00000004, // Enhanced VA Scheme + eMIPSAse_mcu = 0x00000008, // MCU (MicroController) ASE + eMIPSAse_mdmx = 0x00000010, // MDMX ASE + eMIPSAse_mips3d = 0x00000020, // MIPS-3D ASE + eMIPSAse_mt = 0x00000040, // MT ASE + eMIPSAse_smartmips = 0x00000080, // SmartMIPS ASE + eMIPSAse_virt = 0x00000100, // VZ ASE + eMIPSAse_msa = 0x00000200, // MSA ASE + eMIPSAse_mips16 = 0x00000400, // MIPS16 ASE + eMIPSAse_micromips = 0x00000800, // MICROMIPS ASE + eMIPSAse_xpa = 0x00001000, // XPA ASE + eMIPSAse_mask = 0x00001fff + }; + enum Core { eCore_arm_generic, eCore_arm_armv4, eCore_arm_armv4t, eCore_arm_armv5, eCore_arm_armv5e, eCore_arm_armv5t, eCore_arm_armv6, eCore_arm_armv6m, eCore_arm_armv7, eCore_arm_armv7f, eCore_arm_armv7s, eCore_arm_armv7k, eCore_arm_armv7m, eCore_arm_armv7em, eCore_arm_xscale, eCore_thumb, eCore_thumbv4t, eCore_thumbv5, eCore_thumbv5e, eCore_thumbv6, eCore_thumbv6m, eCore_thumbv7, eCore_thumbv7s, eCore_thumbv7k, eCore_thumbv7f, eCore_thumbv7m, eCore_thumbv7em, eCore_arm_arm64, eCore_arm_armv8, eCore_arm_aarch64, eCore_mips32, eCore_mips32r2, eCore_mips32r3, eCore_mips32r5, eCore_mips32r6, eCore_mips32el, eCore_mips32r2el, eCore_mips32r3el, eCore_mips32r5el, eCore_mips32r6el, eCore_mips64, eCore_mips64r2, eCore_mips64r3, eCore_mips64r5, eCore_mips64r6, eCore_mips64el, eCore_mips64r2el, eCore_mips64r3el, eCore_mips64r5el, eCore_mips64r6el, eCore_ppc_generic, eCore_ppc_ppc601, eCore_ppc_ppc602, eCore_ppc_ppc603, eCore_ppc_ppc603e, eCore_ppc_ppc603ev, eCore_ppc_ppc604, eCore_ppc_ppc604e, eCore_ppc_ppc620, eCore_ppc_ppc750, eCore_ppc_ppc7400, eCore_ppc_ppc7450, eCore_ppc_ppc970, eCore_ppc64_generic, eCore_ppc64_ppc970_64, eCore_sparc_generic, eCore_sparc9_generic, eCore_x86_32_i386, eCore_x86_32_i486, eCore_x86_32_i486sx, eCore_x86_32_i686, eCore_x86_64_x86_64, eCore_x86_64_x86_64h, // Haswell enabled x86_64 eCore_hexagon_generic, eCore_hexagon_hexagonv4, eCore_hexagon_hexagonv5, eCore_uknownMach32, eCore_uknownMach64, eCore_kalimba3, eCore_kalimba4, eCore_kalimba5, kNumCores, kCore_invalid, // The following constants are used for wildcard matching only kCore_any, kCore_arm_any, kCore_ppc_any, kCore_ppc64_any, kCore_x86_32_any, kCore_x86_64_any, kCore_hexagon_any, kCore_arm_first = eCore_arm_generic, kCore_arm_last = eCore_arm_xscale, kCore_thumb_first = eCore_thumb, kCore_thumb_last = eCore_thumbv7em, kCore_ppc_first = eCore_ppc_generic, kCore_ppc_last = eCore_ppc_ppc970, kCore_ppc64_first = eCore_ppc64_generic, kCore_ppc64_last = eCore_ppc64_ppc970_64, kCore_x86_32_first = eCore_x86_32_i386, kCore_x86_32_last = eCore_x86_32_i686, kCore_x86_64_first = eCore_x86_64_x86_64, kCore_x86_64_last = eCore_x86_64_x86_64h, kCore_hexagon_first = eCore_hexagon_generic, kCore_hexagon_last = eCore_hexagon_hexagonv5, kCore_kalimba_first = eCore_kalimba3, kCore_kalimba_last = eCore_kalimba5, kCore_mips32_first = eCore_mips32, kCore_mips32_last = eCore_mips32r6, kCore_mips32el_first = eCore_mips32el, kCore_mips32el_last = eCore_mips32r6el, kCore_mips64_first = eCore_mips64, kCore_mips64_last = eCore_mips64r6, kCore_mips64el_first = eCore_mips64el, kCore_mips64el_last = eCore_mips64r6el }; typedef void (* StopInfoOverrideCallbackType)(lldb_private::Thread &thread); //------------------------------------------------------------------ /// Default constructor. /// /// Default constructor that initializes the object with invalid /// cpu type and subtype values. //------------------------------------------------------------------ ArchSpec (); //------------------------------------------------------------------ /// Constructor over triple. /// /// Constructs an ArchSpec with properties consistent with the given /// Triple. //------------------------------------------------------------------ explicit ArchSpec (const llvm::Triple &triple); explicit ArchSpec (const char *triple_cstr); explicit ArchSpec (const char *triple_cstr, Platform *platform); //------------------------------------------------------------------ /// Constructor over architecture name. /// /// Constructs an ArchSpec with properties consistent with the given /// object type and architecture name. //------------------------------------------------------------------ explicit ArchSpec (ArchitectureType arch_type, uint32_t cpu_type, uint32_t cpu_subtype); //------------------------------------------------------------------ /// Destructor. //------------------------------------------------------------------ ~ArchSpec (); //------------------------------------------------------------------ /// Assignment operator. /// /// @param[in] rhs another ArchSpec object to copy. /// /// @return A const reference to this object. //------------------------------------------------------------------ const ArchSpec& operator= (const ArchSpec& rhs); static size_t AutoComplete (const char *name, StringList &matches); //------------------------------------------------------------------ /// Returns a static string representing the current architecture. /// /// @return A static string correcponding to the current /// architecture. //------------------------------------------------------------------ const char * GetArchitectureName () const; //------------------------------------------------------------------ /// Clears the object state. /// /// Clears the object state back to a default invalid state. //------------------------------------------------------------------ void Clear (); //------------------------------------------------------------------ /// Returns the size in bytes of an address of the current /// architecture. /// /// @return The byte size of an address of the current architecture. //------------------------------------------------------------------ uint32_t GetAddressByteSize () const; //------------------------------------------------------------------ /// Returns a machine family for the current architecture. /// /// @return An LLVM arch type. //------------------------------------------------------------------ llvm::Triple::ArchType GetMachine () const; //------------------------------------------------------------------ /// Returns the distribution id of the architecture. /// /// This will be something like "ubuntu", "fedora", etc. on Linux. /// /// @return A ConstString ref containing the distribution id, /// potentially empty. //------------------------------------------------------------------ const ConstString& GetDistributionId () const; //------------------------------------------------------------------ /// Set the distribution id of the architecture. /// /// This will be something like "ubuntu", "fedora", etc. on Linux. /// This should be the same value returned by /// HostInfo::GetDistributionId (). ///------------------------------------------------------------------ void SetDistributionId (const char* distribution_id); //------------------------------------------------------------------ /// Tests if this ArchSpec is valid. /// /// @return True if the current architecture is valid, false /// otherwise. //------------------------------------------------------------------ bool IsValid () const { return m_core >= eCore_arm_generic && m_core < kNumCores; } bool TripleVendorWasSpecified() const { return !m_triple.getVendorName().empty(); } bool TripleOSWasSpecified() const { return !m_triple.getOSName().empty(); } //------------------------------------------------------------------ /// Merges fields from another ArchSpec into this ArchSpec. /// /// This will use the supplied ArchSpec to fill in any fields of /// the triple in this ArchSpec which were unspecified. This can /// be used to refine a generic ArchSpec with a more specific one. /// For example, if this ArchSpec's triple is something like /// i386-unknown-unknown-unknown, and we have a triple which is /// x64-pc-windows-msvc, then merging that triple into this one /// will result in the triple i386-pc-windows-msvc. /// //------------------------------------------------------------------ void MergeFrom(const ArchSpec &other); //------------------------------------------------------------------ /// Change the architecture object type, CPU type and OS type. /// /// @param[in] arch_type The object type of this ArchSpec. /// /// @param[in] cpu The required CPU type. /// /// @param[in] os The optional OS type /// The default value of 0 was choosen to from the ELF spec value /// ELFOSABI_NONE. ELF is the only one using this parameter. If another /// format uses this parameter and 0 does not work, use a value over /// 255 because in the ELF header this is value is only a byte. /// /// @return True if the object, and CPU were successfully set. /// /// As a side effect, the vendor value is usually set to unknown. /// The exections are /// aarch64-apple-ios /// arm-apple-ios /// thumb-apple-ios /// x86-apple- /// x86_64-apple- /// /// As a side effect, the os value is usually set to unknown /// The exceptions are /// *-*-aix /// aarch64-apple-ios /// arm-apple-ios /// thumb-apple-ios /// powerpc-apple-darwin /// *-*-freebsd /// *-*-linux /// *-*-netbsd /// *-*-openbsd /// *-*-solaris //------------------------------------------------------------------ bool SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub, uint32_t os = 0); //------------------------------------------------------------------ /// Returns the byte order for the architecture specification. /// /// @return The endian enumeration for the current endianness of /// the architecture specification //------------------------------------------------------------------ lldb::ByteOrder GetByteOrder () const; //------------------------------------------------------------------ /// Sets this ArchSpec's byte order. /// /// In the common case there is no need to call this method as the /// byte order can almost always be determined by the architecture. /// However, many CPU's are bi-endian (ARM, Alpha, PowerPC, etc) /// and the default/assumed byte order may be incorrect. //------------------------------------------------------------------ void SetByteOrder (lldb::ByteOrder byte_order) { m_byte_order = byte_order; } uint32_t GetMinimumOpcodeByteSize() const; uint32_t GetMaximumOpcodeByteSize() const; Core GetCore () const { return m_core; } uint32_t GetMachOCPUType () const; uint32_t GetMachOCPUSubType () const; //------------------------------------------------------------------ /// Architecture data byte width accessor /// /// @return the size in 8-bit (host) bytes of a minimum addressable /// unit from the Architecture's data bus //------------------------------------------------------------------ uint32_t GetDataByteSize() const; //------------------------------------------------------------------ /// Architecture code byte width accessor /// /// @return the size in 8-bit (host) bytes of a minimum addressable /// unit from the Architecture's code bus //------------------------------------------------------------------ uint32_t GetCodeByteSize() const; //------------------------------------------------------------------ /// Architecture tripple accessor. /// /// @return A triple describing this ArchSpec. //------------------------------------------------------------------ llvm::Triple & GetTriple () { return m_triple; } //------------------------------------------------------------------ /// Architecture tripple accessor. /// /// @return A triple describing this ArchSpec. //------------------------------------------------------------------ const llvm::Triple & GetTriple () const { return m_triple; } //------------------------------------------------------------------ /// Architecture tripple setter. /// /// Configures this ArchSpec according to the given triple. If the /// triple has unknown components in all of the vendor, OS, and /// the optional environment field (i.e. "i386-unknown-unknown") /// then default values are taken from the host. Architecture and /// environment components are used to further resolve the CPU type /// and subtype, endian characteristics, etc. /// /// @return A triple describing this ArchSpec. //------------------------------------------------------------------ bool SetTriple (const llvm::Triple &triple); bool SetTriple (const char *triple_cstr); bool SetTriple (const char *triple_cstr, Platform *platform); //------------------------------------------------------------------ /// Returns the default endianness of the architecture. /// /// @return The endian enumeration for the default endianness of /// the architecture. //------------------------------------------------------------------ lldb::ByteOrder GetDefaultEndian () const; //------------------------------------------------------------------ /// Returns true if 'char' is a signed type by defualt in the /// architecture false otherwise /// /// @return True if 'char' is a signed type by default on the /// architecture and false otherwise. //------------------------------------------------------------------ bool CharIsSignedByDefault () const; //------------------------------------------------------------------ /// Compare an ArchSpec to another ArchSpec, requiring an exact cpu /// type match between them. /// e.g. armv7s is not an exact match with armv7 - this would return false /// /// @return true if the two ArchSpecs match. //------------------------------------------------------------------ bool IsExactMatch (const ArchSpec& rhs) const; //------------------------------------------------------------------ /// Compare an ArchSpec to another ArchSpec, requiring a compatible /// cpu type match between them. /// e.g. armv7s is compatible with armv7 - this method would return true /// /// @return true if the two ArchSpecs are compatible //------------------------------------------------------------------ bool IsCompatibleMatch (const ArchSpec& rhs) const; //------------------------------------------------------------------ /// Get a stop info override callback for the current architecture. /// /// Most platform specific code should go in lldb_private::Platform, /// but there are cases where no matter which platform you are on /// certain things hold true. /// /// This callback is currently intended to handle cases where a /// program stops at an instruction that won't get executed and it /// allows the stop reasonm, like "breakpoint hit", to be replaced /// with a different stop reason like "no stop reason". /// /// This is specifically used for ARM in Thumb code when we stop in /// an IT instruction (if/then/else) where the instruction won't get /// executed and therefore it wouldn't be correct to show the program /// stopped at the current PC. The code is generic and applies to all /// ARM CPUs. /// /// @return NULL or a valid stop info override callback for the /// current architecture. //------------------------------------------------------------------ StopInfoOverrideCallbackType GetStopInfoOverrideCallback () const; + uint32_t + GetFlags () const + { + return m_flags; + } + + void + SetFlags (uint32_t flags) + { + m_flags = flags; + } + protected: bool IsEqualTo (const ArchSpec& rhs, bool exact_match) const; llvm::Triple m_triple; Core m_core; lldb::ByteOrder m_byte_order; + + // Additional arch flags which we cannot get from triple and core + // For MIPS these are application specific extensions like + // micromips, mips16 etc. + uint32_t m_flags; ConstString m_distribution_id; // Called when m_def or m_entry are changed. Fills in all remaining // members with default values. void CoreUpdated (bool update_triple); }; //------------------------------------------------------------------ /// @fn bool operator< (const ArchSpec& lhs, const ArchSpec& rhs) /// @brief Less than operator. /// /// Tests two ArchSpec objects to see if \a lhs is less than \a /// rhs. /// /// @param[in] lhs The Left Hand Side ArchSpec object to compare. /// @param[in] rhs The Left Hand Side ArchSpec object to compare. /// /// @return true if \a lhs is less than \a rhs //------------------------------------------------------------------ bool operator< (const ArchSpec& lhs, const ArchSpec& rhs); } // namespace lldb_private #endif // #if defined(__cplusplus) #endif // #ifndef liblldb_ArchSpec_h_ Index: vendor/lldb/dist/include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/common/NativeRegisterContext.h (revision 287513) +++ vendor/lldb/dist/include/lldb/Host/common/NativeRegisterContext.h (revision 287514) @@ -1,212 +1,225 @@ //===-- NativeRegisterContext.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_NativeRegisterContext_h_ #define liblldb_NativeRegisterContext_h_ // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/lldb-private.h" #include "lldb/Host/common/NativeWatchpointList.h" namespace lldb_private { class NativeThreadProtocol; class NativeRegisterContext: public std::enable_shared_from_this { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ NativeRegisterContext (NativeThreadProtocol &thread, uint32_t concrete_frame_idx); virtual ~NativeRegisterContext (); // void // InvalidateIfNeeded (bool force); //------------------------------------------------------------------ // Subclasses must override these functions //------------------------------------------------------------------ // virtual void // InvalidateAllRegisters () = 0; virtual uint32_t GetRegisterCount () const = 0; virtual uint32_t GetUserRegisterCount () const = 0; virtual const RegisterInfo * GetRegisterInfoAtIndex (uint32_t reg) const = 0; const char * GetRegisterSetNameForRegisterAtIndex (uint32_t reg_index) const; virtual uint32_t GetRegisterSetCount () const = 0; virtual const RegisterSet * GetRegisterSet (uint32_t set_index) const = 0; virtual Error ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) = 0; virtual Error WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) = 0; virtual Error ReadAllRegisterValues (lldb::DataBufferSP &data_sp) = 0; virtual Error WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) = 0; uint32_t ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const; //------------------------------------------------------------------ // Subclasses can override these functions if desired //------------------------------------------------------------------ virtual uint32_t NumSupportedHardwareBreakpoints (); virtual uint32_t SetHardwareBreakpoint (lldb::addr_t addr, size_t size); virtual bool ClearHardwareBreakpoint (uint32_t hw_idx); virtual uint32_t NumSupportedHardwareWatchpoints (); virtual uint32_t SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags); virtual bool ClearHardwareWatchpoint (uint32_t hw_index); virtual Error ClearAllHardwareWatchpoints (); virtual Error IsWatchpointHit(uint32_t wp_index, bool &is_hit); virtual Error GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr); virtual Error IsWatchpointVacant (uint32_t wp_index, bool &is_vacant); virtual lldb::addr_t GetWatchpointAddress (uint32_t wp_index); + // MIPS Linux kernel returns a masked address (last 3bits are masked) + // when a HW watchpoint is hit. However user may not have set a watchpoint + // on this address. This function emulates the instruction at PC and + // finds the base address used in the load/store instruction. This gives the + // exact address used to read/write the variable being watched. + // For example: + // 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', + // then watch exception is generated even when 'n' is read/written. This function + // returns address of 'n' so that client can check whether a watchpoint is set + // on this address or not. + virtual lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index); + virtual bool HardwareSingleStep (bool enable); virtual Error ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, size_t src_len, RegisterValue ®_value); virtual Error WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, size_t dst_len, const RegisterValue ®_value); //------------------------------------------------------------------ // Subclasses should not override these //------------------------------------------------------------------ virtual lldb::tid_t GetThreadID() const; virtual NativeThreadProtocol & GetThread () { return m_thread; } const RegisterInfo * GetRegisterInfoByName (const char *reg_name, uint32_t start_idx = 0); const RegisterInfo * GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num); lldb::addr_t GetPC (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS); virtual lldb::addr_t GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS); Error SetPC (lldb::addr_t pc); lldb::addr_t GetSP (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS); Error SetSP (lldb::addr_t sp); lldb::addr_t GetFP (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS); Error SetFP (lldb::addr_t fp); const char * GetRegisterName (uint32_t reg); lldb::addr_t GetReturnAddress (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS); lldb::addr_t GetFlags (lldb::addr_t fail_value = 0); lldb::addr_t ReadRegisterAsUnsigned (uint32_t reg, lldb::addr_t fail_value); lldb::addr_t ReadRegisterAsUnsigned (const RegisterInfo *reg_info, lldb::addr_t fail_value); Error WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval); Error WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval); // uint32_t // GetStopID () const // { // return m_stop_id; // } // void // SetStopID (uint32_t stop_id) // { // m_stop_id = stop_id; // } protected: //------------------------------------------------------------------ // Classes that inherit from RegisterContext can see and modify these //------------------------------------------------------------------ NativeThreadProtocol &m_thread; // The thread that this register context belongs to. uint32_t m_concrete_frame_idx; // The concrete frame index for this register context // uint32_t m_stop_id; // The stop ID that any data in this context is valid for private: //------------------------------------------------------------------ // For RegisterContext only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN (NativeRegisterContext); }; } // namespace lldb_private #endif // liblldb_NativeRegisterContext_h_ Index: vendor/lldb/dist/include/lldb/Target/StopInfo.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/StopInfo.h (revision 287513) +++ vendor/lldb/dist/include/lldb/Target/StopInfo.h (revision 287514) @@ -1,247 +1,247 @@ //===-- StopInfo.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_StopInfo_h_ #define liblldb_StopInfo_h_ // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/lldb-public.h" #include "lldb/Target/Process.h" #include "lldb/Core/StructuredData.h" namespace lldb_private { class StopInfo { friend class Process::ProcessEventData; friend class ThreadPlanBase; public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ StopInfo (Thread &thread, uint64_t value); virtual ~StopInfo() { } bool IsValid () const; void SetThread (const lldb::ThreadSP &thread_sp) { m_thread_wp = thread_sp; } lldb::ThreadSP GetThread() const { return m_thread_wp.lock(); } // The value of the StopInfo depends on the StopReason. // StopReason Meaning // ---------------------------------------------- // eStopReasonBreakpoint BreakpointSiteID // eStopReasonSignal Signal number // eStopReasonWatchpoint WatchpointLocationID // eStopReasonPlanComplete No significance uint64_t GetValue() const { return m_value; } virtual lldb::StopReason GetStopReason () const = 0; // ShouldStopSynchronous will get called before any thread plans are consulted, and if it says we should // resume the target, then we will just immediately resume. This should not run any code in or resume the // target. virtual bool ShouldStopSynchronous (Event *event_ptr) { return true; } void OverrideShouldNotify (bool override_value) { m_override_should_notify = override_value ? eLazyBoolYes : eLazyBoolNo; } // If should stop returns false, check if we should notify of this event virtual bool ShouldNotify (Event *event_ptr) { if (m_override_should_notify == eLazyBoolCalculate) return DoShouldNotify (event_ptr); else return m_override_should_notify == eLazyBoolYes; } virtual void WillResume (lldb::StateType resume_state) { // By default, don't do anything } virtual const char * GetDescription () { return m_description.c_str(); } virtual void SetDescription (const char *desc_cstr) { if (desc_cstr && desc_cstr[0]) m_description.assign (desc_cstr); else m_description.clear(); } virtual bool IsValidForOperatingSystemThread (Thread &thread) { return true; } // Sometimes the thread plan logic will know that it wants a given stop to stop or not, // regardless of what the ordinary logic for that StopInfo would dictate. The main example // of this is the ThreadPlanCallFunction, which for instance knows - based on how that particular // expression was executed - whether it wants all breakpoints to auto-continue or not. // Use OverrideShouldStop on the StopInfo to implement this. void OverrideShouldStop (bool override_value) { m_override_should_stop = override_value ? eLazyBoolYes : eLazyBoolNo; } bool GetOverrideShouldStop() { return m_override_should_stop != eLazyBoolCalculate; } bool GetOverriddenShouldStopValue () { return m_override_should_stop == eLazyBoolYes; } StructuredData::ObjectSP GetExtendedInfo () { return m_extended_info; } static lldb::StopInfoSP CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id); // This creates a StopInfo for the thread where the should_stop is already set, and won't be recalculated. static lldb::StopInfoSP CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id, bool should_stop); static lldb::StopInfoSP - CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id); + CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id, lldb::addr_t watch_hit_addr = LLDB_INVALID_ADDRESS); static lldb::StopInfoSP CreateStopReasonWithSignal (Thread &thread, int signo, const char *description = nullptr); static lldb::StopInfoSP CreateStopReasonToTrace (Thread &thread); static lldb::StopInfoSP CreateStopReasonWithPlan (lldb::ThreadPlanSP &plan, lldb::ValueObjectSP return_valobj_sp, lldb::ClangExpressionVariableSP expression_variable_sp); static lldb::StopInfoSP CreateStopReasonWithException (Thread &thread, const char *description); static lldb::StopInfoSP CreateStopReasonWithExec (Thread &thread); static lldb::ValueObjectSP GetReturnValueObject (lldb::StopInfoSP &stop_info_sp); static lldb::ClangExpressionVariableSP GetExpressionVariable (lldb::StopInfoSP &stop_info_sp); protected: // Perform any action that is associated with this stop. This is done as the // Event is removed from the event queue. ProcessEventData::DoOnRemoval does the job. virtual void PerformAction (Event *event_ptr) { } virtual bool DoShouldNotify (Event *event_ptr) { return false; } // Stop the thread by default. Subclasses can override this to allow // the thread to continue if desired. The ShouldStop method should not do anything // that might run code. If you need to run code when deciding whether to stop // at this StopInfo, that must be done in the PerformAction. // The PerformAction will always get called before the ShouldStop. This is done by the // ProcessEventData::DoOnRemoval, though the ThreadPlanBase needs to consult this later on. virtual bool ShouldStop (Event *event_ptr) { return true; } //------------------------------------------------------------------ // Classes that inherit from StackID can see and modify these //------------------------------------------------------------------ lldb::ThreadWP m_thread_wp; // The thread corresponding to the stop reason. uint32_t m_stop_id; // The process stop ID for which this stop info is valid uint32_t m_resume_id; // This is the resume ID when we made this stop ID. uint64_t m_value; // A generic value that can be used for things pertaining to this stop info std::string m_description; // A textual description describing this stop. LazyBool m_override_should_notify; LazyBool m_override_should_stop; StructuredData::ObjectSP m_extended_info; // The extended info for this stop info // This determines whether the target has run since this stop info. // N.B. running to evaluate a user expression does not count. bool HasTargetRunSinceMe (); // MakeStopInfoValid is necessary to allow saved stop infos to resurrect themselves as valid. // It should only be used by Thread::RestoreThreadStateFromCheckpoint and to make sure the one-step // needed for before-the-fact watchpoints does not prevent us from stopping void MakeStopInfoValid (); private: friend class Thread; DISALLOW_COPY_AND_ASSIGN (StopInfo); }; } // namespace lldb_private #endif // liblldb_StopInfo_h_ Index: vendor/lldb/dist/source/Core/ArchSpec.cpp =================================================================== --- vendor/lldb/dist/source/Core/ArchSpec.cpp (revision 287513) +++ vendor/lldb/dist/source/Core/ArchSpec.cpp (revision 287514) @@ -1,1423 +1,1430 @@ //===-- 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 #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/Support/COFF.h" #include "llvm/Support/ELF.h" #include "llvm/Support/Host.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/StringList.h" #include "lldb/Host/Endian.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/SafeMachO.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/InstructionUtils.h" using namespace lldb; using namespace lldb_private; #define ARCH_SPEC_SEPARATOR_CHAR '-' 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; }; } // 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, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32 , "mips" }, - { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r2 , "mipsr2" }, - { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r3 , "mipsr3" }, - { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r5 , "mipsr5" }, - { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r6 , "mipsr6" }, - { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el , "mipsel" }, - { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r2el , "mipsr2el" }, - { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r3el , "mipsr3el" }, - { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r5el , "mipsr5el" }, - { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r6el , "mipsr6el" }, + { 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, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r2 , "mips64r2" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r3 , "mips64r3" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r5 , "mips64r5" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r6 , "mips64r6" }, - { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64el , "mips64el" }, - { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r2el , "mips64r2el" }, - { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r3el , "mips64r3el" }, - { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r5el , "mips64r5el" }, - { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r6el , "mips64r6el" }, + { 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" }, { 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 (const char *name, StringList &matches) { uint32_t i; if (name && name[0]) { for (i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { if (NameMatches(g_core_definitions[i].name, eNameMatchStartsWith, name)) matches.AppendString (g_core_definitions[i].name); } } else { for (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_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 NULL; } // 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 NULL; } static inline const CoreDefinition * FindCoreDefinition (ArchSpec::Core core) { if (core >= 0 && core < llvm::array_lengthof(g_core_definitions)) return &g_core_definitions[core]; return NULL; } // Get a definition entry by cpu type and subtype. static const ArchDefinitionEntry * FindArchDefinitionEntry (const ArchDefinition *def, uint32_t cpu, uint32_t sub) { if (def == NULL) return NULL; 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 NULL; } static const ArchDefinitionEntry * FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) { if (def == NULL) return NULL; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].core == core) return &entries[i]; } return NULL; } //===----------------------------------------------------------------------===// // Constructors and destructors. ArchSpec::ArchSpec() : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid), - m_distribution_id () + m_distribution_id (), + m_flags (0) { } ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid), - m_distribution_id () + m_distribution_id (), + m_flags (0) { if (triple_cstr) SetTriple(triple_cstr, platform); } ArchSpec::ArchSpec (const char *triple_cstr) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid), - m_distribution_id () + m_distribution_id (), + m_flags (0) { if (triple_cstr) SetTriple(triple_cstr); } ArchSpec::ArchSpec(const llvm::Triple &triple) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid), - m_distribution_id () + m_distribution_id (), + m_flags (0) { SetTriple(triple); } ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid), - m_distribution_id () + m_distribution_id (), + m_flags (0) { SetArchitecture (arch_type, cpu, subtype); } ArchSpec::~ArchSpec() { } //===----------------------------------------------------------------------===// // 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"; } 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) 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; 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(); } return IsValid(); } static bool ParseMachCPUDashSubtypeTriple (const char *triple_cstr, ArchSpec &arch) { // Accept "12-10" or "12.10" as cpu type/subtype if (isdigit(triple_cstr[0])) { char *end = NULL; errno = 0; uint32_t cpu = (uint32_t)::strtoul (triple_cstr, &end, 0); if (errno == 0 && cpu != 0 && end && ((*end == '-') || (*end == '.'))) { errno = 0; uint32_t sub = (uint32_t)::strtoul (end + 1, &end, 0); if (errno == 0 && end && ((*end == '-') || (*end == '.') || (*end == '\0'))) { if (arch.SetArchitecture (eArchTypeMachO, cpu, sub)) { if (*end == '-') { llvm::StringRef vendor_os (end + 1); size_t dash_pos = vendor_os.find('-'); if (dash_pos != llvm::StringRef::npos) { llvm::StringRef vendor_str(vendor_os.substr(0, dash_pos)); arch.GetTriple().setVendorName(vendor_str); const size_t vendor_start_pos = dash_pos+1; dash_pos = vendor_os.find('-', vendor_start_pos); if (dash_pos == llvm::StringRef::npos) { if (vendor_start_pos < vendor_os.size()) arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos)); } else { arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos, dash_pos - vendor_start_pos)); } } } return true; } } } } return false; } bool ArchSpec::SetTriple (const char *triple_cstr) { if (triple_cstr && triple_cstr[0]) { if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) return true; llvm::StringRef triple_stref (triple_cstr); if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind32); else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind64); else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); } else { std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); triple_stref = normalized_triple_sstr; SetTriple (llvm::Triple (triple_stref)); } } else Clear(); return IsValid(); } bool ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) { if (triple_cstr && triple_cstr[0]) { if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) return true; llvm::StringRef triple_stref (triple_cstr); if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind32); else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKind64); else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) *this = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); } else { ArchSpec raw_arch (triple_cstr); std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); triple_stref = normalized_triple_sstr; llvm::Triple normalized_triple (triple_stref); const bool os_specified = normalized_triple.getOSName().size() > 0; const bool vendor_specified = normalized_triple.getVendorName().size() > 0; const bool env_specified = normalized_triple.getEnvironmentName().size() > 0; // If we got an arch only, then default the vendor, os, environment // to match the platform if one is supplied if (!(os_specified || vendor_specified || env_specified)) { if (platform) { // 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)) { 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.getEnvironmentName().size()) normalized_triple.setEnvironment(compatible_triple.getEnvironment()); } } else { *this = raw_arch; return IsValid(); } } else { // No platform specified, fall back to the host system for // the default vendor, os, and environment. 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); } } else Clear(); return IsValid(); } void ArchSpec::MergeFrom(const ArchSpec &other) { if (GetTriple().getVendor() == llvm::Triple::UnknownVendor && !TripleVendorWasSpecified()) GetTriple().setVendor(other.GetTriple().getVendor()); if (GetTriple().getOS() == llvm::Triple::UnknownOS && !TripleOSWasSpecified()) GetTriple().setOS(other.GetTriple().getOS()); if (GetTriple().getArch() == llvm::Triple::UnknownArch) GetTriple().setArch(other.GetTriple().getArch()); if (GetTriple().getEnvironment() == llvm::Triple::UnknownEnvironment) GetTriple().setEnvironment(other.GetTriple().getEnvironment()); } 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); switch (core_def->machine) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::thumb: m_triple.setOS (llvm::Triple::IOS); break; case llvm::Triple::x86: case llvm::Triple::x86_64: // Don't set the OS for x86_64 or for x86 as we want to leave it as an "unspecified unknown" // which means if we ask for the OS from the llvm::Triple we get back llvm::Triple::UnknownOS, but // if we ask for the string value for the OS it will come back empty (unspecified). // We do this because we now have iOS and MacOSX as the OS values for x86 and x86_64 for // normal desktop and simulator binaries. And if we compare a "x86_64-apple-ios" to a "x86_64-apple-" // triple, it will say it is compatible (because the OS is unspecified in the second one and will match // anything in the first break; default: m_triple.setOS (llvm::Triple::MacOSX); break; } } 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; } } // 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); } 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) { if (exact_match) { 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) { if (exact_match) { 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 (lhs_triple_env != rhs_triple_env) { // Only fail if both environment types are not unknown if (lhs_triple_env != llvm::Triple::UnknownEnvironment && rhs_triple_env != llvm::Triple::UnknownEnvironment) return false; } return true; } return false; } //===----------------------------------------------------------------------===// // 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; // Fall through to case below 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; 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; 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 = false; } 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; } 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; } 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; 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(); Error 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) == false) { // 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) == false) { // 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 NULL; } Index: vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp (revision 287513) +++ vendor/lldb/dist/source/Host/common/NativeRegisterContext.cpp (revision 287514) @@ -1,510 +1,516 @@ //===-- NativeRegisterContext.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/common/NativeRegisterContext.h" #include "lldb/Core/Log.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" using namespace lldb; using namespace lldb_private; NativeRegisterContext::NativeRegisterContext (NativeThreadProtocol &thread, uint32_t concrete_frame_idx) : m_thread (thread), m_concrete_frame_idx (concrete_frame_idx) { } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- NativeRegisterContext::~NativeRegisterContext() { } // FIXME revisit invalidation, process stop ids, etc. Right now we don't // support caching in NativeRegisterContext. We can do this later by // utilizing NativeProcessProtocol::GetStopID () and adding a stop id to // NativeRegisterContext. // void // NativeRegisterContext::InvalidateIfNeeded (bool force) // { // ProcessSP process_sp (m_thread.GetProcess()); // bool invalidate = force; // uint32_t process_stop_id = UINT32_MAX; // if (process_sp) // process_stop_id = process_sp->GetStopID(); // else // invalidate = true; // if (!invalidate) // invalidate = process_stop_id != GetStopID(); // if (invalidate) // { // InvalidateAllRegisters (); // SetStopID (process_stop_id); // } // } const RegisterInfo * NativeRegisterContext::GetRegisterInfoByName (const char *reg_name, uint32_t start_idx) { if (reg_name && reg_name[0]) { const uint32_t num_registers = GetRegisterCount(); for (uint32_t reg = start_idx; reg < num_registers; ++reg) { const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); if ((reg_info->name != nullptr && ::strcasecmp (reg_info->name, reg_name) == 0) || (reg_info->alt_name != nullptr && ::strcasecmp (reg_info->alt_name, reg_name) == 0)) { return reg_info; } } } return nullptr; } const RegisterInfo * NativeRegisterContext::GetRegisterInfo (uint32_t kind, uint32_t num) { const uint32_t reg_num = ConvertRegisterKindToRegisterNumber(kind, num); if (reg_num == LLDB_INVALID_REGNUM) return nullptr; return GetRegisterInfoAtIndex (reg_num); } const char * NativeRegisterContext::GetRegisterName (uint32_t reg) { const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg); if (reg_info) return reg_info->name; return nullptr; } const char* NativeRegisterContext::GetRegisterSetNameForRegisterAtIndex (uint32_t reg_index) const { const RegisterInfo *const reg_info = GetRegisterInfoAtIndex(reg_index); if (!reg_info) return nullptr; for (uint32_t set_index = 0; set_index < GetRegisterSetCount (); ++set_index) { const RegisterSet *const reg_set = GetRegisterSet (set_index); if (!reg_set) continue; for (uint32_t reg_num_index = 0; reg_num_index < reg_set->num_registers; ++reg_num_index) { const uint32_t reg_num = reg_set->registers[reg_num_index]; // FIXME double check we're checking the right register kind here. if (reg_info->kinds[RegisterKind::eRegisterKindLLDB] == reg_num) { // The given register is a member of this register set. Return the register set name. return reg_set->name; } } } // Didn't find it. return nullptr; } lldb::addr_t NativeRegisterContext::GetPC (lldb::addr_t fail_value) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); if (log) log->Printf ("NativeRegisterContext::%s using reg index %" PRIu32 " (default %" PRIu64 ")", __FUNCTION__, reg, fail_value); const uint64_t retval = ReadRegisterAsUnsigned (reg, fail_value); if (log) log->Printf ("NativeRegisterContext::%s " PRIu32 " retval %" PRIu64, __FUNCTION__, retval); return retval; } lldb::addr_t NativeRegisterContext::GetPCfromBreakpointLocation (lldb::addr_t fail_value) { return GetPC (fail_value); } Error NativeRegisterContext::SetPC (lldb::addr_t pc) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); return WriteRegisterFromUnsigned (reg, pc); } lldb::addr_t NativeRegisterContext::GetSP (lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); return ReadRegisterAsUnsigned (reg, fail_value); } Error NativeRegisterContext::SetSP (lldb::addr_t sp) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); return WriteRegisterFromUnsigned (reg, sp); } lldb::addr_t NativeRegisterContext::GetFP (lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); return ReadRegisterAsUnsigned (reg, fail_value); } Error NativeRegisterContext::SetFP (lldb::addr_t fp) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); return WriteRegisterFromUnsigned (reg, fp); } lldb::addr_t NativeRegisterContext::GetReturnAddress (lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); return ReadRegisterAsUnsigned (reg, fail_value); } lldb::addr_t NativeRegisterContext::GetFlags (lldb::addr_t fail_value) { uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); return ReadRegisterAsUnsigned (reg, fail_value); } lldb::addr_t NativeRegisterContext::ReadRegisterAsUnsigned (uint32_t reg, lldb::addr_t fail_value) { if (reg != LLDB_INVALID_REGNUM) return ReadRegisterAsUnsigned (GetRegisterInfoAtIndex (reg), fail_value); return fail_value; } uint64_t NativeRegisterContext::ReadRegisterAsUnsigned (const RegisterInfo *reg_info, lldb::addr_t fail_value) { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); if (reg_info) { RegisterValue value; Error error = ReadRegister (reg_info, value); if (error.Success ()) { if (log) log->Printf ("NativeRegisterContext::%s ReadRegister() succeeded, value %" PRIu64, __FUNCTION__, value.GetAsUInt64()); return value.GetAsUInt64(); } else { if (log) log->Printf ("NativeRegisterContext::%s ReadRegister() failed, error %s", __FUNCTION__, error.AsCString ()); } } else { if (log) log->Printf ("NativeRegisterContext::%s ReadRegister() null reg_info", __FUNCTION__); } return fail_value; } Error NativeRegisterContext::WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval) { if (reg == LLDB_INVALID_REGNUM) return Error ("NativeRegisterContext::%s (): reg is invalid", __FUNCTION__); return WriteRegisterFromUnsigned (GetRegisterInfoAtIndex (reg), uval); } Error NativeRegisterContext::WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval) { assert (reg_info); if (!reg_info) return Error ("reg_info is nullptr"); RegisterValue value; if (!value.SetUInt(uval, reg_info->byte_size)) return Error ("RegisterValue::SetUInt () failed"); return WriteRegister (reg_info, value); } lldb::tid_t NativeRegisterContext::GetThreadID() const { return m_thread.GetID(); } uint32_t NativeRegisterContext::NumSupportedHardwareBreakpoints () { return 0; } uint32_t NativeRegisterContext::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) { return LLDB_INVALID_INDEX32; } bool NativeRegisterContext::ClearHardwareBreakpoint (uint32_t hw_idx) { return false; } uint32_t NativeRegisterContext::NumSupportedHardwareWatchpoints () { return 0; } uint32_t NativeRegisterContext::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, uint32_t watch_flags) { return LLDB_INVALID_INDEX32; } bool NativeRegisterContext::ClearHardwareWatchpoint (uint32_t hw_index) { return false; } Error NativeRegisterContext::ClearAllHardwareWatchpoints () { return Error ("not implemented"); } Error NativeRegisterContext::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { is_hit = false; return Error ("not implemented"); } Error NativeRegisterContext::GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) { wp_index = LLDB_INVALID_INDEX32; return Error ("not implemented"); } Error NativeRegisterContext::IsWatchpointVacant (uint32_t wp_index, bool &is_vacant) { is_vacant = false; return Error ("not implemented"); } lldb::addr_t NativeRegisterContext::GetWatchpointAddress (uint32_t wp_index) { return LLDB_INVALID_ADDRESS; } +lldb::addr_t +NativeRegisterContext::GetWatchpointHitAddress (uint32_t wp_index) +{ + return LLDB_INVALID_ADDRESS; +} + bool NativeRegisterContext::HardwareSingleStep (bool enable) { return false; } Error NativeRegisterContext::ReadRegisterValueFromMemory ( const RegisterInfo *reg_info, lldb::addr_t src_addr, size_t src_len, RegisterValue ®_value) { Error error; if (reg_info == nullptr) { error.SetErrorString ("invalid register info argument."); return error; } // Moving from addr into a register // // Case 1: src_len == dst_len // // |AABBCCDD| Address contents // |AABBCCDD| Register contents // // Case 2: src_len > dst_len // // Error! (The register should always be big enough to hold the data) // // Case 3: src_len < dst_len // // |AABB| Address contents // |AABB0000| Register contents [on little-endian hardware] // |0000AABB| Register contents [on big-endian hardware] if (src_len > RegisterValue::kMaxRegisterByteSize) { error.SetErrorString ("register too small to receive memory data"); return error; } const size_t dst_len = reg_info->byte_size; if (src_len > dst_len) { error.SetErrorStringWithFormat("%" PRIu64 " bytes is too big to store in register %s (%" PRIu64 " bytes)", static_cast(src_len), reg_info->name, static_cast(dst_len)); return error; } NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); if (!process_sp) { error.SetErrorString("invalid process"); return error; } uint8_t src[RegisterValue::kMaxRegisterByteSize]; // Read the memory size_t bytes_read; error = process_sp->ReadMemory (src_addr, src, src_len, bytes_read); if (error.Fail ()) return error; // Make sure the memory read succeeded... if (bytes_read != src_len) { // This might happen if we read _some_ bytes but not all error.SetErrorStringWithFormat("read %" PRIu64 " of %" PRIu64 " bytes", static_cast(bytes_read), static_cast(src_len)); return error; } // We now have a memory buffer that contains the part or all of the register // value. Set the register value using this memory data. // TODO: we might need to add a parameter to this function in case the byte // order of the memory data doesn't match the process. For now we are assuming // they are the same. lldb::ByteOrder byte_order; if (!process_sp->GetByteOrder (byte_order)) { error.SetErrorString ( "NativeProcessProtocol::GetByteOrder () failed"); return error; } reg_value.SetFromMemoryData ( reg_info, src, src_len, byte_order, error); return error; } Error NativeRegisterContext::WriteRegisterValueToMemory ( const RegisterInfo *reg_info, lldb::addr_t dst_addr, size_t dst_len, const RegisterValue ®_value) { uint8_t dst[RegisterValue::kMaxRegisterByteSize]; Error error; NativeProcessProtocolSP process_sp (m_thread.GetProcess ()); if (process_sp) { // TODO: we might need to add a parameter to this function in case the byte // order of the memory data doesn't match the process. For now we are assuming // they are the same. lldb::ByteOrder byte_order; if (!process_sp->GetByteOrder (byte_order)) return Error ("NativeProcessProtocol::GetByteOrder () failed"); const size_t bytes_copied = reg_value.GetAsMemoryData ( reg_info, dst, dst_len, byte_order, error); if (error.Success()) { if (bytes_copied == 0) { error.SetErrorString("byte copy failed."); } else { size_t bytes_written; error = process_sp->WriteMemory(dst_addr, dst, bytes_copied, bytes_written); if (error.Fail ()) return error; if (bytes_written != bytes_copied) { // This might happen if we read _some_ bytes but not all error.SetErrorStringWithFormat("only wrote %" PRIu64 " of %" PRIu64 " bytes", static_cast(bytes_written), static_cast(bytes_copied)); } } } } else error.SetErrorString("invalid process"); return error; } uint32_t NativeRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const { const uint32_t num_regs = GetRegisterCount(); assert (kind < kNumRegisterKinds); for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg_idx); if (reg_info->kinds[kind] == num) return reg_idx; } return LLDB_INVALID_REGNUM; } Index: vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 287514) @@ -1,926 +1,941 @@ //===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "DisassemblerLLVMC.h" #include "llvm-c/Disassembler.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCExternalSymbolizer.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCRelocationInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/ADT/SmallString.h" #include "lldb/Core/Address.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/StackFrame.h" #include "lldb/Core/RegularExpression.h" using namespace lldb; using namespace lldb_private; class InstructionLLVMC : public lldb_private::Instruction { public: InstructionLLVMC (DisassemblerLLVMC &disasm, const lldb_private::Address &address, AddressClass addr_class) : Instruction (address, addr_class), m_disasm_sp (disasm.shared_from_this()), m_does_branch (eLazyBoolCalculate), m_is_valid (false), m_using_file_addr (false) { } virtual ~InstructionLLVMC () { } virtual bool DoesBranch () { if (m_does_branch == eLazyBoolCalculate) { GetDisassemblerLLVMC().Lock(this, NULL); DataExtractor data; if (m_opcode.GetData(data)) { bool is_alternate_isa; lldb::addr_t pc = m_address.GetFileAddress(); DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); const uint8_t *opcode_data = data.GetDataStart(); const size_t opcode_data_len = data.GetByteSize(); llvm::MCInst inst; const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, opcode_data_len, pc, inst); // Be conservative, if we didn't understand the instruction, say it might branch... if (inst_size == 0) m_does_branch = eLazyBoolYes; else { const bool can_branch = mc_disasm_ptr->CanBranch(inst); if (can_branch) m_does_branch = eLazyBoolYes; else m_does_branch = eLazyBoolNo; } } GetDisassemblerLLVMC().Unlock(); } return m_does_branch == eLazyBoolYes; } DisassemblerLLVMC::LLVMCDisassembler * GetDisasmToUse (bool &is_alternate_isa) { is_alternate_isa = false; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); if (llvm_disasm.m_alternate_disasm_ap.get() != NULL) { const AddressClass address_class = GetAddressClass (); if (address_class == eAddressClassCodeAlternateISA) { is_alternate_isa = true; return llvm_disasm.m_alternate_disasm_ap.get(); } } return llvm_disasm.m_disasm_ap.get(); } virtual size_t Decode (const lldb_private::Disassembler &disassembler, const lldb_private::DataExtractor &data, lldb::offset_t data_offset) { // All we have to do is read the opcode which can be easy for some // architectures bool got_op = false; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); const ArchSpec &arch = llvm_disasm.GetArchitecture(); const lldb::ByteOrder byte_order = data.GetByteOrder(); const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize(); const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize(); if (min_op_byte_size == max_op_byte_size) { // Fixed size instructions, just read that amount of data. if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size)) return false; switch (min_op_byte_size) { case 1: m_opcode.SetOpcode8 (data.GetU8 (&data_offset), byte_order); got_op = true; break; case 2: m_opcode.SetOpcode16 (data.GetU16 (&data_offset), byte_order); got_op = true; break; case 4: m_opcode.SetOpcode32 (data.GetU32 (&data_offset), byte_order); got_op = true; break; case 8: m_opcode.SetOpcode64 (data.GetU64 (&data_offset), byte_order); got_op = true; break; default: m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), min_op_byte_size); got_op = true; break; } } if (!got_op) { bool is_alternate_isa = false; DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); const llvm::Triple::ArchType machine = arch.GetMachine(); if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) { if (machine == llvm::Triple::thumb || is_alternate_isa) { uint32_t thumb_opcode = data.GetU16(&data_offset); if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0)) { m_opcode.SetOpcode16 (thumb_opcode, byte_order); m_is_valid = true; } else { thumb_opcode <<= 16; thumb_opcode |= data.GetU16(&data_offset); m_opcode.SetOpcode16_2 (thumb_opcode, byte_order); m_is_valid = true; } } else { m_opcode.SetOpcode32 (data.GetU32(&data_offset), byte_order); m_is_valid = true; } } else { // The opcode isn't evenly sized, so we need to actually use the llvm // disassembler to parse it and get the size. uint8_t *opcode_data = const_cast(data.PeekData (data_offset, 1)); const size_t opcode_data_len = data.BytesLeft(data_offset); const addr_t pc = m_address.GetFileAddress(); llvm::MCInst inst; llvm_disasm.Lock(this, NULL); const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst); llvm_disasm.Unlock(); if (inst_size == 0) m_opcode.Clear(); else { m_opcode.SetOpcodeBytes(opcode_data, inst_size); m_is_valid = true; } } } return m_opcode.GetByteSize(); } void AppendComment (std::string &description) { if (m_comment.empty()) m_comment.swap (description); else { m_comment.append(", "); m_comment.append(description); } } virtual void CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx) { DataExtractor data; const AddressClass address_class = GetAddressClass (); if (m_opcode.GetData(data)) { char out_string[512]; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr; if (address_class == eAddressClassCodeAlternateISA) mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get(); else mc_disasm_ptr = llvm_disasm.m_disasm_ap.get(); lldb::addr_t pc = m_address.GetFileAddress(); m_using_file_addr = true; const bool data_from_file = GetDisassemblerLLVMC().m_data_from_file; bool use_hex_immediates = true; Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC; if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { use_hex_immediates = target->GetUseHexImmediates(); hex_style = target->GetHexImmediateStyle(); if (!data_from_file) { const lldb::addr_t load_addr = m_address.GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { pc = load_addr; m_using_file_addr = false; } } } } llvm_disasm.Lock(this, exe_ctx); const uint8_t *opcode_data = data.GetDataStart(); const size_t opcode_data_len = data.GetByteSize(); llvm::MCInst inst; size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, opcode_data_len, pc, inst); if (inst_size > 0) { mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style); mc_disasm_ptr->PrintMCInst(inst, out_string, sizeof(out_string)); } llvm_disasm.Unlock(); if (inst_size == 0) { m_comment.assign ("unknown opcode"); inst_size = m_opcode.GetByteSize(); StreamString mnemonic_strm; lldb::offset_t offset = 0; lldb::ByteOrder byte_order = data.GetByteOrder(); switch (inst_size) { case 1: { const uint8_t uval8 = data.GetU8 (&offset); m_opcode.SetOpcode8 (uval8, byte_order); m_opcode_name.assign (".byte"); mnemonic_strm.Printf("0x%2.2x", uval8); } break; case 2: { const uint16_t uval16 = data.GetU16(&offset); m_opcode.SetOpcode16(uval16, byte_order); m_opcode_name.assign (".short"); mnemonic_strm.Printf("0x%4.4x", uval16); } break; case 4: { const uint32_t uval32 = data.GetU32(&offset); m_opcode.SetOpcode32(uval32, byte_order); m_opcode_name.assign (".long"); mnemonic_strm.Printf("0x%8.8x", uval32); } break; case 8: { const uint64_t uval64 = data.GetU64(&offset); m_opcode.SetOpcode64(uval64, byte_order); m_opcode_name.assign (".quad"); mnemonic_strm.Printf("0x%16.16" PRIx64, uval64); } break; default: if (inst_size == 0) return; else { const uint8_t *bytes = data.PeekData(offset, inst_size); if (bytes == NULL) return; m_opcode_name.assign (".byte"); m_opcode.SetOpcodeBytes(bytes, inst_size); mnemonic_strm.Printf("0x%2.2x", bytes[0]); for (uint32_t i=1; iCanBranch(inst); if (can_branch) m_does_branch = eLazyBoolYes; else m_does_branch = eLazyBoolNo; } } static RegularExpression s_regex("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?"); RegularExpression::Match matches(3); if (s_regex.Execute(out_string, &matches)) { matches.GetMatchAtIndex(out_string, 1, m_opcode_name); matches.GetMatchAtIndex(out_string, 2, m_mnemonics); } } } bool IsValid () const { return m_is_valid; } bool UsingFileAddress() const { return m_using_file_addr; } size_t GetByteSize () const { return m_opcode.GetByteSize(); } DisassemblerLLVMC & GetDisassemblerLLVMC () { return *(DisassemblerLLVMC *)m_disasm_sp.get(); } protected: DisassemblerSP m_disasm_sp; // for ownership LazyBool m_does_branch; bool m_is_valid; bool m_using_file_addr; }; -DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, const char *cpu, unsigned flavor, DisassemblerLLVMC &owner): +DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, const char *cpu, const char *features_str, unsigned flavor, DisassemblerLLVMC &owner): m_is_valid(true) { std::string Error; const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error); if (!curr_target) { m_is_valid = false; return; } m_instr_info_ap.reset(curr_target->createMCInstrInfo()); m_reg_info_ap.reset (curr_target->createMCRegInfo(triple)); - std::string features_str; - m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, cpu, features_str)); std::unique_ptr reg_info(curr_target->createMCRegInfo(triple)); m_asm_info_ap.reset(curr_target->createMCAsmInfo(*reg_info, triple)); if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL) { m_is_valid = false; return; } m_context_ap.reset(new llvm::MCContext(m_asm_info_ap.get(), m_reg_info_ap.get(), 0)); m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get(), *m_context_ap.get())); if (m_disasm_ap.get() && m_context_ap.get()) { std::unique_ptr RelInfo(curr_target->createMCRelocationInfo(triple, *m_context_ap.get())); if (!RelInfo) { m_is_valid = false; return; } std::unique_ptr symbolizer_up(curr_target->createMCSymbolizer(triple, NULL, DisassemblerLLVMC::SymbolLookupCallback, (void *) &owner, m_context_ap.get(), std::move(RelInfo))); m_disasm_ap->setSymbolizer(std::move(symbolizer_up)); unsigned asm_printer_variant; if (flavor == ~0U) asm_printer_variant = m_asm_info_ap->getAssemblerDialect(); else { asm_printer_variant = flavor; } m_instr_printer_ap.reset(curr_target->createMCInstPrinter(llvm::Triple{triple}, asm_printer_variant, *m_asm_info_ap.get(), *m_instr_info_ap.get(), *m_reg_info_ap.get())); if (m_instr_printer_ap.get() == NULL) { m_disasm_ap.reset(); m_is_valid = false; } } else m_is_valid = false; } DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() { } uint64_t DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst) { llvm::ArrayRef data(opcode_data, opcode_data_len); llvm::MCDisassembler::DecodeStatus status; uint64_t new_inst_size; status = m_disasm_ap->getInstruction(mc_inst, new_inst_size, data, pc, llvm::nulls(), llvm::nulls()); if (status == llvm::MCDisassembler::Success) return new_inst_size; else return 0; } uint64_t DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst, char *dst, size_t dst_len) { llvm::StringRef unused_annotations; llvm::SmallString<64> inst_string; llvm::raw_svector_ostream inst_stream(inst_string); m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations, *m_subtarget_info_ap); inst_stream.flush(); const size_t output_size = std::min(dst_len - 1, inst_string.size()); std::memcpy(dst, inst_string.data(), output_size); dst[output_size] = '\0'; return output_size; } void DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style) { m_instr_printer_ap->setPrintImmHex(use_hex_immed); switch(hex_style) { case eHexStyleC: m_instr_printer_ap->setPrintHexStyle(llvm::HexStyle::C); break; case eHexStyleAsm: m_instr_printer_ap->setPrintHexStyle(llvm::HexStyle::Asm); break; } } bool DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst) { return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get()); } bool DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) { llvm::Triple triple = arch.GetTriple(); if (flavor == NULL || strcmp (flavor, "default") == 0) return true; if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64) { if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0) return true; else return false; } else return false; } Disassembler * DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor) { if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) { std::unique_ptr disasm_ap (new DisassemblerLLVMC(arch, flavor)); if (disasm_ap.get() && disasm_ap->IsValid()) return disasm_ap.release(); } return NULL; } DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) : Disassembler(arch, flavor_string), m_exe_ctx (NULL), m_inst (NULL), m_data_from_file (false) { if (!FlavorValidForArchSpec (arch, m_flavor.c_str())) { m_flavor.assign("default"); } const char *triple = arch.GetTriple().getTriple().c_str(); unsigned flavor = ~0U; // So far the only supported flavor is "intel" on x86. The base class will set this // correctly coming in. if (arch.GetTriple().getArch() == llvm::Triple::x86 || arch.GetTriple().getArch() == llvm::Triple::x86_64) { if (m_flavor == "intel") { flavor = 1; } else if (m_flavor == "att") { flavor = 0; } } ArchSpec thumb_arch(arch); if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::string thumb_arch_name (thumb_arch.GetTriple().getArchName().str()); // Replace "arm" with "thumb" so we get all thumb variants correct if (thumb_arch_name.size() > 3) { thumb_arch_name.erase(0,3); thumb_arch_name.insert(0, "thumb"); } else { thumb_arch_name = "thumbv7"; } thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str())); } // Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions, // so hardcode the primary disassembler to thumb mode. Same for Cortex-M4 (armv7em). // // Handle the Cortex-M0 (armv6m) the same; the ISA is a subset of the T and T32 // instructions defined in ARMv7-A. if (arch.GetTriple().getArch() == llvm::Triple::arm && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em || arch.GetCore() == ArchSpec::Core::eCore_arm_armv6m)) { triple = thumb_arch.GetTriple().getTriple().c_str(); } const char *cpu = ""; switch (arch.GetCore()) { 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: cpu = ""; break; } + + std::string features_str = ""; + if (arch.GetTriple().getArch() == llvm::Triple::mips || arch.GetTriple().getArch() == llvm::Triple::mipsel + || arch.GetTriple().getArch() == llvm::Triple::mips64 || arch.GetTriple().getArch() == llvm::Triple::mips64el) + { + uint32_t arch_flags = arch.GetFlags (); + if (arch_flags & ArchSpec::eMIPSAse_msa) + features_str += "+msa,"; + if (arch_flags & ArchSpec::eMIPSAse_dsp) + features_str += "+dsp,"; + if (arch_flags & ArchSpec::eMIPSAse_dspr2) + features_str += "+dspr2,"; + if (arch_flags & ArchSpec::eMIPSAse_mips16) + features_str += "+mips16,"; + if (arch_flags & ArchSpec::eMIPSAse_micromips) + features_str += "+micromips,"; + } - m_disasm_ap.reset (new LLVMCDisassembler(triple, cpu, flavor, *this)); + m_disasm_ap.reset (new LLVMCDisassembler(triple, cpu, features_str.c_str(), flavor, *this)); if (!m_disasm_ap->IsValid()) { // We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason, // we reset it, and then we won't be valid and FindPlugin will fail and we won't get used. m_disasm_ap.reset(); } // For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler. if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::string thumb_triple(thumb_arch.GetTriple().getTriple()); - m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), "", flavor, *this)); + m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), "", "", flavor, *this)); if (!m_alternate_disasm_ap->IsValid()) { m_disasm_ap.reset(); m_alternate_disasm_ap.reset(); } } } DisassemblerLLVMC::~DisassemblerLLVMC() { } size_t DisassemblerLLVMC::DecodeInstructions (const Address &base_addr, const DataExtractor& data, lldb::offset_t data_offset, size_t num_instructions, bool append, bool data_from_file) { if (!append) m_instruction_list.Clear(); if (!IsValid()) return 0; m_data_from_file = data_from_file; uint32_t data_cursor = data_offset; const size_t data_byte_size = data.GetByteSize(); uint32_t instructions_parsed = 0; Address inst_addr(base_addr); while (data_cursor < data_byte_size && instructions_parsed < num_instructions) { AddressClass address_class = eAddressClassCode; if (m_alternate_disasm_ap.get() != NULL) address_class = inst_addr.GetAddressClass (); InstructionSP inst_sp(new InstructionLLVMC(*this, inst_addr, address_class)); if (!inst_sp) break; uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); if (inst_size == 0) break; m_instruction_list.Append(inst_sp); data_cursor += inst_size; inst_addr.Slide(inst_size); instructions_parsed++; } return data_cursor - data_offset; } void DisassemblerLLVMC::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), "Disassembler that uses LLVM MC to disassemble i386, x86_64, ARM, and ARM64.", CreateInstance); llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); } void DisassemblerLLVMC::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } ConstString DisassemblerLLVMC::GetPluginNameStatic() { static ConstString g_name("llvm-mc"); return g_name; } int DisassemblerLLVMC::OpInfoCallback (void *disassembler, uint64_t pc, uint64_t offset, uint64_t size, int tag_type, void *tag_bug) { return static_cast(disassembler)->OpInfo (pc, offset, size, tag_type, tag_bug); } const char *DisassemblerLLVMC::SymbolLookupCallback (void *disassembler, uint64_t value, uint64_t *type, uint64_t pc, const char **name) { return static_cast(disassembler)->SymbolLookup(value, type, pc, name); } int DisassemblerLLVMC::OpInfo (uint64_t PC, uint64_t Offset, uint64_t Size, int tag_type, void *tag_bug) { switch (tag_type) { default: break; case 1: memset (tag_bug, 0, sizeof(::LLVMOpInfo1)); break; } return 0; } const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, uint64_t *type_ptr, uint64_t pc, const char **name) { if (*type_ptr) { if (m_exe_ctx && m_inst) { //std::string remove_this_prior_to_checkin; Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : NULL; Address value_so_addr; Address pc_so_addr; if (m_inst->UsingFileAddress()) { ModuleSP module_sp(m_inst->GetAddress().GetModule()); if (module_sp) { module_sp->ResolveFileAddress(value, value_so_addr); module_sp->ResolveFileAddress(pc, pc_so_addr); } } else if (target && !target->GetSectionLoadList().IsEmpty()) { target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr); target->GetSectionLoadList().ResolveLoadAddress(pc, pc_so_addr); } SymbolContext sym_ctx; const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; if (pc_so_addr.IsValid() && pc_so_addr.GetModule()) { pc_so_addr.GetModule()->ResolveSymbolContextForAddress (pc_so_addr, resolve_scope, sym_ctx); } if (value_so_addr.IsValid() && value_so_addr.GetSection()) { StreamString ss; bool format_omitting_current_func_name = false; if (sym_ctx.symbol || sym_ctx.function) { AddressRange range; if (sym_ctx.GetAddressRange (resolve_scope, 0, false, range) && range.GetBaseAddress().IsValid() && range.ContainsLoadAddress (value_so_addr, target)) { format_omitting_current_func_name = true; } } // If the "value" address (the target address we're symbolicating) // is inside the same SymbolContext as the current instruction pc // (pc_so_addr), don't print the full function name - just print it // with DumpStyleNoFunctionName style, e.g. "<+36>". if (format_omitting_current_func_name) { value_so_addr.Dump (&ss, target, Address::DumpStyleNoFunctionName, Address::DumpStyleSectionNameOffset); } else { value_so_addr.Dump (&ss, target, Address::DumpStyleResolvedDescriptionNoFunctionArguments, Address::DumpStyleSectionNameOffset); } if (!ss.GetString().empty()) { // If Address::Dump returned a multi-line description, most commonly seen when we // have multiple levels of inlined functions at an address, only show the first line. std::string &str(ss.GetString()); size_t first_eol_char = str.find_first_of ("\r\n"); if (first_eol_char != std::string::npos) { str.erase (first_eol_char); } m_inst->AppendComment(ss.GetString()); } } } } *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; *name = NULL; return NULL; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString DisassemblerLLVMC::GetPluginName() { return GetPluginNameStatic(); } uint32_t DisassemblerLLVMC::GetPluginVersion() { return 1; } Index: vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h =================================================================== --- vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h (revision 287514) @@ -1,166 +1,166 @@ //===-- DisassemblerLLVMC.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_DisassemblerLLVMC_h_ #define liblldb_DisassemblerLLVMC_h_ #include #include "llvm-c/Disassembler.h" // Opaque references to C++ Objects in LLVM's MC. namespace llvm { class MCContext; class MCInst; class MCInstrInfo; class MCRegisterInfo; class MCDisassembler; class MCInstPrinter; class MCAsmInfo; class MCSubtargetInfo; } #include "lldb/Core/Address.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/Mutex.h" class InstructionLLVMC; class DisassemblerLLVMC : public lldb_private::Disassembler { // Since we need to make two actual MC Disassemblers for ARM (ARM & THUMB), and there's a bit of goo to set up and own // in the MC disassembler world, I added this class to manage the actual disassemblers. class LLVMCDisassembler { public: - LLVMCDisassembler (const char *triple, const char *cpu, unsigned flavor, DisassemblerLLVMC &owner); + LLVMCDisassembler (const char *triple, const char *cpu, const char *features_str, unsigned flavor, DisassemblerLLVMC &owner); ~LLVMCDisassembler(); uint64_t GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst); uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len); void SetStyle (bool use_hex_immed, HexImmediateStyle hex_style); bool CanBranch (llvm::MCInst &mc_inst); bool IsValid() { return m_is_valid; } private: bool m_is_valid; std::unique_ptr m_context_ap; std::unique_ptr m_asm_info_ap; std::unique_ptr m_subtarget_info_ap; std::unique_ptr m_instr_info_ap; std::unique_ptr m_reg_info_ap; std::unique_ptr m_instr_printer_ap; std::unique_ptr m_disasm_ap; }; public: //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ static void Initialize(); static void Terminate(); static lldb_private::ConstString GetPluginNameStatic(); static lldb_private::Disassembler * CreateInstance(const lldb_private::ArchSpec &arch, const char *flavor); DisassemblerLLVMC(const lldb_private::ArchSpec &arch, const char *flavor /* = NULL */); virtual ~DisassemblerLLVMC(); virtual size_t DecodeInstructions (const lldb_private::Address &base_addr, const lldb_private::DataExtractor& data, lldb::offset_t data_offset, size_t num_instructions, bool append, bool data_from_file); //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ virtual lldb_private::ConstString GetPluginName(); virtual uint32_t GetPluginVersion(); protected: friend class InstructionLLVMC; virtual bool FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor); bool IsValid() { return (m_disasm_ap.get() != NULL && m_disasm_ap->IsValid()); } int OpInfo(uint64_t PC, uint64_t Offset, uint64_t Size, int TagType, void *TagBug); const char *SymbolLookup (uint64_t ReferenceValue, uint64_t *ReferenceType, uint64_t ReferencePC, const char **ReferenceName); static int OpInfoCallback (void *DisInfo, uint64_t PC, uint64_t Offset, uint64_t Size, int TagType, void *TagBug); static const char *SymbolLookupCallback(void *DisInfo, uint64_t ReferenceValue, uint64_t *ReferenceType, uint64_t ReferencePC, const char **ReferenceName); void Lock(InstructionLLVMC *inst, const lldb_private::ExecutionContext *exe_ctx) { m_mutex.Lock(); m_inst = inst; m_exe_ctx = exe_ctx; } void Unlock() { m_inst = NULL; m_exe_ctx = NULL; m_mutex.Unlock(); } const lldb_private::ExecutionContext *m_exe_ctx; InstructionLLVMC *m_inst; lldb_private::Mutex m_mutex; bool m_data_from_file; std::unique_ptr m_disasm_ap; std::unique_ptr m_alternate_disasm_ap; }; #endif // liblldb_DisassemblerLLVM_h_ Index: vendor/lldb/dist/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp (revision 287514) @@ -1,2918 +1,2931 @@ //===-- EmulateInstructionMIPS.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "EmulateInstructionMIPS.h" #include #include "llvm-c/Disassembler.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCContext.h" #include "lldb/Core/Address.h" #include "lldb/Core/Opcode.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/UnwindPlan.h" #include "llvm/ADT/STLExtras.h" #include "Plugins/Process/Utility/InstructionUtils.h" -#include "Plugins/Process/Utility/RegisterContext_mips64.h" //mips32 has same registers nos as mips64 +#include "Plugins/Process/Utility/RegisterContext_mips.h" //mips32 has same registers nos as mips64 using namespace lldb; using namespace lldb_private; #define UInt(x) ((uint64_t)x) #define integer int64_t //---------------------------------------------------------------------- // // EmulateInstructionMIPS implementation // //---------------------------------------------------------------------- #ifdef __mips__ extern "C" { void LLVMInitializeMipsTargetInfo (); void LLVMInitializeMipsTarget (); void LLVMInitializeMipsAsmPrinter (); void LLVMInitializeMipsTargetMC (); void LLVMInitializeMipsDisassembler (); } #endif EmulateInstructionMIPS::EmulateInstructionMIPS (const lldb_private::ArchSpec &arch) : EmulateInstruction (arch) { /* Create instance of llvm::MCDisassembler */ std::string Error; llvm::Triple triple = arch.GetTriple(); const llvm::Target *target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); /* * If we fail to get the target then we haven't registered it. The SystemInitializerCommon * does not initialize targets, MCs and disassemblers. However we need the MCDisassembler * to decode the instructions so that the decoding complexity stays with LLVM. * Initialize the MIPS targets and disassemblers. */ #ifdef __mips__ if (!target) { LLVMInitializeMipsTargetInfo (); LLVMInitializeMipsTarget (); LLVMInitializeMipsAsmPrinter (); LLVMInitializeMipsTargetMC (); LLVMInitializeMipsDisassembler (); target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); } #endif assert (target); llvm::StringRef cpu; switch (arch.GetCore()) { 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: cpu = "generic"; break; } + std::string features = ""; + uint32_t arch_flags = arch.GetFlags (); + if (arch_flags & ArchSpec::eMIPSAse_msa) + features += "+msa,"; + if (arch_flags & ArchSpec::eMIPSAse_dsp) + features += "+dsp,"; + if (arch_flags & ArchSpec::eMIPSAse_dspr2) + features += "+dspr2,"; + if (arch_flags & ArchSpec::eMIPSAse_mips16) + features += "+mips16,"; + if (arch_flags & ArchSpec::eMIPSAse_micromips) + features += "+micromips,"; + m_reg_info.reset (target->createMCRegInfo (triple.getTriple())); assert (m_reg_info.get()); m_insn_info.reset (target->createMCInstrInfo()); assert (m_insn_info.get()); m_asm_info.reset (target->createMCAsmInfo (*m_reg_info, triple.getTriple())); - m_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, "")); + m_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, features)); assert (m_asm_info.get() && m_subtype_info.get()); m_context.reset (new llvm::MCContext (m_asm_info.get(), m_reg_info.get(), nullptr)); assert (m_context.get()); m_disasm.reset (target->createMCDisassembler (*m_subtype_info, *m_context)); assert (m_disasm.get()); } void EmulateInstructionMIPS::Initialize () { PluginManager::RegisterPlugin (GetPluginNameStatic (), GetPluginDescriptionStatic (), CreateInstance); } void EmulateInstructionMIPS::Terminate () { PluginManager::UnregisterPlugin (CreateInstance); } ConstString EmulateInstructionMIPS::GetPluginNameStatic () { ConstString g_plugin_name ("lldb.emulate-instruction.mips32"); return g_plugin_name; } lldb_private::ConstString EmulateInstructionMIPS::GetPluginName() { static ConstString g_plugin_name ("EmulateInstructionMIPS"); return g_plugin_name; } const char * EmulateInstructionMIPS::GetPluginDescriptionStatic () { return "Emulate instructions for the MIPS32 architecture."; } EmulateInstruction * EmulateInstructionMIPS::CreateInstance (const ArchSpec &arch, InstructionType inst_type) { if (EmulateInstructionMIPS::SupportsEmulatingInstructionsOfTypeStatic(inst_type)) { if (arch.GetTriple().getArch() == llvm::Triple::mips || arch.GetTriple().getArch() == llvm::Triple::mipsel) { std::auto_ptr emulate_insn_ap (new EmulateInstructionMIPS (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } } return NULL; } bool EmulateInstructionMIPS::SetTargetTriple (const ArchSpec &arch) { if (arch.GetTriple().getArch () == llvm::Triple::mips || arch.GetTriple().getArch () == llvm::Triple::mipsel) return true; return false; } const char * EmulateInstructionMIPS::GetRegisterName (unsigned reg_num, bool alternate_name) { if (alternate_name) { switch (reg_num) { case gcc_dwarf_sp_mips: return "r29"; case gcc_dwarf_r30_mips: return "r30"; case gcc_dwarf_ra_mips: return "r31"; case gcc_dwarf_f0_mips: return "f0"; case gcc_dwarf_f1_mips: return "f1"; case gcc_dwarf_f2_mips: return "f2"; case gcc_dwarf_f3_mips: return "f3"; case gcc_dwarf_f4_mips: return "f4"; case gcc_dwarf_f5_mips: return "f5"; case gcc_dwarf_f6_mips: return "f6"; case gcc_dwarf_f7_mips: return "f7"; case gcc_dwarf_f8_mips: return "f8"; case gcc_dwarf_f9_mips: return "f9"; case gcc_dwarf_f10_mips: return "f10"; case gcc_dwarf_f11_mips: return "f11"; case gcc_dwarf_f12_mips: return "f12"; case gcc_dwarf_f13_mips: return "f13"; case gcc_dwarf_f14_mips: return "f14"; case gcc_dwarf_f15_mips: return "f15"; case gcc_dwarf_f16_mips: return "f16"; case gcc_dwarf_f17_mips: return "f17"; case gcc_dwarf_f18_mips: return "f18"; case gcc_dwarf_f19_mips: return "f19"; case gcc_dwarf_f20_mips: return "f20"; case gcc_dwarf_f21_mips: return "f21"; case gcc_dwarf_f22_mips: return "f22"; case gcc_dwarf_f23_mips: return "f23"; case gcc_dwarf_f24_mips: return "f24"; case gcc_dwarf_f25_mips: return "f25"; case gcc_dwarf_f26_mips: return "f26"; case gcc_dwarf_f27_mips: return "f27"; case gcc_dwarf_f28_mips: return "f28"; case gcc_dwarf_f29_mips: return "f29"; case gcc_dwarf_f30_mips: return "f30"; case gcc_dwarf_f31_mips: return "f31"; default: break; } return nullptr; } switch (reg_num) { case gcc_dwarf_zero_mips: return "r0"; case gcc_dwarf_r1_mips: return "r1"; case gcc_dwarf_r2_mips: return "r2"; case gcc_dwarf_r3_mips: return "r3"; case gcc_dwarf_r4_mips: return "r4"; case gcc_dwarf_r5_mips: return "r5"; case gcc_dwarf_r6_mips: return "r6"; case gcc_dwarf_r7_mips: return "r7"; case gcc_dwarf_r8_mips: return "r8"; case gcc_dwarf_r9_mips: return "r9"; case gcc_dwarf_r10_mips: return "r10"; case gcc_dwarf_r11_mips: return "r11"; case gcc_dwarf_r12_mips: return "r12"; case gcc_dwarf_r13_mips: return "r13"; case gcc_dwarf_r14_mips: return "r14"; case gcc_dwarf_r15_mips: return "r15"; case gcc_dwarf_r16_mips: return "r16"; case gcc_dwarf_r17_mips: return "r17"; case gcc_dwarf_r18_mips: return "r18"; case gcc_dwarf_r19_mips: return "r19"; case gcc_dwarf_r20_mips: return "r20"; case gcc_dwarf_r21_mips: return "r21"; case gcc_dwarf_r22_mips: return "r22"; case gcc_dwarf_r23_mips: return "r23"; case gcc_dwarf_r24_mips: return "r24"; case gcc_dwarf_r25_mips: return "r25"; case gcc_dwarf_r26_mips: return "r26"; case gcc_dwarf_r27_mips: return "r27"; case gcc_dwarf_gp_mips: return "gp"; case gcc_dwarf_sp_mips: return "sp"; case gcc_dwarf_r30_mips: return "fp"; case gcc_dwarf_ra_mips: return "ra"; case gcc_dwarf_sr_mips: return "sr"; case gcc_dwarf_lo_mips: return "lo"; case gcc_dwarf_hi_mips: return "hi"; case gcc_dwarf_bad_mips: return "bad"; case gcc_dwarf_cause_mips: return "cause"; case gcc_dwarf_pc_mips: return "pc"; - case gcc_dwarf_f0_mips: return "fp_reg[0]"; - case gcc_dwarf_f1_mips: return "fp_reg[1]"; - case gcc_dwarf_f2_mips: return "fp_reg[2]"; - case gcc_dwarf_f3_mips: return "fp_reg[3]"; - case gcc_dwarf_f4_mips: return "fp_reg[4]"; - case gcc_dwarf_f5_mips: return "fp_reg[5]"; - case gcc_dwarf_f6_mips: return "fp_reg[6]"; - case gcc_dwarf_f7_mips: return "fp_reg[7]"; - case gcc_dwarf_f8_mips: return "fp_reg[8]"; - case gcc_dwarf_f9_mips: return "fp_reg[9]"; - case gcc_dwarf_f10_mips: return "fp_reg[10]"; - case gcc_dwarf_f11_mips: return "fp_reg[11]"; - case gcc_dwarf_f12_mips: return "fp_reg[12]"; - case gcc_dwarf_f13_mips: return "fp_reg[13]"; - case gcc_dwarf_f14_mips: return "fp_reg[14]"; - case gcc_dwarf_f15_mips: return "fp_reg[15]"; - case gcc_dwarf_f16_mips: return "fp_reg[16]"; - case gcc_dwarf_f17_mips: return "fp_reg[17]"; - case gcc_dwarf_f18_mips: return "fp_reg[18]"; - case gcc_dwarf_f19_mips: return "fp_reg[19]"; - case gcc_dwarf_f20_mips: return "fp_reg[20]"; - case gcc_dwarf_f21_mips: return "fp_reg[21]"; - case gcc_dwarf_f22_mips: return "fp_reg[22]"; - case gcc_dwarf_f23_mips: return "fp_reg[23]"; - case gcc_dwarf_f24_mips: return "fp_reg[24]"; - case gcc_dwarf_f25_mips: return "fp_reg[25]"; - case gcc_dwarf_f26_mips: return "fp_reg[26]"; - case gcc_dwarf_f27_mips: return "fp_reg[27]"; - case gcc_dwarf_f28_mips: return "fp_reg[28]"; - case gcc_dwarf_f29_mips: return "fp_reg[29]"; - case gcc_dwarf_f30_mips: return "fp_reg[30]"; - case gcc_dwarf_f31_mips: return "fp_reg[31]"; + case gcc_dwarf_f0_mips: return "f0"; + case gcc_dwarf_f1_mips: return "f1"; + case gcc_dwarf_f2_mips: return "f2"; + case gcc_dwarf_f3_mips: return "f3"; + case gcc_dwarf_f4_mips: return "f4"; + case gcc_dwarf_f5_mips: return "f5"; + case gcc_dwarf_f6_mips: return "f6"; + case gcc_dwarf_f7_mips: return "f7"; + case gcc_dwarf_f8_mips: return "f8"; + case gcc_dwarf_f9_mips: return "f9"; + case gcc_dwarf_f10_mips: return "f10"; + case gcc_dwarf_f11_mips: return "f11"; + case gcc_dwarf_f12_mips: return "f12"; + case gcc_dwarf_f13_mips: return "f13"; + case gcc_dwarf_f14_mips: return "f14"; + case gcc_dwarf_f15_mips: return "f15"; + case gcc_dwarf_f16_mips: return "f16"; + case gcc_dwarf_f17_mips: return "f17"; + case gcc_dwarf_f18_mips: return "f18"; + case gcc_dwarf_f19_mips: return "f19"; + case gcc_dwarf_f20_mips: return "f20"; + case gcc_dwarf_f21_mips: return "f21"; + case gcc_dwarf_f22_mips: return "f22"; + case gcc_dwarf_f23_mips: return "f23"; + case gcc_dwarf_f24_mips: return "f24"; + case gcc_dwarf_f25_mips: return "f25"; + case gcc_dwarf_f26_mips: return "f26"; + case gcc_dwarf_f27_mips: return "f27"; + case gcc_dwarf_f28_mips: return "f28"; + case gcc_dwarf_f29_mips: return "f29"; + case gcc_dwarf_f30_mips: return "f30"; + case gcc_dwarf_f31_mips: return "f31"; case gcc_dwarf_fcsr_mips: return "fcsr"; case gcc_dwarf_fir_mips: return "fir"; } return nullptr; } bool EmulateInstructionMIPS::GetRegisterInfo (RegisterKind reg_kind, uint32_t reg_num, RegisterInfo ®_info) { if (reg_kind == eRegisterKindGeneric) { switch (reg_num) { case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_pc_mips; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sp_mips; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_r30_mips; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_ra_mips; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sr_mips; break; default: return false; } } if (reg_kind == eRegisterKindDWARF) { ::memset (®_info, 0, sizeof(RegisterInfo)); ::memset (reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); if (reg_num == gcc_dwarf_sr_mips || reg_num == gcc_dwarf_fcsr_mips || reg_num == gcc_dwarf_fir_mips) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if ((int)reg_num >= gcc_dwarf_zero_mips && (int)reg_num <= gcc_dwarf_f31_mips) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else { return false; } reg_info.name = GetRegisterName (reg_num, false); reg_info.alt_name = GetRegisterName (reg_num, true); reg_info.kinds[eRegisterKindDWARF] = reg_num; switch (reg_num) { case gcc_dwarf_r30_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; break; case gcc_dwarf_ra_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; break; case gcc_dwarf_sp_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; break; case gcc_dwarf_pc_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; break; case gcc_dwarf_sr_mips: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; break; default: break; } return true; } return false; } EmulateInstructionMIPS::MipsOpcode* EmulateInstructionMIPS::GetOpcodeForInstruction (const char *op_name) { static EmulateInstructionMIPS::MipsOpcode g_opcodes[] = { //---------------------------------------------------------------------- // Prologue/Epilogue instructions //---------------------------------------------------------------------- { "ADDiu", &EmulateInstructionMIPS::Emulate_ADDiu, "ADDIU rt,rs,immediate" }, { "SW", &EmulateInstructionMIPS::Emulate_SW, "SW rt,offset(rs)" }, { "LW", &EmulateInstructionMIPS::Emulate_LW, "LW rt,offset(base)" }, //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- { "BEQ", &EmulateInstructionMIPS::Emulate_BEQ, "BEQ rs,rt,offset" }, { "BNE", &EmulateInstructionMIPS::Emulate_BNE, "BNE rs,rt,offset" }, { "BEQL", &EmulateInstructionMIPS::Emulate_BEQL, "BEQL rs,rt,offset" }, { "BNEL", &EmulateInstructionMIPS::Emulate_BNEL, "BNEL rs,rt,offset" }, { "BGEZALL", &EmulateInstructionMIPS::Emulate_BGEZALL, "BGEZALL rt,offset" }, { "BAL", &EmulateInstructionMIPS::Emulate_BAL, "BAL offset" }, { "BGEZAL", &EmulateInstructionMIPS::Emulate_BGEZAL, "BGEZAL rs,offset" }, { "BALC", &EmulateInstructionMIPS::Emulate_BALC, "BALC offset" }, { "BC", &EmulateInstructionMIPS::Emulate_BC, "BC offset" }, { "BGEZ", &EmulateInstructionMIPS::Emulate_BGEZ, "BGEZ rs,offset" }, { "BLEZALC", &EmulateInstructionMIPS::Emulate_BLEZALC, "BLEZALC rs,offset" }, { "BGEZALC", &EmulateInstructionMIPS::Emulate_BGEZALC, "BGEZALC rs,offset" }, { "BLTZALC", &EmulateInstructionMIPS::Emulate_BLTZALC, "BLTZALC rs,offset" }, { "BGTZALC", &EmulateInstructionMIPS::Emulate_BGTZALC, "BGTZALC rs,offset" }, { "BEQZALC", &EmulateInstructionMIPS::Emulate_BEQZALC, "BEQZALC rs,offset" }, { "BNEZALC", &EmulateInstructionMIPS::Emulate_BNEZALC, "BNEZALC rs,offset" }, { "BEQC", &EmulateInstructionMIPS::Emulate_BEQC, "BEQC rs,rt,offset" }, { "BNEC", &EmulateInstructionMIPS::Emulate_BNEC, "BNEC rs,rt,offset" }, { "BLTC", &EmulateInstructionMIPS::Emulate_BLTC, "BLTC rs,rt,offset" }, { "BGEC", &EmulateInstructionMIPS::Emulate_BGEC, "BGEC rs,rt,offset" }, { "BLTUC", &EmulateInstructionMIPS::Emulate_BLTUC, "BLTUC rs,rt,offset" }, { "BGEUC", &EmulateInstructionMIPS::Emulate_BGEUC, "BGEUC rs,rt,offset" }, { "BLTZC", &EmulateInstructionMIPS::Emulate_BLTZC, "BLTZC rt,offset" }, { "BLEZC", &EmulateInstructionMIPS::Emulate_BLEZC, "BLEZC rt,offset" }, { "BGEZC", &EmulateInstructionMIPS::Emulate_BGEZC, "BGEZC rt,offset" }, { "BGTZC", &EmulateInstructionMIPS::Emulate_BGTZC, "BGTZC rt,offset" }, { "BEQZC", &EmulateInstructionMIPS::Emulate_BEQZC, "BEQZC rt,offset" }, { "BNEZC", &EmulateInstructionMIPS::Emulate_BNEZC, "BNEZC rt,offset" }, { "BGEZL", &EmulateInstructionMIPS::Emulate_BGEZL, "BGEZL rt,offset" }, { "BGTZ", &EmulateInstructionMIPS::Emulate_BGTZ, "BGTZ rt,offset" }, { "BGTZL", &EmulateInstructionMIPS::Emulate_BGTZL, "BGTZL rt,offset" }, { "BLEZ", &EmulateInstructionMIPS::Emulate_BLEZ, "BLEZ rt,offset" }, { "BLEZL", &EmulateInstructionMIPS::Emulate_BLEZL, "BLEZL rt,offset" }, { "BLTZ", &EmulateInstructionMIPS::Emulate_BLTZ, "BLTZ rt,offset" }, { "BLTZAL", &EmulateInstructionMIPS::Emulate_BLTZAL, "BLTZAL rt,offset" }, { "BLTZALL", &EmulateInstructionMIPS::Emulate_BLTZALL, "BLTZALL rt,offset" }, { "BLTZL", &EmulateInstructionMIPS::Emulate_BLTZL, "BLTZL rt,offset" }, { "BOVC", &EmulateInstructionMIPS::Emulate_BOVC, "BOVC rs,rt,offset" }, { "BNVC", &EmulateInstructionMIPS::Emulate_BNVC, "BNVC rs,rt,offset" }, { "J", &EmulateInstructionMIPS::Emulate_J, "J target" }, { "JAL", &EmulateInstructionMIPS::Emulate_JAL, "JAL target" }, { "JALX", &EmulateInstructionMIPS::Emulate_JAL, "JALX target" }, { "JALR", &EmulateInstructionMIPS::Emulate_JALR, "JALR target" }, { "JALR_HB", &EmulateInstructionMIPS::Emulate_JALR, "JALR.HB target" }, { "JIALC", &EmulateInstructionMIPS::Emulate_JIALC, "JIALC rt,offset" }, { "JIC", &EmulateInstructionMIPS::Emulate_JIC, "JIC rt,offset" }, { "JR", &EmulateInstructionMIPS::Emulate_JR, "JR target" }, { "JR_HB", &EmulateInstructionMIPS::Emulate_JR, "JR.HB target" }, { "BC1F", &EmulateInstructionMIPS::Emulate_BC1F, "BC1F cc, offset" }, { "BC1T", &EmulateInstructionMIPS::Emulate_BC1T, "BC1T cc, offset" }, { "BC1FL", &EmulateInstructionMIPS::Emulate_BC1FL, "BC1FL cc, offset" }, { "BC1TL", &EmulateInstructionMIPS::Emulate_BC1TL, "BC1TL cc, offset" }, { "BC1EQZ", &EmulateInstructionMIPS::Emulate_BC1EQZ, "BC1EQZ ft, offset" }, { "BC1NEZ", &EmulateInstructionMIPS::Emulate_BC1NEZ, "BC1NEZ ft, offset" }, { "BC1ANY2F", &EmulateInstructionMIPS::Emulate_BC1ANY2F, "BC1ANY2F cc, offset" }, { "BC1ANY2T", &EmulateInstructionMIPS::Emulate_BC1ANY2T, "BC1ANY2T cc, offset" }, { "BC1ANY4F", &EmulateInstructionMIPS::Emulate_BC1ANY4F, "BC1ANY4F cc, offset" }, { "BC1ANY4T", &EmulateInstructionMIPS::Emulate_BC1ANY4T, "BC1ANY4T cc, offset" }, }; static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes); for (size_t i = 0; i < k_num_mips_opcodes; ++i) { if (! strcasecmp (g_opcodes[i].op_name, op_name)) return &g_opcodes[i]; } return NULL; } bool EmulateInstructionMIPS::ReadInstruction () { bool success = false; m_addr = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_ADDRESS, &success); if (success) { Context read_inst_context; read_inst_context.type = eContextReadOpcode; read_inst_context.SetNoArgs (); m_opcode.SetOpcode32 (ReadMemoryUnsigned (read_inst_context, m_addr, 4, 0, &success), GetByteOrder()); } if (!success) m_addr = LLDB_INVALID_ADDRESS; return success; } bool EmulateInstructionMIPS::EvaluateInstruction (uint32_t evaluate_options) { bool success = false; llvm::MCInst mc_insn; uint64_t insn_size; DataExtractor data; /* Keep the complexity of the decode logic with the llvm::MCDisassembler class. */ if (m_opcode.GetData (data)) { llvm::MCDisassembler::DecodeStatus decode_status; llvm::ArrayRef raw_insn (data.GetDataStart(), data.GetByteSize()); decode_status = m_disasm->getInstruction (mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); if (decode_status != llvm::MCDisassembler::Success) return false; } /* * mc_insn.getOpcode() returns decoded opcode. However to make use * of llvm::Mips:: we would need "MipsGenInstrInfo.inc". */ const char *op_name = m_insn_info->getName (mc_insn.getOpcode ()); if (op_name == NULL) return false; /* * Decoding has been done already. Just get the call-back function * and emulate the instruction. */ MipsOpcode *opcode_data = GetOpcodeForInstruction (op_name); if (opcode_data == NULL) return false; uint64_t old_pc = 0, new_pc = 0; const bool auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; if (auto_advance_pc) { old_pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; } /* emulate instruction */ success = (this->*opcode_data->callback) (mc_insn); if (!success) return false; if (auto_advance_pc) { new_pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; /* If we haven't changed the PC, change it here */ if (old_pc == new_pc) { new_pc += 4; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, new_pc)) return false; } } return true; } bool EmulateInstructionMIPS::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); const bool can_replace = false; // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(gcc_dwarf_sp_mips, 0); // Our previous PC is in the RA row->SetRegisterLocationToRegister(gcc_dwarf_pc_mips, gcc_dwarf_ra_mips, can_replace); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("EmulateInstructionMIPS"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); return true; } bool EmulateInstructionMIPS::nonvolatile_reg_p (uint32_t regnum) { switch (regnum) { case gcc_dwarf_r16_mips: case gcc_dwarf_r17_mips: case gcc_dwarf_r18_mips: case gcc_dwarf_r19_mips: case gcc_dwarf_r20_mips: case gcc_dwarf_r21_mips: case gcc_dwarf_r22_mips: case gcc_dwarf_r23_mips: case gcc_dwarf_gp_mips: case gcc_dwarf_sp_mips: case gcc_dwarf_r30_mips: case gcc_dwarf_ra_mips: return true; default: return false; } return false; } bool EmulateInstructionMIPS::Emulate_ADDiu (llvm::MCInst& insn) { bool success = false; const uint32_t imm16 = insn.getOperand(2).getImm(); uint32_t imm = SignedBits(imm16, 15, 0); uint64_t result; uint32_t src, dst; dst = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); src = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* Check if this is addiu sp,,imm16 */ if (dst == gcc_dwarf_sp_mips) { /* read register */ uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + src, 0, &success); if (!success) return false; result = src_opd_val + imm; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_sp_mips, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm); /* We are allocating bytes on stack */ context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_sp_mips, result); } return true; } bool EmulateInstructionMIPS::Emulate_SW (llvm::MCInst& insn) { bool success = false; uint32_t imm16 = insn.getOperand(2).getImm(); uint32_t imm = SignedBits(imm16, 15, 0); uint32_t src, base; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* We look for sp based non-volatile register stores */ if (base == gcc_dwarf_sp_mips && nonvolatile_reg_p (src)) { uint32_t address; RegisterInfo reg_info_base; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips + base, reg_info_base) || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips + src, reg_info_src)) return false; /* read SP */ address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + base, 0, &success); if (!success) return false; /* destination address */ address = address + imm; Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error error; if (!ReadRegister (®_info_base, data_src)) return false; if (data_src.GetAsMemoryData (®_info_src, buffer, reg_info_src.byte_size, eByteOrderLittle, error) == 0) return false; if (!WriteMemory (context, address, buffer, reg_info_src.byte_size)) return false; return true; } return false; } bool EmulateInstructionMIPS::Emulate_LW (llvm::MCInst& insn) { uint32_t src, base; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); if (base == gcc_dwarf_sp_mips && nonvolatile_reg_p (src)) { RegisterValue data_src; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips + src, reg_info_src)) return false; Context context; context.type = eContextRegisterLoad; if (!WriteRegister (context, ®_info_src, data_src)) return false; return true; } return false; } bool EmulateInstructionMIPS::Emulate_BEQ (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BEQ rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNE (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BNE rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BEQL (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BEQL rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNEL (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BNEL rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGEZL rs, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZL rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGTZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGTZL rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLEZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLEZL rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGTZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGTZ rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLEZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLEZ rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZ rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZALL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGEZALL rt, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BAL (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BAL offset * offset = sign_ext (offset << 2) * RA = PC + 8 * PC = PC + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BALC (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BALC offset * offset = sign_ext (offset << 2) * RA = PC + 4 * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZAL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGEZAL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if ((int32_t) rs_val >= 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZAL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZAL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if ((int32_t) rs_val < 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZALL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZALL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val < 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] <= 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val <= 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val >= 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val < 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGTZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGTZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] > 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val > 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BEQZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target, rs_val; /* * BEQZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] == 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val == 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target, rs_val; /* * BNEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] != 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val != 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target, rs_val; /* * BGEZ rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (rs_val >= 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC (llvm::MCInst& insn) { bool success = false; int32_t offset, pc, target; /* * BC offset * offset = sign_ext (offset << 2) * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BEQC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BEQC rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNEC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target, rs_val, rt_val; /* * BNEC rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; int32_t rs_val, rt_val; /* * BLTC rs, rt, offset * condition <- (GPR[rs] < GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val < rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; int32_t rs_val, rt_val; /* * BGEC rs, rt, offset * condition <- (GPR[rs] > GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val > rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTUC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; uint32_t rs_val, rt_val; /* * BLTUC rs, rt, offset * condition <- (GPR[rs] < GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val < rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEUC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; uint32_t rs_val, rt_val; /* * BGEUC rs, rt, offset * condition <- (GPR[rs] > GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (rs_val > rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLTZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLTZC rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BLEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BLEZC rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGEZC rs, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BGTZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; int32_t rs_val; /* * BGTZC rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BEQZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; uint32_t rs_val; /* * BEQZC rs, offset * condition <- (GPR[rs] = 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val == 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int32_t offset, pc, target; uint32_t rs_val; /* * BNEZC rs, offset * condition <- (GPR[rs] != 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; if (rs_val != 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } static int IsAdd64bitOverflow (int32_t a, int32_t b) { int32_t r = (uint32_t) a + (uint32_t) b; return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); } bool EmulateInstructionMIPS::Emulate_BOVC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; int32_t rs_val, rt_val; /* * BOVC rs, rt, offset * condition <- overflow(GPR[rs] + GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (IsAdd64bitOverflow (rs_val, rt_val)) target = pc + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BNVC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int32_t offset, pc, target; int32_t rs_val, rt_val; /* * BNVC rs, rt, offset * condition <- overflow(GPR[rs] + GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; if (! IsAdd64bitOverflow (rs_val, rt_val)) target = pc + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_J (llvm::MCInst& insn) { bool success = false; uint32_t offset, pc; /* * J offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ pc = (pc & 0xF0000000UL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, pc)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JAL (llvm::MCInst& insn) { bool success = false; uint32_t offset, target, pc; /* * JAL offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ target = (pc & 0xF0000000UL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JALR (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; uint32_t pc, rs_val; /* * JALR rt, rs * GPR[rt] = PC + 8 * PC = GPR[rs] */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, rs_val)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, pc + 8)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JIALC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int32_t target, offset, pc, rt_val; /* * JIALC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset * RA = PC + 4 */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips, pc + 4)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JIC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int32_t target, offset, rt_val; /* * JIC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); rt_val = (int32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_JR (llvm::MCInst& insn) { bool success = false; uint32_t rs; uint32_t rs_val; /* * JR rs * PC = GPR[rs] */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, rs_val)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1F cc, offset * condition <- (FPConditionCode(cc) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) == 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1T cc, offset * condition <- (FPConditionCode(cc) != 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1FL (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1F cc, offset * condition <- (FPConditionCode(cc) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) == 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1TL (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1T cc, offset * condition <- (FPConditionCode(cc) != 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) != 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1EQZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint32_t ft_val; int32_t target, pc, offset; /* * BC1EQZ ft, offset * condition <- (FPR[ft].bit0 == 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + ft, 0, &success); if (!success) return false; if ((ft_val & 1) == 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1NEZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint32_t ft_val; int32_t target, pc, offset; /* * BC1NEZ ft, offset * condition <- (FPR[ft].bit0 != 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips + ft, 0, &success); if (!success) return false; if ((ft_val & 1) != 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1ANY2F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1ANY2F cc, offset * condition <- (FPConditionCode(cc) == 0 * || FPConditionCode(cc+1) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 0 */ if (((fcsr >> cc) & 3) != 3) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1ANY2T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1ANY2T cc, offset * condition <- (FPConditionCode(cc) == 1 * || FPConditionCode(cc+1) == 1) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 1 */ if (((fcsr >> cc) & 3) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1ANY4F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1ANY4F cc, offset * condition <- (FPConditionCode(cc) == 0 * || FPConditionCode(cc+1) == 0) * || FPConditionCode(cc+2) == 0) * || FPConditionCode(cc+3) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 0 */ if (((fcsr >> cc) & 0xf) != 0xf) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } bool EmulateInstructionMIPS::Emulate_BC1ANY4T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int32_t target, pc, offset; /* * BC1ANY4T cc, offset * condition <- (FPConditionCode(cc) == 1 * || FPConditionCode(cc+1) == 1) * || FPConditionCode(cc+2) == 1) * || FPConditionCode(cc+3) == 1) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 1 */ if (((fcsr >> cc) & 0xf) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips, target)) return false; return true; } Index: vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp (revision 287514) @@ -1,2920 +1,2998 @@ //===-- EmulateInstructionMIPS64.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "EmulateInstructionMIPS64.h" #include #include "llvm-c/Disassembler.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCContext.h" #include "lldb/Core/Address.h" #include "lldb/Core/Opcode.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/UnwindPlan.h" #include "llvm/ADT/STLExtras.h" #include "Plugins/Process/Utility/InstructionUtils.h" -#include "Plugins/Process/Utility/RegisterContext_mips64.h" +#include "Plugins/Process/Utility/RegisterContext_mips.h" using namespace lldb; using namespace lldb_private; #define UInt(x) ((uint64_t)x) #define integer int64_t //---------------------------------------------------------------------- // // EmulateInstructionMIPS64 implementation // //---------------------------------------------------------------------- #ifdef __mips__ extern "C" { void LLVMInitializeMipsTargetInfo (); void LLVMInitializeMipsTarget (); void LLVMInitializeMipsAsmPrinter (); void LLVMInitializeMipsTargetMC (); void LLVMInitializeMipsDisassembler (); } #endif EmulateInstructionMIPS64::EmulateInstructionMIPS64 (const lldb_private::ArchSpec &arch) : EmulateInstruction (arch) { /* Create instance of llvm::MCDisassembler */ std::string Error; llvm::Triple triple = arch.GetTriple(); const llvm::Target *target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); /* * If we fail to get the target then we haven't registered it. The SystemInitializerCommon * does not initialize targets, MCs and disassemblers. However we need the MCDisassembler * to decode the instructions so that the decoding complexity stays with LLVM. * Initialize the MIPS targets and disassemblers. */ #ifdef __mips__ if (!target) { LLVMInitializeMipsTargetInfo (); LLVMInitializeMipsTarget (); LLVMInitializeMipsAsmPrinter (); LLVMInitializeMipsTargetMC (); LLVMInitializeMipsDisassembler (); target = llvm::TargetRegistry::lookupTarget (triple.getTriple(), Error); } #endif assert (target); llvm::StringRef cpu; switch (arch.GetCore()) { 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: cpu = "generic"; break; } + std::string features = ""; + uint32_t arch_flags = arch.GetFlags (); + if (arch_flags & ArchSpec::eMIPSAse_msa) + features += "+msa,"; + if (arch_flags & ArchSpec::eMIPSAse_dsp) + features += "+dsp,"; + if (arch_flags & ArchSpec::eMIPSAse_dspr2) + features += "+dspr2,"; + if (arch_flags & ArchSpec::eMIPSAse_mips16) + features += "+mips16,"; + if (arch_flags & ArchSpec::eMIPSAse_micromips) + features += "+micromips,"; + m_reg_info.reset (target->createMCRegInfo (triple.getTriple())); assert (m_reg_info.get()); m_insn_info.reset (target->createMCInstrInfo()); assert (m_insn_info.get()); m_asm_info.reset (target->createMCAsmInfo (*m_reg_info, triple.getTriple())); - m_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, "")); + m_subtype_info.reset (target->createMCSubtargetInfo (triple.getTriple(), cpu, features)); assert (m_asm_info.get() && m_subtype_info.get()); m_context.reset (new llvm::MCContext (m_asm_info.get(), m_reg_info.get(), nullptr)); assert (m_context.get()); m_disasm.reset (target->createMCDisassembler (*m_subtype_info, *m_context)); assert (m_disasm.get()); } void EmulateInstructionMIPS64::Initialize () { PluginManager::RegisterPlugin (GetPluginNameStatic (), GetPluginDescriptionStatic (), CreateInstance); } void EmulateInstructionMIPS64::Terminate () { PluginManager::UnregisterPlugin (CreateInstance); } ConstString EmulateInstructionMIPS64::GetPluginNameStatic () { ConstString g_plugin_name ("lldb.emulate-instruction.mips64"); return g_plugin_name; } lldb_private::ConstString EmulateInstructionMIPS64::GetPluginName() { static ConstString g_plugin_name ("EmulateInstructionMIPS64"); return g_plugin_name; } const char * EmulateInstructionMIPS64::GetPluginDescriptionStatic () { return "Emulate instructions for the MIPS64 architecture."; } EmulateInstruction * EmulateInstructionMIPS64::CreateInstance (const ArchSpec &arch, InstructionType inst_type) { if (EmulateInstructionMIPS64::SupportsEmulatingInstructionsOfTypeStatic(inst_type)) { if (arch.GetTriple().getArch() == llvm::Triple::mips64 || arch.GetTriple().getArch() == llvm::Triple::mips64el) { std::auto_ptr emulate_insn_ap (new EmulateInstructionMIPS64 (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } } return NULL; } bool EmulateInstructionMIPS64::SetTargetTriple (const ArchSpec &arch) { if (arch.GetTriple().getArch () == llvm::Triple::mips64 || arch.GetTriple().getArch () == llvm::Triple::mips64el) return true; return false; } const char * EmulateInstructionMIPS64::GetRegisterName (unsigned reg_num, bool alternate_name) { if (alternate_name) { switch (reg_num) { case gcc_dwarf_sp_mips64: return "r29"; case gcc_dwarf_r30_mips64: return "r30"; case gcc_dwarf_ra_mips64: return "r31"; case gcc_dwarf_f0_mips64: return "f0"; case gcc_dwarf_f1_mips64: return "f1"; case gcc_dwarf_f2_mips64: return "f2"; case gcc_dwarf_f3_mips64: return "f3"; case gcc_dwarf_f4_mips64: return "f4"; case gcc_dwarf_f5_mips64: return "f5"; case gcc_dwarf_f6_mips64: return "f6"; case gcc_dwarf_f7_mips64: return "f7"; case gcc_dwarf_f8_mips64: return "f8"; case gcc_dwarf_f9_mips64: return "f9"; case gcc_dwarf_f10_mips64: return "f10"; case gcc_dwarf_f11_mips64: return "f11"; case gcc_dwarf_f12_mips64: return "f12"; case gcc_dwarf_f13_mips64: return "f13"; case gcc_dwarf_f14_mips64: return "f14"; case gcc_dwarf_f15_mips64: return "f15"; case gcc_dwarf_f16_mips64: return "f16"; case gcc_dwarf_f17_mips64: return "f17"; case gcc_dwarf_f18_mips64: return "f18"; case gcc_dwarf_f19_mips64: return "f19"; case gcc_dwarf_f20_mips64: return "f20"; case gcc_dwarf_f21_mips64: return "f21"; case gcc_dwarf_f22_mips64: return "f22"; case gcc_dwarf_f23_mips64: return "f23"; case gcc_dwarf_f24_mips64: return "f24"; case gcc_dwarf_f25_mips64: return "f25"; case gcc_dwarf_f26_mips64: return "f26"; case gcc_dwarf_f27_mips64: return "f27"; case gcc_dwarf_f28_mips64: return "f28"; case gcc_dwarf_f29_mips64: return "f29"; case gcc_dwarf_f30_mips64: return "f30"; case gcc_dwarf_f31_mips64: return "f31"; default: break; } return nullptr; } switch (reg_num) { case gcc_dwarf_zero_mips64: return "r0"; case gcc_dwarf_r1_mips64: return "r1"; case gcc_dwarf_r2_mips64: return "r2"; case gcc_dwarf_r3_mips64: return "r3"; case gcc_dwarf_r4_mips64: return "r4"; case gcc_dwarf_r5_mips64: return "r5"; case gcc_dwarf_r6_mips64: return "r6"; case gcc_dwarf_r7_mips64: return "r7"; case gcc_dwarf_r8_mips64: return "r8"; case gcc_dwarf_r9_mips64: return "r9"; case gcc_dwarf_r10_mips64: return "r10"; case gcc_dwarf_r11_mips64: return "r11"; case gcc_dwarf_r12_mips64: return "r12"; case gcc_dwarf_r13_mips64: return "r13"; case gcc_dwarf_r14_mips64: return "r14"; case gcc_dwarf_r15_mips64: return "r15"; case gcc_dwarf_r16_mips64: return "r16"; case gcc_dwarf_r17_mips64: return "r17"; case gcc_dwarf_r18_mips64: return "r18"; case gcc_dwarf_r19_mips64: return "r19"; case gcc_dwarf_r20_mips64: return "r20"; case gcc_dwarf_r21_mips64: return "r21"; case gcc_dwarf_r22_mips64: return "r22"; case gcc_dwarf_r23_mips64: return "r23"; case gcc_dwarf_r24_mips64: return "r24"; case gcc_dwarf_r25_mips64: return "r25"; case gcc_dwarf_r26_mips64: return "r26"; case gcc_dwarf_r27_mips64: return "r27"; case gcc_dwarf_gp_mips64: return "gp"; case gcc_dwarf_sp_mips64: return "sp"; case gcc_dwarf_r30_mips64: return "fp"; case gcc_dwarf_ra_mips64: return "ra"; case gcc_dwarf_sr_mips64: return "sr"; case gcc_dwarf_lo_mips64: return "lo"; case gcc_dwarf_hi_mips64: return "hi"; case gcc_dwarf_bad_mips64: return "bad"; case gcc_dwarf_cause_mips64: return "cause"; case gcc_dwarf_pc_mips64: return "pc"; - case gcc_dwarf_f0_mips64: return "fp_reg[0]"; - case gcc_dwarf_f1_mips64: return "fp_reg[1]"; - case gcc_dwarf_f2_mips64: return "fp_reg[2]"; - case gcc_dwarf_f3_mips64: return "fp_reg[3]"; - case gcc_dwarf_f4_mips64: return "fp_reg[4]"; - case gcc_dwarf_f5_mips64: return "fp_reg[5]"; - case gcc_dwarf_f6_mips64: return "fp_reg[6]"; - case gcc_dwarf_f7_mips64: return "fp_reg[7]"; - case gcc_dwarf_f8_mips64: return "fp_reg[8]"; - case gcc_dwarf_f9_mips64: return "fp_reg[9]"; - case gcc_dwarf_f10_mips64: return "fp_reg[10]"; - case gcc_dwarf_f11_mips64: return "fp_reg[11]"; - case gcc_dwarf_f12_mips64: return "fp_reg[12]"; - case gcc_dwarf_f13_mips64: return "fp_reg[13]"; - case gcc_dwarf_f14_mips64: return "fp_reg[14]"; - case gcc_dwarf_f15_mips64: return "fp_reg[15]"; - case gcc_dwarf_f16_mips64: return "fp_reg[16]"; - case gcc_dwarf_f17_mips64: return "fp_reg[17]"; - case gcc_dwarf_f18_mips64: return "fp_reg[18]"; - case gcc_dwarf_f19_mips64: return "fp_reg[19]"; - case gcc_dwarf_f20_mips64: return "fp_reg[20]"; - case gcc_dwarf_f21_mips64: return "fp_reg[21]"; - case gcc_dwarf_f22_mips64: return "fp_reg[22]"; - case gcc_dwarf_f23_mips64: return "fp_reg[23]"; - case gcc_dwarf_f24_mips64: return "fp_reg[24]"; - case gcc_dwarf_f25_mips64: return "fp_reg[25]"; - case gcc_dwarf_f26_mips64: return "fp_reg[26]"; - case gcc_dwarf_f27_mips64: return "fp_reg[27]"; - case gcc_dwarf_f28_mips64: return "fp_reg[28]"; - case gcc_dwarf_f29_mips64: return "fp_reg[29]"; - case gcc_dwarf_f30_mips64: return "fp_reg[30]"; - case gcc_dwarf_f31_mips64: return "fp_reg[31]"; + case gcc_dwarf_f0_mips64: return "f0"; + case gcc_dwarf_f1_mips64: return "f1"; + case gcc_dwarf_f2_mips64: return "f2"; + case gcc_dwarf_f3_mips64: return "f3"; + case gcc_dwarf_f4_mips64: return "f4"; + case gcc_dwarf_f5_mips64: return "f5"; + case gcc_dwarf_f6_mips64: return "f6"; + case gcc_dwarf_f7_mips64: return "f7"; + case gcc_dwarf_f8_mips64: return "f8"; + case gcc_dwarf_f9_mips64: return "f9"; + case gcc_dwarf_f10_mips64: return "f10"; + case gcc_dwarf_f11_mips64: return "f11"; + case gcc_dwarf_f12_mips64: return "f12"; + case gcc_dwarf_f13_mips64: return "f13"; + case gcc_dwarf_f14_mips64: return "f14"; + case gcc_dwarf_f15_mips64: return "f15"; + case gcc_dwarf_f16_mips64: return "f16"; + case gcc_dwarf_f17_mips64: return "f17"; + case gcc_dwarf_f18_mips64: return "f18"; + case gcc_dwarf_f19_mips64: return "f19"; + case gcc_dwarf_f20_mips64: return "f20"; + case gcc_dwarf_f21_mips64: return "f21"; + case gcc_dwarf_f22_mips64: return "f22"; + case gcc_dwarf_f23_mips64: return "f23"; + case gcc_dwarf_f24_mips64: return "f24"; + case gcc_dwarf_f25_mips64: return "f25"; + case gcc_dwarf_f26_mips64: return "f26"; + case gcc_dwarf_f27_mips64: return "f27"; + case gcc_dwarf_f28_mips64: return "f28"; + case gcc_dwarf_f29_mips64: return "f29"; + case gcc_dwarf_f30_mips64: return "f30"; + case gcc_dwarf_f31_mips64: return "f31"; case gcc_dwarf_fcsr_mips64: return "fcsr"; case gcc_dwarf_fir_mips64: return "fir"; } return nullptr; } bool EmulateInstructionMIPS64::GetRegisterInfo (RegisterKind reg_kind, uint32_t reg_num, RegisterInfo ®_info) { if (reg_kind == eRegisterKindGeneric) { switch (reg_num) { case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_pc_mips64; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sp_mips64; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_r30_mips64; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_ra_mips64; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = gcc_dwarf_sr_mips64; break; default: return false; } } if (reg_kind == eRegisterKindDWARF) { ::memset (®_info, 0, sizeof(RegisterInfo)); ::memset (reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); if (reg_num == gcc_dwarf_sr_mips64 || reg_num == gcc_dwarf_fcsr_mips64 || reg_num == gcc_dwarf_fir_mips64) { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if ((int)reg_num >= gcc_dwarf_zero_mips64 && (int)reg_num <= gcc_dwarf_f31_mips64) { reg_info.byte_size = 8; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else { return false; } reg_info.name = GetRegisterName (reg_num, false); reg_info.alt_name = GetRegisterName (reg_num, true); reg_info.kinds[eRegisterKindDWARF] = reg_num; switch (reg_num) { case gcc_dwarf_r30_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; break; case gcc_dwarf_ra_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; break; case gcc_dwarf_sp_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; break; case gcc_dwarf_pc_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; break; case gcc_dwarf_sr_mips64: reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; break; default: break; } return true; } return false; } EmulateInstructionMIPS64::MipsOpcode* EmulateInstructionMIPS64::GetOpcodeForInstruction (const char *op_name) { static EmulateInstructionMIPS64::MipsOpcode g_opcodes[] = { //---------------------------------------------------------------------- // Prologue/Epilogue instructions //---------------------------------------------------------------------- { "DADDiu", &EmulateInstructionMIPS64::Emulate_DADDiu, "DADDIU rt,rs,immediate" }, { "SD", &EmulateInstructionMIPS64::Emulate_SD, "SD rt,offset(rs)" }, { "LD", &EmulateInstructionMIPS64::Emulate_LD, "LD rt,offset(base)" }, + { "SW", &EmulateInstructionMIPS64::Emulate_SW, "SW rt,offset(rs)" }, + { "LW", &EmulateInstructionMIPS64::Emulate_LW, "LW rt,offset(rs)" }, + //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- { "BEQ", &EmulateInstructionMIPS64::Emulate_BEQ, "BEQ rs,rt,offset" }, { "BNE", &EmulateInstructionMIPS64::Emulate_BNE, "BNE rs,rt,offset" }, { "BEQL", &EmulateInstructionMIPS64::Emulate_BEQL, "BEQL rs,rt,offset" }, { "BNEL", &EmulateInstructionMIPS64::Emulate_BNEL, "BNEL rs,rt,offset" }, { "BGEZALL", &EmulateInstructionMIPS64::Emulate_BGEZALL, "BGEZALL rt,offset" }, { "BAL", &EmulateInstructionMIPS64::Emulate_BAL, "BAL offset" }, { "BGEZAL", &EmulateInstructionMIPS64::Emulate_BGEZAL, "BGEZAL rs,offset" }, { "BALC", &EmulateInstructionMIPS64::Emulate_BALC, "BALC offset" }, { "BC", &EmulateInstructionMIPS64::Emulate_BC, "BC offset" }, { "BGEZ", &EmulateInstructionMIPS64::Emulate_BGEZ, "BGEZ rs,offset" }, { "BLEZALC", &EmulateInstructionMIPS64::Emulate_BLEZALC, "BLEZALC rs,offset" }, { "BGEZALC", &EmulateInstructionMIPS64::Emulate_BGEZALC, "BGEZALC rs,offset" }, { "BLTZALC", &EmulateInstructionMIPS64::Emulate_BLTZALC, "BLTZALC rs,offset" }, { "BGTZALC", &EmulateInstructionMIPS64::Emulate_BGTZALC, "BGTZALC rs,offset" }, { "BEQZALC", &EmulateInstructionMIPS64::Emulate_BEQZALC, "BEQZALC rs,offset" }, { "BNEZALC", &EmulateInstructionMIPS64::Emulate_BNEZALC, "BNEZALC rs,offset" }, { "BEQC", &EmulateInstructionMIPS64::Emulate_BEQC, "BEQC rs,rt,offset" }, { "BNEC", &EmulateInstructionMIPS64::Emulate_BNEC, "BNEC rs,rt,offset" }, { "BLTC", &EmulateInstructionMIPS64::Emulate_BLTC, "BLTC rs,rt,offset" }, { "BGEC", &EmulateInstructionMIPS64::Emulate_BGEC, "BGEC rs,rt,offset" }, { "BLTUC", &EmulateInstructionMIPS64::Emulate_BLTUC, "BLTUC rs,rt,offset" }, { "BGEUC", &EmulateInstructionMIPS64::Emulate_BGEUC, "BGEUC rs,rt,offset" }, { "BLTZC", &EmulateInstructionMIPS64::Emulate_BLTZC, "BLTZC rt,offset" }, { "BLEZC", &EmulateInstructionMIPS64::Emulate_BLEZC, "BLEZC rt,offset" }, { "BGEZC", &EmulateInstructionMIPS64::Emulate_BGEZC, "BGEZC rt,offset" }, { "BGTZC", &EmulateInstructionMIPS64::Emulate_BGTZC, "BGTZC rt,offset" }, { "BEQZC", &EmulateInstructionMIPS64::Emulate_BEQZC, "BEQZC rt,offset" }, { "BNEZC", &EmulateInstructionMIPS64::Emulate_BNEZC, "BNEZC rt,offset" }, { "BGEZL", &EmulateInstructionMIPS64::Emulate_BGEZL, "BGEZL rt,offset" }, { "BGTZ", &EmulateInstructionMIPS64::Emulate_BGTZ, "BGTZ rt,offset" }, { "BGTZL", &EmulateInstructionMIPS64::Emulate_BGTZL, "BGTZL rt,offset" }, { "BLEZ", &EmulateInstructionMIPS64::Emulate_BLEZ, "BLEZ rt,offset" }, { "BLEZL", &EmulateInstructionMIPS64::Emulate_BLEZL, "BLEZL rt,offset" }, { "BLTZ", &EmulateInstructionMIPS64::Emulate_BLTZ, "BLTZ rt,offset" }, { "BLTZAL", &EmulateInstructionMIPS64::Emulate_BLTZAL, "BLTZAL rt,offset" }, { "BLTZALL", &EmulateInstructionMIPS64::Emulate_BLTZALL, "BLTZALL rt,offset" }, { "BLTZL", &EmulateInstructionMIPS64::Emulate_BLTZL, "BLTZL rt,offset" }, { "BOVC", &EmulateInstructionMIPS64::Emulate_BOVC, "BOVC rs,rt,offset" }, { "BNVC", &EmulateInstructionMIPS64::Emulate_BNVC, "BNVC rs,rt,offset" }, { "J", &EmulateInstructionMIPS64::Emulate_J, "J target" }, { "JAL", &EmulateInstructionMIPS64::Emulate_JAL, "JAL target" }, { "JALX", &EmulateInstructionMIPS64::Emulate_JAL, "JALX target" }, { "JALR", &EmulateInstructionMIPS64::Emulate_JALR, "JALR target" }, { "JALR_HB", &EmulateInstructionMIPS64::Emulate_JALR, "JALR.HB target" }, { "JIALC", &EmulateInstructionMIPS64::Emulate_JIALC, "JIALC rt,offset" }, { "JIC", &EmulateInstructionMIPS64::Emulate_JIC, "JIC rt,offset" }, { "JR", &EmulateInstructionMIPS64::Emulate_JR, "JR target" }, { "JR_HB", &EmulateInstructionMIPS64::Emulate_JR, "JR.HB target" }, { "BC1F", &EmulateInstructionMIPS64::Emulate_BC1F, "BC1F cc, offset" }, { "BC1T", &EmulateInstructionMIPS64::Emulate_BC1T, "BC1T cc, offset" }, { "BC1FL", &EmulateInstructionMIPS64::Emulate_BC1FL, "BC1FL cc, offset" }, { "BC1TL", &EmulateInstructionMIPS64::Emulate_BC1TL, "BC1TL cc, offset" }, { "BC1EQZ", &EmulateInstructionMIPS64::Emulate_BC1EQZ, "BC1EQZ ft, offset" }, { "BC1NEZ", &EmulateInstructionMIPS64::Emulate_BC1NEZ, "BC1NEZ ft, offset" }, { "BC1ANY2F", &EmulateInstructionMIPS64::Emulate_BC1ANY2F, "BC1ANY2F cc, offset" }, { "BC1ANY2T", &EmulateInstructionMIPS64::Emulate_BC1ANY2T, "BC1ANY2T cc, offset" }, { "BC1ANY4F", &EmulateInstructionMIPS64::Emulate_BC1ANY4F, "BC1ANY4F cc, offset" }, { "BC1ANY4T", &EmulateInstructionMIPS64::Emulate_BC1ANY4T, "BC1ANY4T cc, offset" }, }; static const size_t k_num_mips_opcodes = llvm::array_lengthof(g_opcodes); for (size_t i = 0; i < k_num_mips_opcodes; ++i) { if (! strcasecmp (g_opcodes[i].op_name, op_name)) return &g_opcodes[i]; } return NULL; } bool EmulateInstructionMIPS64::ReadInstruction () { bool success = false; m_addr = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_ADDRESS, &success); if (success) { Context read_inst_context; read_inst_context.type = eContextReadOpcode; read_inst_context.SetNoArgs (); m_opcode.SetOpcode32 (ReadMemoryUnsigned (read_inst_context, m_addr, 4, 0, &success), GetByteOrder()); } if (!success) m_addr = LLDB_INVALID_ADDRESS; return success; } bool EmulateInstructionMIPS64::EvaluateInstruction (uint32_t evaluate_options) { bool success = false; llvm::MCInst mc_insn; uint64_t insn_size; DataExtractor data; /* Keep the complexity of the decode logic with the llvm::MCDisassembler class. */ if (m_opcode.GetData (data)) { llvm::MCDisassembler::DecodeStatus decode_status; llvm::ArrayRef raw_insn (data.GetDataStart(), data.GetByteSize()); decode_status = m_disasm->getInstruction (mc_insn, insn_size, raw_insn, m_addr, llvm::nulls(), llvm::nulls()); if (decode_status != llvm::MCDisassembler::Success) return false; } /* * mc_insn.getOpcode() returns decoded opcode. However to make use * of llvm::Mips:: we would need "MipsGenInstrInfo.inc". */ const char *op_name = m_insn_info->getName (mc_insn.getOpcode ()); if (op_name == NULL) return false; /* * Decoding has been done already. Just get the call-back function * and emulate the instruction. */ MipsOpcode *opcode_data = GetOpcodeForInstruction (op_name); if (opcode_data == NULL) return false; uint64_t old_pc = 0, new_pc = 0; const bool auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; if (auto_advance_pc) { old_pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; } /* emulate instruction */ success = (this->*opcode_data->callback) (mc_insn); if (!success) return false; if (auto_advance_pc) { new_pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; /* If we haven't changed the PC, change it here */ if (old_pc == new_pc) { new_pc += 4; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, new_pc)) return false; } } return true; } bool EmulateInstructionMIPS64::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind (eRegisterKindDWARF); UnwindPlan::RowSP row(new UnwindPlan::Row); const bool can_replace = false; // Our previous Call Frame Address is the stack pointer row->GetCFAValue().SetIsRegisterPlusOffset(gcc_dwarf_sp_mips64, 0); // Our previous PC is in the RA row->SetRegisterLocationToRegister(gcc_dwarf_pc_mips64, gcc_dwarf_ra_mips64, can_replace); unwind_plan.AppendRow (row); // All other registers are the same. unwind_plan.SetSourceName ("EmulateInstructionMIPS64"); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); return true; } bool EmulateInstructionMIPS64::nonvolatile_reg_p (uint64_t regnum) { switch (regnum) { case gcc_dwarf_r16_mips64: case gcc_dwarf_r17_mips64: case gcc_dwarf_r18_mips64: case gcc_dwarf_r19_mips64: case gcc_dwarf_r20_mips64: case gcc_dwarf_r21_mips64: case gcc_dwarf_r22_mips64: case gcc_dwarf_r23_mips64: case gcc_dwarf_gp_mips64: case gcc_dwarf_sp_mips64: case gcc_dwarf_r30_mips64: case gcc_dwarf_ra_mips64: return true; default: return false; } return false; } bool EmulateInstructionMIPS64::Emulate_DADDiu (llvm::MCInst& insn) { bool success = false; const uint32_t imm16 = insn.getOperand(2).getImm(); uint64_t imm = SignedBits(imm16, 15, 0); uint64_t result; uint32_t src, dst; dst = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); src = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); /* Check if this is daddiu sp,,imm16 */ if (dst == gcc_dwarf_sp_mips64) { /* read register */ uint64_t src_opd_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, 0, &success); if (!success) return false; result = src_opd_val + imm; Context context; RegisterInfo reg_info_sp; if (GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_sp_mips64, reg_info_sp)) context.SetRegisterPlusOffset (reg_info_sp, imm); /* We are allocating bytes on stack */ context.type = eContextAdjustStackPointer; WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_sp_mips64, result); } return true; } bool +EmulateInstructionMIPS64::Emulate_SW (llvm::MCInst& insn) +{ + bool success = false; + uint32_t base; + int64_t imm, address; + Context bad_vaddr_context; + + base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + imm = insn.getOperand(2).getImm(); + + RegisterInfo reg_info_base; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base)) + return false; + + /* read base register */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; + + /* destination address */ + address = address + imm; + + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; +} + +bool +EmulateInstructionMIPS64::Emulate_LW (llvm::MCInst& insn) +{ + bool success = false; + uint32_t base; + int64_t imm, address; + Context bad_vaddr_context; + + base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + imm = insn.getOperand(2).getImm(); + + RegisterInfo reg_info_base; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base)) + return false; + + /* read base register */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; + + /* destination address */ + address = address + imm; + + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; +} + +bool EmulateInstructionMIPS64::Emulate_SD (llvm::MCInst& insn) { + uint64_t address; + RegisterInfo reg_info_base; + RegisterInfo reg_info_src; bool success = false; uint32_t imm16 = insn.getOperand(2).getImm(); uint64_t imm = SignedBits(imm16, 15, 0); uint32_t src, base; + Context bad_vaddr_context; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - /* We look for sp based non-volatile register stores */ - if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) - { - uint64_t address; - RegisterInfo reg_info_base; - RegisterInfo reg_info_src; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base) + || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) + return false; - if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base) - || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) - return false; + /* read SP */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; - /* read SP */ - address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); - if (!success) - return false; + /* destination address */ + address = address + imm; - /* destination address */ - address = address + imm; - + /* We look for sp based non-volatile register stores */ + if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) + { Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; context.SetRegisterToRegisterPlusOffset (reg_info_src, reg_info_base, 0); uint8_t buffer [RegisterValue::kMaxRegisterByteSize]; Error error; if (!ReadRegister (®_info_base, data_src)) return false; if (data_src.GetAsMemoryData (®_info_src, buffer, reg_info_src.byte_size, eByteOrderLittle, error) == 0) return false; if (!WriteMemory (context, address, buffer, reg_info_src.byte_size)) return false; - - return true; } - return false; + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; } bool EmulateInstructionMIPS64::Emulate_LD (llvm::MCInst& insn) { uint32_t src, base; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) { RegisterValue data_src; RegisterInfo reg_info_src; if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) return false; Context context; context.type = eContextRegisterLoad; if (!WriteRegister (context, ®_info_src, data_src)) return false; return true; } return false; } bool EmulateInstructionMIPS64::Emulate_BEQ (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BEQ rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) * else * PC = PC + 4 */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNE (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BNE rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BEQL (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BEQL rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNEL (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BNEL rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGEZL rs, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZL rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGTZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGTZL rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLEZL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLEZL rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGTZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGTZ rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLEZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLEZ rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZ rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + offset; else target = pc + 8; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZALL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGEZALL rt, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BAL (llvm::MCInst& insn) { bool success = false; int64_t offset, pc, target; /* * BAL offset * offset = sign_ext (offset << 2) * RA = PC + 8 * PC = PC + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; target = pc + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BALC (llvm::MCInst& insn) { bool success = false; int64_t offset, pc, target; /* * BALC offset * offset = sign_ext (offset << 2) * RA = PC + 4 * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZAL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGEZAL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if ((int64_t) rs_val >= 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZAL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZAL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if ((int64_t) rs_val < 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZALL (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZALL rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 8 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val < 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] <= 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val <= 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val >= 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] < 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val < 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGTZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGTZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] > 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val > 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BEQZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target, rs_val; /* * BEQZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] == 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val == 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNEZALC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target, rs_val; /* * BNEZALC rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] != 0) * if condition then * RA = PC + 4 * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val != 0) target = pc + offset; else target = pc + 4; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZ (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target, rs_val; /* * BGEZ rs,offset * offset = sign_ext (offset << 2) * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + offset */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (rs_val >= 0) target = pc + offset; else target = pc + 8; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC (llvm::MCInst& insn) { bool success = false; int64_t offset, pc, target; /* * BC offset * offset = sign_ext (offset << 2) * PC = PC + 4 + offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; target = pc + 4 + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BEQC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BEQC rs, rt, offset * condition <- (GPR[rs] = GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val == rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNEC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target, rs_val, rt_val; /* * BNEC rs, rt, offset * condition <- (GPR[rs] != GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val != rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; int64_t rs_val, rt_val; /* * BLTC rs, rt, offset * condition <- (GPR[rs] < GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val < rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; int64_t rs_val, rt_val; /* * BGEC rs, rt, offset * condition <- (GPR[rs] > GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val > rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTUC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; uint64_t rs_val, rt_val; /* * BLTUC rs, rt, offset * condition <- (GPR[rs] < GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val < rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEUC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; uint64_t rs_val, rt_val; /* * BGEUC rs, rt, offset * condition <- (GPR[rs] > GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (rs_val > rt_val) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLTZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLTZC rs, offset * condition <- (GPR[rs] < 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val < 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BLEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BLEZC rs, offset * condition <- (GPR[rs] <= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val <= 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGEZC rs, offset * condition <- (GPR[rs] >= 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val >= 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BGTZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; int64_t rs_val; /* * BGTZC rs, offset * condition <- (GPR[rs] > 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val > 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BEQZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; uint64_t rs_val; /* * BEQZC rs, offset * condition <- (GPR[rs] = 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val == 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNEZC (llvm::MCInst& insn) { bool success = false; uint32_t rs; int64_t offset, pc, target; uint64_t rs_val; /* * BNEZC rs, offset * condition <- (GPR[rs] != 0) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; if (rs_val != 0) target = pc + 4 + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } static int IsAdd64bitOverflow (int64_t a, int64_t b) { int64_t r = (uint64_t) a + (uint64_t) b; return (a < 0 && b < 0 && r >= 0) || (a >= 0 && b >= 0 && r < 0); } bool EmulateInstructionMIPS64::Emulate_BOVC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; int64_t rs_val, rt_val; /* * BOVC rs, rt, offset * condition <- overflow(GPR[rs] + GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (IsAdd64bitOverflow (rs_val, rt_val)) target = pc + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BNVC (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; int64_t offset, pc, target; int64_t rs_val, rt_val; /* * BNVC rs, rt, offset * condition <- overflow(GPR[rs] + GPR[rt]) * if condition then * PC = PC + sign_ext (offset << 2) */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rt = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); offset = insn.getOperand(2).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; if (! IsAdd64bitOverflow (rs_val, rt_val)) target = pc + offset; else target = pc + 4; Context context; context.type = eContextRelativeBranchImmediate; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_J (llvm::MCInst& insn) { bool success = false; uint64_t offset, pc; /* * J offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ pc = (pc & 0xFFFFFFFFF0000000ULL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, pc)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_JAL (llvm::MCInst& insn) { bool success = false; uint64_t offset, target, pc; /* * JAL offset * offset = sign_ext (offset << 2) * PC = PC[63-28] | offset */ offset = insn.getOperand(0).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; /* This is a PC-region branch and not PC-relative */ target = (pc & 0xFFFFFFFFF0000000ULL) | offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_JALR (llvm::MCInst& insn) { bool success = false; uint32_t rs, rt; uint64_t pc, rs_val; /* * JALR rt, rs * GPR[rt] = PC + 8 * PC = GPR[rs] */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, rs_val)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, pc + 8)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_JIALC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int64_t target, offset, pc, rt_val; /* * JIALC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset * RA = PC + 4 */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_ra_mips64, pc + 4)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_JIC (llvm::MCInst& insn) { bool success = false; uint32_t rt; int64_t target, offset, rt_val; /* * JIC rt, offset * offset = sign_ext (offset) * PC = GPR[rt] + offset */ rt = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); rt_val = (int64_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rt, 0, &success); if (!success) return false; target = rt_val + offset; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_JR (llvm::MCInst& insn) { bool success = false; uint32_t rs; uint64_t rs_val; /* * JR rs * PC = GPR[rs] */ rs = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); rs_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + rs, 0, &success); if (!success) return false; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, rs_val)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1F cc, offset * condition <- (FPConditionCode(cc) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) == 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1T cc, offset * condition <- (FPConditionCode(cc) != 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1FL (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1F cc, offset * condition <- (FPConditionCode(cc) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) == 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1TL (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1T cc, offset * condition <- (FPConditionCode(cc) != 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); if ((fcsr & (1 << cc)) != 0) target = pc + offset; else target = pc + 8; /* skip delay slot */ Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1EQZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint64_t ft_val; int64_t target, pc, offset; /* * BC1EQZ ft, offset * condition <- (FPR[ft].bit0 == 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + ft, 0, &success); if (!success) return false; if ((ft_val & 1) == 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1NEZ (llvm::MCInst& insn) { bool success = false; uint32_t ft; uint64_t ft_val; int64_t target, pc, offset; /* * BC1NEZ ft, offset * condition <- (FPR[ft].bit0 != 0) * if condition then * offset = sign_ext (offset) * PC = PC + 4 + offset */ ft = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; ft_val = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + ft, 0, &success); if (!success) return false; if ((ft_val & 1) != 0) target = pc + 4 + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1ANY2F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1ANY2F cc, offset * condition <- (FPConditionCode(cc) == 0 * || FPConditionCode(cc+1) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 0 */ if (((fcsr >> cc) & 3) != 3) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1ANY2T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1ANY2T cc, offset * condition <- (FPConditionCode(cc) == 1 * || FPConditionCode(cc+1) == 1) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 1 */ if (((fcsr >> cc) & 3) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1ANY4F (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1ANY4F cc, offset * condition <- (FPConditionCode(cc) == 0 * || FPConditionCode(cc+1) == 0) * || FPConditionCode(cc+2) == 0) * || FPConditionCode(cc+3) == 0) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 0 */ if (((fcsr >> cc) & 0xf) != 0xf) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } bool EmulateInstructionMIPS64::Emulate_BC1ANY4T (llvm::MCInst& insn) { bool success = false; uint32_t cc, fcsr; int64_t target, pc, offset; /* * BC1ANY4T cc, offset * condition <- (FPConditionCode(cc) == 1 * || FPConditionCode(cc+1) == 1) * || FPConditionCode(cc+2) == 1) * || FPConditionCode(cc+3) == 1) * if condition then * offset = sign_ext (offset) * PC = PC + offset */ cc = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); offset = insn.getOperand(1).getImm(); pc = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_pc_mips64, 0, &success); if (!success) return false; fcsr = (uint32_t) ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_fcsr_mips64, 0, &success); if (!success) return false; /* fcsr[23], fcsr[25-31] are vaild condition bits */ fcsr = ((fcsr >> 24) & 0xfe) | ((fcsr >> 23) & 0x01); /* if any one bit is 1 */ if (((fcsr >> cc) & 0xf) != 0) target = pc + offset; else target = pc + 8; Context context; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, gcc_dwarf_pc_mips64, target)) return false; return true; } Index: vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h =================================================================== --- vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h (revision 287514) @@ -1,313 +1,319 @@ //===-- EmulateInstructionMIPS64.h ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef EmulateInstructionMIPS64_h_ #define EmulateInstructionMIPS64_h_ namespace llvm { class MCDisassembler; class MCSubtargetInfo; class MCRegisterInfo; class MCAsmInfo; class MCContext; class MCInstrInfo; class MCInst; } #include "lldb/Core/EmulateInstruction.h" #include "lldb/Core/Error.h" #include "lldb/Interpreter/OptionValue.h" class EmulateInstructionMIPS64 : public lldb_private::EmulateInstruction { public: static void Initialize (); static void Terminate (); static lldb_private::ConstString GetPluginNameStatic (); static const char * GetPluginDescriptionStatic (); static lldb_private::EmulateInstruction * CreateInstance (const lldb_private::ArchSpec &arch, lldb_private::InstructionType inst_type); static bool SupportsEmulatingInstructionsOfTypeStatic (lldb_private::InstructionType inst_type) { switch (inst_type) { case lldb_private::eInstructionTypeAny: case lldb_private::eInstructionTypePrologueEpilogue: case lldb_private::eInstructionTypePCModifying: return true; case lldb_private::eInstructionTypeAll: return false; } return false; } virtual lldb_private::ConstString GetPluginName(); virtual lldb_private::ConstString GetShortPluginName() { return GetPluginNameStatic(); } virtual uint32_t GetPluginVersion() { return 1; } bool SetTargetTriple (const lldb_private::ArchSpec &arch); EmulateInstructionMIPS64 (const lldb_private::ArchSpec &arch); virtual bool SupportsEmulatingInstructionsOfType (lldb_private::InstructionType inst_type) { return SupportsEmulatingInstructionsOfTypeStatic (inst_type); } virtual bool ReadInstruction (); virtual bool EvaluateInstruction (uint32_t evaluate_options); virtual bool TestEmulation (lldb_private::Stream *out_stream, lldb_private::ArchSpec &arch, lldb_private::OptionValueDictionary *test_data) { return false; } virtual bool GetRegisterInfo (lldb::RegisterKind reg_kind, uint32_t reg_num, lldb_private::RegisterInfo ®_info); virtual bool CreateFunctionEntryUnwind (lldb_private::UnwindPlan &unwind_plan); protected: typedef struct { const char *op_name; bool (EmulateInstructionMIPS64::*callback) (llvm::MCInst& insn); const char *insn_name; } MipsOpcode; static MipsOpcode* GetOpcodeForInstruction (const char *op_name); bool Emulate_DADDiu (llvm::MCInst& insn); bool Emulate_SD (llvm::MCInst& insn); bool + Emulate_SW (llvm::MCInst& insn); + + bool + Emulate_LW (llvm::MCInst& insn); + + bool Emulate_LD (llvm::MCInst& insn); bool Emulate_BEQ (llvm::MCInst& insn); bool Emulate_BNE (llvm::MCInst& insn); bool Emulate_BEQL (llvm::MCInst& insn); bool Emulate_BNEL (llvm::MCInst& insn); bool Emulate_BGEZALL (llvm::MCInst& insn); bool Emulate_BAL (llvm::MCInst& insn); bool Emulate_BGEZAL (llvm::MCInst& insn); bool Emulate_BALC (llvm::MCInst& insn); bool Emulate_BC (llvm::MCInst& insn); bool Emulate_BGEZ (llvm::MCInst& insn); bool Emulate_BLEZALC (llvm::MCInst& insn); bool Emulate_BGEZALC (llvm::MCInst& insn); bool Emulate_BLTZALC (llvm::MCInst& insn); bool Emulate_BGTZALC (llvm::MCInst& insn); bool Emulate_BEQZALC (llvm::MCInst& insn); bool Emulate_BNEZALC (llvm::MCInst& insn); bool Emulate_BEQC (llvm::MCInst& insn); bool Emulate_BNEC (llvm::MCInst& insn); bool Emulate_BLTC (llvm::MCInst& insn); bool Emulate_BGEC (llvm::MCInst& insn); bool Emulate_BLTUC (llvm::MCInst& insn); bool Emulate_BGEUC (llvm::MCInst& insn); bool Emulate_BLTZC (llvm::MCInst& insn); bool Emulate_BLEZC (llvm::MCInst& insn); bool Emulate_BGEZC (llvm::MCInst& insn); bool Emulate_BGTZC (llvm::MCInst& insn); bool Emulate_BEQZC (llvm::MCInst& insn); bool Emulate_BNEZC (llvm::MCInst& insn); bool Emulate_BGEZL (llvm::MCInst& insn); bool Emulate_BGTZ (llvm::MCInst& insn); bool Emulate_BGTZL (llvm::MCInst& insn); bool Emulate_BLEZ (llvm::MCInst& insn); bool Emulate_BLEZL (llvm::MCInst& insn); bool Emulate_BLTZ (llvm::MCInst& insn); bool Emulate_BLTZAL (llvm::MCInst& insn); bool Emulate_BLTZALL (llvm::MCInst& insn); bool Emulate_BLTZL (llvm::MCInst& insn); bool Emulate_BOVC (llvm::MCInst& insn); bool Emulate_BNVC (llvm::MCInst& insn); bool Emulate_J (llvm::MCInst& insn); bool Emulate_JAL (llvm::MCInst& insn); bool Emulate_JALR (llvm::MCInst& insn); bool Emulate_JIALC (llvm::MCInst& insn); bool Emulate_JIC (llvm::MCInst& insn); bool Emulate_JR (llvm::MCInst& insn); bool Emulate_BC1F (llvm::MCInst& insn); bool Emulate_BC1T (llvm::MCInst& insn); bool Emulate_BC1FL (llvm::MCInst& insn); bool Emulate_BC1TL (llvm::MCInst& insn); bool Emulate_BC1EQZ (llvm::MCInst& insn); bool Emulate_BC1NEZ (llvm::MCInst& insn); bool Emulate_BC1ANY2F (llvm::MCInst& insn); bool Emulate_BC1ANY2T (llvm::MCInst& insn); bool Emulate_BC1ANY4F (llvm::MCInst& insn); bool Emulate_BC1ANY4T (llvm::MCInst& insn); bool nonvolatile_reg_p (uint64_t regnum); const char * GetRegisterName (unsigned reg_num, bool altnernate_name); private: std::unique_ptr m_disasm; std::unique_ptr m_subtype_info; std::unique_ptr m_reg_info; std::unique_ptr m_asm_info; std::unique_ptr m_context; std::unique_ptr m_insn_info; }; #endif // EmulateInstructionMIPS64_h_ Index: vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp (revision 287514) @@ -1,3042 +1,3077 @@ //===-- ObjectFileELF.cpp ------------------------------------- -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ObjectFileELF.h" #include #include #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/Error.h" #include "lldb/Core/FileSpecList.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MathExtras.h" #define CASE_AND_STREAM(s, def, width) \ case def: s->Printf("%-*s", width, #def); break; using namespace lldb; using namespace lldb_private; using namespace elf; using namespace llvm::ELF; namespace { // ELF note owner definitions const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD"; const char *const LLDB_NT_OWNER_GNU = "GNU"; const char *const LLDB_NT_OWNER_NETBSD = "NetBSD"; const char *const LLDB_NT_OWNER_CSR = "csr"; const char *const LLDB_NT_OWNER_ANDROID = "Android"; // ELF note type definitions const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01; const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4; const elf_word LLDB_NT_GNU_ABI_TAG = 0x01; const elf_word LLDB_NT_GNU_ABI_SIZE = 16; const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03; const elf_word LLDB_NT_NETBSD_ABI_TAG = 0x01; const elf_word LLDB_NT_NETBSD_ABI_SIZE = 4; // GNU ABI note OS constants const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00; const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01; const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02; //===----------------------------------------------------------------------===// /// @class ELFRelocation /// @brief Generic wrapper for ELFRel and ELFRela. /// /// This helper class allows us to parse both ELFRel and ELFRela relocation /// entries in a generic manner. class ELFRelocation { public: /// Constructs an ELFRelocation entry with a personality as given by @p /// type. /// /// @param type Either DT_REL or DT_RELA. Any other value is invalid. ELFRelocation(unsigned type); ~ELFRelocation(); bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); static unsigned RelocType32(const ELFRelocation &rel); static unsigned RelocType64(const ELFRelocation &rel); static unsigned RelocSymbol32(const ELFRelocation &rel); static unsigned RelocSymbol64(const ELFRelocation &rel); static unsigned RelocOffset32(const ELFRelocation &rel); static unsigned RelocOffset64(const ELFRelocation &rel); static unsigned RelocAddend32(const ELFRelocation &rel); static unsigned RelocAddend64(const ELFRelocation &rel); private: typedef llvm::PointerUnion RelocUnion; RelocUnion reloc; }; ELFRelocation::ELFRelocation(unsigned type) { if (type == DT_REL || type == SHT_REL) reloc = new ELFRel(); else if (type == DT_RELA || type == SHT_RELA) reloc = new ELFRela(); else { assert(false && "unexpected relocation type"); reloc = static_cast(NULL); } } ELFRelocation::~ELFRelocation() { if (reloc.is()) delete reloc.get(); else delete reloc.get(); } bool ELFRelocation::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) { if (reloc.is()) return reloc.get()->Parse(data, offset); else return reloc.get()->Parse(data, offset); } unsigned ELFRelocation::RelocType32(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocType32(*rel.reloc.get()); else return ELFRela::RelocType32(*rel.reloc.get()); } unsigned ELFRelocation::RelocType64(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocType64(*rel.reloc.get()); else return ELFRela::RelocType64(*rel.reloc.get()); } unsigned ELFRelocation::RelocSymbol32(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocSymbol32(*rel.reloc.get()); else return ELFRela::RelocSymbol32(*rel.reloc.get()); } unsigned ELFRelocation::RelocSymbol64(const ELFRelocation &rel) { if (rel.reloc.is()) return ELFRel::RelocSymbol64(*rel.reloc.get()); else return ELFRela::RelocSymbol64(*rel.reloc.get()); } unsigned ELFRelocation::RelocOffset32(const ELFRelocation &rel) { if (rel.reloc.is()) return rel.reloc.get()->r_offset; else return rel.reloc.get()->r_offset; } unsigned ELFRelocation::RelocOffset64(const ELFRelocation &rel) { if (rel.reloc.is()) return rel.reloc.get()->r_offset; else return rel.reloc.get()->r_offset; } unsigned ELFRelocation::RelocAddend32(const ELFRelocation &rel) { if (rel.reloc.is()) return 0; else return rel.reloc.get()->r_addend; } unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) { if (rel.reloc.is()) return 0; else return rel.reloc.get()->r_addend; } } // end anonymous namespace bool ELFNote::Parse(const DataExtractor &data, lldb::offset_t *offset) { // Read all fields. if (data.GetU32(offset, &n_namesz, 3) == NULL) return false; // The name field is required to be nul-terminated, and n_namesz // includes the terminating nul in observed implementations (contrary // to the ELF-64 spec). A special case is needed for cores generated // by some older Linux versions, which write a note named "CORE" // without a nul terminator and n_namesz = 4. if (n_namesz == 4) { char buf[4]; if (data.ExtractBytes (*offset, 4, data.GetByteOrder(), buf) != 4) return false; if (strncmp (buf, "CORE", 4) == 0) { n_name = "CORE"; *offset += 4; return true; } } const char *cstr = data.GetCStr(offset, llvm::RoundUpToAlignment (n_namesz, 4)); if (cstr == NULL) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SYMBOLS)); if (log) log->Printf("Failed to parse note name lacking nul terminator"); return false; } n_name = cstr; return true; } static uint32_t kalimbaVariantFromElfFlags(const elf::elf_word e_flags) { const uint32_t dsp_rev = e_flags & 0xFF; uint32_t kal_arch_variant = LLDB_INVALID_CPUTYPE; switch(dsp_rev) { // TODO(mg11) Support more variants case 10: kal_arch_variant = llvm::Triple::KalimbaSubArch_v3; break; case 14: kal_arch_variant = llvm::Triple::KalimbaSubArch_v4; break; case 17: case 20: kal_arch_variant = llvm::Triple::KalimbaSubArch_v5; break; default: break; } return kal_arch_variant; } static uint32_t mipsVariantFromElfFlags(const elf::elf_word e_flags, uint32_t endian) { const uint32_t mips_arch = e_flags & llvm::ELF::EF_MIPS_ARCH; uint32_t arch_variant = ArchSpec::eMIPSSubType_unknown; switch (mips_arch) { case llvm::ELF::EF_MIPS_ARCH_32: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32el : ArchSpec::eMIPSSubType_mips32; case llvm::ELF::EF_MIPS_ARCH_32R2: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r2el : ArchSpec::eMIPSSubType_mips32r2; case llvm::ELF::EF_MIPS_ARCH_32R6: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips32r6el : ArchSpec::eMIPSSubType_mips32r6; case llvm::ELF::EF_MIPS_ARCH_64: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64el : ArchSpec::eMIPSSubType_mips64; case llvm::ELF::EF_MIPS_ARCH_64R2: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r2el : ArchSpec::eMIPSSubType_mips64r2; case llvm::ELF::EF_MIPS_ARCH_64R6: return (endian == ELFDATA2LSB) ? ArchSpec::eMIPSSubType_mips64r6el : ArchSpec::eMIPSSubType_mips64r6; default: break; } return arch_variant; } static uint32_t subTypeFromElfHeader(const elf::ELFHeader& header) { if (header.e_machine == llvm::ELF::EM_MIPS) return mipsVariantFromElfFlags (header.e_flags, header.e_ident[EI_DATA]); return llvm::ELF::EM_CSR_KALIMBA == header.e_machine ? kalimbaVariantFromElfFlags(header.e_flags) : LLDB_INVALID_CPUTYPE; } //! The kalimba toolchain identifies a code section as being //! one with the SHT_PROGBITS set in the section sh_type and the top //! bit in the 32-bit address field set. static lldb::SectionType kalimbaSectionType( const elf::ELFHeader& header, const elf::ELFSectionHeader& sect_hdr) { if (llvm::ELF::EM_CSR_KALIMBA != header.e_machine) { return eSectionTypeOther; } if (llvm::ELF::SHT_NOBITS == sect_hdr.sh_type) { return eSectionTypeZeroFill; } if (llvm::ELF::SHT_PROGBITS == sect_hdr.sh_type) { const lldb::addr_t KAL_CODE_BIT = 1 << 31; return KAL_CODE_BIT & sect_hdr.sh_addr ? eSectionTypeCode : eSectionTypeData; } return eSectionTypeOther; } // Arbitrary constant used as UUID prefix for core files. const uint32_t ObjectFileELF::g_core_uuid_magic(0xE210C); //------------------------------------------------------------------ // Static methods. //------------------------------------------------------------------ void ObjectFileELF::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, GetModuleSpecifications); } void ObjectFileELF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ObjectFileELF::GetPluginNameStatic() { static ConstString g_name("elf"); return g_name; } const char * ObjectFileELF::GetPluginDescriptionStatic() { return "ELF object file reader."; } ObjectFile * ObjectFileELF::CreateInstance (const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, const lldb_private::FileSpec* file, lldb::offset_t file_offset, lldb::offset_t length) { if (!data_sp) { data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); data_offset = 0; } if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) { const uint8_t *magic = data_sp->GetBytes() + data_offset; if (ELFHeader::MagicBytesMatch(magic)) { // Update the data to contain the entire file if it doesn't already if (data_sp->GetByteSize() < length) { data_sp = file->MemoryMapFileContentsIfLocal(file_offset, length); data_offset = 0; magic = data_sp->GetBytes(); } unsigned address_size = ELFHeader::AddressSizeInBytes(magic); if (address_size == 4 || address_size == 8) { std::unique_ptr objfile_ap(new ObjectFileELF(module_sp, data_sp, data_offset, file, file_offset, length)); ArchSpec spec; if (objfile_ap->GetArchitecture(spec) && objfile_ap->SetModulesArchitecture(spec)) return objfile_ap.release(); } } } return NULL; } ObjectFile* ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, DataBufferSP& data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) { const uint8_t *magic = data_sp->GetBytes(); if (ELFHeader::MagicBytesMatch(magic)) { unsigned address_size = ELFHeader::AddressSizeInBytes(magic); if (address_size == 4 || address_size == 8) { std::auto_ptr objfile_ap(new ObjectFileELF(module_sp, data_sp, process_sp, header_addr)); ArchSpec spec; if (objfile_ap->GetArchitecture(spec) && objfile_ap->SetModulesArchitecture(spec)) return objfile_ap.release(); } } } return NULL; } bool ObjectFileELF::MagicBytesMatch (DataBufferSP& data_sp, lldb::addr_t data_offset, lldb::addr_t data_length) { if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) { const uint8_t *magic = data_sp->GetBytes() + data_offset; return ELFHeader::MagicBytesMatch(magic); } return false; } /* * crc function from http://svnweb.freebsd.org/base/head/sys/libkern/crc32.c * * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ static uint32_t calc_crc32(uint32_t crc, const void *buf, size_t size) { static const uint32_t g_crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; const uint8_t *p = (const uint8_t *)buf; crc = crc ^ ~0U; while (size--) crc = g_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); return crc ^ ~0U; } static uint32_t calc_gnu_debuglink_crc32(const void *buf, size_t size) { return calc_crc32(0U, buf, size); } uint32_t ObjectFileELF::CalculateELFNotesSegmentsCRC32 (const ProgramHeaderColl& program_headers, DataExtractor& object_data) { typedef ProgramHeaderCollConstIter Iter; uint32_t core_notes_crc = 0; for (Iter I = program_headers.begin(); I != program_headers.end(); ++I) { if (I->p_type == llvm::ELF::PT_NOTE) { const elf_off ph_offset = I->p_offset; const size_t ph_size = I->p_filesz; DataExtractor segment_data; if (segment_data.SetData(object_data, ph_offset, ph_size) != ph_size) { // The ELF program header contained incorrect data, // probably corefile is incomplete or corrupted. break; } core_notes_crc = calc_crc32(core_notes_crc, segment_data.GetDataStart(), segment_data.GetByteSize()); } } return core_notes_crc; } static const char* OSABIAsCString (unsigned char osabi_byte) { #define _MAKE_OSABI_CASE(x) case x: return #x switch (osabi_byte) { _MAKE_OSABI_CASE(ELFOSABI_NONE); _MAKE_OSABI_CASE(ELFOSABI_HPUX); _MAKE_OSABI_CASE(ELFOSABI_NETBSD); _MAKE_OSABI_CASE(ELFOSABI_GNU); _MAKE_OSABI_CASE(ELFOSABI_HURD); _MAKE_OSABI_CASE(ELFOSABI_SOLARIS); _MAKE_OSABI_CASE(ELFOSABI_AIX); _MAKE_OSABI_CASE(ELFOSABI_IRIX); _MAKE_OSABI_CASE(ELFOSABI_FREEBSD); _MAKE_OSABI_CASE(ELFOSABI_TRU64); _MAKE_OSABI_CASE(ELFOSABI_MODESTO); _MAKE_OSABI_CASE(ELFOSABI_OPENBSD); _MAKE_OSABI_CASE(ELFOSABI_OPENVMS); _MAKE_OSABI_CASE(ELFOSABI_NSK); _MAKE_OSABI_CASE(ELFOSABI_AROS); _MAKE_OSABI_CASE(ELFOSABI_FENIXOS); _MAKE_OSABI_CASE(ELFOSABI_C6000_ELFABI); _MAKE_OSABI_CASE(ELFOSABI_C6000_LINUX); _MAKE_OSABI_CASE(ELFOSABI_ARM); _MAKE_OSABI_CASE(ELFOSABI_STANDALONE); default: return ""; } #undef _MAKE_OSABI_CASE } // // WARNING : This function is being deprecated // It's functionality has moved to ArchSpec::SetArchitecture // This function is only being kept to validate the move. // // TODO : Remove this function static bool GetOsFromOSABI (unsigned char osabi_byte, llvm::Triple::OSType &ostype) { switch (osabi_byte) { case ELFOSABI_AIX: ostype = llvm::Triple::OSType::AIX; break; case ELFOSABI_FREEBSD: ostype = llvm::Triple::OSType::FreeBSD; break; case ELFOSABI_GNU: ostype = llvm::Triple::OSType::Linux; break; case ELFOSABI_NETBSD: ostype = llvm::Triple::OSType::NetBSD; break; case ELFOSABI_OPENBSD: ostype = llvm::Triple::OSType::OpenBSD; break; case ELFOSABI_SOLARIS: ostype = llvm::Triple::OSType::Solaris; break; default: ostype = llvm::Triple::OSType::UnknownOS; } return ostype != llvm::Triple::OSType::UnknownOS; } size_t ObjectFileELF::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) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); const size_t initial_count = specs.GetSize(); if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) { DataExtractor data; data.SetData(data_sp); elf::ELFHeader header; if (header.Parse(data, &data_offset)) { if (data_sp) { ModuleSpec spec (file); const uint32_t sub_type = subTypeFromElfHeader(header); spec.GetArchitecture().SetArchitecture(eArchTypeELF, header.e_machine, sub_type, header.e_ident[EI_OSABI]); if (spec.GetArchitecture().IsValid()) { llvm::Triple::OSType ostype; llvm::Triple::VendorType vendor; llvm::Triple::OSType spec_ostype = spec.GetArchitecture ().GetTriple ().getOS (); if (log) log->Printf ("ObjectFileELF::%s file '%s' module OSABI: %s", __FUNCTION__, file.GetPath ().c_str (), OSABIAsCString (header.e_ident[EI_OSABI])); // SetArchitecture should have set the vendor to unknown vendor = spec.GetArchitecture ().GetTriple ().getVendor (); assert(vendor == llvm::Triple::UnknownVendor); // // Validate it is ok to remove GetOsFromOSABI GetOsFromOSABI (header.e_ident[EI_OSABI], ostype); assert(spec_ostype == ostype); if (spec_ostype != llvm::Triple::OSType::UnknownOS) { if (log) log->Printf ("ObjectFileELF::%s file '%s' set ELF module OS type from ELF header OSABI.", __FUNCTION__, file.GetPath ().c_str ()); } // Try to get the UUID from the section list. Usually that's at the end, so // map the file in if we don't have it already. size_t section_header_end = header.e_shoff + header.e_shnum * header.e_shentsize; if (section_header_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal (file_offset, section_header_end); data.SetData(data_sp); } uint32_t gnu_debuglink_crc = 0; std::string gnu_debuglink_file; SectionHeaderColl section_headers; lldb_private::UUID &uuid = spec.GetUUID(); GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc, spec.GetArchitecture ()); llvm::Triple &spec_triple = spec.GetArchitecture ().GetTriple (); if (log) log->Printf ("ObjectFileELF::%s file '%s' module set to triple: %s (architecture %s)", __FUNCTION__, file.GetPath ().c_str (), spec_triple.getTriple ().c_str (), spec.GetArchitecture ().GetArchitectureName ()); if (!uuid.IsValid()) { uint32_t core_notes_crc = 0; if (!gnu_debuglink_crc) { lldb_private::Timer scoped_timer (__PRETTY_FUNCTION__, "Calculating module crc32 %s with size %" PRIu64 " KiB", file.GetLastPathComponent().AsCString(), (file.GetByteSize()-file_offset)/1024); // For core files - which usually don't happen to have a gnu_debuglink, // and are pretty bulky - calculating whole contents crc32 would be too much of luxury. // Thus we will need to fallback to something simpler. if (header.e_type == llvm::ELF::ET_CORE) { size_t program_headers_end = header.e_phoff + header.e_phnum * header.e_phentsize; if (program_headers_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal(file_offset, program_headers_end); data.SetData(data_sp); } ProgramHeaderColl program_headers; GetProgramHeaderInfo(program_headers, data, header); size_t segment_data_end = 0; for (ProgramHeaderCollConstIter I = program_headers.begin(); I != program_headers.end(); ++I) { segment_data_end = std::max (I->p_offset + I->p_filesz, segment_data_end); } if (segment_data_end > data_sp->GetByteSize()) { data_sp = file.MemoryMapFileContentsIfLocal(file_offset, segment_data_end); data.SetData(data_sp); } core_notes_crc = CalculateELFNotesSegmentsCRC32 (program_headers, data); } else { // Need to map entire file into memory to calculate the crc. data_sp = file.MemoryMapFileContentsIfLocal (file_offset, SIZE_MAX); data.SetData(data_sp); gnu_debuglink_crc = calc_gnu_debuglink_crc32 (data.GetDataStart(), data.GetByteSize()); } } if (gnu_debuglink_crc) { // Use 4 bytes of crc from the .gnu_debuglink section. uint32_t uuidt[4] = { gnu_debuglink_crc, 0, 0, 0 }; uuid.SetBytes (uuidt, sizeof(uuidt)); } else if (core_notes_crc) { // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it look different form // .gnu_debuglink crc followed by 4 bytes of note segments crc. uint32_t uuidt[4] = { g_core_uuid_magic, core_notes_crc, 0, 0 }; uuid.SetBytes (uuidt, sizeof(uuidt)); } } specs.Append(spec); } } } } return specs.GetSize() - initial_count; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString ObjectFileELF::GetPluginName() { return GetPluginNameStatic(); } uint32_t ObjectFileELF::GetPluginVersion() { return m_plugin_version; } //------------------------------------------------------------------ // ObjectFile protocol //------------------------------------------------------------------ ObjectFileELF::ObjectFileELF (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_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), m_dynamic_symbols(), m_filespec_ap(), m_entry_point_address(), m_arch_spec() { if (file) m_file = *file; ::memset(&m_header, 0, sizeof(m_header)); } ObjectFileELF::ObjectFileELF (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_header(), m_uuid(), m_gnu_debuglink_file(), m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), m_dynamic_symbols(), m_filespec_ap(), m_entry_point_address(), m_arch_spec() { ::memset(&m_header, 0, sizeof(m_header)); } ObjectFileELF::~ObjectFileELF() { } bool ObjectFileELF::IsExecutable() const { return ((m_header.e_type & ET_EXEC) != 0) || (m_header.e_entry != 0); } bool ObjectFileELF::SetLoadAddress (Target &target, lldb::addr_t value, bool value_is_offset) { ModuleSP module_sp = GetModule(); if (module_sp) { size_t num_loaded_sections = 0; SectionList *section_list = GetSectionList (); if (section_list) { if (value_is_offset) { 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 (section_sp && section_sp->Test(SHF_ALLOC)) { lldb::addr_t load_addr = section_sp->GetFileAddress() + value; // On 32-bit systems the load address have to fit into 4 bytes. The rest of // the bytes are the overflow from the addition. if (GetAddressByteSize() == 4) load_addr &= 0xFFFFFFFF; if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, load_addr)) ++num_loaded_sections; } } return num_loaded_sections > 0; } else { // Not sure how to slide an ELF file given the base address // of the ELF file in memory } } } return false; // If it changed } ByteOrder ObjectFileELF::GetByteOrder() const { if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) return eByteOrderBig; if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) return eByteOrderLittle; return eByteOrderInvalid; } uint32_t ObjectFileELF::GetAddressByteSize() const { return m_data.GetAddressByteSize(); } // Top 16 bits of the `Symbol` flags are available. #define ARM_ELF_SYM_IS_THUMB (1 << 16) AddressClass ObjectFileELF::GetAddressClass (addr_t file_addr) { auto res = ObjectFile::GetAddressClass (file_addr); if (res != eAddressClassCode) return res; auto ub = m_address_class_map.upper_bound(file_addr); if (ub == m_address_class_map.begin()) { // No entry in the address class map before the address. Return // default address class for an address in a code section. return eAddressClassCode; } // Move iterator to the address class entry preceding address --ub; return ub->second; } size_t ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) { return std::distance(m_section_headers.begin(), I) + 1u; } size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const { return std::distance(m_section_headers.begin(), I) + 1u; } bool ObjectFileELF::ParseHeader() { lldb::offset_t offset = 0; if (!m_header.Parse(m_data, &offset)) return false; if (!IsInMemory()) return true; // For in memory object files m_data might not contain the full object file. Try to load it // until the end of the "Section header table" what is at the end of the ELF file. addr_t file_size = m_header.e_shoff + m_header.e_shnum * m_header.e_shentsize; if (m_data.GetByteSize() < file_size) { ProcessSP process_sp (m_process_wp.lock()); if (!process_sp) return false; DataBufferSP data_sp = ReadMemory(process_sp, m_memory_addr, file_size); if (!data_sp) return false; m_data.SetData(data_sp, 0, file_size); } return true; } bool ObjectFileELF::GetUUID(lldb_private::UUID* uuid) { // Need to parse the section list to get the UUIDs, so make sure that's been done. if (!ParseSectionHeaders() && GetType() != ObjectFile::eTypeCoreFile) return false; if (m_uuid.IsValid()) { // We have the full build id uuid. *uuid = m_uuid; return true; } else if (GetType() == ObjectFile::eTypeCoreFile) { uint32_t core_notes_crc = 0; if (!ParseProgramHeaders()) return false; core_notes_crc = CalculateELFNotesSegmentsCRC32(m_program_headers, m_data); if (core_notes_crc) { // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it // look different form .gnu_debuglink crc - followed by 4 bytes of note // segments crc. uint32_t uuidt[4] = { g_core_uuid_magic, core_notes_crc, 0, 0 }; m_uuid.SetBytes (uuidt, sizeof(uuidt)); } } else { if (!m_gnu_debuglink_crc) m_gnu_debuglink_crc = calc_gnu_debuglink_crc32 (m_data.GetDataStart(), m_data.GetByteSize()); if (m_gnu_debuglink_crc) { // Use 4 bytes of crc from the .gnu_debuglink section. uint32_t uuidt[4] = { m_gnu_debuglink_crc, 0, 0, 0 }; m_uuid.SetBytes (uuidt, sizeof(uuidt)); } } if (m_uuid.IsValid()) { *uuid = m_uuid; return true; } return false; } lldb_private::FileSpecList ObjectFileELF::GetDebugSymbolFilePaths() { FileSpecList file_spec_list; if (!m_gnu_debuglink_file.empty()) { FileSpec file_spec (m_gnu_debuglink_file.c_str(), false); file_spec_list.Append (file_spec); } return file_spec_list; } uint32_t ObjectFileELF::GetDependentModules(FileSpecList &files) { size_t num_modules = ParseDependentModules(); uint32_t num_specs = 0; for (unsigned i = 0; i < num_modules; ++i) { if (files.AppendIfUnique(m_filespec_ap->GetFileSpecAtIndex(i))) num_specs++; } return num_specs; } Address ObjectFileELF::GetImageInfoAddress(Target *target) { if (!ParseDynamicSymbols()) return Address(); SectionList *section_list = GetSectionList(); if (!section_list) return Address(); // Find the SHT_DYNAMIC (.dynamic) section. SectionSP dynsym_section_sp (section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true)); if (!dynsym_section_sp) return Address(); assert (dynsym_section_sp->GetObjectFile() == this); user_id_t dynsym_id = dynsym_section_sp->GetID(); const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id); if (!dynsym_hdr) return Address(); for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) { ELFDynamic &symbol = m_dynamic_symbols[i]; if (symbol.d_tag == DT_DEBUG) { // Compute the offset as the number of previous entries plus the // size of d_tag. addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize(); return Address(dynsym_section_sp, offset); } else if (symbol.d_tag == DT_MIPS_RLD_MAP && target) { addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize(); addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target); if (dyn_base == LLDB_INVALID_ADDRESS) return Address(); Address addr; Error error; if (target->ReadPointerFromMemory(dyn_base + offset, false, error, addr)) return addr; } } return Address(); } lldb_private::Address ObjectFileELF::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_header.e_entry; if (!section_list) m_entry_point_address.SetOffset(offset); else m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); return m_entry_point_address; } //---------------------------------------------------------------------- // ParseDependentModules //---------------------------------------------------------------------- size_t ObjectFileELF::ParseDependentModules() { if (m_filespec_ap.get()) return m_filespec_ap->GetSize(); m_filespec_ap.reset(new FileSpecList()); if (!ParseSectionHeaders()) return 0; SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Find the SHT_DYNAMIC section. Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); if (!dynsym) return 0; assert (dynsym->GetObjectFile() == this); const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex (dynsym->GetID()); if (!header) return 0; // sh_link: section header index of string table used by entries in the section. Section *dynstr = section_list->FindSectionByID (header->sh_link + 1).get(); if (!dynstr) return 0; DataExtractor dynsym_data; DataExtractor dynstr_data; if (ReadSectionData(dynsym, dynsym_data) && ReadSectionData(dynstr, dynstr_data)) { ELFDynamic symbol; const lldb::offset_t section_size = dynsym_data.GetByteSize(); lldb::offset_t offset = 0; // The only type of entries we are concerned with are tagged DT_NEEDED, // yielding the name of a required library. while (offset < section_size) { if (!symbol.Parse(dynsym_data, &offset)) break; if (symbol.d_tag != DT_NEEDED) continue; uint32_t str_index = static_cast(symbol.d_val); const char *lib_name = dynstr_data.PeekCStr(str_index); m_filespec_ap->Append(FileSpec(lib_name, true)); } } return m_filespec_ap->GetSize(); } //---------------------------------------------------------------------- // GetProgramHeaderInfo //---------------------------------------------------------------------- size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, DataExtractor &object_data, const ELFHeader &header) { // We have already parsed the program headers if (!program_headers.empty()) return program_headers.size(); // If there are no program headers to read we are done. if (header.e_phnum == 0) return 0; program_headers.resize(header.e_phnum); if (program_headers.size() != header.e_phnum) return 0; const size_t ph_size = header.e_phnum * header.e_phentsize; const elf_off ph_offset = header.e_phoff; DataExtractor data; if (data.SetData(object_data, ph_offset, ph_size) != ph_size) return 0; uint32_t idx; lldb::offset_t offset; for (idx = 0, offset = 0; idx < header.e_phnum; ++idx) { if (program_headers[idx].Parse(data, &offset) == false) break; } if (idx < program_headers.size()) program_headers.resize(idx); return program_headers.size(); } //---------------------------------------------------------------------- // ParseProgramHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseProgramHeaders() { return GetProgramHeaderInfo(m_program_headers, m_data, m_header); } lldb_private::Error ObjectFileELF::RefineModuleDetailsFromNote (lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch_spec, lldb_private::UUID &uuid) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); Error error; lldb::offset_t offset = 0; while (true) { // Parse the note header. If this fails, bail out. ELFNote note = ELFNote(); if (!note.Parse(data, &offset)) { // We're done. return error; } // If a tag processor handles the tag, it should set processed to true, and // the loop will assume the tag processing has moved entirely past the note's payload. // Otherwise, leave it false and the end of the loop will handle the offset properly. bool processed = false; if (log) log->Printf ("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32, __FUNCTION__, note.n_name.c_str (), note.n_type); // Process FreeBSD ELF notes. if ((note.n_name == LLDB_NT_OWNER_FREEBSD) && (note.n_type == LLDB_NT_FREEBSD_ABI_TAG) && (note.n_descsz == LLDB_NT_FREEBSD_ABI_SIZE)) { // We'll consume the payload below. processed = true; // Pull out the min version info. uint32_t version_info; if (data.GetU32 (&offset, &version_info, 1) == nullptr) { error.SetErrorString ("failed to read FreeBSD ABI note payload"); return error; } // Convert the version info into a major/minor number. const uint32_t version_major = version_info / 100000; const uint32_t version_minor = (version_info / 1000) % 100; char os_name[32]; snprintf (os_name, sizeof (os_name), "freebsd%" PRIu32 ".%" PRIu32, version_major, version_minor); // Set the elf OS version to FreeBSD. Also clear the vendor. arch_spec.GetTriple ().setOSName (os_name); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected FreeBSD %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_major, version_minor, static_cast (version_info % 1000)); } // Process GNU ELF notes. else if (note.n_name == LLDB_NT_OWNER_GNU) { switch (note.n_type) { case LLDB_NT_GNU_ABI_TAG: if (note.n_descsz == LLDB_NT_GNU_ABI_SIZE) { // We'll consume the payload below. processed = true; // Pull out the min OS version supporting the ABI. uint32_t version_info[4]; if (data.GetU32 (&offset, &version_info[0], note.n_descsz / 4) == nullptr) { error.SetErrorString ("failed to read GNU ABI note payload"); return error; } // Set the OS per the OS field. switch (version_info[0]) { case LLDB_NT_GNU_ABI_OS_LINUX: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Linux); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Linux, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); // FIXME we have the minimal version number, we could be propagating that. version_info[1] = OS Major, version_info[2] = OS Minor, version_info[3] = Revision. break; case LLDB_NT_GNU_ABI_OS_HURD: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::UnknownOS); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Hurd (unsupported), min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); break; case LLDB_NT_GNU_ABI_OS_SOLARIS: arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Solaris); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected Solaris, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); break; default: if (log) log->Printf ("ObjectFileELF::%s unrecognized OS in note, id %" PRIu32 ", min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[0], version_info[1], version_info[2], version_info[3]); break; } } break; case LLDB_NT_GNU_BUILD_ID_TAG: // Only bother processing this if we don't already have the uuid set. if (!uuid.IsValid()) { // We'll consume the payload below. processed = true; // 16 bytes is UUID|MD5, 20 bytes is SHA1 if ((note.n_descsz == 16 || note.n_descsz == 20)) { uint8_t uuidbuf[20]; if (data.GetU8 (&offset, &uuidbuf, note.n_descsz) == nullptr) { error.SetErrorString ("failed to read GNU_BUILD_ID note payload"); return error; } // Save the build id as the UUID for the module. uuid.SetBytes (uuidbuf, note.n_descsz); } } break; } } // Process NetBSD ELF notes. else if ((note.n_name == LLDB_NT_OWNER_NETBSD) && (note.n_type == LLDB_NT_NETBSD_ABI_TAG) && (note.n_descsz == LLDB_NT_NETBSD_ABI_SIZE)) { // We'll consume the payload below. processed = true; // Pull out the min version info. uint32_t version_info; if (data.GetU32 (&offset, &version_info, 1) == nullptr) { error.SetErrorString ("failed to read NetBSD ABI note payload"); return error; } // Set the elf OS version to NetBSD. Also clear the vendor. arch_spec.GetTriple ().setOS (llvm::Triple::OSType::NetBSD); arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); if (log) log->Printf ("ObjectFileELF::%s detected NetBSD, min version constant %" PRIu32, __FUNCTION__, version_info); } // Process CSR kalimba notes else if ((note.n_type == LLDB_NT_GNU_ABI_TAG) && (note.n_name == LLDB_NT_OWNER_CSR)) { // We'll consume the payload below. processed = true; arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS); arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::CSR); // TODO At some point the description string could be processed. // It could provide a steer towards the kalimba variant which // this ELF targets. if(note.n_descsz) { const char *cstr = data.GetCStr(&offset, llvm::RoundUpToAlignment (note.n_descsz, 4)); (void)cstr; } } else if (note.n_name == LLDB_NT_OWNER_ANDROID) { arch_spec.GetTriple().setOS(llvm::Triple::OSType::Linux); arch_spec.GetTriple().setEnvironment(llvm::Triple::EnvironmentType::Android); } if (!processed) offset += llvm::RoundUpToAlignment(note.n_descsz, 4); } return error; } //---------------------------------------------------------------------- // GetSectionHeaderInfo //---------------------------------------------------------------------- size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, lldb_private::DataExtractor &object_data, const elf::ELFHeader &header, lldb_private::UUID &uuid, std::string &gnu_debuglink_file, uint32_t &gnu_debuglink_crc, ArchSpec &arch_spec) { // Don't reparse the section headers if we already did that. if (!section_headers.empty()) return section_headers.size(); // Only initialize the arch_spec to okay defaults if they're not already set. // We'll refine this with note data as we parse the notes. if (arch_spec.GetTriple ().getOS () == llvm::Triple::OSType::UnknownOS) { llvm::Triple::OSType ostype; llvm::Triple::OSType spec_ostype; const uint32_t sub_type = subTypeFromElfHeader(header); arch_spec.SetArchitecture (eArchTypeELF, header.e_machine, sub_type, header.e_ident[EI_OSABI]); // // Validate if it is ok to remove GetOsFromOSABI GetOsFromOSABI (header.e_ident[EI_OSABI], ostype); spec_ostype = arch_spec.GetTriple ().getOS (); assert(spec_ostype == ostype); } + if (arch_spec.GetMachine() == llvm::Triple::mips || arch_spec.GetMachine() == llvm::Triple::mipsel + || arch_spec.GetMachine() == llvm::Triple::mips64 || arch_spec.GetMachine() == llvm::Triple::mips64el) + { + switch (header.e_flags & llvm::ELF::EF_MIPS_ARCH_ASE) + { + case llvm::ELF::EF_MIPS_MICROMIPS: + arch_spec.SetFlags (ArchSpec::eMIPSAse_micromips); + break; + case llvm::ELF::EF_MIPS_ARCH_ASE_M16: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mips16); + break; + case llvm::ELF::EF_MIPS_ARCH_ASE_MDMX: + arch_spec.SetFlags (ArchSpec::eMIPSAse_mdmx); + break; + default: + break; + } + } + // If there are no section headers we are done. if (header.e_shnum == 0) return 0; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); section_headers.resize(header.e_shnum); if (section_headers.size() != header.e_shnum) return 0; const size_t sh_size = header.e_shnum * header.e_shentsize; const elf_off sh_offset = header.e_shoff; DataExtractor sh_data; if (sh_data.SetData (object_data, sh_offset, sh_size) != sh_size) return 0; uint32_t idx; lldb::offset_t offset; for (idx = 0, offset = 0; idx < header.e_shnum; ++idx) { if (section_headers[idx].Parse(sh_data, &offset) == false) break; } if (idx < section_headers.size()) section_headers.resize(idx); const unsigned strtab_idx = header.e_shstrndx; if (strtab_idx && strtab_idx < section_headers.size()) { const ELFSectionHeaderInfo &sheader = section_headers[strtab_idx]; const size_t byte_size = sheader.sh_size; const Elf64_Off offset = sheader.sh_offset; lldb_private::DataExtractor shstr_data; if (shstr_data.SetData (object_data, offset, byte_size) == byte_size) { for (SectionHeaderCollIter I = section_headers.begin(); I != section_headers.end(); ++I) { static ConstString g_sect_name_gnu_debuglink (".gnu_debuglink"); const ELFSectionHeaderInfo &header = *I; const uint64_t section_size = header.sh_type == SHT_NOBITS ? 0 : header.sh_size; ConstString name(shstr_data.PeekCStr(I->sh_name)); I->section_name = name; + + if (arch_spec.GetMachine() == llvm::Triple::mips || arch_spec.GetMachine() == llvm::Triple::mipsel + || arch_spec.GetMachine() == llvm::Triple::mips64 || arch_spec.GetMachine() == llvm::Triple::mips64el) + { + if (header.sh_type == SHT_MIPS_ABIFLAGS) + { + DataExtractor data; + if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) + { + lldb::offset_t ase_offset = 12; // MIPS ABI Flags Version: 0 + uint32_t arch_flags = arch_spec.GetFlags (); + arch_flags |= data.GetU32 (&ase_offset); + arch_spec.SetFlags (arch_flags); + } + } + } if (name == g_sect_name_gnu_debuglink) { DataExtractor data; if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) { lldb::offset_t gnu_debuglink_offset = 0; gnu_debuglink_file = data.GetCStr (&gnu_debuglink_offset); gnu_debuglink_offset = llvm::RoundUpToAlignment (gnu_debuglink_offset, 4); data.GetU32 (&gnu_debuglink_offset, &gnu_debuglink_crc, 1); } } // Process ELF note section entries. bool is_note_header = (header.sh_type == SHT_NOTE); // The section header ".note.android.ident" is stored as a // PROGBITS type header but it is actually a note header. static ConstString g_sect_name_android_ident (".note.android.ident"); if (!is_note_header && name == g_sect_name_android_ident) is_note_header = true; if (is_note_header) { // Allow notes to refine module info. DataExtractor data; if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) { Error error = RefineModuleDetailsFromNote (data, arch_spec, uuid); if (error.Fail ()) { if (log) log->Printf ("ObjectFileELF::%s ELF note processing failed: %s", __FUNCTION__, error.AsCString ()); } } } } return section_headers.size(); } } section_headers.clear(); return 0; } size_t ObjectFileELF::GetProgramHeaderCount() { return ParseProgramHeaders(); } const elf::ELFProgramHeader * ObjectFileELF::GetProgramHeaderByIndex(lldb::user_id_t id) { if (!id || !ParseProgramHeaders()) return NULL; if (--id < m_program_headers.size()) return &m_program_headers[id]; return NULL; } DataExtractor ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id) { const elf::ELFProgramHeader *segment_header = GetProgramHeaderByIndex(id); if (segment_header == NULL) return DataExtractor(); return DataExtractor(m_data, segment_header->p_offset, segment_header->p_filesz); } std::string ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { size_t pos = symbol_name.find('@'); return symbol_name.substr(0, pos).str(); } //---------------------------------------------------------------------- // ParseSectionHeaders //---------------------------------------------------------------------- size_t ObjectFileELF::ParseSectionHeaders() { return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc, m_arch_spec); } const ObjectFileELF::ELFSectionHeaderInfo * ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) { if (!id || !ParseSectionHeaders()) return NULL; if (--id < m_section_headers.size()) return &m_section_headers[id]; return NULL; } lldb::user_id_t ObjectFileELF::GetSectionIndexByName(const char* name) { if (!name || !name[0] || !ParseSectionHeaders()) return 0; for (size_t i = 1; i < m_section_headers.size(); ++i) if (m_section_headers[i].section_name == ConstString(name)) return i; return 0; } void ObjectFileELF::CreateSections(SectionList &unified_section_list) { if (!m_sections_ap.get() && ParseSectionHeaders()) { m_sections_ap.reset(new SectionList()); for (SectionHeaderCollIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I) { const ELFSectionHeaderInfo &header = *I; ConstString& name = I->section_name; const uint64_t file_size = header.sh_type == SHT_NOBITS ? 0 : header.sh_size; const uint64_t vm_size = header.sh_flags & SHF_ALLOC ? header.sh_size : 0; static ConstString g_sect_name_text (".text"); static ConstString g_sect_name_data (".data"); static ConstString g_sect_name_bss (".bss"); static ConstString g_sect_name_tdata (".tdata"); static ConstString g_sect_name_tbss (".tbss"); 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"); SectionType sect_type = eSectionTypeOther; bool is_thread_specific = false; if (name == g_sect_name_text) sect_type = eSectionTypeCode; else if (name == g_sect_name_data) sect_type = eSectionTypeData; else if (name == g_sect_name_bss) sect_type = eSectionTypeZeroFill; else if (name == g_sect_name_tdata) { sect_type = eSectionTypeData; is_thread_specific = true; } else if (name == g_sect_name_tbss) { sect_type = eSectionTypeZeroFill; is_thread_specific = true; } // .debug_abbrev – Abbreviations used in the .debug_info section // .debug_aranges – Lookup table for mapping addresses to compilation units // .debug_frame – Call frame information // .debug_info – The core DWARF information section // .debug_line – Line number information // .debug_loc – Location lists used in DW_AT_location attributes // .debug_macinfo – Macro information // .debug_pubnames – Lookup table for mapping object and function names to compilation units // .debug_pubtypes – Lookup table for mapping type names to compilation units // .debug_ranges – Address ranges used in DW_AT_ranges attributes // .debug_str – String table used in .debug_info // MISSING? .gnu_debugdata - "mini debuginfo / MiniDebugInfo" section, http://sourceware.org/gdb/onlinedocs/gdb/MiniDebugInfo.html // MISSING? .debug-index - http://src.chromium.org/viewvc/chrome/trunk/src/build/gdb-add-index?pathrev=144644 // MISSING? .debug_types - Type descriptions from DWARF 4? See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo else if (name == g_sect_name_dwarf_debug_abbrev) sect_type = eSectionTypeDWARFDebugAbbrev; else if (name == g_sect_name_dwarf_debug_aranges) sect_type = eSectionTypeDWARFDebugAranges; else if (name == g_sect_name_dwarf_debug_frame) sect_type = eSectionTypeDWARFDebugFrame; else if (name == g_sect_name_dwarf_debug_info) sect_type = eSectionTypeDWARFDebugInfo; else if (name == g_sect_name_dwarf_debug_line) sect_type = eSectionTypeDWARFDebugLine; else if (name == g_sect_name_dwarf_debug_loc) sect_type = eSectionTypeDWARFDebugLoc; else if (name == g_sect_name_dwarf_debug_macinfo) sect_type = eSectionTypeDWARFDebugMacInfo; else if (name == g_sect_name_dwarf_debug_pubnames) sect_type = eSectionTypeDWARFDebugPubNames; else if (name == g_sect_name_dwarf_debug_pubtypes) sect_type = eSectionTypeDWARFDebugPubTypes; else if (name == g_sect_name_dwarf_debug_ranges) sect_type = eSectionTypeDWARFDebugRanges; else if (name == g_sect_name_dwarf_debug_str) sect_type = eSectionTypeDWARFDebugStr; else if (name == g_sect_name_eh_frame) sect_type = eSectionTypeEHFrame; switch (header.sh_type) { case SHT_SYMTAB: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFSymbolTable; break; case SHT_DYNSYM: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFDynamicSymbols; break; case SHT_RELA: case SHT_REL: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFRelocationEntries; break; case SHT_DYNAMIC: assert (sect_type == eSectionTypeOther); sect_type = eSectionTypeELFDynamicLinkInfo; break; } if (eSectionTypeOther == sect_type) { // the kalimba toolchain assumes that ELF section names are free-form. It does // supports linkscripts which (can) give rise to various arbitarily named // sections being "Code" or "Data". sect_type = kalimbaSectionType(m_header, header); } const uint32_t target_bytes_size = (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? m_arch_spec.GetDataByteSize() : eSectionTypeCode == sect_type ? m_arch_spec.GetCodeByteSize() : 1; elf::elf_xword log2align = (header.sh_addralign==0) ? 0 : llvm::Log2_64(header.sh_addralign); SectionSP section_sp (new Section(GetModule(), // Module to which this section belongs. this, // ObjectFile to which this section belongs and should read section data from. SectionIndex(I), // Section ID. name, // Section name. sect_type, // Section type. header.sh_addr, // VM address. vm_size, // VM size in bytes of this section. header.sh_offset, // Offset of this section in the file. file_size, // Size of the section as found in the file. log2align, // Alignment of the section header.sh_flags, // Flags for this section. target_bytes_size));// Number of host bytes per target byte if (is_thread_specific) section_sp->SetIsThreadSpecific (is_thread_specific); m_sections_ap->AddSection(section_sp); } } if (m_sections_ap.get()) { if (GetType() == eTypeDebugInfo) { static const SectionType g_sections[] = { eSectionTypeDWARFDebugAranges, eSectionTypeDWARFDebugInfo, eSectionTypeDWARFDebugAbbrev, eSectionTypeDWARFDebugFrame, eSectionTypeDWARFDebugLine, eSectionTypeDWARFDebugStr, eSectionTypeDWARFDebugLoc, eSectionTypeDWARFDebugMacInfo, eSectionTypeDWARFDebugPubNames, eSectionTypeDWARFDebugPubTypes, eSectionTypeDWARFDebugRanges, eSectionTypeELFSymbolTable, }; SectionList *elf_section_list = m_sections_ap.get(); for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]); ++idx) { SectionType section_type = g_sections[idx]; SectionSP section_sp (elf_section_list->FindSectionByType (section_type, true)); if (section_sp) { SectionSP module_section_sp (unified_section_list.FindSectionByType (section_type, true)); if (module_section_sp) unified_section_list.ReplaceSection (module_section_sp->GetID(), section_sp); else unified_section_list.AddSection (section_sp); } } } else { unified_section_list = *m_sections_ap; } } } // private unsigned ObjectFileELF::ParseSymbols (Symtab *symtab, user_id_t start_id, SectionList *section_list, const size_t num_symbols, const DataExtractor &symtab_data, const DataExtractor &strtab_data) { ELFSymbol symbol; lldb::offset_t offset = 0; static ConstString text_section_name(".text"); static ConstString init_section_name(".init"); static ConstString fini_section_name(".fini"); static ConstString ctors_section_name(".ctors"); static ConstString dtors_section_name(".dtors"); static ConstString data_section_name(".data"); static ConstString rodata_section_name(".rodata"); static ConstString rodata1_section_name(".rodata1"); static ConstString data2_section_name(".data1"); static ConstString bss_section_name(".bss"); static ConstString opd_section_name(".opd"); // For ppc64 // On Android the oatdata and the oatexec symbols in system@framework@boot.oat covers the full // .text section what causes issues with displaying unusable symbol name to the user and very // slow unwinding speed because the instruction emulation based unwind plans try to emulate all // instructions in these symbols. Don't add these symbols to the symbol list as they have no // use for the debugger and they are causing a lot of trouble. // Filtering can't be restricted to Android because this special object file don't contain the // note section specifying the environment to Android but the custom extension and file name // makes it highly unlikely that this will collide with anything else. bool skip_oatdata_oatexec = m_file.GetFilename() == ConstString("system@framework@boot.oat"); unsigned i; for (i = 0; i < num_symbols; ++i) { if (symbol.Parse(symtab_data, &offset) == false) break; const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); // No need to add non-section symbols that have no names if (symbol.getType() != STT_SECTION && (symbol_name == NULL || symbol_name[0] == '\0')) continue; // Skipping oatdata and oatexec sections if it is requested. See details above the // definition of skip_oatdata_oatexec for the reasons. if (skip_oatdata_oatexec && (::strcmp(symbol_name, "oatdata") == 0 || ::strcmp(symbol_name, "oatexec") == 0)) continue; SectionSP symbol_section_sp; SymbolType symbol_type = eSymbolTypeInvalid; Elf64_Half symbol_idx = symbol.st_shndx; switch (symbol_idx) { case SHN_ABS: symbol_type = eSymbolTypeAbsolute; break; case SHN_UNDEF: symbol_type = eSymbolTypeUndefined; break; default: symbol_section_sp = section_list->GetSectionAtIndex(symbol_idx); break; } // If a symbol is undefined do not process it further even if it has a STT type if (symbol_type != eSymbolTypeUndefined) { switch (symbol.getType()) { default: case STT_NOTYPE: // The symbol's type is not specified. break; case STT_OBJECT: // The symbol is associated with a data object, such as a variable, // an array, etc. symbol_type = eSymbolTypeData; break; case STT_FUNC: // The symbol is associated with a function or other executable code. symbol_type = eSymbolTypeCode; break; case STT_SECTION: // The symbol is associated with a section. Symbol table entries of // this type exist primarily for relocation and normally have // STB_LOCAL binding. break; case STT_FILE: // Conventionally, the symbol's name gives the name of the source // file associated with the object file. A file symbol has STB_LOCAL // binding, its section index is SHN_ABS, and it precedes the other // STB_LOCAL symbols for the file, if it is present. symbol_type = eSymbolTypeSourceFile; break; case STT_GNU_IFUNC: // The symbol is associated with an indirect function. The actual // function will be resolved if it is referenced. symbol_type = eSymbolTypeResolver; break; } } if (symbol_type == eSymbolTypeInvalid) { if (symbol_section_sp) { const ConstString §_name = symbol_section_sp->GetName(); if (sect_name == text_section_name || sect_name == init_section_name || sect_name == fini_section_name || sect_name == ctors_section_name || sect_name == dtors_section_name) { symbol_type = eSymbolTypeCode; } else if (sect_name == data_section_name || sect_name == data2_section_name || sect_name == rodata_section_name || sect_name == rodata1_section_name || sect_name == bss_section_name) { symbol_type = eSymbolTypeData; } } } int64_t symbol_value_offset = 0; uint32_t additional_flags = 0; ArchSpec arch; if (GetArchitecture(arch)) { if (arch.GetMachine() == llvm::Triple::arm) { if (symbol.getBinding() == STB_LOCAL && symbol_name && symbol_name[0] == '$') { // These are reserved for the specification (e.g.: mapping // symbols). We don't want to add them to the symbol table. if (symbol_type == eSymbolTypeCode) { llvm::StringRef symbol_name_ref(symbol_name); if (symbol_name_ref == "$a" || symbol_name_ref.startswith("$a.")) { // $a[.]* - marks an ARM instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCode; } else if (symbol_name_ref == "$b" || symbol_name_ref.startswith("$b.") || symbol_name_ref == "$t" || symbol_name_ref.startswith("$t.")) { // $b[.]* - marks a THUMB BL instruction sequence // $t[.]* - marks a THUMB instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCodeAlternateISA; } else if (symbol_name_ref == "$d" || symbol_name_ref.startswith("$d.")) { // $d[.]* - marks a data item sequence (e.g. lit pool) m_address_class_map[symbol.st_value] = eAddressClassData; } } continue; } } else if (arch.GetMachine() == llvm::Triple::aarch64) { if (symbol.getBinding() == STB_LOCAL && symbol_name && symbol_name[0] == '$') { // These are reserved for the specification (e.g.: mapping // symbols). We don't want to add them to the symbol table. if (symbol_type == eSymbolTypeCode) { llvm::StringRef symbol_name_ref(symbol_name); if (symbol_name_ref == "$x" || symbol_name_ref.startswith("$x.")) { // $x[.]* - marks an A64 instruction sequence m_address_class_map[symbol.st_value] = eAddressClassCode; } else if (symbol_name_ref == "$d" || symbol_name_ref.startswith("$d.")) { // $d[.]* - marks a data item sequence (e.g. lit pool) m_address_class_map[symbol.st_value] = eAddressClassData; } } continue; } } if (arch.GetMachine() == llvm::Triple::arm) { if (symbol_type == eSymbolTypeCode) { if (symbol.st_value & 1) { // Subtracting 1 from the address effectively unsets // the low order bit, which results in the address // actually pointing to the beginning of the symbol. // This delta will be used below in conjunction with // symbol.st_value to produce the final symbol_value // that we store in the symtab. symbol_value_offset = -1; additional_flags = ARM_ELF_SYM_IS_THUMB; m_address_class_map[symbol.st_value^1] = eAddressClassCodeAlternateISA; } else { // This address is ARM m_address_class_map[symbol.st_value] = eAddressClassCode; } } } } // If the symbol section we've found has no data (SHT_NOBITS), then check the module section // list. This can happen if we're parsing the debug file and it has no .text section, for example. if (symbol_section_sp && (symbol_section_sp->GetFileSize() == 0)) { ModuleSP module_sp(GetModule()); if (module_sp) { SectionList *module_section_list = module_sp->GetSectionList(); if (module_section_list && module_section_list != section_list) { const ConstString §_name = symbol_section_sp->GetName(); lldb::SectionSP section_sp (module_section_list->FindSectionByName (sect_name)); if (section_sp && section_sp->GetFileSize()) { symbol_section_sp = section_sp; } } } } // symbol_value_offset may contain 0 for ARM symbols or -1 for // THUMB symbols. See above for more details. uint64_t symbol_value = symbol.st_value + symbol_value_offset; if (symbol_section_sp && CalculateType() != ObjectFile::Type::eTypeObjectFile) symbol_value -= symbol_section_sp->GetFileAddress(); bool is_global = symbol.getBinding() == STB_GLOBAL; uint32_t flags = symbol.st_other << 8 | symbol.st_info | additional_flags; bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; llvm::StringRef symbol_ref(symbol_name); // Symbol names may contain @VERSION suffixes. Find those and strip them temporarily. size_t version_pos = symbol_ref.find('@'); bool has_suffix = version_pos != llvm::StringRef::npos; llvm::StringRef symbol_bare = symbol_ref.substr(0, version_pos); Mangled mangled(ConstString(symbol_bare), is_mangled); // Now append the suffix back to mangled and unmangled names. Only do it if the // demangling was sucessful (string is not empty). if (has_suffix) { llvm::StringRef suffix = symbol_ref.substr(version_pos); llvm::StringRef mangled_name = mangled.GetMangledName().GetStringRef(); if (! mangled_name.empty()) mangled.SetMangledName( ConstString((mangled_name + suffix).str()) ); ConstString demangled = mangled.GetDemangledName(lldb::eLanguageTypeUnknown); llvm::StringRef demangled_name = demangled.GetStringRef(); if (!demangled_name.empty()) mangled.SetDemangledName( ConstString((demangled_name + suffix).str()) ); } Symbol dc_symbol( i + start_id, // ID is the original symbol table index. mangled, symbol_type, // Type of this symbol is_global, // Is this globally visible? false, // Is this symbol debug info? false, // Is this symbol a trampoline? false, // Is this symbol artificial? AddressRange( symbol_section_sp, // Section in which this symbol is defined or null. symbol_value, // Offset in section or symbol value. symbol.st_size), // Size in bytes of this symbol. symbol.st_size != 0, // Size is valid if it is not 0 has_suffix, // Contains linker annotations? flags); // Symbol flags. symtab->AddSymbol(dc_symbol); } return i; } unsigned ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, lldb_private::Section *symtab) { if (symtab->GetObjectFile() != this) { // If the symbol table section is owned by a different object file, have it do the // parsing. ObjectFileELF *obj_file_elf = static_cast(symtab->GetObjectFile()); return obj_file_elf->ParseSymbolTable (symbol_table, start_id, symtab); } // Get section list for this object file. SectionList *section_list = m_sections_ap.get(); if (!section_list) return 0; user_id_t symtab_id = symtab->GetID(); const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id); assert(symtab_hdr->sh_type == SHT_SYMTAB || symtab_hdr->sh_type == SHT_DYNSYM); // sh_link: section header index of associated string table. // Section ID's are ones based. user_id_t strtab_id = symtab_hdr->sh_link + 1; Section *strtab = section_list->FindSectionByID(strtab_id).get(); if (symtab && strtab) { assert (symtab->GetObjectFile() == this); assert (strtab->GetObjectFile() == this); DataExtractor symtab_data; DataExtractor strtab_data; if (ReadSectionData(symtab, symtab_data) && ReadSectionData(strtab, strtab_data)) { size_t num_symbols = symtab_data.GetByteSize() / symtab_hdr->sh_entsize; return ParseSymbols(symbol_table, start_id, section_list, num_symbols, symtab_data, strtab_data); } } return 0; } size_t ObjectFileELF::ParseDynamicSymbols() { if (m_dynamic_symbols.size()) return m_dynamic_symbols.size(); SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Find the SHT_DYNAMIC section. Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); if (!dynsym) return 0; assert (dynsym->GetObjectFile() == this); ELFDynamic symbol; DataExtractor dynsym_data; if (ReadSectionData(dynsym, dynsym_data)) { const lldb::offset_t section_size = dynsym_data.GetByteSize(); lldb::offset_t cursor = 0; while (cursor < section_size) { if (!symbol.Parse(dynsym_data, &cursor)) break; m_dynamic_symbols.push_back(symbol); } } return m_dynamic_symbols.size(); } const ELFDynamic * ObjectFileELF::FindDynamicSymbol(unsigned tag) { if (!ParseDynamicSymbols()) return NULL; DynamicSymbolCollIter I = m_dynamic_symbols.begin(); DynamicSymbolCollIter E = m_dynamic_symbols.end(); for ( ; I != E; ++I) { ELFDynamic *symbol = &*I; if (symbol->d_tag == tag) return symbol; } return NULL; } unsigned ObjectFileELF::PLTRelocationType() { // DT_PLTREL // This member specifies the type of relocation entry to which the // procedure linkage table refers. The d_val member holds DT_REL or // DT_RELA, as appropriate. All relocations in a procedure linkage table // must use the same relocation. const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL); if (symbol) return symbol->d_val; return 0; } // Returns the size of the normal plt entries and the offset of the first normal plt entry. The // 0th entry in the plt table is ususally a resolution entry which have different size in some // architectures then the rest of the plt entries. static std::pair GetPltEntrySizeAndOffset(const ELFSectionHeader* rel_hdr, const ELFSectionHeader* plt_hdr) { const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; // Clang 3.3 sets entsize to 4 for 32-bit binaries, but the plt entries are 16 bytes. // So round the entsize up by the alignment if addralign is set. elf_xword plt_entsize = plt_hdr->sh_addralign ? llvm::RoundUpToAlignment (plt_hdr->sh_entsize, plt_hdr->sh_addralign) : plt_hdr->sh_entsize; if (plt_entsize == 0) { // The linker haven't set the plt_hdr->sh_entsize field. Try to guess the size of the plt // entries based on the number of entries and the size of the plt section with the // asumption that the size of the 0th entry is at least as big as the size of the normal // entries and it isn't mutch bigger then that. if (plt_hdr->sh_addralign) plt_entsize = plt_hdr->sh_size / plt_hdr->sh_addralign / (num_relocations + 1) * plt_hdr->sh_addralign; else plt_entsize = plt_hdr->sh_size / (num_relocations + 1); } elf_xword plt_offset = plt_hdr->sh_size - num_relocations * plt_entsize; return std::make_pair(plt_entsize, plt_offset); } static unsigned ParsePLTRelocations(Symtab *symbol_table, user_id_t start_id, unsigned rel_type, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr, const ELFSectionHeader *plt_hdr, const ELFSectionHeader *sym_hdr, const lldb::SectionSP &plt_section_sp, DataExtractor &rel_data, DataExtractor &symtab_data, DataExtractor &strtab_data) { ELFRelocation rel(rel_type); ELFSymbol symbol; lldb::offset_t offset = 0; uint64_t plt_offset, plt_entsize; std::tie(plt_entsize, plt_offset) = GetPltEntrySizeAndOffset(rel_hdr, plt_hdr); const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); reloc_info_fn reloc_type; reloc_info_fn reloc_symbol; if (hdr->Is32Bit()) { reloc_type = ELFRelocation::RelocType32; reloc_symbol = ELFRelocation::RelocSymbol32; } else { reloc_type = ELFRelocation::RelocType64; reloc_symbol = ELFRelocation::RelocSymbol64; } unsigned slot_type = hdr->GetRelocationJumpSlotType(); unsigned i; for (i = 0; i < num_relocations; ++i) { if (rel.Parse(rel_data, &offset) == false) break; if (reloc_type(rel) != slot_type) continue; lldb::offset_t symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize; if (!symbol.Parse(symtab_data, &symbol_offset)) break; const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; uint64_t plt_index = plt_offset + i * plt_entsize; Symbol jump_symbol( i + start_id, // Symbol table index symbol_name, // symbol name. is_mangled, // is the symbol name mangled? eSymbolTypeTrampoline, // Type of this symbol false, // Is this globally visible? false, // Is this symbol debug info? true, // Is this symbol a trampoline? true, // Is this symbol artificial? plt_section_sp, // Section in which this symbol is defined or null. plt_index, // Offset in section or symbol value. plt_entsize, // Size in bytes of this symbol. true, // Size is valid false, // Contains linker annotations? 0); // Symbol flags. symbol_table->AddSymbol(jump_symbol); } return i; } unsigned ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, user_id_t start_id, const ELFSectionHeaderInfo *rel_hdr, user_id_t rel_id) { assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); // The link field points to the associated symbol table. The info field // points to the section holding the plt. user_id_t symtab_id = rel_hdr->sh_link; user_id_t plt_id = rel_hdr->sh_info; // If the link field doesn't point to the appropriate symbol name table then // try to find it by name as some compiler don't fill in the link fields. if (!symtab_id) symtab_id = GetSectionIndexByName(".dynsym"); if (!plt_id) plt_id = GetSectionIndexByName(".plt"); if (!symtab_id || !plt_id) return 0; // Section ID's are ones based; symtab_id++; plt_id++; const ELFSectionHeaderInfo *plt_hdr = GetSectionHeaderByIndex(plt_id); if (!plt_hdr) return 0; const ELFSectionHeaderInfo *sym_hdr = GetSectionHeaderByIndex(symtab_id); if (!sym_hdr) return 0; SectionList *section_list = m_sections_ap.get(); if (!section_list) return 0; Section *rel_section = section_list->FindSectionByID(rel_id).get(); if (!rel_section) return 0; SectionSP plt_section_sp (section_list->FindSectionByID(plt_id)); if (!plt_section_sp) return 0; Section *symtab = section_list->FindSectionByID(symtab_id).get(); if (!symtab) return 0; // sh_link points to associated string table. Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link + 1).get(); if (!strtab) return 0; DataExtractor rel_data; if (!ReadSectionData(rel_section, rel_data)) return 0; DataExtractor symtab_data; if (!ReadSectionData(symtab, symtab_data)) return 0; DataExtractor strtab_data; if (!ReadSectionData(strtab, strtab_data)) return 0; unsigned rel_type = PLTRelocationType(); if (!rel_type) return 0; return ParsePLTRelocations (symbol_table, start_id, rel_type, &m_header, rel_hdr, plt_hdr, sym_hdr, plt_section_sp, rel_data, symtab_data, strtab_data); } unsigned ObjectFileELF::RelocateSection(Symtab* symtab, const ELFHeader *hdr, const ELFSectionHeader *rel_hdr, const ELFSectionHeader *symtab_hdr, const ELFSectionHeader *debug_hdr, DataExtractor &rel_data, DataExtractor &symtab_data, DataExtractor &debug_data, Section* rel_section) { ELFRelocation rel(rel_hdr->sh_type); lldb::addr_t offset = 0; const unsigned num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); reloc_info_fn reloc_type; reloc_info_fn reloc_symbol; if (hdr->Is32Bit()) { reloc_type = ELFRelocation::RelocType32; reloc_symbol = ELFRelocation::RelocSymbol32; } else { reloc_type = ELFRelocation::RelocType64; reloc_symbol = ELFRelocation::RelocSymbol64; } for (unsigned i = 0; i < num_relocations; ++i) { if (rel.Parse(rel_data, &offset) == false) break; Symbol* symbol = NULL; if (hdr->Is32Bit()) { switch (reloc_type(rel)) { case R_386_32: case R_386_PC32: default: assert(false && "unexpected relocation type"); } } else { switch (reloc_type(rel)) { case R_X86_64_64: { symbol = symtab->FindSymbolByID(reloc_symbol(rel)); if (symbol) { addr_t value = symbol->GetAddressRef().GetFileAddress(); DataBufferSP& data_buffer_sp = debug_data.GetSharedDataBuffer(); uint64_t* dst = reinterpret_cast(data_buffer_sp->GetBytes() + rel_section->GetFileOffset() + ELFRelocation::RelocOffset64(rel)); *dst = value + ELFRelocation::RelocAddend64(rel); } break; } case R_X86_64_32: case R_X86_64_32S: { symbol = symtab->FindSymbolByID(reloc_symbol(rel)); if (symbol) { addr_t value = symbol->GetAddressRef().GetFileAddress(); value += ELFRelocation::RelocAddend32(rel); assert((reloc_type(rel) == R_X86_64_32 && (value <= UINT32_MAX)) || (reloc_type(rel) == R_X86_64_32S && ((int64_t)value <= INT32_MAX && (int64_t)value >= INT32_MIN))); uint32_t truncated_addr = (value & 0xFFFFFFFF); DataBufferSP& data_buffer_sp = debug_data.GetSharedDataBuffer(); uint32_t* dst = reinterpret_cast(data_buffer_sp->GetBytes() + rel_section->GetFileOffset() + ELFRelocation::RelocOffset32(rel)); *dst = truncated_addr; } break; } case R_X86_64_PC32: default: assert(false && "unexpected relocation type"); } } } return 0; } unsigned ObjectFileELF::RelocateDebugSections(const ELFSectionHeader *rel_hdr, user_id_t rel_id) { assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); // Parse in the section list if needed. SectionList *section_list = GetSectionList(); if (!section_list) return 0; // Section ID's are ones based. user_id_t symtab_id = rel_hdr->sh_link + 1; user_id_t debug_id = rel_hdr->sh_info + 1; const ELFSectionHeader *symtab_hdr = GetSectionHeaderByIndex(symtab_id); if (!symtab_hdr) return 0; const ELFSectionHeader *debug_hdr = GetSectionHeaderByIndex(debug_id); if (!debug_hdr) return 0; Section *rel = section_list->FindSectionByID(rel_id).get(); if (!rel) return 0; Section *symtab = section_list->FindSectionByID(symtab_id).get(); if (!symtab) return 0; Section *debug = section_list->FindSectionByID(debug_id).get(); if (!debug) return 0; DataExtractor rel_data; DataExtractor symtab_data; DataExtractor debug_data; if (ReadSectionData(rel, rel_data) && ReadSectionData(symtab, symtab_data) && ReadSectionData(debug, debug_data)) { RelocateSection(m_symtab_ap.get(), &m_header, rel_hdr, symtab_hdr, debug_hdr, rel_data, symtab_data, debug_data, debug); } return 0; } Symtab * ObjectFileELF::GetSymtab() { ModuleSP module_sp(GetModule()); if (!module_sp) return NULL; // We always want to use the main object file so we (hopefully) only have one cached copy // of our symtab, dynamic sections, etc. ObjectFile *module_obj_file = module_sp->GetObjectFile(); if (module_obj_file && module_obj_file != this) return module_obj_file->GetSymtab(); if (m_symtab_ap.get() == NULL) { SectionList *section_list = module_sp->GetSectionList(); if (!section_list) return NULL; uint64_t symbol_id = 0; lldb_private::Mutex::Locker locker(module_sp->GetMutex()); m_symtab_ap.reset(new Symtab(this)); // Sharable objects and dynamic executables usually have 2 distinct symbol // tables, one named ".symtab", and the other ".dynsym". The dynsym is a smaller // version of the symtab that only contains global symbols. The information found // in the dynsym is therefore also found in the symtab, while the reverse is not // necessarily true. Section *symtab = section_list->FindSectionByType (eSectionTypeELFSymbolTable, true).get(); if (!symtab) { // The symtab section is non-allocable and can be stripped, so if it doesn't exist // then use the dynsym section which should always be there. symtab = section_list->FindSectionByType (eSectionTypeELFDynamicSymbols, true).get(); } if (symtab) symbol_id += ParseSymbolTable (m_symtab_ap.get(), symbol_id, symtab); // DT_JMPREL // If present, this entry's d_ptr member holds the address of relocation // entries associated solely with the procedure linkage table. Separating // these relocation entries lets the dynamic linker ignore them during // process initialization, if lazy binding is enabled. If this entry is // present, the related entries of types DT_PLTRELSZ and DT_PLTREL must // also be present. const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL); if (symbol) { // Synthesize trampoline symbols to help navigate the PLT. addr_t addr = symbol->d_ptr; Section *reloc_section = section_list->FindSectionContainingFileAddress(addr).get(); if (reloc_section) { user_id_t reloc_id = reloc_section->GetID(); const ELFSectionHeaderInfo *reloc_header = GetSectionHeaderByIndex(reloc_id); assert(reloc_header); ParseTrampolineSymbols (m_symtab_ap.get(), symbol_id, reloc_header, reloc_id); } } m_symtab_ap->CalculateSymbolSizes(); } for (SectionHeaderCollIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I) { if (I->sh_type == SHT_RELA || I->sh_type == SHT_REL) { if (CalculateType() == eTypeObjectFile) { const char *section_name = I->section_name.AsCString(""); if (strstr(section_name, ".rela.debug") || strstr(section_name, ".rel.debug")) { const ELFSectionHeader &reloc_header = *I; user_id_t reloc_id = SectionIndex(I); RelocateDebugSections(&reloc_header, reloc_id); } } } } return m_symtab_ap.get(); } Symbol * ObjectFileELF::ResolveSymbolForAddress(const Address& so_addr, bool verify_unique) { if (!m_symtab_ap.get()) return nullptr; // GetSymtab() should be called first. const SectionList *section_list = GetSectionList(); if (!section_list) return nullptr; if (DWARFCallFrameInfo *eh_frame = GetUnwindTable().GetEHFrameInfo()) { AddressRange range; if (eh_frame->GetAddressRange (so_addr, range)) { const addr_t file_addr = range.GetBaseAddress().GetFileAddress(); Symbol * symbol = verify_unique ? m_symtab_ap->FindSymbolContainingFileAddress(file_addr) : nullptr; if (symbol) return symbol; // Note that a (stripped) symbol won't be found by GetSymtab()... lldb::SectionSP eh_sym_section_sp = section_list->FindSectionContainingFileAddress(file_addr); if (eh_sym_section_sp.get()) { addr_t section_base = eh_sym_section_sp->GetFileAddress(); addr_t offset = file_addr - section_base; uint64_t symbol_id = m_symtab_ap->GetNumSymbols(); Symbol eh_symbol( symbol_id, // Symbol table index. "???", // Symbol name. false, // Is the symbol name mangled? eSymbolTypeCode, // Type of this symbol. true, // Is this globally visible? false, // Is this symbol debug info? false, // Is this symbol a trampoline? true, // Is this symbol artificial? eh_sym_section_sp, // Section in which this symbol is defined or null. offset, // Offset in section or symbol value. range.GetByteSize(), // Size in bytes of this symbol. true, // Size is valid. false, // Contains linker annotations? 0); // Symbol flags. if (symbol_id == m_symtab_ap->AddSymbol(eh_symbol)) return m_symtab_ap->SymbolAtIndex(symbol_id); } } } return nullptr; } bool ObjectFileELF::IsStripped () { // TODO: determine this for ELF return false; } //===----------------------------------------------------------------------===// // Dump // // Dump the specifics of the runtime file container (such as any headers // segments, sections, etc). //---------------------------------------------------------------------- void ObjectFileELF::Dump(Stream *s) { DumpELFHeader(s, m_header); s->EOL(); DumpELFProgramHeaders(s); s->EOL(); DumpELFSectionHeaders(s); s->EOL(); SectionList *section_list = GetSectionList(); if (section_list) section_list->Dump(s, NULL, true, UINT32_MAX); Symtab *symtab = GetSymtab(); if (symtab) symtab->Dump(s, NULL, eSortOrderNone); s->EOL(); DumpDependentModules(s); s->EOL(); } //---------------------------------------------------------------------- // DumpELFHeader // // Dump the ELF header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader(Stream *s, const ELFHeader &header) { s->PutCString("ELF Header\n"); s->Printf("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); s->Printf("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); s->Printf("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); s->Printf("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); s->Printf("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); s->Printf("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); s->Printf("e_type = 0x%4.4x ", header.e_type); DumpELFHeader_e_type(s, header.e_type); s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); s->Printf("e_version = 0x%8.8x\n", header.e_version); s->Printf("e_entry = 0x%8.8" PRIx64 "\n", header.e_entry); s->Printf("e_phoff = 0x%8.8" PRIx64 "\n", header.e_phoff); s->Printf("e_shoff = 0x%8.8" PRIx64 "\n", header.e_shoff); s->Printf("e_flags = 0x%8.8x\n", header.e_flags); s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); } //---------------------------------------------------------------------- // DumpELFHeader_e_type // // Dump an token value for the ELF header member e_type //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader_e_type(Stream *s, elf_half e_type) { switch (e_type) { case ET_NONE: *s << "ET_NONE"; break; case ET_REL: *s << "ET_REL"; break; case ET_EXEC: *s << "ET_EXEC"; break; case ET_DYN: *s << "ET_DYN"; break; case ET_CORE: *s << "ET_CORE"; break; default: break; } } //---------------------------------------------------------------------- // DumpELFHeader_e_ident_EI_DATA // // Dump an token value for the ELF header member e_ident[EI_DATA] //---------------------------------------------------------------------- void ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, unsigned char ei_data) { switch (ei_data) { case ELFDATANONE: *s << "ELFDATANONE"; break; case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; default: break; } } //---------------------------------------------------------------------- // DumpELFProgramHeader // // Dump a single ELF program header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader(Stream *s, const ELFProgramHeader &ph) { DumpELFProgramHeader_p_type(s, ph.p_type); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, ph.p_offset, ph.p_vaddr, ph.p_paddr); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8x (", ph.p_filesz, ph.p_memsz, ph.p_flags); DumpELFProgramHeader_p_flags(s, ph.p_flags); s->Printf(") %8.8" PRIx64, ph.p_align); } //---------------------------------------------------------------------- // DumpELFProgramHeader_p_type // // Dump an token value for the ELF program header member p_type which // describes the type of the program header // ---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) { const int kStrWidth = 15; switch (p_type) { CASE_AND_STREAM(s, PT_NULL , kStrWidth); CASE_AND_STREAM(s, PT_LOAD , kStrWidth); CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); CASE_AND_STREAM(s, PT_INTERP , kStrWidth); CASE_AND_STREAM(s, PT_NOTE , kStrWidth); CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); CASE_AND_STREAM(s, PT_PHDR , kStrWidth); CASE_AND_STREAM(s, PT_TLS , kStrWidth); CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth); default: s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); break; } } //---------------------------------------------------------------------- // DumpELFProgramHeader_p_flags // // Dump an token value for the ELF program header member p_flags //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, elf_word p_flags) { *s << ((p_flags & PF_X) ? "PF_X" : " ") << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') << ((p_flags & PF_W) ? "PF_W" : " ") << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') << ((p_flags & PF_R) ? "PF_R" : " "); } //---------------------------------------------------------------------- // DumpELFProgramHeaders // // Dump all of the ELF program header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFProgramHeaders(Stream *s) { if (!ParseProgramHeaders()) return; s->PutCString("Program Headers\n"); s->PutCString("IDX p_type p_offset p_vaddr p_paddr " "p_filesz p_memsz p_flags p_align\n"); s->PutCString("==== --------------- -------- -------- -------- " "-------- -------- ------------------------- --------\n"); uint32_t idx = 0; for (ProgramHeaderCollConstIter I = m_program_headers.begin(); I != m_program_headers.end(); ++I, ++idx) { s->Printf("[%2u] ", idx); ObjectFileELF::DumpELFProgramHeader(s, *I); s->EOL(); } } //---------------------------------------------------------------------- // DumpELFSectionHeader // // Dump a single ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader(Stream *s, const ELFSectionHeaderInfo &sh) { s->Printf("%8.8x ", sh.sh_name); DumpELFSectionHeader_sh_type(s, sh.sh_type); s->Printf(" %8.8" PRIx64 " (", sh.sh_flags); DumpELFSectionHeader_sh_flags(s, sh.sh_flags); s->Printf(") %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addr, sh.sh_offset, sh.sh_size); s->Printf(" %8.8x %8.8x", sh.sh_link, sh.sh_info); s->Printf(" %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addralign, sh.sh_entsize); } //---------------------------------------------------------------------- // DumpELFSectionHeader_sh_type // // Dump an token value for the ELF section header member sh_type which // describes the type of the section //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, elf_word sh_type) { const int kStrWidth = 12; switch (sh_type) { CASE_AND_STREAM(s, SHT_NULL , kStrWidth); CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); CASE_AND_STREAM(s, SHT_RELA , kStrWidth); CASE_AND_STREAM(s, SHT_HASH , kStrWidth); CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); CASE_AND_STREAM(s, SHT_REL , kStrWidth); CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); default: s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); break; } } //---------------------------------------------------------------------- // DumpELFSectionHeader_sh_flags // // Dump an token value for the ELF section header member sh_flags //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, elf_xword sh_flags) { *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); } //---------------------------------------------------------------------- // DumpELFSectionHeaders // // Dump all of the ELF section header to the specified output stream //---------------------------------------------------------------------- void ObjectFileELF::DumpELFSectionHeaders(Stream *s) { if (!ParseSectionHeaders()) return; s->PutCString("Section Headers\n"); s->PutCString("IDX name type flags " "addr offset size link info addralgn " "entsize Name\n"); s->PutCString("==== -------- ------------ -------------------------------- " "-------- -------- -------- -------- -------- -------- " "-------- ====================\n"); uint32_t idx = 0; for (SectionHeaderCollConstIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I, ++idx) { s->Printf("[%2u] ", idx); ObjectFileELF::DumpELFSectionHeader(s, *I); const char* section_name = I->section_name.AsCString(""); if (section_name) *s << ' ' << section_name << "\n"; } } void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) { size_t num_modules = ParseDependentModules(); if (num_modules > 0) { s->PutCString("Dependent Modules:\n"); for (unsigned i = 0; i < num_modules; ++i) { const FileSpec &spec = m_filespec_ap->GetFileSpecAtIndex(i); s->Printf(" %s\n", spec.GetFilename().GetCString()); } } } bool ObjectFileELF::GetArchitecture (ArchSpec &arch) { if (!ParseHeader()) return false; if (m_section_headers.empty()) { // Allow elf notes to be parsed which may affect the detected architecture. ParseSectionHeaders(); } arch = m_arch_spec; return true; } ObjectFile::Type ObjectFileELF::CalculateType() { switch (m_header.e_type) { case llvm::ELF::ET_NONE: // 0 - No file type return eTypeUnknown; case llvm::ELF::ET_REL: // 1 - Relocatable file return eTypeObjectFile; case llvm::ELF::ET_EXEC: // 2 - Executable file return eTypeExecutable; case llvm::ELF::ET_DYN: // 3 - Shared object file return eTypeSharedLibrary; case ET_CORE: // 4 - Core file return eTypeCoreFile; default: break; } return eTypeUnknown; } ObjectFile::Strata ObjectFileELF::CalculateStrata() { switch (m_header.e_type) { case llvm::ELF::ET_NONE: // 0 - No file type return eStrataUnknown; case llvm::ELF::ET_REL: // 1 - Relocatable file return eStrataUnknown; case llvm::ELF::ET_EXEC: // 2 - Executable file // TODO: is there any way to detect that an executable is a kernel // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUser; case llvm::ELF::ET_DYN: // 3 - Shared object file // TODO: is there any way to detect that an shared library is a kernel // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; case ET_CORE: // 4 - Core file // TODO: is there any way to detect that an core file is a kernel // related executable by inspecting the program headers, section // headers, symbols, or any other flag bits??? return eStrataUnknown; default: break; } return eStrataUnknown; } Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips64.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips64.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips64.h (nonexistent) @@ -1,336 +0,0 @@ -//===-- RegisterContext_mips64.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_RegisterContext_mips64_H_ -#define liblldb_RegisterContext_mips64_H_ - -// GCC and DWARF Register numbers (eRegisterKindGCC & eRegisterKindDWARF) -enum -{ - // GP Registers - gcc_dwarf_zero_mips = 0, - gcc_dwarf_r1_mips, - gcc_dwarf_r2_mips, - gcc_dwarf_r3_mips, - gcc_dwarf_r4_mips, - gcc_dwarf_r5_mips, - gcc_dwarf_r6_mips, - gcc_dwarf_r7_mips, - gcc_dwarf_r8_mips, - gcc_dwarf_r9_mips, - gcc_dwarf_r10_mips, - gcc_dwarf_r11_mips, - gcc_dwarf_r12_mips, - gcc_dwarf_r13_mips, - gcc_dwarf_r14_mips, - gcc_dwarf_r15_mips, - gcc_dwarf_r16_mips, - gcc_dwarf_r17_mips, - gcc_dwarf_r18_mips, - gcc_dwarf_r19_mips, - gcc_dwarf_r20_mips, - gcc_dwarf_r21_mips, - gcc_dwarf_r22_mips, - gcc_dwarf_r23_mips, - gcc_dwarf_r24_mips, - gcc_dwarf_r25_mips, - gcc_dwarf_r26_mips, - gcc_dwarf_r27_mips, - gcc_dwarf_gp_mips, - gcc_dwarf_sp_mips, - gcc_dwarf_r30_mips, - gcc_dwarf_ra_mips, - gcc_dwarf_lo_mips, - gcc_dwarf_hi_mips, - gcc_dwarf_pc_mips, - gcc_dwarf_bad_mips, - gcc_dwarf_sr_mips, - gcc_dwarf_cause_mips, - gcc_dwarf_f0_mips, - gcc_dwarf_f1_mips, - gcc_dwarf_f2_mips, - gcc_dwarf_f3_mips, - gcc_dwarf_f4_mips, - gcc_dwarf_f5_mips, - gcc_dwarf_f6_mips, - gcc_dwarf_f7_mips, - gcc_dwarf_f8_mips, - gcc_dwarf_f9_mips, - gcc_dwarf_f10_mips, - gcc_dwarf_f11_mips, - gcc_dwarf_f12_mips, - gcc_dwarf_f13_mips, - gcc_dwarf_f14_mips, - gcc_dwarf_f15_mips, - gcc_dwarf_f16_mips, - gcc_dwarf_f17_mips, - gcc_dwarf_f18_mips, - gcc_dwarf_f19_mips, - gcc_dwarf_f20_mips, - gcc_dwarf_f21_mips, - gcc_dwarf_f22_mips, - gcc_dwarf_f23_mips, - gcc_dwarf_f24_mips, - gcc_dwarf_f25_mips, - gcc_dwarf_f26_mips, - gcc_dwarf_f27_mips, - gcc_dwarf_f28_mips, - gcc_dwarf_f29_mips, - gcc_dwarf_f30_mips, - gcc_dwarf_f31_mips, - gcc_dwarf_fcsr_mips, - gcc_dwarf_fir_mips, - gcc_dwarf_ic_mips, - gcc_dwarf_dummy_mips -}; - -enum -{ - gcc_dwarf_zero_mips64 = 0, - gcc_dwarf_r1_mips64, - gcc_dwarf_r2_mips64, - gcc_dwarf_r3_mips64, - gcc_dwarf_r4_mips64, - gcc_dwarf_r5_mips64, - gcc_dwarf_r6_mips64, - gcc_dwarf_r7_mips64, - gcc_dwarf_r8_mips64, - gcc_dwarf_r9_mips64, - gcc_dwarf_r10_mips64, - gcc_dwarf_r11_mips64, - gcc_dwarf_r12_mips64, - gcc_dwarf_r13_mips64, - gcc_dwarf_r14_mips64, - gcc_dwarf_r15_mips64, - gcc_dwarf_r16_mips64, - gcc_dwarf_r17_mips64, - gcc_dwarf_r18_mips64, - gcc_dwarf_r19_mips64, - gcc_dwarf_r20_mips64, - gcc_dwarf_r21_mips64, - gcc_dwarf_r22_mips64, - gcc_dwarf_r23_mips64, - gcc_dwarf_r24_mips64, - gcc_dwarf_r25_mips64, - gcc_dwarf_r26_mips64, - gcc_dwarf_r27_mips64, - gcc_dwarf_gp_mips64, - gcc_dwarf_sp_mips64, - gcc_dwarf_r30_mips64, - gcc_dwarf_ra_mips64, - gcc_dwarf_sr_mips64, - gcc_dwarf_lo_mips64, - gcc_dwarf_hi_mips64, - gcc_dwarf_bad_mips64, - gcc_dwarf_cause_mips64, - gcc_dwarf_pc_mips64, - gcc_dwarf_f0_mips64, - gcc_dwarf_f1_mips64, - gcc_dwarf_f2_mips64, - gcc_dwarf_f3_mips64, - gcc_dwarf_f4_mips64, - gcc_dwarf_f5_mips64, - gcc_dwarf_f6_mips64, - gcc_dwarf_f7_mips64, - gcc_dwarf_f8_mips64, - gcc_dwarf_f9_mips64, - gcc_dwarf_f10_mips64, - gcc_dwarf_f11_mips64, - gcc_dwarf_f12_mips64, - gcc_dwarf_f13_mips64, - gcc_dwarf_f14_mips64, - gcc_dwarf_f15_mips64, - gcc_dwarf_f16_mips64, - gcc_dwarf_f17_mips64, - gcc_dwarf_f18_mips64, - gcc_dwarf_f19_mips64, - gcc_dwarf_f20_mips64, - gcc_dwarf_f21_mips64, - gcc_dwarf_f22_mips64, - gcc_dwarf_f23_mips64, - gcc_dwarf_f24_mips64, - gcc_dwarf_f25_mips64, - gcc_dwarf_f26_mips64, - gcc_dwarf_f27_mips64, - gcc_dwarf_f28_mips64, - gcc_dwarf_f29_mips64, - gcc_dwarf_f30_mips64, - gcc_dwarf_f31_mips64, - gcc_dwarf_fcsr_mips64, - gcc_dwarf_fir_mips64, - gcc_dwarf_ic_mips64, - gcc_dwarf_dummy_mips64 -}; - -// GDB Register numbers (eRegisterKindGDB) -enum -{ - gdb_zero_mips = 0, - gdb_r1_mips, - gdb_r2_mips, - gdb_r3_mips, - gdb_r4_mips, - gdb_r5_mips, - gdb_r6_mips, - gdb_r7_mips, - gdb_r8_mips, - gdb_r9_mips, - gdb_r10_mips, - gdb_r11_mips, - gdb_r12_mips, - gdb_r13_mips, - gdb_r14_mips, - gdb_r15_mips, - gdb_r16_mips, - gdb_r17_mips, - gdb_r18_mips, - gdb_r19_mips, - gdb_r20_mips, - gdb_r21_mips, - gdb_r22_mips, - gdb_r23_mips, - gdb_r24_mips, - gdb_r25_mips, - gdb_r26_mips, - gdb_r27_mips, - gdb_gp_mips, - gdb_sp_mips, - gdb_r30_mips, - gdb_ra_mips, - gdb_lo_mips, - gdb_hi_mips, - gdb_pc_mips, - gdb_bad_mips, - gdb_sr_mips, - gdb_cause_mips, - gdb_f0_mips, - gdb_f1_mips, - gdb_f2_mips, - gdb_f3_mips, - gdb_f4_mips, - gdb_f5_mips, - gdb_f6_mips, - gdb_f7_mips, - gdb_f8_mips, - gdb_f9_mips, - gdb_f10_mips, - gdb_f11_mips, - gdb_f12_mips, - gdb_f13_mips, - gdb_f14_mips, - gdb_f15_mips, - gdb_f16_mips, - gdb_f17_mips, - gdb_f18_mips, - gdb_f19_mips, - gdb_f20_mips, - gdb_f21_mips, - gdb_f22_mips, - gdb_f23_mips, - gdb_f24_mips, - gdb_f25_mips, - gdb_f26_mips, - gdb_f27_mips, - gdb_f28_mips, - gdb_f29_mips, - gdb_f30_mips, - gdb_f31_mips, - gdb_fcsr_mips, - gdb_fir_mips, - gdb_ic_mips, - gdb_dummy_mips -}; - -enum -{ - gdb_zero_mips64 = 0, - gdb_r1_mips64, - gdb_r2_mips64, - gdb_r3_mips64, - gdb_r4_mips64, - gdb_r5_mips64, - gdb_r6_mips64, - gdb_r7_mips64, - gdb_r8_mips64, - gdb_r9_mips64, - gdb_r10_mips64, - gdb_r11_mips64, - gdb_r12_mips64, - gdb_r13_mips64, - gdb_r14_mips64, - gdb_r15_mips64, - gdb_r16_mips64, - gdb_r17_mips64, - gdb_r18_mips64, - gdb_r19_mips64, - gdb_r20_mips64, - gdb_r21_mips64, - gdb_r22_mips64, - gdb_r23_mips64, - gdb_r24_mips64, - gdb_r25_mips64, - gdb_r26_mips64, - gdb_r27_mips64, - gdb_gp_mips64, - gdb_sp_mips64, - gdb_r30_mips64, - gdb_ra_mips64, - gdb_sr_mips64, - gdb_lo_mips64, - gdb_hi_mips64, - gdb_bad_mips64, - gdb_cause_mips64, - gdb_pc_mips64, - gdb_f0_mips64, - gdb_f1_mips64, - gdb_f2_mips64, - gdb_f3_mips64, - gdb_f4_mips64, - gdb_f5_mips64, - gdb_f6_mips64, - gdb_f7_mips64, - gdb_f8_mips64, - gdb_f9_mips64, - gdb_f10_mips64, - gdb_f11_mips64, - gdb_f12_mips64, - gdb_f13_mips64, - gdb_f14_mips64, - gdb_f15_mips64, - gdb_f16_mips64, - gdb_f17_mips64, - gdb_f18_mips64, - gdb_f19_mips64, - gdb_f20_mips64, - gdb_f21_mips64, - gdb_f22_mips64, - gdb_f23_mips64, - gdb_f24_mips64, - gdb_f25_mips64, - gdb_f26_mips64, - gdb_f27_mips64, - gdb_f28_mips64, - gdb_f29_mips64, - gdb_f30_mips64, - gdb_f31_mips64, - gdb_fcsr_mips64, - gdb_fir_mips64, - gdb_ic_mips64, - gdb_dummy_mips64 -}; - -// FP registers -struct FPR_mips -{ - uint64_t fp_reg[32]; - uint32_t fcsr; /* FPU status register */ - uint32_t fir; /* FPU control register */ -}; - -#endif // liblldb_RegisterContext_mips64_H_ Property changes on: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips64.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips64-register-enums.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips64-register-enums.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips64-register-enums.h (nonexistent) @@ -1,199 +0,0 @@ -//===-- lldb-mips64-register-enums.h -------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef lldb_mips64_register_enums_h -#define lldb_mips64_register_enums_h - -namespace lldb_private -{ - // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) - - //--------------------------------------------------------------------------- - // Internal codes for all mips registers. - //--------------------------------------------------------------------------- - enum - { - k_first_gpr_mips, - gpr_zero_mips = k_first_gpr_mips, - gpr_r1_mips, - gpr_r2_mips, - gpr_r3_mips, - gpr_r4_mips, - gpr_r5_mips, - gpr_r6_mips, - gpr_r7_mips, - gpr_r8_mips, - gpr_r9_mips, - gpr_r10_mips, - gpr_r11_mips, - gpr_r12_mips, - gpr_r13_mips, - gpr_r14_mips, - gpr_r15_mips, - gpr_r16_mips, - gpr_r17_mips, - gpr_r18_mips, - gpr_r19_mips, - gpr_r20_mips, - gpr_r21_mips, - gpr_r22_mips, - gpr_r23_mips, - gpr_r24_mips, - gpr_r25_mips, - gpr_r26_mips, - gpr_r27_mips, - gpr_gp_mips, - gpr_sp_mips, - gpr_r30_mips, - gpr_ra_mips, - gpr_mullo_mips, - gpr_mulhi_mips, - gpr_pc_mips, - gpr_badvaddr_mips, - gpr_sr_mips, - gpr_cause_mips, - - k_last_gpr_mips = gpr_cause_mips, - - k_first_fpr_mips, - fpr_f0_mips = k_first_fpr_mips, - fpr_f1_mips, - fpr_f2_mips, - fpr_f3_mips, - fpr_f4_mips, - fpr_f5_mips, - fpr_f6_mips, - fpr_f7_mips, - fpr_f8_mips, - fpr_f9_mips, - fpr_f10_mips, - fpr_f11_mips, - fpr_f12_mips, - fpr_f13_mips, - fpr_f14_mips, - fpr_f15_mips, - fpr_f16_mips, - fpr_f17_mips, - fpr_f18_mips, - fpr_f19_mips, - fpr_f20_mips, - fpr_f21_mips, - fpr_f22_mips, - fpr_f23_mips, - fpr_f24_mips, - fpr_f25_mips, - fpr_f26_mips, - fpr_f27_mips, - fpr_f28_mips, - fpr_f29_mips, - fpr_f30_mips, - fpr_f31_mips, - fpr_fcsr_mips, - fpr_fir_mips, - k_last_fpr_mips = fpr_fir_mips, - - k_num_registers_mips, - k_num_gpr_registers_mips = k_last_gpr_mips - k_first_gpr_mips + 1, - k_num_fpr_registers_mips = k_last_fpr_mips - k_first_fpr_mips + 1, - k_num_user_registers_mips = k_num_gpr_registers_mips + k_num_fpr_registers_mips, - }; - - //--------------------------------------------------------------------------- - // Internal codes for all mips64 registers. - //--------------------------------------------------------------------------- - enum - { - k_first_gpr_mips64, - gpr_zero_mips64 = k_first_gpr_mips64, - gpr_r1_mips64, - gpr_r2_mips64, - gpr_r3_mips64, - gpr_r4_mips64, - gpr_r5_mips64, - gpr_r6_mips64, - gpr_r7_mips64, - gpr_r8_mips64, - gpr_r9_mips64, - gpr_r10_mips64, - gpr_r11_mips64, - gpr_r12_mips64, - gpr_r13_mips64, - gpr_r14_mips64, - gpr_r15_mips64, - gpr_r16_mips64, - gpr_r17_mips64, - gpr_r18_mips64, - gpr_r19_mips64, - gpr_r20_mips64, - gpr_r21_mips64, - gpr_r22_mips64, - gpr_r23_mips64, - gpr_r24_mips64, - gpr_r25_mips64, - gpr_r26_mips64, - gpr_r27_mips64, - gpr_gp_mips64, - gpr_sp_mips64, - gpr_r30_mips64, - gpr_ra_mips64, - gpr_mullo_mips64, - gpr_mulhi_mips64, - gpr_pc_mips64, - gpr_badvaddr_mips64, - gpr_sr_mips64, - gpr_cause_mips64, - gpr_ic_mips64, - gpr_dummy_mips64, - - k_last_gpr_mips64 = gpr_dummy_mips64, - - k_first_fpr_mips64, - fpr_f0_mips64 = k_first_fpr_mips64, - fpr_f1_mips64, - fpr_f2_mips64, - fpr_f3_mips64, - fpr_f4_mips64, - fpr_f5_mips64, - fpr_f6_mips64, - fpr_f7_mips64, - fpr_f8_mips64, - fpr_f9_mips64, - fpr_f10_mips64, - fpr_f11_mips64, - fpr_f12_mips64, - fpr_f13_mips64, - fpr_f14_mips64, - fpr_f15_mips64, - fpr_f16_mips64, - fpr_f17_mips64, - fpr_f18_mips64, - fpr_f19_mips64, - fpr_f20_mips64, - fpr_f21_mips64, - fpr_f22_mips64, - fpr_f23_mips64, - fpr_f24_mips64, - fpr_f25_mips64, - fpr_f26_mips64, - fpr_f27_mips64, - fpr_f28_mips64, - fpr_f29_mips64, - fpr_f30_mips64, - fpr_f31_mips64, - fpr_fcsr_mips64, - fpr_fir_mips64, - k_last_fpr_mips64 = fpr_fir_mips64, - - k_num_registers_mips64, - k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1, - k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1, - }; -} - -#endif // #ifndef fpr_mips64_register_enums_h Property changes on: vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips64-register-enums.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextFreeBSD_mips64.cpp (revision 287514) @@ -1,93 +1,93 @@ //===-- RegisterContextFreeBSD_mips64.cpp ----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// #include #include "RegisterContextPOSIX_mips64.h" #include "RegisterContextFreeBSD_mips64.h" using namespace lldb_private; using namespace lldb; // http://svnweb.freebsd.org/base/head/sys/mips/include/regnum.h typedef struct _GPR { uint64_t zero; uint64_t r1; uint64_t r2; uint64_t r3; uint64_t r4; uint64_t r5; uint64_t r6; uint64_t r7; uint64_t r8; uint64_t r9; uint64_t r10; uint64_t r11; uint64_t r12; uint64_t r13; uint64_t r14; uint64_t r15; uint64_t r16; uint64_t r17; uint64_t r18; uint64_t r19; uint64_t r20; uint64_t r21; uint64_t r22; uint64_t r23; uint64_t r24; uint64_t r25; uint64_t r26; uint64_t r27; uint64_t gp; uint64_t sp; uint64_t r30; uint64_t ra; uint64_t sr; uint64_t mullo; uint64_t mulhi; uint64_t badvaddr; uint64_t cause; uint64_t pc; uint64_t ic; uint64_t dummy; -} GPR; +} GPR_freebsd_mips; //--------------------------------------------------------------------------- // Include RegisterInfos_mips64 to declare our g_register_infos_mips64 structure. //--------------------------------------------------------------------------- #define DECLARE_REGISTER_INFOS_MIPS64_STRUCT #include "RegisterInfos_mips64.h" #undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT RegisterContextFreeBSD_mips64::RegisterContextFreeBSD_mips64(const ArchSpec &target_arch) : RegisterInfoInterface(target_arch) { } size_t RegisterContextFreeBSD_mips64::GetGPRSize() const { - return sizeof(GPR); + return sizeof(GPR_freebsd_mips); } const RegisterInfo * RegisterContextFreeBSD_mips64::GetRegisterInfo() const { assert (m_target_arch.GetCore() == ArchSpec::eCore_mips64); return g_register_infos_mips64; } uint32_t RegisterContextFreeBSD_mips64::GetRegisterCount () const { return static_cast (sizeof (g_register_infos_mips64) / sizeof (g_register_infos_mips64 [0])); } Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.cpp (revision 287514) @@ -1,102 +1,67 @@ //===-- RegisterContextLinux_mips.cpp ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// #include #include // For GDB, GCC and DWARF Register numbers #include "RegisterContextLinux_mips.h" // Internal codes for mips registers -#include "lldb-mips64-register-enums.h" -#include "RegisterContext_mips64.h" +#include "lldb-mips-linux-register-enums.h" +// For GP and FP buffers +#include "RegisterContext_mips.h" + using namespace lldb_private; using namespace lldb; -// GP registers -typedef struct _GPR -{ - uint32_t zero; - uint32_t r1; - uint32_t r2; - uint32_t r3; - uint32_t r4; - uint32_t r5; - uint32_t r6; - uint32_t r7; - uint32_t r8; - uint32_t r9; - uint32_t r10; - uint32_t r11; - uint32_t r12; - uint32_t r13; - uint32_t r14; - uint32_t r15; - uint32_t r16; - uint32_t r17; - uint32_t r18; - uint32_t r19; - uint32_t r20; - uint32_t r21; - uint32_t r22; - uint32_t r23; - uint32_t r24; - uint32_t r25; - uint32_t r26; - uint32_t r27; - uint32_t gp; - uint32_t sp; - uint32_t r30; - uint32_t ra; - uint32_t mullo; - uint32_t mulhi; - uint32_t pc; - uint32_t badvaddr; - uint32_t sr; - uint32_t cause; -} GPR; - //--------------------------------------------------------------------------- // Include RegisterInfos_mips to declare our g_register_infos_mips structure. //--------------------------------------------------------------------------- #define DECLARE_REGISTER_INFOS_MIPS_STRUCT #include "RegisterInfos_mips.h" #undef DECLARE_REGISTER_INFOS_MIPS_STRUCT RegisterContextLinux_mips::RegisterContextLinux_mips(const ArchSpec &target_arch) : RegisterInfoInterface(target_arch) { } size_t RegisterContextLinux_mips::GetGPRSize() const { - return sizeof(GPR); + return sizeof(GPR_linux_mips); } const RegisterInfo * RegisterContextLinux_mips::GetRegisterInfo() const { switch (m_target_arch.GetMachine()) { case llvm::Triple::mips: case llvm::Triple::mipsel: return g_register_infos_mips; default: assert(false && "Unhandled target architecture."); return NULL; } } uint32_t RegisterContextLinux_mips::GetRegisterCount () const { return static_cast (sizeof (g_register_infos_mips) / sizeof (g_register_infos_mips [0])); +} + +uint32_t +RegisterContextLinux_mips::GetUserRegisterCount () const +{ + return static_cast (k_num_user_registers_mips); } Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips.h (revision 287514) @@ -1,32 +1,35 @@ //===-- RegisterContextLinux_mips.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_RegisterContextLinux_mips_H_ #define liblldb_RegisterContextLinux_mips_H_ #include "lldb/lldb-private.h" #include "RegisterInfoInterface.h" class RegisterContextLinux_mips : public lldb_private::RegisterInfoInterface { public: RegisterContextLinux_mips(const lldb_private::ArchSpec &target_arch); size_t GetGPRSize() const override; const lldb_private::RegisterInfo * GetRegisterInfo() const override; uint32_t GetRegisterCount () const override; + + uint32_t + GetUserRegisterCount () const override; }; #endif Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.cpp (revision 287514) @@ -1,143 +1,126 @@ //===-- RegisterContextLinux_mips64.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 (__mips__) #include #include // For GDB, GCC and DWARF Register numbers #include "RegisterContextLinux_mips64.h" -// Internal codes for all mips64 registers -#include "lldb-mips64-register-enums.h" -#include "RegisterContext_mips64.h" +// For GP and FP buffers +#include "RegisterContext_mips.h" +// Internal codes for all mips32 and mips64 registers +#include "lldb-mips-linux-register-enums.h" + using namespace lldb; using namespace lldb_private; -// GP registers -typedef struct _GPR -{ - uint64_t zero; - uint64_t r1; - uint64_t r2; - uint64_t r3; - uint64_t r4; - uint64_t r5; - uint64_t r6; - uint64_t r7; - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t r16; - uint64_t r17; - uint64_t r18; - uint64_t r19; - uint64_t r20; - uint64_t r21; - uint64_t r22; - uint64_t r23; - uint64_t r24; - uint64_t r25; - uint64_t r26; - uint64_t r27; - uint64_t gp; - uint64_t sp; - uint64_t r30; - uint64_t ra; - uint64_t mullo; - uint64_t mulhi; - uint64_t pc; - uint64_t badvaddr; - uint64_t sr; - uint64_t cause; - uint64_t ic; - uint64_t dummy; -} GPR; - //--------------------------------------------------------------------------- // Include RegisterInfos_mips64 to declare our g_register_infos_mips64 structure. //--------------------------------------------------------------------------- #define DECLARE_REGISTER_INFOS_MIPS64_STRUCT +#define LINUX_MIPS64 #include "RegisterInfos_mips64.h" +#undef LINUX_MIPS64 #undef DECLARE_REGISTER_INFOS_MIPS64_STRUCT //--------------------------------------------------------------------------- // Include RegisterInfos_mips to declare our g_register_infos_mips structure. //--------------------------------------------------------------------------- #define DECLARE_REGISTER_INFOS_MIPS_STRUCT #include "RegisterInfos_mips.h" #undef DECLARE_REGISTER_INFOS_MIPS_STRUCT static const RegisterInfo * GetRegisterInfoPtr (const ArchSpec &target_arch) { switch (target_arch.GetMachine()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: return g_register_infos_mips64; case llvm::Triple::mips: case llvm::Triple::mipsel: return g_register_infos_mips; default: assert(false && "Unhandled target architecture."); return nullptr; } } static uint32_t GetRegisterInfoCount (const ArchSpec &target_arch) { switch (target_arch.GetMachine()) { case llvm::Triple::mips64: case llvm::Triple::mips64el: return static_cast (sizeof (g_register_infos_mips64) / sizeof (g_register_infos_mips64 [0])); case llvm::Triple::mips: case llvm::Triple::mipsel: return static_cast (sizeof (g_register_infos_mips) / sizeof (g_register_infos_mips [0])); default: assert(false && "Unhandled target architecture."); return 0; } } +uint32_t +GetUserRegisterInfoCount (const ArchSpec &target_arch) +{ + switch (target_arch.GetMachine()) + { + case llvm::Triple::mips: + case llvm::Triple::mipsel: + return static_cast (k_num_user_registers_mips); + case llvm::Triple::mips64el: + case llvm::Triple::mips64: + return static_cast (k_num_user_registers_mips64); + default: + assert(false && "Unhandled target architecture."); + return 0; + } +} + RegisterContextLinux_mips64::RegisterContextLinux_mips64(const ArchSpec &target_arch) : lldb_private::RegisterInfoInterface(target_arch), m_register_info_p (GetRegisterInfoPtr (target_arch)), - m_register_info_count (GetRegisterInfoCount (target_arch)) + m_register_info_count (GetRegisterInfoCount (target_arch)), + m_user_register_count (GetUserRegisterInfoCount (target_arch)) { } size_t RegisterContextLinux_mips64::GetGPRSize() const { - return sizeof(GPR); + return sizeof(GPR_linux_mips); } const RegisterInfo * RegisterContextLinux_mips64::GetRegisterInfo() const { return m_register_info_p; } uint32_t RegisterContextLinux_mips64::GetRegisterCount () const { return m_register_info_count; +} + +uint32_t +RegisterContextLinux_mips64::GetUserRegisterCount () const +{ + return m_user_register_count; } #endif Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLinux_mips64.h (revision 287514) @@ -1,40 +1,44 @@ //===-- RegisterContextLinux_mips64.h ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined (__mips__) #ifndef liblldb_RegisterContextLinux_mips64_H_ #define liblldb_RegisterContextLinux_mips64_H_ #include "lldb/lldb-private.h" #include "RegisterInfoInterface.h" class RegisterContextLinux_mips64 : public lldb_private::RegisterInfoInterface { public: RegisterContextLinux_mips64(const lldb_private::ArchSpec &target_arch); size_t GetGPRSize() const override; const lldb_private::RegisterInfo * GetRegisterInfo() const override; uint32_t GetRegisterCount () const override; + uint32_t + GetUserRegisterCount () const override; + private: const lldb_private::RegisterInfo *m_register_info_p; uint32_t m_register_info_count; + uint32_t m_user_register_count; }; #endif #endif Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextPOSIX_mips64.h (revision 287514) @@ -1,90 +1,90 @@ //===-- RegisterContextPOSIX_mips64.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_RegisterContextPOSIX_mips64_H_ #define liblldb_RegisterContextPOSIX_mips64_H_ #include "lldb/Core/Log.h" #include "RegisterContextPOSIX.h" -#include "RegisterContext_mips64.h" -#include "lldb-mips64-register-enums.h" +#include "RegisterContext_mips.h" +#include "lldb-mips-freebsd-register-enums.h" using namespace lldb_private; class ProcessMonitor; class RegisterContextPOSIX_mips64 : public lldb_private::RegisterContext { public: RegisterContextPOSIX_mips64 (lldb_private::Thread &thread, uint32_t concrete_frame_idx, lldb_private::RegisterInfoInterface *register_info); ~RegisterContextPOSIX_mips64(); void Invalidate(); void InvalidateAllRegisters(); size_t GetRegisterCount(); virtual size_t GetGPRSize(); virtual unsigned GetRegisterSize(unsigned reg); virtual unsigned GetRegisterOffset(unsigned reg); const lldb_private::RegisterInfo * GetRegisterInfoAtIndex(size_t reg); size_t GetRegisterSetCount(); const lldb_private::RegisterSet * GetRegisterSet(size_t set); const char * GetRegisterName(unsigned reg); uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num); protected: uint64_t m_gpr_mips64[k_num_gpr_registers_mips64]; // general purpose registers. std::unique_ptr m_register_info_ap; // Register Info Interface (FreeBSD or Linux) // Determines if an extended register set is supported on the processor running the inferior process. virtual bool IsRegisterSetAvailable(size_t set_index); virtual const lldb_private::RegisterInfo * GetRegisterInfo(); bool IsGPR(unsigned reg); bool IsFPR(unsigned reg); lldb::ByteOrder GetByteOrder(); virtual bool ReadGPR() = 0; virtual bool ReadFPR() = 0; virtual bool WriteGPR() = 0; virtual bool WriteFPR() = 0; }; #endif // #ifndef liblldb_RegisterContextPOSIX_mips64_H_ Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips.h (nonexistent) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips.h (revision 287514) @@ -0,0 +1,611 @@ +//===-- RegisterContext_mips.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_RegisterContext_mips64_H_ +#define liblldb_RegisterContext_mips64_H_ + +// eh_frame and DWARF Register numbers (eRegisterKindEHFrame & eRegisterKindDWARF) + +enum +{ + // GP Registers + gcc_dwarf_zero_mips = 0, + gcc_dwarf_r1_mips, + gcc_dwarf_r2_mips, + gcc_dwarf_r3_mips, + gcc_dwarf_r4_mips, + gcc_dwarf_r5_mips, + gcc_dwarf_r6_mips, + gcc_dwarf_r7_mips, + gcc_dwarf_r8_mips, + gcc_dwarf_r9_mips, + gcc_dwarf_r10_mips, + gcc_dwarf_r11_mips, + gcc_dwarf_r12_mips, + gcc_dwarf_r13_mips, + gcc_dwarf_r14_mips, + gcc_dwarf_r15_mips, + gcc_dwarf_r16_mips, + gcc_dwarf_r17_mips, + gcc_dwarf_r18_mips, + gcc_dwarf_r19_mips, + gcc_dwarf_r20_mips, + gcc_dwarf_r21_mips, + gcc_dwarf_r22_mips, + gcc_dwarf_r23_mips, + gcc_dwarf_r24_mips, + gcc_dwarf_r25_mips, + gcc_dwarf_r26_mips, + gcc_dwarf_r27_mips, + gcc_dwarf_gp_mips, + gcc_dwarf_sp_mips, + gcc_dwarf_r30_mips, + gcc_dwarf_ra_mips, + gcc_dwarf_sr_mips, + gcc_dwarf_lo_mips, + gcc_dwarf_hi_mips, + gcc_dwarf_bad_mips, + gcc_dwarf_cause_mips, + gcc_dwarf_pc_mips, + gcc_dwarf_f0_mips, + gcc_dwarf_f1_mips, + gcc_dwarf_f2_mips, + gcc_dwarf_f3_mips, + gcc_dwarf_f4_mips, + gcc_dwarf_f5_mips, + gcc_dwarf_f6_mips, + gcc_dwarf_f7_mips, + gcc_dwarf_f8_mips, + gcc_dwarf_f9_mips, + gcc_dwarf_f10_mips, + gcc_dwarf_f11_mips, + gcc_dwarf_f12_mips, + gcc_dwarf_f13_mips, + gcc_dwarf_f14_mips, + gcc_dwarf_f15_mips, + gcc_dwarf_f16_mips, + gcc_dwarf_f17_mips, + gcc_dwarf_f18_mips, + gcc_dwarf_f19_mips, + gcc_dwarf_f20_mips, + gcc_dwarf_f21_mips, + gcc_dwarf_f22_mips, + gcc_dwarf_f23_mips, + gcc_dwarf_f24_mips, + gcc_dwarf_f25_mips, + gcc_dwarf_f26_mips, + gcc_dwarf_f27_mips, + gcc_dwarf_f28_mips, + gcc_dwarf_f29_mips, + gcc_dwarf_f30_mips, + gcc_dwarf_f31_mips, + gcc_dwarf_fcsr_mips, + gcc_dwarf_fir_mips, + gcc_dwarf_w0_mips, + gcc_dwarf_w1_mips, + gcc_dwarf_w2_mips, + gcc_dwarf_w3_mips, + gcc_dwarf_w4_mips, + gcc_dwarf_w5_mips, + gcc_dwarf_w6_mips, + gcc_dwarf_w7_mips, + gcc_dwarf_w8_mips, + gcc_dwarf_w9_mips, + gcc_dwarf_w10_mips, + gcc_dwarf_w11_mips, + gcc_dwarf_w12_mips, + gcc_dwarf_w13_mips, + gcc_dwarf_w14_mips, + gcc_dwarf_w15_mips, + gcc_dwarf_w16_mips, + gcc_dwarf_w17_mips, + gcc_dwarf_w18_mips, + gcc_dwarf_w19_mips, + gcc_dwarf_w20_mips, + gcc_dwarf_w21_mips, + gcc_dwarf_w22_mips, + gcc_dwarf_w23_mips, + gcc_dwarf_w24_mips, + gcc_dwarf_w25_mips, + gcc_dwarf_w26_mips, + gcc_dwarf_w27_mips, + gcc_dwarf_w28_mips, + gcc_dwarf_w29_mips, + gcc_dwarf_w30_mips, + gcc_dwarf_w31_mips, + gcc_dwarf_mcsr_mips, + gcc_dwarf_mir_mips, + gcc_dwarf_config5_mips, + gcc_dwarf_ic_mips, + gcc_dwarf_dummy_mips +}; + +enum +{ + gcc_dwarf_zero_mips64 = 0, + gcc_dwarf_r1_mips64, + gcc_dwarf_r2_mips64, + gcc_dwarf_r3_mips64, + gcc_dwarf_r4_mips64, + gcc_dwarf_r5_mips64, + gcc_dwarf_r6_mips64, + gcc_dwarf_r7_mips64, + gcc_dwarf_r8_mips64, + gcc_dwarf_r9_mips64, + gcc_dwarf_r10_mips64, + gcc_dwarf_r11_mips64, + gcc_dwarf_r12_mips64, + gcc_dwarf_r13_mips64, + gcc_dwarf_r14_mips64, + gcc_dwarf_r15_mips64, + gcc_dwarf_r16_mips64, + gcc_dwarf_r17_mips64, + gcc_dwarf_r18_mips64, + gcc_dwarf_r19_mips64, + gcc_dwarf_r20_mips64, + gcc_dwarf_r21_mips64, + gcc_dwarf_r22_mips64, + gcc_dwarf_r23_mips64, + gcc_dwarf_r24_mips64, + gcc_dwarf_r25_mips64, + gcc_dwarf_r26_mips64, + gcc_dwarf_r27_mips64, + gcc_dwarf_gp_mips64, + gcc_dwarf_sp_mips64, + gcc_dwarf_r30_mips64, + gcc_dwarf_ra_mips64, + gcc_dwarf_sr_mips64, + gcc_dwarf_lo_mips64, + gcc_dwarf_hi_mips64, + gcc_dwarf_bad_mips64, + gcc_dwarf_cause_mips64, + gcc_dwarf_pc_mips64, + gcc_dwarf_f0_mips64, + gcc_dwarf_f1_mips64, + gcc_dwarf_f2_mips64, + gcc_dwarf_f3_mips64, + gcc_dwarf_f4_mips64, + gcc_dwarf_f5_mips64, + gcc_dwarf_f6_mips64, + gcc_dwarf_f7_mips64, + gcc_dwarf_f8_mips64, + gcc_dwarf_f9_mips64, + gcc_dwarf_f10_mips64, + gcc_dwarf_f11_mips64, + gcc_dwarf_f12_mips64, + gcc_dwarf_f13_mips64, + gcc_dwarf_f14_mips64, + gcc_dwarf_f15_mips64, + gcc_dwarf_f16_mips64, + gcc_dwarf_f17_mips64, + gcc_dwarf_f18_mips64, + gcc_dwarf_f19_mips64, + gcc_dwarf_f20_mips64, + gcc_dwarf_f21_mips64, + gcc_dwarf_f22_mips64, + gcc_dwarf_f23_mips64, + gcc_dwarf_f24_mips64, + gcc_dwarf_f25_mips64, + gcc_dwarf_f26_mips64, + gcc_dwarf_f27_mips64, + gcc_dwarf_f28_mips64, + gcc_dwarf_f29_mips64, + gcc_dwarf_f30_mips64, + gcc_dwarf_f31_mips64, + gcc_dwarf_fcsr_mips64, + gcc_dwarf_fir_mips64, + gcc_dwarf_ic_mips64, + gcc_dwarf_dummy_mips64, + gcc_dwarf_w0_mips64, + gcc_dwarf_w1_mips64, + gcc_dwarf_w2_mips64, + gcc_dwarf_w3_mips64, + gcc_dwarf_w4_mips64, + gcc_dwarf_w5_mips64, + gcc_dwarf_w6_mips64, + gcc_dwarf_w7_mips64, + gcc_dwarf_w8_mips64, + gcc_dwarf_w9_mips64, + gcc_dwarf_w10_mips64, + gcc_dwarf_w11_mips64, + gcc_dwarf_w12_mips64, + gcc_dwarf_w13_mips64, + gcc_dwarf_w14_mips64, + gcc_dwarf_w15_mips64, + gcc_dwarf_w16_mips64, + gcc_dwarf_w17_mips64, + gcc_dwarf_w18_mips64, + gcc_dwarf_w19_mips64, + gcc_dwarf_w20_mips64, + gcc_dwarf_w21_mips64, + gcc_dwarf_w22_mips64, + gcc_dwarf_w23_mips64, + gcc_dwarf_w24_mips64, + gcc_dwarf_w25_mips64, + gcc_dwarf_w26_mips64, + gcc_dwarf_w27_mips64, + gcc_dwarf_w28_mips64, + gcc_dwarf_w29_mips64, + gcc_dwarf_w30_mips64, + gcc_dwarf_w31_mips64, + gcc_dwarf_mcsr_mips64, + gcc_dwarf_mir_mips64, + gcc_dwarf_config5_mips64, +}; + +// GDB Register numbers (eRegisterKindGDB) +enum +{ + gdb_zero_mips = 0, + gdb_r1_mips, + gdb_r2_mips, + gdb_r3_mips, + gdb_r4_mips, + gdb_r5_mips, + gdb_r6_mips, + gdb_r7_mips, + gdb_r8_mips, + gdb_r9_mips, + gdb_r10_mips, + gdb_r11_mips, + gdb_r12_mips, + gdb_r13_mips, + gdb_r14_mips, + gdb_r15_mips, + gdb_r16_mips, + gdb_r17_mips, + gdb_r18_mips, + gdb_r19_mips, + gdb_r20_mips, + gdb_r21_mips, + gdb_r22_mips, + gdb_r23_mips, + gdb_r24_mips, + gdb_r25_mips, + gdb_r26_mips, + gdb_r27_mips, + gdb_gp_mips, + gdb_sp_mips, + gdb_r30_mips, + gdb_ra_mips, + gdb_sr_mips, + gdb_lo_mips, + gdb_hi_mips, + gdb_bad_mips, + gdb_cause_mips, + gdb_pc_mips, + gdb_f0_mips, + gdb_f1_mips, + gdb_f2_mips, + gdb_f3_mips, + gdb_f4_mips, + gdb_f5_mips, + gdb_f6_mips, + gdb_f7_mips, + gdb_f8_mips, + gdb_f9_mips, + gdb_f10_mips, + gdb_f11_mips, + gdb_f12_mips, + gdb_f13_mips, + gdb_f14_mips, + gdb_f15_mips, + gdb_f16_mips, + gdb_f17_mips, + gdb_f18_mips, + gdb_f19_mips, + gdb_f20_mips, + gdb_f21_mips, + gdb_f22_mips, + gdb_f23_mips, + gdb_f24_mips, + gdb_f25_mips, + gdb_f26_mips, + gdb_f27_mips, + gdb_f28_mips, + gdb_f29_mips, + gdb_f30_mips, + gdb_f31_mips, + gdb_fcsr_mips, + gdb_fir_mips, + gdb_w0_mips, + gdb_w1_mips, + gdb_w2_mips, + gdb_w3_mips, + gdb_w4_mips, + gdb_w5_mips, + gdb_w6_mips, + gdb_w7_mips, + gdb_w8_mips, + gdb_w9_mips, + gdb_w10_mips, + gdb_w11_mips, + gdb_w12_mips, + gdb_w13_mips, + gdb_w14_mips, + gdb_w15_mips, + gdb_w16_mips, + gdb_w17_mips, + gdb_w18_mips, + gdb_w19_mips, + gdb_w20_mips, + gdb_w21_mips, + gdb_w22_mips, + gdb_w23_mips, + gdb_w24_mips, + gdb_w25_mips, + gdb_w26_mips, + gdb_w27_mips, + gdb_w28_mips, + gdb_w29_mips, + gdb_w30_mips, + gdb_w31_mips, + gdb_mcsr_mips, + gdb_mir_mips, + gdb_config5_mips, + gdb_ic_mips, + gdb_dummy_mips +}; + +enum +{ + gdb_zero_mips64 = 0, + gdb_r1_mips64, + gdb_r2_mips64, + gdb_r3_mips64, + gdb_r4_mips64, + gdb_r5_mips64, + gdb_r6_mips64, + gdb_r7_mips64, + gdb_r8_mips64, + gdb_r9_mips64, + gdb_r10_mips64, + gdb_r11_mips64, + gdb_r12_mips64, + gdb_r13_mips64, + gdb_r14_mips64, + gdb_r15_mips64, + gdb_r16_mips64, + gdb_r17_mips64, + gdb_r18_mips64, + gdb_r19_mips64, + gdb_r20_mips64, + gdb_r21_mips64, + gdb_r22_mips64, + gdb_r23_mips64, + gdb_r24_mips64, + gdb_r25_mips64, + gdb_r26_mips64, + gdb_r27_mips64, + gdb_gp_mips64, + gdb_sp_mips64, + gdb_r30_mips64, + gdb_ra_mips64, + gdb_sr_mips64, + gdb_lo_mips64, + gdb_hi_mips64, + gdb_bad_mips64, + gdb_cause_mips64, + gdb_pc_mips64, + gdb_f0_mips64, + gdb_f1_mips64, + gdb_f2_mips64, + gdb_f3_mips64, + gdb_f4_mips64, + gdb_f5_mips64, + gdb_f6_mips64, + gdb_f7_mips64, + gdb_f8_mips64, + gdb_f9_mips64, + gdb_f10_mips64, + gdb_f11_mips64, + gdb_f12_mips64, + gdb_f13_mips64, + gdb_f14_mips64, + gdb_f15_mips64, + gdb_f16_mips64, + gdb_f17_mips64, + gdb_f18_mips64, + gdb_f19_mips64, + gdb_f20_mips64, + gdb_f21_mips64, + gdb_f22_mips64, + gdb_f23_mips64, + gdb_f24_mips64, + gdb_f25_mips64, + gdb_f26_mips64, + gdb_f27_mips64, + gdb_f28_mips64, + gdb_f29_mips64, + gdb_f30_mips64, + gdb_f31_mips64, + gdb_fcsr_mips64, + gdb_fir_mips64, + gdb_ic_mips64, + gdb_dummy_mips64, + gdb_w0_mips64, + gdb_w1_mips64, + gdb_w2_mips64, + gdb_w3_mips64, + gdb_w4_mips64, + gdb_w5_mips64, + gdb_w6_mips64, + gdb_w7_mips64, + gdb_w8_mips64, + gdb_w9_mips64, + gdb_w10_mips64, + gdb_w11_mips64, + gdb_w12_mips64, + gdb_w13_mips64, + gdb_w14_mips64, + gdb_w15_mips64, + gdb_w16_mips64, + gdb_w17_mips64, + gdb_w18_mips64, + gdb_w19_mips64, + gdb_w20_mips64, + gdb_w21_mips64, + gdb_w22_mips64, + gdb_w23_mips64, + gdb_w24_mips64, + gdb_w25_mips64, + gdb_w26_mips64, + gdb_w27_mips64, + gdb_w28_mips64, + gdb_w29_mips64, + gdb_w30_mips64, + gdb_w31_mips64, + gdb_mcsr_mips64, + gdb_mir_mips64, + gdb_config5_mips64, +}; + +struct IOVEC_mips +{ + void *iov_base; + size_t iov_len; +}; + +// GP registers +struct GPR_linux_mips +{ + uint64_t zero; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t gp; + uint64_t sp; + uint64_t r30; + uint64_t ra; + uint64_t mullo; + uint64_t mulhi; + uint64_t pc; + uint64_t badvaddr; + uint64_t sr; + uint64_t cause; + uint64_t config5; +}; + +struct FPR_linux_mips +{ + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint32_t fcsr; + uint32_t fir; + uint32_t config5; +}; + +struct MSAReg +{ + uint8_t byte[16]; +}; + +struct MSA_linux_mips +{ + MSAReg w0; + MSAReg w1; + MSAReg w2; + MSAReg w3; + MSAReg w4; + MSAReg w5; + MSAReg w6; + MSAReg w7; + MSAReg w8; + MSAReg w9; + MSAReg w10; + MSAReg w11; + MSAReg w12; + MSAReg w13; + MSAReg w14; + MSAReg w15; + MSAReg w16; + MSAReg w17; + MSAReg w18; + MSAReg w19; + MSAReg w20; + MSAReg w21; + MSAReg w22; + MSAReg w23; + MSAReg w24; + MSAReg w25; + MSAReg w26; + MSAReg w27; + MSAReg w28; + MSAReg w29; + MSAReg w30; + MSAReg w31; + uint32_t fcsr; /* FPU control status register */ + uint32_t fir; /* FPU implementaion revision */ + uint32_t mcsr; /* MSA control status register */ + uint32_t mir; /* MSA implementation revision */ + uint32_t config5; /* Config5 register */ +}; + +struct UserArea +{ + GPR_linux_mips gpr; // General purpose registers. + FPR_linux_mips fpr; // Floating point registers. + MSA_linux_mips msa; // MSA registers. +}; + +#endif // liblldb_RegisterContext_mips64_H_ Property changes on: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContext_mips.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/source/Plugins/Process/Utility/RegisterInfos_mips.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterInfos_mips.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterInfos_mips.h (revision 287514) @@ -1,122 +1,175 @@ //===-- RegisterInfos_mips.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// #include "llvm/Support/Compiler.h" #include #ifdef DECLARE_REGISTER_INFOS_MIPS_STRUCT // Computes the offset of the given GPR in the user data area. #define GPR_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(GPR, regname)) + (LLVM_EXTENSION offsetof(UserArea, gpr) + \ + LLVM_EXTENSION offsetof(GPR_linux_mips, regname)) // Computes the offset of the given FPR in the extended data area. #define FPR_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(FPR_mips, regname)) + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR_linux_mips, regname)) +// Computes the offset of the given MSA in the extended data area. +#define MSA_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, msa) + \ + LLVM_EXTENSION offsetof(MSA_linux_mips, regname)) + // Note that the size and offset will be updated by platform-specific classes. #define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ + { #reg, alt, sizeof(((GPR_linux_mips*)NULL)->reg) / 2, GPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips }, NULL, NULL } -#define DEFINE_FPR(member, reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((FPR_mips*)NULL)->member) / 2, FPR_OFFSET(member), eEncodingUint, \ +#define DEFINE_FPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_linux_mips*)NULL)->reg), FPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } -#define DEFINE_FPR_INFO(member, reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((FPR_mips*)NULL)->member), FPR_OFFSET(member), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips }, NULL, NULL } +#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((MSA_linux_mips*)0)->reg), MSA_OFFSET(reg), eEncodingVector, \ + eFormatVectorOfUInt8, { kind1, kind2, kind3, kind4, msa_##reg##_mips }, NULL, NULL } +#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((MSA_linux_mips*)0)->reg), MSA_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, msa_##reg##_mips }, NULL, NULL } + // RegisterKind: GCC, DWARF, Generic, GDB, LLDB static RegisterInfo g_register_infos_mips[] = { DEFINE_GPR (zero, "zero", gcc_dwarf_zero_mips, gcc_dwarf_zero_mips, LLDB_INVALID_REGNUM, gdb_zero_mips), DEFINE_GPR (r1, "at", gcc_dwarf_r1_mips, gcc_dwarf_r1_mips, LLDB_INVALID_REGNUM, gdb_r1_mips), DEFINE_GPR (r2, NULL, gcc_dwarf_r2_mips, gcc_dwarf_r2_mips, LLDB_INVALID_REGNUM, gdb_r2_mips), DEFINE_GPR (r3, NULL, gcc_dwarf_r3_mips, gcc_dwarf_r3_mips, LLDB_INVALID_REGNUM, gdb_r3_mips), DEFINE_GPR (r4, NULL, gcc_dwarf_r4_mips, gcc_dwarf_r4_mips, LLDB_REGNUM_GENERIC_ARG1, gdb_r4_mips), DEFINE_GPR (r5, NULL, gcc_dwarf_r5_mips, gcc_dwarf_r5_mips, LLDB_REGNUM_GENERIC_ARG2, gdb_r5_mips), DEFINE_GPR (r6, NULL, gcc_dwarf_r6_mips, gcc_dwarf_r6_mips, LLDB_REGNUM_GENERIC_ARG3, gdb_r6_mips), DEFINE_GPR (r7, NULL, gcc_dwarf_r7_mips, gcc_dwarf_r7_mips, LLDB_REGNUM_GENERIC_ARG4, gdb_r7_mips), DEFINE_GPR (r8, NULL, gcc_dwarf_r8_mips, gcc_dwarf_r8_mips, LLDB_INVALID_REGNUM, gdb_r8_mips), DEFINE_GPR (r9, NULL, gcc_dwarf_r9_mips, gcc_dwarf_r9_mips, LLDB_INVALID_REGNUM, gdb_r9_mips), DEFINE_GPR (r10, NULL, gcc_dwarf_r10_mips, gcc_dwarf_r10_mips, LLDB_INVALID_REGNUM, gdb_r10_mips), DEFINE_GPR (r11, NULL, gcc_dwarf_r11_mips, gcc_dwarf_r11_mips, LLDB_INVALID_REGNUM, gdb_r11_mips), DEFINE_GPR (r12, NULL, gcc_dwarf_r12_mips, gcc_dwarf_r12_mips, LLDB_INVALID_REGNUM, gdb_r12_mips), DEFINE_GPR (r13, NULL, gcc_dwarf_r13_mips, gcc_dwarf_r13_mips, LLDB_INVALID_REGNUM, gdb_r13_mips), DEFINE_GPR (r14, NULL, gcc_dwarf_r14_mips, gcc_dwarf_r14_mips, LLDB_INVALID_REGNUM, gdb_r14_mips), DEFINE_GPR (r15, NULL, gcc_dwarf_r15_mips, gcc_dwarf_r15_mips, LLDB_INVALID_REGNUM, gdb_r15_mips), DEFINE_GPR (r16, NULL, gcc_dwarf_r16_mips, gcc_dwarf_r16_mips, LLDB_INVALID_REGNUM, gdb_r16_mips), DEFINE_GPR (r17, NULL, gcc_dwarf_r17_mips, gcc_dwarf_r17_mips, LLDB_INVALID_REGNUM, gdb_r17_mips), DEFINE_GPR (r18, NULL, gcc_dwarf_r18_mips, gcc_dwarf_r18_mips, LLDB_INVALID_REGNUM, gdb_r18_mips), DEFINE_GPR (r19, NULL, gcc_dwarf_r19_mips, gcc_dwarf_r19_mips, LLDB_INVALID_REGNUM, gdb_r19_mips), DEFINE_GPR (r20, NULL, gcc_dwarf_r20_mips, gcc_dwarf_r20_mips, LLDB_INVALID_REGNUM, gdb_r20_mips), DEFINE_GPR (r21, NULL, gcc_dwarf_r21_mips, gcc_dwarf_r21_mips, LLDB_INVALID_REGNUM, gdb_r21_mips), DEFINE_GPR (r22, NULL, gcc_dwarf_r22_mips, gcc_dwarf_r22_mips, LLDB_INVALID_REGNUM, gdb_r22_mips), DEFINE_GPR (r23, NULL, gcc_dwarf_r23_mips, gcc_dwarf_r23_mips, LLDB_INVALID_REGNUM, gdb_r23_mips), DEFINE_GPR (r24, NULL, gcc_dwarf_r24_mips, gcc_dwarf_r24_mips, LLDB_INVALID_REGNUM, gdb_r24_mips), DEFINE_GPR (r25, NULL, gcc_dwarf_r25_mips, gcc_dwarf_r25_mips, LLDB_INVALID_REGNUM, gdb_r25_mips), DEFINE_GPR (r26, NULL, gcc_dwarf_r26_mips, gcc_dwarf_r26_mips, LLDB_INVALID_REGNUM, gdb_r26_mips), DEFINE_GPR (r27, NULL, gcc_dwarf_r27_mips, gcc_dwarf_r27_mips, LLDB_INVALID_REGNUM, gdb_r27_mips), DEFINE_GPR (gp, "gp", gcc_dwarf_gp_mips, gcc_dwarf_gp_mips, LLDB_INVALID_REGNUM, gdb_gp_mips), DEFINE_GPR (sp, "sp", gcc_dwarf_sp_mips, gcc_dwarf_sp_mips, LLDB_REGNUM_GENERIC_SP, gdb_sp_mips), DEFINE_GPR (r30, "fp", gcc_dwarf_r30_mips, gcc_dwarf_r30_mips, LLDB_REGNUM_GENERIC_FP, gdb_r30_mips), DEFINE_GPR (ra, "ra", gcc_dwarf_ra_mips, gcc_dwarf_ra_mips, LLDB_REGNUM_GENERIC_RA, gdb_ra_mips), + DEFINE_GPR (sr, "status", gcc_dwarf_sr_mips, gcc_dwarf_sr_mips, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), DEFINE_GPR (mullo, NULL, gcc_dwarf_lo_mips, gcc_dwarf_lo_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_GPR (mulhi, NULL, gcc_dwarf_hi_mips, gcc_dwarf_hi_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_GPR (pc, NULL, gcc_dwarf_pc_mips, gcc_dwarf_pc_mips, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), DEFINE_GPR (badvaddr, NULL, gcc_dwarf_bad_mips, gcc_dwarf_bad_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_GPR (sr, "status", gcc_dwarf_sr_mips, gcc_dwarf_sr_mips, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), DEFINE_GPR (cause, NULL, gcc_dwarf_cause_mips, gcc_dwarf_cause_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_FPR (fp_reg[0], f0, NULL, gcc_dwarf_f0_mips, gcc_dwarf_f0_mips, LLDB_INVALID_REGNUM, gdb_f0_mips), - DEFINE_FPR (fp_reg[1], f1, NULL, gcc_dwarf_f1_mips, gcc_dwarf_f1_mips, LLDB_INVALID_REGNUM, gdb_f1_mips), - DEFINE_FPR (fp_reg[2], f2, NULL, gcc_dwarf_f2_mips, gcc_dwarf_f2_mips, LLDB_INVALID_REGNUM, gdb_f2_mips), - DEFINE_FPR (fp_reg[3], f3, NULL, gcc_dwarf_f3_mips, gcc_dwarf_f3_mips, LLDB_INVALID_REGNUM, gdb_f3_mips), - DEFINE_FPR (fp_reg[4], f4, NULL, gcc_dwarf_f4_mips, gcc_dwarf_f4_mips, LLDB_INVALID_REGNUM, gdb_f4_mips), - DEFINE_FPR (fp_reg[5], f5, NULL, gcc_dwarf_f5_mips, gcc_dwarf_f5_mips, LLDB_INVALID_REGNUM, gdb_f5_mips), - DEFINE_FPR (fp_reg[6], f6, NULL, gcc_dwarf_f6_mips, gcc_dwarf_f6_mips, LLDB_INVALID_REGNUM, gdb_f6_mips), - DEFINE_FPR (fp_reg[7], f7, NULL, gcc_dwarf_f7_mips, gcc_dwarf_f7_mips, LLDB_INVALID_REGNUM, gdb_f7_mips), - DEFINE_FPR (fp_reg[8], f8, NULL, gcc_dwarf_f8_mips, gcc_dwarf_f8_mips, LLDB_INVALID_REGNUM, gdb_f8_mips), - DEFINE_FPR (fp_reg[9], f9, NULL, gcc_dwarf_f9_mips, gcc_dwarf_f9_mips, LLDB_INVALID_REGNUM, gdb_f9_mips), - DEFINE_FPR (fp_reg[10], f10, NULL, gcc_dwarf_f10_mips, gcc_dwarf_f10_mips, LLDB_INVALID_REGNUM, gdb_f10_mips), - DEFINE_FPR (fp_reg[11], f11, NULL, gcc_dwarf_f11_mips, gcc_dwarf_f11_mips, LLDB_INVALID_REGNUM, gdb_f11_mips), - DEFINE_FPR (fp_reg[12], f12, NULL, gcc_dwarf_f12_mips, gcc_dwarf_f12_mips, LLDB_INVALID_REGNUM, gdb_f12_mips), - DEFINE_FPR (fp_reg[13], f13, NULL, gcc_dwarf_f13_mips, gcc_dwarf_f13_mips, LLDB_INVALID_REGNUM, gdb_f13_mips), - DEFINE_FPR (fp_reg[14], f14, NULL, gcc_dwarf_f14_mips, gcc_dwarf_f14_mips, LLDB_INVALID_REGNUM, gdb_f14_mips), - DEFINE_FPR (fp_reg[15], f15, NULL, gcc_dwarf_f15_mips, gcc_dwarf_f15_mips, LLDB_INVALID_REGNUM, gdb_f15_mips), - DEFINE_FPR (fp_reg[16], f16, NULL, gcc_dwarf_f16_mips, gcc_dwarf_f16_mips, LLDB_INVALID_REGNUM, gdb_f16_mips), - DEFINE_FPR (fp_reg[17], f17, NULL, gcc_dwarf_f17_mips, gcc_dwarf_f17_mips, LLDB_INVALID_REGNUM, gdb_f17_mips), - DEFINE_FPR (fp_reg[18], f18, NULL, gcc_dwarf_f18_mips, gcc_dwarf_f18_mips, LLDB_INVALID_REGNUM, gdb_f18_mips), - DEFINE_FPR (fp_reg[19], f19, NULL, gcc_dwarf_f19_mips, gcc_dwarf_f19_mips, LLDB_INVALID_REGNUM, gdb_f19_mips), - DEFINE_FPR (fp_reg[20], f20, NULL, gcc_dwarf_f20_mips, gcc_dwarf_f20_mips, LLDB_INVALID_REGNUM, gdb_f20_mips), - DEFINE_FPR (fp_reg[21], f21, NULL, gcc_dwarf_f21_mips, gcc_dwarf_f21_mips, LLDB_INVALID_REGNUM, gdb_f21_mips), - DEFINE_FPR (fp_reg[22], f22, NULL, gcc_dwarf_f22_mips, gcc_dwarf_f22_mips, LLDB_INVALID_REGNUM, gdb_f22_mips), - DEFINE_FPR (fp_reg[23], f23, NULL, gcc_dwarf_f23_mips, gcc_dwarf_f23_mips, LLDB_INVALID_REGNUM, gdb_f23_mips), - DEFINE_FPR (fp_reg[24], f24, NULL, gcc_dwarf_f24_mips, gcc_dwarf_f24_mips, LLDB_INVALID_REGNUM, gdb_f24_mips), - DEFINE_FPR (fp_reg[25], f25, NULL, gcc_dwarf_f25_mips, gcc_dwarf_f25_mips, LLDB_INVALID_REGNUM, gdb_f25_mips), - DEFINE_FPR (fp_reg[26], f26, NULL, gcc_dwarf_f26_mips, gcc_dwarf_f26_mips, LLDB_INVALID_REGNUM, gdb_f26_mips), - DEFINE_FPR (fp_reg[27], f27, NULL, gcc_dwarf_f27_mips, gcc_dwarf_f27_mips, LLDB_INVALID_REGNUM, gdb_f27_mips), - DEFINE_FPR (fp_reg[28], f28, NULL, gcc_dwarf_f28_mips, gcc_dwarf_f28_mips, LLDB_INVALID_REGNUM, gdb_f28_mips), - DEFINE_FPR (fp_reg[29], f29, NULL, gcc_dwarf_f29_mips, gcc_dwarf_f29_mips, LLDB_INVALID_REGNUM, gdb_f29_mips), - DEFINE_FPR (fp_reg[30], f30, NULL, gcc_dwarf_f30_mips, gcc_dwarf_f30_mips, LLDB_INVALID_REGNUM, gdb_f30_mips), - DEFINE_FPR (fp_reg[31], f31, NULL, gcc_dwarf_f31_mips, gcc_dwarf_f31_mips, LLDB_INVALID_REGNUM, gdb_f31_mips), - DEFINE_FPR_INFO (fcsr, fcsr, NULL, gcc_dwarf_fcsr_mips, gcc_dwarf_fcsr_mips, LLDB_INVALID_REGNUM, gdb_fcsr_mips), - DEFINE_FPR_INFO (fir, fir, NULL, gcc_dwarf_fir_mips, gcc_dwarf_fir_mips, LLDB_INVALID_REGNUM, gdb_fir_mips) + DEFINE_GPR (pc, NULL, gcc_dwarf_pc_mips, gcc_dwarf_pc_mips, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR (config5, NULL, gcc_dwarf_config5_mips, gcc_dwarf_config5_mips, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR (f0, NULL, gcc_dwarf_f0_mips, gcc_dwarf_f0_mips, LLDB_INVALID_REGNUM, gdb_f0_mips), + DEFINE_FPR (f1, NULL, gcc_dwarf_f1_mips, gcc_dwarf_f1_mips, LLDB_INVALID_REGNUM, gdb_f1_mips), + DEFINE_FPR (f2, NULL, gcc_dwarf_f2_mips, gcc_dwarf_f2_mips, LLDB_INVALID_REGNUM, gdb_f2_mips), + DEFINE_FPR (f3, NULL, gcc_dwarf_f3_mips, gcc_dwarf_f3_mips, LLDB_INVALID_REGNUM, gdb_f3_mips), + DEFINE_FPR (f4, NULL, gcc_dwarf_f4_mips, gcc_dwarf_f4_mips, LLDB_INVALID_REGNUM, gdb_f4_mips), + DEFINE_FPR (f5, NULL, gcc_dwarf_f5_mips, gcc_dwarf_f5_mips, LLDB_INVALID_REGNUM, gdb_f5_mips), + DEFINE_FPR (f6, NULL, gcc_dwarf_f6_mips, gcc_dwarf_f6_mips, LLDB_INVALID_REGNUM, gdb_f6_mips), + DEFINE_FPR (f7, NULL, gcc_dwarf_f7_mips, gcc_dwarf_f7_mips, LLDB_INVALID_REGNUM, gdb_f7_mips), + DEFINE_FPR (f8, NULL, gcc_dwarf_f8_mips, gcc_dwarf_f8_mips, LLDB_INVALID_REGNUM, gdb_f8_mips), + DEFINE_FPR (f9, NULL, gcc_dwarf_f9_mips, gcc_dwarf_f9_mips, LLDB_INVALID_REGNUM, gdb_f9_mips), + DEFINE_FPR (f10, NULL, gcc_dwarf_f10_mips, gcc_dwarf_f10_mips, LLDB_INVALID_REGNUM, gdb_f10_mips), + DEFINE_FPR (f11, NULL, gcc_dwarf_f11_mips, gcc_dwarf_f11_mips, LLDB_INVALID_REGNUM, gdb_f11_mips), + DEFINE_FPR (f12, NULL, gcc_dwarf_f12_mips, gcc_dwarf_f12_mips, LLDB_INVALID_REGNUM, gdb_f12_mips), + DEFINE_FPR (f13, NULL, gcc_dwarf_f13_mips, gcc_dwarf_f13_mips, LLDB_INVALID_REGNUM, gdb_f13_mips), + DEFINE_FPR (f14, NULL, gcc_dwarf_f14_mips, gcc_dwarf_f14_mips, LLDB_INVALID_REGNUM, gdb_f14_mips), + DEFINE_FPR (f15, NULL, gcc_dwarf_f15_mips, gcc_dwarf_f15_mips, LLDB_INVALID_REGNUM, gdb_f15_mips), + DEFINE_FPR (f16, NULL, gcc_dwarf_f16_mips, gcc_dwarf_f16_mips, LLDB_INVALID_REGNUM, gdb_f16_mips), + DEFINE_FPR (f17, NULL, gcc_dwarf_f17_mips, gcc_dwarf_f17_mips, LLDB_INVALID_REGNUM, gdb_f17_mips), + DEFINE_FPR (f18, NULL, gcc_dwarf_f18_mips, gcc_dwarf_f18_mips, LLDB_INVALID_REGNUM, gdb_f18_mips), + DEFINE_FPR (f19, NULL, gcc_dwarf_f19_mips, gcc_dwarf_f19_mips, LLDB_INVALID_REGNUM, gdb_f19_mips), + DEFINE_FPR (f20, NULL, gcc_dwarf_f20_mips, gcc_dwarf_f20_mips, LLDB_INVALID_REGNUM, gdb_f20_mips), + DEFINE_FPR (f21, NULL, gcc_dwarf_f21_mips, gcc_dwarf_f21_mips, LLDB_INVALID_REGNUM, gdb_f21_mips), + DEFINE_FPR (f22, NULL, gcc_dwarf_f22_mips, gcc_dwarf_f22_mips, LLDB_INVALID_REGNUM, gdb_f22_mips), + DEFINE_FPR (f23, NULL, gcc_dwarf_f23_mips, gcc_dwarf_f23_mips, LLDB_INVALID_REGNUM, gdb_f23_mips), + DEFINE_FPR (f24, NULL, gcc_dwarf_f24_mips, gcc_dwarf_f24_mips, LLDB_INVALID_REGNUM, gdb_f24_mips), + DEFINE_FPR (f25, NULL, gcc_dwarf_f25_mips, gcc_dwarf_f25_mips, LLDB_INVALID_REGNUM, gdb_f25_mips), + DEFINE_FPR (f26, NULL, gcc_dwarf_f26_mips, gcc_dwarf_f26_mips, LLDB_INVALID_REGNUM, gdb_f26_mips), + DEFINE_FPR (f27, NULL, gcc_dwarf_f27_mips, gcc_dwarf_f27_mips, LLDB_INVALID_REGNUM, gdb_f27_mips), + DEFINE_FPR (f28, NULL, gcc_dwarf_f28_mips, gcc_dwarf_f28_mips, LLDB_INVALID_REGNUM, gdb_f28_mips), + DEFINE_FPR (f29, NULL, gcc_dwarf_f29_mips, gcc_dwarf_f29_mips, LLDB_INVALID_REGNUM, gdb_f29_mips), + DEFINE_FPR (f30, NULL, gcc_dwarf_f30_mips, gcc_dwarf_f30_mips, LLDB_INVALID_REGNUM, gdb_f30_mips), + DEFINE_FPR (f31, NULL, gcc_dwarf_f31_mips, gcc_dwarf_f31_mips, LLDB_INVALID_REGNUM, gdb_f31_mips), + DEFINE_FPR (fcsr, NULL, gcc_dwarf_fcsr_mips, gcc_dwarf_fcsr_mips, LLDB_INVALID_REGNUM, gdb_fcsr_mips), + DEFINE_FPR (fir, NULL, gcc_dwarf_fir_mips, gcc_dwarf_fir_mips, LLDB_INVALID_REGNUM, gdb_fir_mips), + DEFINE_FPR (config5, NULL, gcc_dwarf_config5_mips, gcc_dwarf_config5_mips, LLDB_INVALID_REGNUM, gdb_config5_mips), + DEFINE_MSA (w0, NULL, gcc_dwarf_w0_mips, gcc_dwarf_w0_mips, LLDB_INVALID_REGNUM, gdb_w0_mips), + DEFINE_MSA (w1, NULL, gcc_dwarf_w1_mips, gcc_dwarf_w1_mips, LLDB_INVALID_REGNUM, gdb_w1_mips), + DEFINE_MSA (w2, NULL, gcc_dwarf_w2_mips, gcc_dwarf_w2_mips, LLDB_INVALID_REGNUM, gdb_w2_mips), + DEFINE_MSA (w3, NULL, gcc_dwarf_w3_mips, gcc_dwarf_w3_mips, LLDB_INVALID_REGNUM, gdb_w3_mips), + DEFINE_MSA (w4, NULL, gcc_dwarf_w4_mips, gcc_dwarf_w4_mips, LLDB_INVALID_REGNUM, gdb_w4_mips), + DEFINE_MSA (w5, NULL, gcc_dwarf_w5_mips, gcc_dwarf_w5_mips, LLDB_INVALID_REGNUM, gdb_w5_mips), + DEFINE_MSA (w6, NULL, gcc_dwarf_w6_mips, gcc_dwarf_w6_mips, LLDB_INVALID_REGNUM, gdb_w6_mips), + DEFINE_MSA (w7, NULL, gcc_dwarf_w7_mips, gcc_dwarf_w7_mips, LLDB_INVALID_REGNUM, gdb_w7_mips), + DEFINE_MSA (w8, NULL, gcc_dwarf_w8_mips, gcc_dwarf_w8_mips, LLDB_INVALID_REGNUM, gdb_w8_mips), + DEFINE_MSA (w9, NULL, gcc_dwarf_w9_mips, gcc_dwarf_w9_mips, LLDB_INVALID_REGNUM, gdb_w9_mips), + DEFINE_MSA (w10, NULL, gcc_dwarf_w10_mips, gcc_dwarf_w10_mips, LLDB_INVALID_REGNUM, gdb_w10_mips), + DEFINE_MSA (w11, NULL, gcc_dwarf_w11_mips, gcc_dwarf_w11_mips, LLDB_INVALID_REGNUM, gdb_w11_mips), + DEFINE_MSA (w12, NULL, gcc_dwarf_w12_mips, gcc_dwarf_w12_mips, LLDB_INVALID_REGNUM, gdb_w12_mips), + DEFINE_MSA (w13, NULL, gcc_dwarf_w13_mips, gcc_dwarf_w13_mips, LLDB_INVALID_REGNUM, gdb_w13_mips), + DEFINE_MSA (w14, NULL, gcc_dwarf_w14_mips, gcc_dwarf_w14_mips, LLDB_INVALID_REGNUM, gdb_w14_mips), + DEFINE_MSA (w15, NULL, gcc_dwarf_w15_mips, gcc_dwarf_w15_mips, LLDB_INVALID_REGNUM, gdb_w15_mips), + DEFINE_MSA (w16, NULL, gcc_dwarf_w16_mips, gcc_dwarf_w16_mips, LLDB_INVALID_REGNUM, gdb_w16_mips), + DEFINE_MSA (w17, NULL, gcc_dwarf_w17_mips, gcc_dwarf_w17_mips, LLDB_INVALID_REGNUM, gdb_w17_mips), + DEFINE_MSA (w18, NULL, gcc_dwarf_w18_mips, gcc_dwarf_w18_mips, LLDB_INVALID_REGNUM, gdb_w18_mips), + DEFINE_MSA (w19, NULL, gcc_dwarf_w19_mips, gcc_dwarf_w19_mips, LLDB_INVALID_REGNUM, gdb_w19_mips), + DEFINE_MSA (w20, NULL, gcc_dwarf_w10_mips, gcc_dwarf_w20_mips, LLDB_INVALID_REGNUM, gdb_w20_mips), + DEFINE_MSA (w21, NULL, gcc_dwarf_w21_mips, gcc_dwarf_w21_mips, LLDB_INVALID_REGNUM, gdb_w21_mips), + DEFINE_MSA (w22, NULL, gcc_dwarf_w22_mips, gcc_dwarf_w22_mips, LLDB_INVALID_REGNUM, gdb_w22_mips), + DEFINE_MSA (w23, NULL, gcc_dwarf_w23_mips, gcc_dwarf_w23_mips, LLDB_INVALID_REGNUM, gdb_w23_mips), + DEFINE_MSA (w24, NULL, gcc_dwarf_w24_mips, gcc_dwarf_w24_mips, LLDB_INVALID_REGNUM, gdb_w24_mips), + DEFINE_MSA (w25, NULL, gcc_dwarf_w25_mips, gcc_dwarf_w25_mips, LLDB_INVALID_REGNUM, gdb_w25_mips), + DEFINE_MSA (w26, NULL, gcc_dwarf_w26_mips, gcc_dwarf_w26_mips, LLDB_INVALID_REGNUM, gdb_w26_mips), + DEFINE_MSA (w27, NULL, gcc_dwarf_w27_mips, gcc_dwarf_w27_mips, LLDB_INVALID_REGNUM, gdb_w27_mips), + DEFINE_MSA (w28, NULL, gcc_dwarf_w28_mips, gcc_dwarf_w28_mips, LLDB_INVALID_REGNUM, gdb_w28_mips), + DEFINE_MSA (w29, NULL, gcc_dwarf_w29_mips, gcc_dwarf_w29_mips, LLDB_INVALID_REGNUM, gdb_w29_mips), + DEFINE_MSA (w30, NULL, gcc_dwarf_w30_mips, gcc_dwarf_w30_mips, LLDB_INVALID_REGNUM, gdb_w30_mips), + DEFINE_MSA (w31, NULL, gcc_dwarf_w31_mips, gcc_dwarf_w31_mips, LLDB_INVALID_REGNUM, gdb_w31_mips), + DEFINE_MSA_INFO (mcsr, NULL, gcc_dwarf_mcsr_mips, gcc_dwarf_mcsr_mips, LLDB_INVALID_REGNUM, gdb_mcsr_mips), + DEFINE_MSA_INFO (mir, NULL, gcc_dwarf_mir_mips, gcc_dwarf_mir_mips, LLDB_INVALID_REGNUM, gdb_mir_mips), + DEFINE_MSA_INFO (fcsr, NULL, gcc_dwarf_fcsr_mips, gcc_dwarf_fcsr_mips, LLDB_INVALID_REGNUM, gdb_fcsr_mips), + DEFINE_MSA_INFO (fir, NULL, gcc_dwarf_fir_mips, gcc_dwarf_fir_mips, LLDB_INVALID_REGNUM, gdb_fir_mips), + DEFINE_MSA_INFO (config5, NULL, gcc_dwarf_config5_mips, gcc_dwarf_config5_mips, LLDB_INVALID_REGNUM, gdb_config5_mips) }; static_assert((sizeof(g_register_infos_mips) / sizeof(g_register_infos_mips[0])) == k_num_registers_mips, "g_register_infos_mips has wrong number of register infos"); #undef GPR_OFFSET #undef FPR_OFFSET +#undef MSA_OFFSET #undef DEFINE_GPR #undef DEFINE_FPR +#undef DEFINE_MSA +#undef DEFINE_MSA_INFO #endif // DECLARE_REGISTER_INFOS_MIPS_STRUCT Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterInfos_mips64.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterInfos_mips64.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterInfos_mips64.h (revision 287514) @@ -1,122 +1,237 @@ //===-- RegisterInfos_mips64.h ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===---------------------------------------------------------------------===// #include "llvm/Support/Compiler.h" #include #ifdef DECLARE_REGISTER_INFOS_MIPS64_STRUCT // Computes the offset of the given GPR in the user data area. -#define GPR_OFFSET(regname) \ - (LLVM_EXTENSION offsetof(GPR, regname)) +#ifdef LINUX_MIPS64 + #define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, gpr) + \ + LLVM_EXTENSION offsetof(GPR_linux_mips, regname)) +#else + #define GPR_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(GPR_freebsd_mips, regname)) +#endif // Computes the offset of the given FPR in the extended data area. #define FPR_OFFSET(regname) \ - LLVM_EXTENSION offsetof(FPR_mips, regname) \ + (LLVM_EXTENSION offsetof(UserArea, fpr) + \ + LLVM_EXTENSION offsetof(FPR_linux_mips, regname)) +// Computes the offset of the given MSA in the extended data area. +#define MSA_OFFSET(regname) \ + (LLVM_EXTENSION offsetof(UserArea, msa) + \ + LLVM_EXTENSION offsetof(MSA_linux_mips, regname)) + // RegisterKind: GCC, DWARF, Generic, GDB, LLDB // Note that the size and offset will be updated by platform-specific classes. -#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((GPR*)0)->reg), GPR_OFFSET(reg), eEncodingUint, \ +#ifdef LINUX_MIPS64 + #define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR_linux_mips*)0)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips64 }, NULL, NULL } +#else + #define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR_freebsd_mips*)0)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips64 }, NULL, NULL } +#endif + +#define DEFINE_GPR_INFO(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((GPR_linux_mips*)0)->reg) / 2, GPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_mips64 }, NULL, NULL } -#define DEFINE_FPR(member, reg, alt, kind1, kind2, kind3, kind4) \ - { #reg, alt, sizeof(((FPR_mips*)0)->member), FPR_OFFSET(member), eEncodingUint, \ +#define DEFINE_FPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((FPR_linux_mips*)0)->reg), FPR_OFFSET(reg), eEncodingUint, \ eFormatHex, { kind1, kind2, kind3, kind4, fpr_##reg##_mips64 }, NULL, NULL } +#define DEFINE_MSA(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((MSA_linux_mips*)0)->reg), MSA_OFFSET(reg), eEncodingVector, \ + eFormatVectorOfUInt8, { kind1, kind2, kind3, kind4, msa_##reg##_mips64 }, NULL, NULL } + +#define DEFINE_MSA_INFO(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, sizeof(((MSA_linux_mips*)0)->reg), MSA_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, msa_##reg##_mips64 }, NULL, NULL } + static RegisterInfo g_register_infos_mips64[] = { // General purpose registers. GCC, DWARF, Generic, GDB +#ifndef LINUX_MIPS64 DEFINE_GPR(zero, "r0", gcc_dwarf_zero_mips64, gcc_dwarf_zero_mips64, LLDB_INVALID_REGNUM, gdb_zero_mips64), DEFINE_GPR(r1, NULL, gcc_dwarf_r1_mips64, gcc_dwarf_r1_mips64, LLDB_INVALID_REGNUM, gdb_r1_mips64), DEFINE_GPR(r2, NULL, gcc_dwarf_r2_mips64, gcc_dwarf_r2_mips64, LLDB_INVALID_REGNUM, gdb_r2_mips64), DEFINE_GPR(r3, NULL, gcc_dwarf_r3_mips64, gcc_dwarf_r3_mips64, LLDB_INVALID_REGNUM, gdb_r3_mips64), DEFINE_GPR(r4, NULL, gcc_dwarf_r4_mips64, gcc_dwarf_r4_mips64, LLDB_REGNUM_GENERIC_ARG1, gdb_r4_mips64), DEFINE_GPR(r5, NULL, gcc_dwarf_r5_mips64, gcc_dwarf_r5_mips64, LLDB_REGNUM_GENERIC_ARG2, gdb_r5_mips64), DEFINE_GPR(r6, NULL, gcc_dwarf_r6_mips64, gcc_dwarf_r6_mips64, LLDB_REGNUM_GENERIC_ARG3, gdb_r6_mips64), DEFINE_GPR(r7, NULL, gcc_dwarf_r7_mips64, gcc_dwarf_r7_mips64, LLDB_REGNUM_GENERIC_ARG4, gdb_r7_mips64), DEFINE_GPR(r8, NULL, gcc_dwarf_r8_mips64, gcc_dwarf_r8_mips64, LLDB_REGNUM_GENERIC_ARG5, gdb_r8_mips64), DEFINE_GPR(r9, NULL, gcc_dwarf_r9_mips64, gcc_dwarf_r9_mips64, LLDB_REGNUM_GENERIC_ARG6, gdb_r9_mips64), DEFINE_GPR(r10, NULL, gcc_dwarf_r10_mips64, gcc_dwarf_r10_mips64, LLDB_REGNUM_GENERIC_ARG7, gdb_r10_mips64), DEFINE_GPR(r11, NULL, gcc_dwarf_r11_mips64, gcc_dwarf_r11_mips64, LLDB_REGNUM_GENERIC_ARG8, gdb_r11_mips64), DEFINE_GPR(r12, NULL, gcc_dwarf_r12_mips64, gcc_dwarf_r12_mips64, LLDB_INVALID_REGNUM, gdb_r12_mips64), DEFINE_GPR(r13, NULL, gcc_dwarf_r13_mips64, gcc_dwarf_r13_mips64, LLDB_INVALID_REGNUM, gdb_r13_mips64), DEFINE_GPR(r14, NULL, gcc_dwarf_r14_mips64, gcc_dwarf_r14_mips64, LLDB_INVALID_REGNUM, gdb_r14_mips64), DEFINE_GPR(r15, NULL, gcc_dwarf_r15_mips64, gcc_dwarf_r15_mips64, LLDB_INVALID_REGNUM, gdb_r15_mips64), DEFINE_GPR(r16, NULL, gcc_dwarf_r16_mips64, gcc_dwarf_r16_mips64, LLDB_INVALID_REGNUM, gdb_r16_mips64), DEFINE_GPR(r17, NULL, gcc_dwarf_r17_mips64, gcc_dwarf_r17_mips64, LLDB_INVALID_REGNUM, gdb_r17_mips64), DEFINE_GPR(r18, NULL, gcc_dwarf_r18_mips64, gcc_dwarf_r18_mips64, LLDB_INVALID_REGNUM, gdb_r18_mips64), DEFINE_GPR(r19, NULL, gcc_dwarf_r19_mips64, gcc_dwarf_r19_mips64, LLDB_INVALID_REGNUM, gdb_r19_mips64), DEFINE_GPR(r20, NULL, gcc_dwarf_r20_mips64, gcc_dwarf_r20_mips64, LLDB_INVALID_REGNUM, gdb_r20_mips64), DEFINE_GPR(r21, NULL, gcc_dwarf_r21_mips64, gcc_dwarf_r21_mips64, LLDB_INVALID_REGNUM, gdb_r21_mips64), DEFINE_GPR(r22, NULL, gcc_dwarf_r22_mips64, gcc_dwarf_r22_mips64, LLDB_INVALID_REGNUM, gdb_r22_mips64), DEFINE_GPR(r23, NULL, gcc_dwarf_r23_mips64, gcc_dwarf_r23_mips64, LLDB_INVALID_REGNUM, gdb_r23_mips64), DEFINE_GPR(r24, NULL, gcc_dwarf_r24_mips64, gcc_dwarf_r24_mips64, LLDB_INVALID_REGNUM, gdb_r24_mips64), DEFINE_GPR(r25, NULL, gcc_dwarf_r25_mips64, gcc_dwarf_r25_mips64, LLDB_INVALID_REGNUM, gdb_r25_mips64), DEFINE_GPR(r26, NULL, gcc_dwarf_r26_mips64, gcc_dwarf_r26_mips64, LLDB_INVALID_REGNUM, gdb_r26_mips64), DEFINE_GPR(r27, NULL, gcc_dwarf_r27_mips64, gcc_dwarf_r27_mips64, LLDB_INVALID_REGNUM, gdb_r27_mips64), DEFINE_GPR(gp, "r28", gcc_dwarf_gp_mips64, gcc_dwarf_gp_mips64, LLDB_INVALID_REGNUM, gdb_gp_mips64), DEFINE_GPR(sp, "r29", gcc_dwarf_sp_mips64, gcc_dwarf_sp_mips64, LLDB_REGNUM_GENERIC_SP, gdb_sp_mips64), DEFINE_GPR(r30, NULL, gcc_dwarf_r30_mips64, gcc_dwarf_r30_mips64, LLDB_REGNUM_GENERIC_FP, gdb_r30_mips64), DEFINE_GPR(ra, "r31", gcc_dwarf_ra_mips64, gcc_dwarf_ra_mips64, LLDB_REGNUM_GENERIC_RA, gdb_ra_mips64), + DEFINE_GPR(sr, NULL, gcc_dwarf_sr_mips64, gcc_dwarf_sr_mips64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), DEFINE_GPR(mullo, NULL, gcc_dwarf_lo_mips64, gcc_dwarf_lo_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_GPR(mulhi, NULL, gcc_dwarf_hi_mips64, gcc_dwarf_hi_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_GPR(pc, "pc", gcc_dwarf_pc_mips64, gcc_dwarf_pc_mips64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), DEFINE_GPR(badvaddr, NULL, gcc_dwarf_bad_mips64, gcc_dwarf_bad_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), - DEFINE_GPR(sr, NULL, gcc_dwarf_sr_mips64, gcc_dwarf_sr_mips64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), DEFINE_GPR(cause, NULL, gcc_dwarf_cause_mips64, gcc_dwarf_cause_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", gcc_dwarf_pc_mips64, gcc_dwarf_pc_mips64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), DEFINE_GPR(ic, NULL, gcc_dwarf_ic_mips64, gcc_dwarf_ic_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), DEFINE_GPR(dummy, NULL, gcc_dwarf_dummy_mips64, gcc_dwarf_dummy_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), +#else - DEFINE_FPR (fp_reg[0], f0, NULL, gcc_dwarf_f0_mips64, gcc_dwarf_f0_mips64, LLDB_INVALID_REGNUM, gdb_f0_mips64), - DEFINE_FPR (fp_reg[1], f1, NULL, gcc_dwarf_f1_mips64, gcc_dwarf_f1_mips64, LLDB_INVALID_REGNUM, gdb_f1_mips64), - DEFINE_FPR (fp_reg[2], f2, NULL, gcc_dwarf_f2_mips64, gcc_dwarf_f2_mips64, LLDB_INVALID_REGNUM, gdb_f2_mips64), - DEFINE_FPR (fp_reg[3], f3, NULL, gcc_dwarf_f3_mips64, gcc_dwarf_f3_mips64, LLDB_INVALID_REGNUM, gdb_f3_mips64), - DEFINE_FPR (fp_reg[4], f4, NULL, gcc_dwarf_f4_mips64, gcc_dwarf_f4_mips64, LLDB_INVALID_REGNUM, gdb_f4_mips64), - DEFINE_FPR (fp_reg[5], f5, NULL, gcc_dwarf_f5_mips64, gcc_dwarf_f5_mips64, LLDB_INVALID_REGNUM, gdb_f5_mips64), - DEFINE_FPR (fp_reg[6], f6, NULL, gcc_dwarf_f6_mips64, gcc_dwarf_f6_mips64, LLDB_INVALID_REGNUM, gdb_f6_mips64), - DEFINE_FPR (fp_reg[7], f7, NULL, gcc_dwarf_f7_mips64, gcc_dwarf_f7_mips64, LLDB_INVALID_REGNUM, gdb_f7_mips64), - DEFINE_FPR (fp_reg[8], f8, NULL, gcc_dwarf_f8_mips64, gcc_dwarf_f8_mips64, LLDB_INVALID_REGNUM, gdb_f8_mips64), - DEFINE_FPR (fp_reg[9], f9, NULL, gcc_dwarf_f9_mips64, gcc_dwarf_f9_mips64, LLDB_INVALID_REGNUM, gdb_f9_mips64), - DEFINE_FPR (fp_reg[10], f10, NULL, gcc_dwarf_f10_mips64, gcc_dwarf_f10_mips64, LLDB_INVALID_REGNUM, gdb_f10_mips64), - DEFINE_FPR (fp_reg[11], f11, NULL, gcc_dwarf_f11_mips64, gcc_dwarf_f11_mips64, LLDB_INVALID_REGNUM, gdb_f11_mips64), - DEFINE_FPR (fp_reg[12], f12, NULL, gcc_dwarf_f12_mips64, gcc_dwarf_f12_mips64, LLDB_INVALID_REGNUM, gdb_f12_mips64), - DEFINE_FPR (fp_reg[13], f13, NULL, gcc_dwarf_f13_mips64, gcc_dwarf_f13_mips64, LLDB_INVALID_REGNUM, gdb_f13_mips64), - DEFINE_FPR (fp_reg[14], f14, NULL, gcc_dwarf_f14_mips64, gcc_dwarf_f14_mips64, LLDB_INVALID_REGNUM, gdb_f14_mips64), - DEFINE_FPR (fp_reg[15], f15, NULL, gcc_dwarf_f15_mips64, gcc_dwarf_f15_mips64, LLDB_INVALID_REGNUM, gdb_f15_mips64), - DEFINE_FPR (fp_reg[16], f16, NULL, gcc_dwarf_f16_mips64, gcc_dwarf_f16_mips64, LLDB_INVALID_REGNUM, gdb_f16_mips64), - DEFINE_FPR (fp_reg[17], f17, NULL, gcc_dwarf_f17_mips64, gcc_dwarf_f17_mips64, LLDB_INVALID_REGNUM, gdb_f17_mips64), - DEFINE_FPR (fp_reg[18], f18, NULL, gcc_dwarf_f18_mips64, gcc_dwarf_f18_mips64, LLDB_INVALID_REGNUM, gdb_f18_mips64), - DEFINE_FPR (fp_reg[19], f19, NULL, gcc_dwarf_f19_mips64, gcc_dwarf_f19_mips64, LLDB_INVALID_REGNUM, gdb_f19_mips64), - DEFINE_FPR (fp_reg[20], f20, NULL, gcc_dwarf_f20_mips64, gcc_dwarf_f20_mips64, LLDB_INVALID_REGNUM, gdb_f20_mips64), - DEFINE_FPR (fp_reg[21], f21, NULL, gcc_dwarf_f21_mips64, gcc_dwarf_f21_mips64, LLDB_INVALID_REGNUM, gdb_f21_mips64), - DEFINE_FPR (fp_reg[22], f22, NULL, gcc_dwarf_f22_mips64, gcc_dwarf_f22_mips64, LLDB_INVALID_REGNUM, gdb_f22_mips64), - DEFINE_FPR (fp_reg[23], f23, NULL, gcc_dwarf_f23_mips64, gcc_dwarf_f23_mips64, LLDB_INVALID_REGNUM, gdb_f23_mips64), - DEFINE_FPR (fp_reg[24], f24, NULL, gcc_dwarf_f24_mips64, gcc_dwarf_f24_mips64, LLDB_INVALID_REGNUM, gdb_f24_mips64), - DEFINE_FPR (fp_reg[25], f25, NULL, gcc_dwarf_f25_mips64, gcc_dwarf_f25_mips64, LLDB_INVALID_REGNUM, gdb_f25_mips64), - DEFINE_FPR (fp_reg[26], f26, NULL, gcc_dwarf_f26_mips64, gcc_dwarf_f26_mips64, LLDB_INVALID_REGNUM, gdb_f26_mips64), - DEFINE_FPR (fp_reg[27], f27, NULL, gcc_dwarf_f27_mips64, gcc_dwarf_f27_mips64, LLDB_INVALID_REGNUM, gdb_f27_mips64), - DEFINE_FPR (fp_reg[28], f28, NULL, gcc_dwarf_f28_mips64, gcc_dwarf_f28_mips64, LLDB_INVALID_REGNUM, gdb_f28_mips64), - DEFINE_FPR (fp_reg[29], f29, NULL, gcc_dwarf_f29_mips64, gcc_dwarf_f29_mips64, LLDB_INVALID_REGNUM, gdb_f29_mips64), - DEFINE_FPR (fp_reg[30], f30, NULL, gcc_dwarf_f30_mips64, gcc_dwarf_f30_mips64, LLDB_INVALID_REGNUM, gdb_f30_mips64), - DEFINE_FPR (fp_reg[31], f31, NULL, gcc_dwarf_f31_mips64, gcc_dwarf_f31_mips64, LLDB_INVALID_REGNUM, gdb_f31_mips64), - DEFINE_FPR (fcsr, fcsr, NULL, gcc_dwarf_fcsr_mips64, gcc_dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, gdb_fcsr_mips64), - DEFINE_FPR (fir, fir, NULL, gcc_dwarf_fir_mips64, gcc_dwarf_fir_mips64, LLDB_INVALID_REGNUM, gdb_fir_mips64) + DEFINE_GPR(zero, "r0", gcc_dwarf_zero_mips64, gcc_dwarf_zero_mips64, LLDB_INVALID_REGNUM, gdb_zero_mips64), + DEFINE_GPR(r1, NULL, gcc_dwarf_r1_mips64, gcc_dwarf_r1_mips64, LLDB_INVALID_REGNUM, gdb_r1_mips64), + DEFINE_GPR(r2, NULL, gcc_dwarf_r2_mips64, gcc_dwarf_r2_mips64, LLDB_INVALID_REGNUM, gdb_r2_mips64), + DEFINE_GPR(r3, NULL, gcc_dwarf_r3_mips64, gcc_dwarf_r3_mips64, LLDB_INVALID_REGNUM, gdb_r3_mips64), + DEFINE_GPR(r4, NULL, gcc_dwarf_r4_mips64, gcc_dwarf_r4_mips64, LLDB_REGNUM_GENERIC_ARG1, gdb_r4_mips64), + DEFINE_GPR(r5, NULL, gcc_dwarf_r5_mips64, gcc_dwarf_r5_mips64, LLDB_REGNUM_GENERIC_ARG2, gdb_r5_mips64), + DEFINE_GPR(r6, NULL, gcc_dwarf_r6_mips64, gcc_dwarf_r6_mips64, LLDB_REGNUM_GENERIC_ARG3, gdb_r6_mips64), + DEFINE_GPR(r7, NULL, gcc_dwarf_r7_mips64, gcc_dwarf_r7_mips64, LLDB_REGNUM_GENERIC_ARG4, gdb_r7_mips64), + DEFINE_GPR(r8, NULL, gcc_dwarf_r8_mips64, gcc_dwarf_r8_mips64, LLDB_REGNUM_GENERIC_ARG5, gdb_r8_mips64), + DEFINE_GPR(r9, NULL, gcc_dwarf_r9_mips64, gcc_dwarf_r9_mips64, LLDB_REGNUM_GENERIC_ARG6, gdb_r9_mips64), + DEFINE_GPR(r10, NULL, gcc_dwarf_r10_mips64, gcc_dwarf_r10_mips64, LLDB_REGNUM_GENERIC_ARG7, gdb_r10_mips64), + DEFINE_GPR(r11, NULL, gcc_dwarf_r11_mips64, gcc_dwarf_r11_mips64, LLDB_REGNUM_GENERIC_ARG8, gdb_r11_mips64), + DEFINE_GPR(r12, NULL, gcc_dwarf_r12_mips64, gcc_dwarf_r12_mips64, LLDB_INVALID_REGNUM, gdb_r12_mips64), + DEFINE_GPR(r13, NULL, gcc_dwarf_r13_mips64, gcc_dwarf_r13_mips64, LLDB_INVALID_REGNUM, gdb_r13_mips64), + DEFINE_GPR(r14, NULL, gcc_dwarf_r14_mips64, gcc_dwarf_r14_mips64, LLDB_INVALID_REGNUM, gdb_r14_mips64), + DEFINE_GPR(r15, NULL, gcc_dwarf_r15_mips64, gcc_dwarf_r15_mips64, LLDB_INVALID_REGNUM, gdb_r15_mips64), + DEFINE_GPR(r16, NULL, gcc_dwarf_r16_mips64, gcc_dwarf_r16_mips64, LLDB_INVALID_REGNUM, gdb_r16_mips64), + DEFINE_GPR(r17, NULL, gcc_dwarf_r17_mips64, gcc_dwarf_r17_mips64, LLDB_INVALID_REGNUM, gdb_r17_mips64), + DEFINE_GPR(r18, NULL, gcc_dwarf_r18_mips64, gcc_dwarf_r18_mips64, LLDB_INVALID_REGNUM, gdb_r18_mips64), + DEFINE_GPR(r19, NULL, gcc_dwarf_r19_mips64, gcc_dwarf_r19_mips64, LLDB_INVALID_REGNUM, gdb_r19_mips64), + DEFINE_GPR(r20, NULL, gcc_dwarf_r20_mips64, gcc_dwarf_r20_mips64, LLDB_INVALID_REGNUM, gdb_r20_mips64), + DEFINE_GPR(r21, NULL, gcc_dwarf_r21_mips64, gcc_dwarf_r21_mips64, LLDB_INVALID_REGNUM, gdb_r21_mips64), + DEFINE_GPR(r22, NULL, gcc_dwarf_r22_mips64, gcc_dwarf_r22_mips64, LLDB_INVALID_REGNUM, gdb_r22_mips64), + DEFINE_GPR(r23, NULL, gcc_dwarf_r23_mips64, gcc_dwarf_r23_mips64, LLDB_INVALID_REGNUM, gdb_r23_mips64), + DEFINE_GPR(r24, NULL, gcc_dwarf_r24_mips64, gcc_dwarf_r24_mips64, LLDB_INVALID_REGNUM, gdb_r24_mips64), + DEFINE_GPR(r25, NULL, gcc_dwarf_r25_mips64, gcc_dwarf_r25_mips64, LLDB_INVALID_REGNUM, gdb_r25_mips64), + DEFINE_GPR(r26, NULL, gcc_dwarf_r26_mips64, gcc_dwarf_r26_mips64, LLDB_INVALID_REGNUM, gdb_r26_mips64), + DEFINE_GPR(r27, NULL, gcc_dwarf_r27_mips64, gcc_dwarf_r27_mips64, LLDB_INVALID_REGNUM, gdb_r27_mips64), + DEFINE_GPR(gp, "r28", gcc_dwarf_gp_mips64, gcc_dwarf_gp_mips64, LLDB_INVALID_REGNUM, gdb_gp_mips64), + DEFINE_GPR(sp, "r29", gcc_dwarf_sp_mips64, gcc_dwarf_sp_mips64, LLDB_REGNUM_GENERIC_SP, gdb_sp_mips64), + DEFINE_GPR(r30, NULL, gcc_dwarf_r30_mips64, gcc_dwarf_r30_mips64, LLDB_REGNUM_GENERIC_FP, gdb_r30_mips64), + DEFINE_GPR(ra, "r31", gcc_dwarf_ra_mips64, gcc_dwarf_ra_mips64, LLDB_REGNUM_GENERIC_RA, gdb_ra_mips64), + DEFINE_GPR_INFO(sr, NULL, gcc_dwarf_sr_mips64, gcc_dwarf_sr_mips64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(mullo, NULL, gcc_dwarf_lo_mips64, gcc_dwarf_lo_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(mulhi, NULL, gcc_dwarf_hi_mips64, gcc_dwarf_hi_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(badvaddr, NULL, gcc_dwarf_bad_mips64, gcc_dwarf_bad_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR_INFO(cause, NULL, gcc_dwarf_cause_mips64, gcc_dwarf_cause_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", gcc_dwarf_pc_mips64, gcc_dwarf_pc_mips64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR_INFO(config5, NULL, gcc_dwarf_config5_mips64, gcc_dwarf_config5_mips64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_FPR (f0, NULL, gcc_dwarf_f0_mips64, gcc_dwarf_f0_mips64, LLDB_INVALID_REGNUM, gdb_f0_mips64), + DEFINE_FPR (f1, NULL, gcc_dwarf_f1_mips64, gcc_dwarf_f1_mips64, LLDB_INVALID_REGNUM, gdb_f1_mips64), + DEFINE_FPR (f2, NULL, gcc_dwarf_f2_mips64, gcc_dwarf_f2_mips64, LLDB_INVALID_REGNUM, gdb_f2_mips64), + DEFINE_FPR (f3, NULL, gcc_dwarf_f3_mips64, gcc_dwarf_f3_mips64, LLDB_INVALID_REGNUM, gdb_f3_mips64), + DEFINE_FPR (f4, NULL, gcc_dwarf_f4_mips64, gcc_dwarf_f4_mips64, LLDB_INVALID_REGNUM, gdb_f4_mips64), + DEFINE_FPR (f5, NULL, gcc_dwarf_f5_mips64, gcc_dwarf_f5_mips64, LLDB_INVALID_REGNUM, gdb_f5_mips64), + DEFINE_FPR (f6, NULL, gcc_dwarf_f6_mips64, gcc_dwarf_f6_mips64, LLDB_INVALID_REGNUM, gdb_f6_mips64), + DEFINE_FPR (f7, NULL, gcc_dwarf_f7_mips64, gcc_dwarf_f7_mips64, LLDB_INVALID_REGNUM, gdb_f7_mips64), + DEFINE_FPR (f8, NULL, gcc_dwarf_f8_mips64, gcc_dwarf_f8_mips64, LLDB_INVALID_REGNUM, gdb_f8_mips64), + DEFINE_FPR (f9, NULL, gcc_dwarf_f9_mips64, gcc_dwarf_f9_mips64, LLDB_INVALID_REGNUM, gdb_f9_mips64), + DEFINE_FPR (f10, NULL, gcc_dwarf_f10_mips64, gcc_dwarf_f10_mips64, LLDB_INVALID_REGNUM, gdb_f10_mips64), + DEFINE_FPR (f11, NULL, gcc_dwarf_f11_mips64, gcc_dwarf_f11_mips64, LLDB_INVALID_REGNUM, gdb_f11_mips64), + DEFINE_FPR (f12, NULL, gcc_dwarf_f12_mips64, gcc_dwarf_f12_mips64, LLDB_INVALID_REGNUM, gdb_f12_mips64), + DEFINE_FPR (f13, NULL, gcc_dwarf_f13_mips64, gcc_dwarf_f13_mips64, LLDB_INVALID_REGNUM, gdb_f13_mips64), + DEFINE_FPR (f14, NULL, gcc_dwarf_f14_mips64, gcc_dwarf_f14_mips64, LLDB_INVALID_REGNUM, gdb_f14_mips64), + DEFINE_FPR (f15, NULL, gcc_dwarf_f15_mips64, gcc_dwarf_f15_mips64, LLDB_INVALID_REGNUM, gdb_f15_mips64), + DEFINE_FPR (f16, NULL, gcc_dwarf_f16_mips64, gcc_dwarf_f16_mips64, LLDB_INVALID_REGNUM, gdb_f16_mips64), + DEFINE_FPR (f17, NULL, gcc_dwarf_f17_mips64, gcc_dwarf_f17_mips64, LLDB_INVALID_REGNUM, gdb_f17_mips64), + DEFINE_FPR (f18, NULL, gcc_dwarf_f18_mips64, gcc_dwarf_f18_mips64, LLDB_INVALID_REGNUM, gdb_f18_mips64), + DEFINE_FPR (f19, NULL, gcc_dwarf_f19_mips64, gcc_dwarf_f19_mips64, LLDB_INVALID_REGNUM, gdb_f19_mips64), + DEFINE_FPR (f20, NULL, gcc_dwarf_f20_mips64, gcc_dwarf_f20_mips64, LLDB_INVALID_REGNUM, gdb_f20_mips64), + DEFINE_FPR (f21, NULL, gcc_dwarf_f21_mips64, gcc_dwarf_f21_mips64, LLDB_INVALID_REGNUM, gdb_f21_mips64), + DEFINE_FPR (f22, NULL, gcc_dwarf_f22_mips64, gcc_dwarf_f22_mips64, LLDB_INVALID_REGNUM, gdb_f22_mips64), + DEFINE_FPR (f23, NULL, gcc_dwarf_f23_mips64, gcc_dwarf_f23_mips64, LLDB_INVALID_REGNUM, gdb_f23_mips64), + DEFINE_FPR (f24, NULL, gcc_dwarf_f24_mips64, gcc_dwarf_f24_mips64, LLDB_INVALID_REGNUM, gdb_f24_mips64), + DEFINE_FPR (f25, NULL, gcc_dwarf_f25_mips64, gcc_dwarf_f25_mips64, LLDB_INVALID_REGNUM, gdb_f25_mips64), + DEFINE_FPR (f26, NULL, gcc_dwarf_f26_mips64, gcc_dwarf_f26_mips64, LLDB_INVALID_REGNUM, gdb_f26_mips64), + DEFINE_FPR (f27, NULL, gcc_dwarf_f27_mips64, gcc_dwarf_f27_mips64, LLDB_INVALID_REGNUM, gdb_f27_mips64), + DEFINE_FPR (f28, NULL, gcc_dwarf_f28_mips64, gcc_dwarf_f28_mips64, LLDB_INVALID_REGNUM, gdb_f28_mips64), + DEFINE_FPR (f29, NULL, gcc_dwarf_f29_mips64, gcc_dwarf_f29_mips64, LLDB_INVALID_REGNUM, gdb_f29_mips64), + DEFINE_FPR (f30, NULL, gcc_dwarf_f30_mips64, gcc_dwarf_f30_mips64, LLDB_INVALID_REGNUM, gdb_f30_mips64), + DEFINE_FPR (f31, NULL, gcc_dwarf_f31_mips64, gcc_dwarf_f31_mips64, LLDB_INVALID_REGNUM, gdb_f31_mips64), + DEFINE_FPR (fcsr, NULL, gcc_dwarf_fcsr_mips64, gcc_dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, gdb_fcsr_mips64), + DEFINE_FPR (fir, NULL, gcc_dwarf_fir_mips64, gcc_dwarf_fir_mips64, LLDB_INVALID_REGNUM, gdb_fir_mips64), + DEFINE_FPR (config5, NULL, gcc_dwarf_config5_mips64, gcc_dwarf_config5_mips64, LLDB_INVALID_REGNUM, gdb_config5_mips64), + DEFINE_MSA (w0, NULL, gcc_dwarf_w0_mips64, gcc_dwarf_w0_mips64, LLDB_INVALID_REGNUM, gdb_w0_mips64), + DEFINE_MSA (w1, NULL, gcc_dwarf_w1_mips64, gcc_dwarf_w1_mips64, LLDB_INVALID_REGNUM, gdb_w1_mips64), + DEFINE_MSA (w2, NULL, gcc_dwarf_w2_mips64, gcc_dwarf_w2_mips64, LLDB_INVALID_REGNUM, gdb_w2_mips64), + DEFINE_MSA (w3, NULL, gcc_dwarf_w3_mips64, gcc_dwarf_w3_mips64, LLDB_INVALID_REGNUM, gdb_w3_mips64), + DEFINE_MSA (w4, NULL, gcc_dwarf_w4_mips64, gcc_dwarf_w4_mips64, LLDB_INVALID_REGNUM, gdb_w4_mips64), + DEFINE_MSA (w5, NULL, gcc_dwarf_w5_mips64, gcc_dwarf_w5_mips64, LLDB_INVALID_REGNUM, gdb_w5_mips64), + DEFINE_MSA (w6, NULL, gcc_dwarf_w6_mips64, gcc_dwarf_w6_mips64, LLDB_INVALID_REGNUM, gdb_w6_mips64), + DEFINE_MSA (w7, NULL, gcc_dwarf_w7_mips64, gcc_dwarf_w7_mips64, LLDB_INVALID_REGNUM, gdb_w7_mips64), + DEFINE_MSA (w8, NULL, gcc_dwarf_w8_mips64, gcc_dwarf_w8_mips64, LLDB_INVALID_REGNUM, gdb_w8_mips64), + DEFINE_MSA (w9, NULL, gcc_dwarf_w9_mips64, gcc_dwarf_w9_mips64, LLDB_INVALID_REGNUM, gdb_w9_mips64), + DEFINE_MSA (w10, NULL, gcc_dwarf_w10_mips64, gcc_dwarf_w10_mips64, LLDB_INVALID_REGNUM, gdb_w10_mips64), + DEFINE_MSA (w11, NULL, gcc_dwarf_w11_mips64, gcc_dwarf_w11_mips64, LLDB_INVALID_REGNUM, gdb_w11_mips64), + DEFINE_MSA (w12, NULL, gcc_dwarf_w12_mips64, gcc_dwarf_w12_mips64, LLDB_INVALID_REGNUM, gdb_w12_mips64), + DEFINE_MSA (w13, NULL, gcc_dwarf_w13_mips64, gcc_dwarf_w13_mips64, LLDB_INVALID_REGNUM, gdb_w13_mips64), + DEFINE_MSA (w14, NULL, gcc_dwarf_w14_mips64, gcc_dwarf_w14_mips64, LLDB_INVALID_REGNUM, gdb_w14_mips64), + DEFINE_MSA (w15, NULL, gcc_dwarf_w15_mips64, gcc_dwarf_w15_mips64, LLDB_INVALID_REGNUM, gdb_w15_mips64), + DEFINE_MSA (w16, NULL, gcc_dwarf_w16_mips64, gcc_dwarf_w16_mips64, LLDB_INVALID_REGNUM, gdb_w16_mips64), + DEFINE_MSA (w17, NULL, gcc_dwarf_w17_mips64, gcc_dwarf_w17_mips64, LLDB_INVALID_REGNUM, gdb_w17_mips64), + DEFINE_MSA (w18, NULL, gcc_dwarf_w18_mips64, gcc_dwarf_w18_mips64, LLDB_INVALID_REGNUM, gdb_w18_mips64), + DEFINE_MSA (w19, NULL, gcc_dwarf_w19_mips64, gcc_dwarf_w19_mips64, LLDB_INVALID_REGNUM, gdb_w19_mips64), + DEFINE_MSA (w20, NULL, gcc_dwarf_w10_mips64, gcc_dwarf_w20_mips64, LLDB_INVALID_REGNUM, gdb_w20_mips64), + DEFINE_MSA (w21, NULL, gcc_dwarf_w21_mips64, gcc_dwarf_w21_mips64, LLDB_INVALID_REGNUM, gdb_w21_mips64), + DEFINE_MSA (w22, NULL, gcc_dwarf_w22_mips64, gcc_dwarf_w22_mips64, LLDB_INVALID_REGNUM, gdb_w22_mips64), + DEFINE_MSA (w23, NULL, gcc_dwarf_w23_mips64, gcc_dwarf_w23_mips64, LLDB_INVALID_REGNUM, gdb_w23_mips64), + DEFINE_MSA (w24, NULL, gcc_dwarf_w24_mips64, gcc_dwarf_w24_mips64, LLDB_INVALID_REGNUM, gdb_w24_mips64), + DEFINE_MSA (w25, NULL, gcc_dwarf_w25_mips64, gcc_dwarf_w25_mips64, LLDB_INVALID_REGNUM, gdb_w25_mips64), + DEFINE_MSA (w26, NULL, gcc_dwarf_w26_mips64, gcc_dwarf_w26_mips64, LLDB_INVALID_REGNUM, gdb_w26_mips64), + DEFINE_MSA (w27, NULL, gcc_dwarf_w27_mips64, gcc_dwarf_w27_mips64, LLDB_INVALID_REGNUM, gdb_w27_mips64), + DEFINE_MSA (w28, NULL, gcc_dwarf_w28_mips64, gcc_dwarf_w28_mips64, LLDB_INVALID_REGNUM, gdb_w28_mips64), + DEFINE_MSA (w29, NULL, gcc_dwarf_w29_mips64, gcc_dwarf_w29_mips64, LLDB_INVALID_REGNUM, gdb_w29_mips64), + DEFINE_MSA (w30, NULL, gcc_dwarf_w30_mips64, gcc_dwarf_w30_mips64, LLDB_INVALID_REGNUM, gdb_w30_mips64), + DEFINE_MSA (w31, NULL, gcc_dwarf_w31_mips64, gcc_dwarf_w31_mips64, LLDB_INVALID_REGNUM, gdb_w31_mips64), + DEFINE_MSA_INFO (mcsr, NULL, gcc_dwarf_mcsr_mips64, gcc_dwarf_mcsr_mips64, LLDB_INVALID_REGNUM, gdb_mcsr_mips64), + DEFINE_MSA_INFO (mir, NULL, gcc_dwarf_mir_mips64, gcc_dwarf_mir_mips64, LLDB_INVALID_REGNUM, gdb_mir_mips64), + DEFINE_MSA_INFO (fcsr, NULL, gcc_dwarf_fcsr_mips64, gcc_dwarf_fcsr_mips64, LLDB_INVALID_REGNUM, gdb_fcsr_mips64), + DEFINE_MSA_INFO (fir, NULL, gcc_dwarf_fir_mips64, gcc_dwarf_fir_mips64, LLDB_INVALID_REGNUM, gdb_fir_mips64), + DEFINE_MSA_INFO (config5, NULL, gcc_dwarf_config5_mips64, gcc_dwarf_config5_mips64, LLDB_INVALID_REGNUM, gdb_config5_mips64) +#endif }; + static_assert((sizeof(g_register_infos_mips64) / sizeof(g_register_infos_mips64[0])) == k_num_registers_mips64, "g_register_infos_mips64 has wrong number of register infos"); #undef DEFINE_GPR +#undef DEFINE_GPR_INFO #undef DEFINE_FPR +#undef DEFINE_MSA +#undef DEFINE_MSA_INFO #undef GPR_OFFSET #undef FPR_OFFSET +#undef MSA_OFFSET #endif // DECLARE_REGISTER_INFOS_MIPS64_STRUCT Index: vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h (nonexistent) +++ vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.h (revision 287514) @@ -0,0 +1,70 @@ +//===-- lldb-mips-frebsd-register-enums.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_mips_freebsd_register_enums_h +#define lldb_mips_freebsd_register_enums_h + +namespace lldb_private +{ + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + + //--------------------------------------------------------------------------- + // Internal codes for all mips registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_mips64, + gpr_zero_mips64 = k_first_gpr_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_sr_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_badvaddr_mips64, + gpr_cause_mips64, + gpr_pc_mips64, + gpr_ic_mips64, + gpr_dummy_mips64, + k_last_gpr_mips64 = gpr_dummy_mips64, + + k_num_registers_mips64, + + k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1 + }; +} +#endif // #ifndef lldb_mips_freebsd_register_enums_h Property changes on: vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-freebsd-register-enums.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/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h (nonexistent) +++ vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.h (revision 287514) @@ -0,0 +1,285 @@ +//===-- lldb-mips-linux-register-enums.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_mips_linux_register_enums_h +#define lldb_mips_linux_register_enums_h + +namespace lldb_private +{ + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) + + //--------------------------------------------------------------------------- + // Internal codes for all mips registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_mips, + gpr_zero_mips = k_first_gpr_mips, + gpr_r1_mips, + gpr_r2_mips, + gpr_r3_mips, + gpr_r4_mips, + gpr_r5_mips, + gpr_r6_mips, + gpr_r7_mips, + gpr_r8_mips, + gpr_r9_mips, + gpr_r10_mips, + gpr_r11_mips, + gpr_r12_mips, + gpr_r13_mips, + gpr_r14_mips, + gpr_r15_mips, + gpr_r16_mips, + gpr_r17_mips, + gpr_r18_mips, + gpr_r19_mips, + gpr_r20_mips, + gpr_r21_mips, + gpr_r22_mips, + gpr_r23_mips, + gpr_r24_mips, + gpr_r25_mips, + gpr_r26_mips, + gpr_r27_mips, + gpr_gp_mips, + gpr_sp_mips, + gpr_r30_mips, + gpr_ra_mips, + gpr_sr_mips, + gpr_mullo_mips, + gpr_mulhi_mips, + gpr_badvaddr_mips, + gpr_cause_mips, + gpr_pc_mips, + gpr_config5_mips, + + k_last_gpr_mips = gpr_config5_mips, + + k_first_fpr_mips, + fpr_f0_mips = k_first_fpr_mips, + fpr_f1_mips, + fpr_f2_mips, + fpr_f3_mips, + fpr_f4_mips, + fpr_f5_mips, + fpr_f6_mips, + fpr_f7_mips, + fpr_f8_mips, + fpr_f9_mips, + fpr_f10_mips, + fpr_f11_mips, + fpr_f12_mips, + fpr_f13_mips, + fpr_f14_mips, + fpr_f15_mips, + fpr_f16_mips, + fpr_f17_mips, + fpr_f18_mips, + fpr_f19_mips, + fpr_f20_mips, + fpr_f21_mips, + fpr_f22_mips, + fpr_f23_mips, + fpr_f24_mips, + fpr_f25_mips, + fpr_f26_mips, + fpr_f27_mips, + fpr_f28_mips, + fpr_f29_mips, + fpr_f30_mips, + fpr_f31_mips, + fpr_fcsr_mips, + fpr_fir_mips, + fpr_config5_mips, + k_last_fpr_mips = fpr_config5_mips, + + k_first_msa_mips, + msa_w0_mips = k_first_msa_mips, + msa_w1_mips, + msa_w2_mips, + msa_w3_mips, + msa_w4_mips, + msa_w5_mips, + msa_w6_mips, + msa_w7_mips, + msa_w8_mips, + msa_w9_mips, + msa_w10_mips, + msa_w11_mips, + msa_w12_mips, + msa_w13_mips, + msa_w14_mips, + msa_w15_mips, + msa_w16_mips, + msa_w17_mips, + msa_w18_mips, + msa_w19_mips, + msa_w20_mips, + msa_w21_mips, + msa_w22_mips, + msa_w23_mips, + msa_w24_mips, + msa_w25_mips, + msa_w26_mips, + msa_w27_mips, + msa_w28_mips, + msa_w29_mips, + msa_w30_mips, + msa_w31_mips, + msa_fcsr_mips, + msa_fir_mips, + msa_mcsr_mips, + msa_mir_mips, + msa_config5_mips, + k_last_msa_mips = msa_config5_mips, + + k_num_registers_mips, + + k_num_gpr_registers_mips = k_last_gpr_mips - k_first_gpr_mips + 1, + k_num_fpr_registers_mips = k_last_fpr_mips - k_first_fpr_mips + 1, + k_num_msa_registers_mips = k_last_msa_mips - k_first_msa_mips + 1, + k_num_user_registers_mips = k_num_gpr_registers_mips + k_num_fpr_registers_mips + k_num_msa_registers_mips + }; + + //--------------------------------------------------------------------------- + // Internal codes for all mips64 registers. + //--------------------------------------------------------------------------- + enum + { + k_first_gpr_mips64, + gpr_zero_mips64 = k_first_gpr_mips64, + gpr_r1_mips64, + gpr_r2_mips64, + gpr_r3_mips64, + gpr_r4_mips64, + gpr_r5_mips64, + gpr_r6_mips64, + gpr_r7_mips64, + gpr_r8_mips64, + gpr_r9_mips64, + gpr_r10_mips64, + gpr_r11_mips64, + gpr_r12_mips64, + gpr_r13_mips64, + gpr_r14_mips64, + gpr_r15_mips64, + gpr_r16_mips64, + gpr_r17_mips64, + gpr_r18_mips64, + gpr_r19_mips64, + gpr_r20_mips64, + gpr_r21_mips64, + gpr_r22_mips64, + gpr_r23_mips64, + gpr_r24_mips64, + gpr_r25_mips64, + gpr_r26_mips64, + gpr_r27_mips64, + gpr_gp_mips64, + gpr_sp_mips64, + gpr_r30_mips64, + gpr_ra_mips64, + gpr_sr_mips64, + gpr_mullo_mips64, + gpr_mulhi_mips64, + gpr_badvaddr_mips64, + gpr_cause_mips64, + gpr_pc_mips64, + gpr_config5_mips64, + k_last_gpr_mips64 = gpr_config5_mips64, + + k_first_fpr_mips64, + fpr_f0_mips64 = k_first_fpr_mips64, + fpr_f1_mips64, + fpr_f2_mips64, + fpr_f3_mips64, + fpr_f4_mips64, + fpr_f5_mips64, + fpr_f6_mips64, + fpr_f7_mips64, + fpr_f8_mips64, + fpr_f9_mips64, + fpr_f10_mips64, + fpr_f11_mips64, + fpr_f12_mips64, + fpr_f13_mips64, + fpr_f14_mips64, + fpr_f15_mips64, + fpr_f16_mips64, + fpr_f17_mips64, + fpr_f18_mips64, + fpr_f19_mips64, + fpr_f20_mips64, + fpr_f21_mips64, + fpr_f22_mips64, + fpr_f23_mips64, + fpr_f24_mips64, + fpr_f25_mips64, + fpr_f26_mips64, + fpr_f27_mips64, + fpr_f28_mips64, + fpr_f29_mips64, + fpr_f30_mips64, + fpr_f31_mips64, + fpr_fcsr_mips64, + fpr_fir_mips64, + fpr_config5_mips64, + k_last_fpr_mips64 = fpr_config5_mips64, + + k_first_msa_mips64, + msa_w0_mips64 = k_first_msa_mips64, + msa_w1_mips64, + msa_w2_mips64, + msa_w3_mips64, + msa_w4_mips64, + msa_w5_mips64, + msa_w6_mips64, + msa_w7_mips64, + msa_w8_mips64, + msa_w9_mips64, + msa_w10_mips64, + msa_w11_mips64, + msa_w12_mips64, + msa_w13_mips64, + msa_w14_mips64, + msa_w15_mips64, + msa_w16_mips64, + msa_w17_mips64, + msa_w18_mips64, + msa_w19_mips64, + msa_w20_mips64, + msa_w21_mips64, + msa_w22_mips64, + msa_w23_mips64, + msa_w24_mips64, + msa_w25_mips64, + msa_w26_mips64, + msa_w27_mips64, + msa_w28_mips64, + msa_w29_mips64, + msa_w30_mips64, + msa_w31_mips64, + msa_fcsr_mips64, + msa_fir_mips64, + msa_mcsr_mips64, + msa_mir_mips64, + msa_config5_mips64, + k_last_msa_mips64 = msa_config5_mips64, + + k_num_registers_mips64, + + k_num_gpr_registers_mips64 = k_last_gpr_mips64 - k_first_gpr_mips64 + 1, + k_num_fpr_registers_mips64 = k_last_fpr_mips64 - k_first_fpr_mips64 + 1, + k_num_msa_registers_mips64 = k_last_msa_mips64 - k_first_msa_mips64 + 1, + k_num_user_registers_mips64 = k_num_gpr_registers_mips64 + k_num_fpr_registers_mips64 + k_num_msa_registers_mips64 + }; +} + +#endif // #ifndef lldb_mips_linux_register_enums_h Property changes on: vendor/lldb/dist/source/Plugins/Process/Utility/lldb-mips-linux-register-enums.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/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 287514) @@ -1,4421 +1,4455 @@ //===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "GDBRemoteCommunicationClient.h" // C Includes #include #include // C++ Includes #include #include // Other libraries and framework includes #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/Log.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/TimeValue.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Target.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/UnixSignals.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "lldb/Host/Config.h" #if defined (HAVE_LIBCOMPRESSION) #include #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet"), m_supports_not_sending_acks (eLazyBoolCalculate), m_supports_thread_suffix (eLazyBoolCalculate), m_supports_threads_in_stop_reply (eLazyBoolCalculate), m_supports_vCont_all (eLazyBoolCalculate), m_supports_vCont_any (eLazyBoolCalculate), m_supports_vCont_c (eLazyBoolCalculate), m_supports_vCont_C (eLazyBoolCalculate), m_supports_vCont_s (eLazyBoolCalculate), m_supports_vCont_S (eLazyBoolCalculate), m_qHostInfo_is_valid (eLazyBoolCalculate), m_curr_pid_is_valid (eLazyBoolCalculate), m_qProcessInfo_is_valid (eLazyBoolCalculate), m_qGDBServerVersion_is_valid (eLazyBoolCalculate), m_supports_alloc_dealloc_memory (eLazyBoolCalculate), m_supports_memory_region_info (eLazyBoolCalculate), m_supports_watchpoint_support_info (eLazyBoolCalculate), m_supports_detach_stay_stopped (eLazyBoolCalculate), m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply (eLazyBoolCalculate), m_supports_p (eLazyBoolCalculate), m_supports_x (eLazyBoolCalculate), m_avoid_g_packets (eLazyBoolCalculate), m_supports_QSaveRegisterState (eLazyBoolCalculate), m_supports_qXfer_auxv_read (eLazyBoolCalculate), m_supports_qXfer_libraries_read (eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read (eLazyBoolCalculate), m_supports_qXfer_features_read (eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read (eLazyBoolCalculate), m_supports_jThreadExtendedInfo (eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos (eLazyBoolCalculate), m_supports_qProcessInfoPID (true), m_supports_qfProcessInfo (true), m_supports_qUserName (true), m_supports_qGroupName (true), m_supports_qThreadStopInfo (true), m_supports_z0 (true), m_supports_z1 (true), m_supports_z2 (true), m_supports_z3 (true), m_supports_z4 (true), m_supports_QEnvironment (true), m_supports_QEnvironmentHexEncoded (true), m_supports_qSymbol (true), m_supports_jThreadsInfo (true), m_curr_pid (LLDB_INVALID_PROCESS_ID), m_curr_tid (LLDB_INVALID_THREAD_ID), m_curr_tid_run (LLDB_INVALID_THREAD_ID), m_num_supported_hardware_watchpoints (0), m_async_mutex (Mutex::eMutexTypeRecursive), m_async_packet_predicate (false), m_async_packet (), m_async_result (PacketResult::Success), m_async_response (), m_async_signal (-1), m_interrupt_sent (false), m_thread_id_to_used_usec_map (), m_host_arch(), m_process_arch(), m_os_version_major (UINT32_MAX), m_os_version_minor (UINT32_MAX), m_os_version_update (UINT32_MAX), m_os_build (), m_os_kernel (), m_hostname (), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX), m_default_packet_timeout (0), m_max_packet_size (0) { } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { if (IsConnected()) Disconnect(); } bool GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) { ResetDiscoverableSettings(false); // Start the read thread after we send the handshake ack since if we // fail to send the handshake ack, there is no reason to continue... if (SendAck()) { // Wait for any responses that might have been queued up in the remote // GDB server and flush them all StringExtractorGDBRemote response; PacketResult packet_result = PacketResult::Success; const uint32_t timeout_usec = 10 * 1000; // Wait for 10 ms for a response while (packet_result == PacketResult::Success) packet_result = ReadPacket (response, timeout_usec, false); // The return value from QueryNoAckModeSupported() is true if the packet // was sent and _any_ response (including UNIMPLEMENTED) was received), // or false if no response was received. This quickly tells us if we have // a live connection to a remote GDB server... if (QueryNoAckModeSupported()) { return true; } else { if (error_ptr) error_ptr->SetErrorString("failed to get reply to handshake packet"); } } else { if (error_ptr) error_ptr->SetErrorString("failed to send the handshake ack"); } return false; } bool GDBRemoteCommunicationClient::GetEchoSupported () { if (m_supports_qEcho == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qEcho == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported () { if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported () { if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported () { if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported () { if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_auxv_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported () { if (m_supports_qXfer_features_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_features_read == eLazyBoolYes; } uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { if (m_max_packet_size == 0) { GetRemoteQSupported(); } return m_max_packet_size; } bool GDBRemoteCommunicationClient::QueryNoAckModeSupported () { if (m_supports_not_sending_acks == eLazyBoolCalculate) { m_send_acks = true; m_supports_not_sending_acks = eLazyBoolNo; // This is the first real packet that we'll send in a debug session and it may take a little // longer than normal to receive a reply. Wait at least 6 seconds for a reply to this packet. const uint32_t minimum_timeout = 6; uint32_t old_timeout = GetPacketTimeoutInMicroSeconds() / lldb_private::TimeValue::MicroSecPerSec; GDBRemoteCommunication::ScopedTimeout timeout (*this, std::max (old_timeout, minimum_timeout)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_send_acks = false; m_supports_not_sending_acks = eLazyBoolYes; } return true; } } return false; } void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported () { if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { m_supports_threads_in_stop_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_threads_in_stop_reply = eLazyBoolYes; } } } bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported () { if (m_attach_or_wait_reply == eLazyBoolCalculate) { m_attach_or_wait_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_attach_or_wait_reply = eLazyBoolYes; } } if (m_attach_or_wait_reply == eLazyBoolYes) return true; else return false; } bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported () { if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { m_prepare_for_reg_writing_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_prepare_for_reg_writing_reply = eLazyBoolYes; } } if (m_prepare_for_reg_writing_reply == eLazyBoolYes) return true; else return false; } void GDBRemoteCommunicationClient::ResetDiscoverableSettings (bool did_exec) { if (did_exec == false) { // Hard reset everything, this is when we first connect to a GDB server m_supports_not_sending_acks = eLazyBoolCalculate; m_supports_thread_suffix = eLazyBoolCalculate; m_supports_threads_in_stop_reply = eLazyBoolCalculate; m_supports_vCont_c = eLazyBoolCalculate; m_supports_vCont_C = eLazyBoolCalculate; m_supports_vCont_s = eLazyBoolCalculate; m_supports_vCont_S = eLazyBoolCalculate; m_supports_p = eLazyBoolCalculate; m_supports_x = eLazyBoolCalculate; m_supports_QSaveRegisterState = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; m_curr_pid_is_valid = eLazyBoolCalculate; m_qGDBServerVersion_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; m_supports_memory_region_info = eLazyBoolCalculate; m_prepare_for_reg_writing_reply = eLazyBoolCalculate; m_attach_or_wait_reply = eLazyBoolCalculate; m_avoid_g_packets = eLazyBoolCalculate; m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; m_supports_qfProcessInfo = true; m_supports_qUserName = true; m_supports_qGroupName = true; m_supports_qThreadStopInfo = true; m_supports_z0 = true; m_supports_z1 = true; m_supports_z2 = true; m_supports_z3 = true; m_supports_z4 = true; m_supports_QEnvironment = true; m_supports_QEnvironmentHexEncoded = true; m_supports_qSymbol = true; m_host_arch.Clear(); m_os_version_major = UINT32_MAX; m_os_version_minor = UINT32_MAX; m_os_version_update = UINT32_MAX; m_os_build.clear(); m_os_kernel.clear(); m_hostname.clear(); m_gdb_server_name.clear(); m_gdb_server_version = UINT32_MAX; m_default_packet_timeout = 0; m_max_packet_size = 0; } // These flags should be reset when we first connect to a GDB server // and when our inferior process execs m_qProcessInfo_is_valid = eLazyBoolCalculate; m_process_arch.Clear(); } void GDBRemoteCommunicationClient::GetRemoteQSupported () { // Clear out any capabilities we expect to see in the qSupported response m_supports_qXfer_auxv_read = eLazyBoolNo; m_supports_qXfer_libraries_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if not, we assume no limit // build the qSupported packet std::vector features = {"xmlRegisters=i386,arm,mips"}; StreamString packet; packet.PutCString( "qSupported" ); for ( uint32_t i = 0; i < features.size( ); ++i ) { packet.PutCString( i==0 ? ":" : ";"); packet.PutCString( features[i].c_str( ) ); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetData(), response, /*send_async=*/false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); if (::strstr (response_cstr, "qXfer:auxv:read+")) m_supports_qXfer_auxv_read = eLazyBoolYes; if (::strstr (response_cstr, "qXfer:libraries-svr4:read+")) m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; if (::strstr (response_cstr, "augmented-libraries-svr4-read")) { m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied m_supports_augmented_libraries_svr4_read = eLazyBoolYes; } if (::strstr (response_cstr, "qXfer:libraries:read+")) m_supports_qXfer_libraries_read = eLazyBoolYes; if (::strstr (response_cstr, "qXfer:features:read+")) m_supports_qXfer_features_read = eLazyBoolYes; // Look for a list of compressions in the features list e.g. // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma const char *features_list = ::strstr (response_cstr, "qXfer:features:"); if (features_list) { const char *compressions = ::strstr (features_list, "SupportedCompressions="); if (compressions) { std::vector supported_compressions; compressions += sizeof ("SupportedCompressions=") - 1; const char *end_of_compressions = strchr (compressions, ';'); if (end_of_compressions == NULL) { end_of_compressions = strchr (compressions, '\0'); } const char *current_compression = compressions; while (current_compression < end_of_compressions) { const char *next_compression_name = strchr (current_compression, ','); const char *end_of_this_word = next_compression_name; if (next_compression_name == NULL || end_of_compressions < next_compression_name) { end_of_this_word = end_of_compressions; } if (end_of_this_word) { if (end_of_this_word == current_compression) { current_compression++; } else { std::string this_compression (current_compression, end_of_this_word - current_compression); supported_compressions.push_back (this_compression); current_compression = end_of_this_word + 1; } } else { supported_compressions.push_back (current_compression); current_compression = end_of_compressions; } } if (supported_compressions.size() > 0) { MaybeEnableCompression (supported_compressions); } } } if (::strstr (response_cstr, "qEcho")) m_supports_qEcho = eLazyBoolYes; else m_supports_qEcho = eLazyBoolNo; const char *packet_size_str = ::strstr (response_cstr, "PacketSize="); if (packet_size_str) { StringExtractorGDBRemote packet_response(packet_size_str + strlen("PacketSize=")); m_max_packet_size = packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); if (m_max_packet_size == 0) { m_max_packet_size = UINT64_MAX; // Must have been a garbled response Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("Garbled PacketSize spec in qSupported response"); } } } } bool GDBRemoteCommunicationClient::GetThreadSuffixSupported () { if (m_supports_thread_suffix == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_thread_suffix = eLazyBoolNo; if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_thread_suffix = eLazyBoolYes; } } return m_supports_thread_suffix; } bool GDBRemoteCommunicationClient::GetVContSupported (char flavor) { if (m_supports_vCont_c == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_vCont_any = eLazyBoolNo; m_supports_vCont_all = eLazyBoolNo; m_supports_vCont_c = eLazyBoolNo; m_supports_vCont_C = eLazyBoolNo; m_supports_vCont_s = eLazyBoolNo; m_supports_vCont_S = eLazyBoolNo; if (SendPacketAndWaitForResponse("vCont?", response, false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); if (::strstr (response_cstr, ";c")) m_supports_vCont_c = eLazyBoolYes; if (::strstr (response_cstr, ";C")) m_supports_vCont_C = eLazyBoolYes; if (::strstr (response_cstr, ";s")) m_supports_vCont_s = eLazyBoolYes; if (::strstr (response_cstr, ";S")) m_supports_vCont_S = eLazyBoolYes; if (m_supports_vCont_c == eLazyBoolYes && m_supports_vCont_C == eLazyBoolYes && m_supports_vCont_s == eLazyBoolYes && m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_all = eLazyBoolYes; } if (m_supports_vCont_c == eLazyBoolYes || m_supports_vCont_C == eLazyBoolYes || m_supports_vCont_s == eLazyBoolYes || m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_any = eLazyBoolYes; } } } switch (flavor) { case 'a': return m_supports_vCont_any; case 'A': return m_supports_vCont_all; case 'c': return m_supports_vCont_c; case 'C': return m_supports_vCont_C; case 's': return m_supports_vCont_s; case 'S': return m_supports_vCont_S; default: break; } return false; } // Check if the target supports 'p' packet. It sends out a 'p' // packet and checks the response. A normal packet will tell us // that support is available. // // Takes a valid thread ID because p needs to apply to a thread. bool GDBRemoteCommunicationClient::GetpPacketSupported (lldb::tid_t tid) { if (m_supports_p == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_p = eLazyBoolNo; char packet[256]; if (GetThreadSuffixSupported()) snprintf(packet, sizeof(packet), "p0;thread:%" PRIx64 ";", tid); else snprintf(packet, sizeof(packet), "p0"); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) m_supports_p = eLazyBoolYes; } } return m_supports_p; } StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { // Get information on all threads at one using the "jThreadsInfo" packet StructuredData::ObjectSP object_sp; if (m_supports_jThreadsInfo) { StringExtractorGDBRemote response; m_supports_jThreadExtendedInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) { m_supports_jThreadsInfo = false; } else if (!response.Empty()) { object_sp = StructuredData::ParseJSON (response.GetStringRef()); } } } return object_sp; } bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported () { if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jThreadExtendedInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jThreadExtendedInfo = eLazyBoolYes; } } } return m_supports_jThreadExtendedInfo; } bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported () { if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; } } } return m_supports_jLoadedDynamicLibrariesInfos; } bool GDBRemoteCommunicationClient::GetxPacketSupported () { if (m_supports_x == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_x = eLazyBoolNo; char packet[256]; snprintf (packet, sizeof (packet), "x0,0"); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_x = eLazyBoolYes; } } return m_supports_x; } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses ( const char *payload_prefix, std::string &response_string ) { Mutex::Locker locker; if (!GetSequenceMutex(locker, "ProcessGDBRemote::SendPacketsAndConcatenateResponses() failed due to not getting the sequence mutex")) { Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending packets with prefix '%s'", payload_prefix); return PacketResult::ErrorNoSequenceLock; } response_string = ""; std::string payload_prefix_str(payload_prefix); unsigned int response_size = 0x1000; if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet response_size = GetRemoteMaxPacketSize(); } for (unsigned int offset = 0; true; offset += response_size) { StringExtractorGDBRemote this_response; // Construct payload char sizeDescriptor[128]; snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, response_size); PacketResult result = SendPacketAndWaitForResponse((payload_prefix_str + sizeDescriptor).c_str(), this_response, /*send_async=*/false); if (result != PacketResult::Success) return result; const std::string &this_string = this_response.GetStringRef(); // Check for m or l as first character; l seems to mean this is the last chunk char first_char = *this_string.c_str(); if (first_char != 'm' && first_char != 'l') { return PacketResult::ErrorReplyInvalid; } // Concatenate the result so far (skipping 'm' or 'l') response_string.append(this_string, 1, std::string::npos); if (first_char == 'l') // We're done return PacketResult::Success; } } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketAndWaitForResponse ( const char *payload, StringExtractorGDBRemote &response, bool send_async ) { return SendPacketAndWaitForResponse (payload, ::strlen (payload), response, send_async); } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketAndWaitForResponseNoLock (const char *payload, size_t payload_length, StringExtractorGDBRemote &response) { PacketResult packet_result = SendPacketNoLock (payload, payload_length); if (packet_result == PacketResult::Success) packet_result = ReadPacket (response, GetPacketTimeoutInMicroSeconds (), true); return packet_result; } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketAndWaitForResponse ( const char *payload, size_t payload_length, StringExtractorGDBRemote &response, bool send_async ) { PacketResult packet_result = PacketResult::ErrorSendFailed; Mutex::Locker locker; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); // In order to stop async notifications from being processed in the middle of the // send/recieve sequence Hijack the broadcast. Then rebroadcast any events when we are done. static Listener hijack_listener("lldb.NotifyHijacker"); HijackBroadcaster(&hijack_listener, eBroadcastBitGdbReadThreadGotNotify); if (GetSequenceMutex (locker)) { packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response); } else { if (send_async) { if (IsRunning()) { Mutex::Locker async_locker (m_async_mutex); m_async_packet.assign(payload, payload_length); m_async_packet_predicate.SetValue (true, eBroadcastNever); if (log) log->Printf ("async: async packet = %s", m_async_packet.c_str()); bool timed_out = false; if (SendInterrupt(locker, 2, timed_out)) { if (m_interrupt_sent) { m_interrupt_sent = false; TimeValue timeout_time; timeout_time = TimeValue::Now(); timeout_time.OffsetWithSeconds (m_packet_timeout); if (log) log->Printf ("async: sent interrupt"); if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out)) { if (log) log->Printf ("async: got response"); // Swap the response buffer to avoid malloc and string copy response.GetStringRef().swap (m_async_response.GetStringRef()); packet_result = m_async_result; } else { if (log) log->Printf ("async: timed out waiting for response"); } // Make sure we wait until the continue packet has been sent again... if (m_private_is_running.WaitForValueEqualTo (true, &timeout_time, &timed_out)) { if (log) { if (timed_out) log->Printf ("async: timed out waiting for process to resume, but process was resumed"); else log->Printf ("async: async packet sent"); } } else { if (log) log->Printf ("async: timed out waiting for process to resume"); } } else { // We had a racy condition where we went to send the interrupt // yet we were able to get the lock, so the process must have // just stopped? if (log) log->Printf ("async: got lock without sending interrupt"); // Send the packet normally since we got the lock packet_result = SendPacketAndWaitForResponseNoLock (payload, payload_length, response); } } else { if (log) log->Printf ("async: failed to interrupt"); } } else { if (log) log->Printf ("async: not running, async is ignored"); } } else { if (log) log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload); } } // Remove our Hijacking listner from the broadcast. RestoreBroadcaster(); // If a notification event occured, rebroadcast since it can now be processed safely. EventSP event_sp; if (hijack_listener.GetNextEvent(event_sp)) BroadcastEvent(event_sp); return packet_result; } static const char *end_delimiter = "--end--;"; static const int end_delimiter_len = 8; std::string GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData ( ProcessGDBRemote *process, StringExtractorGDBRemote& profileDataExtractor ) { std::map new_thread_id_to_used_usec_map; std::stringstream final_output; std::string name, value; // Going to assuming thread_used_usec comes first, else bail out. while (profileDataExtractor.GetNameColonValue(name, value)) { if (name.compare("thread_used_id") == 0) { StringExtractor threadIDHexExtractor(value.c_str()); uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); bool has_used_usec = false; uint32_t curr_used_usec = 0; std::string usec_name, usec_value; uint32_t input_file_pos = profileDataExtractor.GetFilePos(); if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) { if (usec_name.compare("thread_used_usec") == 0) { has_used_usec = true; curr_used_usec = strtoull(usec_value.c_str(), NULL, 0); } else { // We didn't find what we want, it is probably // an older version. Bail out. profileDataExtractor.SetFilePos(input_file_pos); } } if (has_used_usec) { uint32_t prev_used_usec = 0; std::map::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); if (iterator != m_thread_id_to_used_usec_map.end()) { prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; } uint32_t real_used_usec = curr_used_usec - prev_used_usec; // A good first time record is one that runs for at least 0.25 sec bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); bool good_subsequent_time = (prev_used_usec > 0) && ((real_used_usec > 0) || (process->HasAssignedIndexIDToThread(thread_id))); if (good_first_time || good_subsequent_time) { // We try to avoid doing too many index id reservation, // resulting in fast increase of index ids. final_output << name << ":"; int32_t index_id = process->AssignIndexIDToThread(thread_id); final_output << index_id << ";"; final_output << usec_name << ":" << usec_value << ";"; } else { // Skip past 'thread_used_name'. std::string local_name, local_value; profileDataExtractor.GetNameColonValue(local_name, local_value); } // Store current time as previous time so that they can be compared later. new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; } else { // Bail out and use old string. final_output << name << ":" << value << ";"; } } else { final_output << name << ":" << value << ";"; } } final_output << end_delimiter; m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; return final_output.str(); } bool GDBRemoteCommunicationClient::SendvContPacket ( ProcessGDBRemote *process, const char *payload, size_t packet_length, StringExtractorGDBRemote &response ) { m_curr_tid = LLDB_INVALID_THREAD_ID; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); // we want to lock down packet sending while we continue Mutex::Locker locker(m_sequence_mutex); // here we broadcast this before we even send the packet!! // this signals doContinue() to exit BroadcastEvent(eBroadcastBitRunPacketSent, NULL); // set the public state to running m_public_is_running.SetValue(true, eBroadcastNever); // Set the starting continue packet into "continue_packet". This packet // may change if we are interrupted and we continue after an async packet... std::string continue_packet(payload, packet_length); if (log) log->Printf("GDBRemoteCommunicationClient::%s () sending vCont packet: %s", __FUNCTION__, continue_packet.c_str()); if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success) return false; // set the private state to running and broadcast this m_private_is_running.SetValue(true, eBroadcastAlways); if (log) log->Printf("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); // wait for the response to the vCont if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; } return false; } StateType GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse ( ProcessGDBRemote *process, const char *payload, size_t packet_length, StringExtractorGDBRemote &response ) { m_curr_tid = LLDB_INVALID_THREAD_ID; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); Mutex::Locker locker(m_sequence_mutex); StateType state = eStateRunning; BroadcastEvent(eBroadcastBitRunPacketSent, NULL); m_public_is_running.SetValue (true, eBroadcastNever); // Set the starting continue packet into "continue_packet". This packet // may change if we are interrupted and we continue after an async packet... std::string continue_packet(payload, packet_length); const auto sigstop_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGSTOP"); const auto sigint_signo = process->GetUnixSignals()->GetSignalNumberFromName("SIGINT"); bool got_async_packet = false; while (state == eStateRunning) { if (!got_async_packet) { if (log) log->Printf ("GDBRemoteCommunicationClient::%s () sending continue packet: %s", __FUNCTION__, continue_packet.c_str()); if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) != PacketResult::Success) state = eStateInvalid; else m_interrupt_sent = false; m_private_is_running.SetValue (true, eBroadcastAlways); } got_async_packet = false; if (log) log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(%s)", __FUNCTION__, continue_packet.c_str()); if (ReadPacket(response, UINT32_MAX, false) == PacketResult::Success) { if (response.Empty()) state = eStateInvalid; else { const char stop_type = response.GetChar(); if (log) log->Printf ("GDBRemoteCommunicationClient::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str()); switch (stop_type) { case 'T': case 'S': { if (process->GetStopID() == 0) { if (process->GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = GetCurrentProcessID (); if (pid != LLDB_INVALID_PROCESS_ID) process->SetID (pid); } process->BuildDynamicRegisterInfo (true); } // Privately notify any internal threads that we have stopped // in case we wanted to interrupt our process, yet we might // send a packet and continue without returning control to the // user. m_private_is_running.SetValue (false, eBroadcastAlways); const uint8_t signo = response.GetHexU8 (UINT8_MAX); bool continue_after_async = m_async_signal != -1 || m_async_packet_predicate.GetValue(); if (continue_after_async || m_interrupt_sent) { // We sent an interrupt packet to stop the inferior process // for an async signal or to send an async packet while running // but we might have been single stepping and received the // stop packet for the step instead of for the interrupt packet. // Typically when an interrupt is sent a SIGINT or SIGSTOP // is used, so if we get anything else, we need to try and // get another stop reply packet that may have been sent // due to sending the interrupt when the target is stopped // which will just re-send a copy of the last stop reply // packet. If we don't do this, then the reply for our // async packet will be the repeat stop reply packet and cause // a lot of trouble for us! if (signo != sigint_signo && signo != sigstop_signo) { continue_after_async = false; // We didn't get a SIGINT or SIGSTOP, so try for a // very brief time (1 ms) to get another stop reply // packet to make sure it doesn't get in the way StringExtractorGDBRemote extra_stop_reply_packet; uint32_t timeout_usec = 1000; if (ReadPacket (extra_stop_reply_packet, timeout_usec, false) == PacketResult::Success) { switch (extra_stop_reply_packet.GetChar()) { case 'T': case 'S': // We did get an extra stop reply, which means // our interrupt didn't stop the target so we // shouldn't continue after the async signal // or packet is sent... continue_after_async = false; break; } } } } if (m_async_signal != -1) { if (log) log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal)); // Save off the async signal we are supposed to send const int async_signal = m_async_signal; // Clear the async signal member so we don't end up // sending the signal multiple times... m_async_signal = -1; // Check which signal we stopped with if (signo == async_signal) { if (log) log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo)); // We already stopped with a signal that we wanted // to stop with, so we are done } else { // We stopped with a different signal that the one // we wanted to stop with, so now we must resume // with the signal we want char signal_packet[32]; int signal_packet_len = 0; signal_packet_len = ::snprintf (signal_packet, sizeof (signal_packet), "C%2.2x", async_signal); if (log) log->Printf ("async: stopped with signal %s, resume with %s", Host::GetSignalAsCString (signo), Host::GetSignalAsCString (async_signal)); // Set the continue packet to resume even if the // interrupt didn't cause our stop (ignore continue_after_async) continue_packet.assign(signal_packet, signal_packet_len); continue; } } else if (m_async_packet_predicate.GetValue()) { Log * packet_log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); // We are supposed to send an asynchronous packet while // we are running. m_async_response.Clear(); if (m_async_packet.empty()) { m_async_result = PacketResult::ErrorSendFailed; if (packet_log) packet_log->Printf ("async: error: empty async packet"); } else { if (packet_log) packet_log->Printf ("async: sending packet"); m_async_result = SendPacketAndWaitForResponse (&m_async_packet[0], m_async_packet.size(), m_async_response, false); } // Let the other thread that was trying to send the async // packet know that the packet has been sent and response is // ready... m_async_packet_predicate.SetValue(false, eBroadcastAlways); if (packet_log) packet_log->Printf ("async: sent packet, continue_after_async = %i", continue_after_async); // Set the continue packet to resume if our interrupt // for the async packet did cause the stop if (continue_after_async) { // Reverting this for now as it is causing deadlocks // in programs (). In the future // we should check our thread list and "do the right thing" // for new threads that show up while we stop and run async // packets. Setting the packet to 'c' to continue all threads // is the right thing to do 99.99% of the time because if a // thread was single stepping, and we sent an interrupt, we // will notice above that we didn't stop due to an interrupt // but stopped due to stepping and we would _not_ continue. continue_packet.assign (1, 'c'); continue; } } // Stop with signal and thread info state = eStateStopped; } break; case 'W': case 'X': // process exited state = eStateExited; break; case 'O': // STDOUT { got_async_packet = true; std::string inferior_stdout; inferior_stdout.reserve(response.GetBytesLeft () / 2); char ch; while ((ch = response.GetHexU8()) != '\0') inferior_stdout.append(1, ch); process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size()); } break; case 'A': // Async miscellaneous reply. Right now, only profile data is coming through this channel. { got_async_packet = true; std::string input = response.GetStringRef().substr(1); // '1' to move beyond 'A' if (m_partial_profile_data.length() > 0) { m_partial_profile_data.append(input); input = m_partial_profile_data; m_partial_profile_data.clear(); } size_t found, pos = 0, len = input.length(); while ((found = input.find(end_delimiter, pos)) != std::string::npos) { StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str()); std::string profile_data = HarmonizeThreadIdsForProfileData(process, profileDataExtractor); process->BroadcastAsyncProfileData (profile_data); pos = found + end_delimiter_len; } if (pos < len) { // Last incomplete chunk. m_partial_profile_data = input.substr(pos); } } break; case 'E': // ERROR state = eStateInvalid; break; default: if (log) log->Printf ("GDBRemoteCommunicationClient::%s () unrecognized async packet", __FUNCTION__); state = eStateInvalid; break; } } } else { if (log) log->Printf ("GDBRemoteCommunicationClient::%s () ReadPacket(...) => false", __FUNCTION__); state = eStateInvalid; } } if (log) log->Printf ("GDBRemoteCommunicationClient::%s () => %s", __FUNCTION__, StateAsCString(state)); response.SetFilePos(0); m_private_is_running.SetValue (false, eBroadcastAlways); m_public_is_running.SetValue (false, eBroadcastAlways); return state; } bool GDBRemoteCommunicationClient::SendAsyncSignal (int signo) { Mutex::Locker async_locker (m_async_mutex); m_async_signal = signo; bool timed_out = false; Mutex::Locker locker; if (SendInterrupt (locker, 1, timed_out)) return true; m_async_signal = -1; return false; } // This function takes a mutex locker as a parameter in case the GetSequenceMutex // actually succeeds. If it doesn't succeed in acquiring the sequence mutex // (the expected result), then it will send the halt packet. If it does succeed // then the caller that requested the interrupt will want to keep the sequence // locked down so that no one else can send packets while the caller has control. // This function usually gets called when we are running and need to stop the // target. It can also be used when we are running and we need to do something // else (like read/write memory), so we need to interrupt the running process // (gdb remote protocol requires this), and do what we need to do, then resume. bool GDBRemoteCommunicationClient::SendInterrupt ( Mutex::Locker& locker, uint32_t seconds_to_wait_for_stop, bool &timed_out ) { timed_out = false; Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (IsRunning()) { // Only send an interrupt if our debugserver is running... if (GetSequenceMutex (locker)) { if (log) log->Printf ("SendInterrupt () - got sequence mutex without having to interrupt"); } else { // Someone has the mutex locked waiting for a response or for the // inferior to stop, so send the interrupt on the down low... char ctrl_c = '\x03'; ConnectionStatus status = eConnectionStatusSuccess; size_t bytes_written = Write (&ctrl_c, 1, status, NULL); if (log) log->PutCString("send packet: \\x03"); if (bytes_written > 0) { m_interrupt_sent = true; if (seconds_to_wait_for_stop) { TimeValue timeout; if (seconds_to_wait_for_stop) { timeout = TimeValue::Now(); timeout.OffsetWithSeconds (seconds_to_wait_for_stop); } if (m_private_is_running.WaitForValueEqualTo (false, &timeout, &timed_out)) { if (log) log->PutCString ("SendInterrupt () - sent interrupt, private state stopped"); return true; } else { if (log) log->Printf ("SendInterrupt () - sent interrupt, timed out wating for async thread resume"); } } else { if (log) log->Printf ("SendInterrupt () - sent interrupt, not waiting for stop..."); return true; } } else { if (log) log->Printf ("SendInterrupt () - failed to write interrupt"); } return false; } } else { if (log) log->Printf ("SendInterrupt () - not running"); } return true; } lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID (bool allow_lazy) { if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) return m_curr_pid; // First try to retrieve the pid via the qProcessInfo request. GetCurrentProcessInfo (allow_lazy); if (m_curr_pid_is_valid == eLazyBoolYes) { // We really got it. return m_curr_pid; } else { // If we don't get a response for qProcessInfo, check if $qC gives us a result. // $qC only returns a real process id on older debugserver and lldb-platform stubs. // The gdb remote protocol documents $qC as returning the thread id, which newer // debugserver and lldb-gdbserver stubs return correctly. StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false) == PacketResult::Success) { if (response.GetChar() == 'Q') { if (response.GetChar() == 'C') { m_curr_pid = response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; } } } } + + // If we don't get a response for $qC, check if $qfThreadID gives us a result. + if (m_curr_pid == LLDB_INVALID_PROCESS_ID) + { + std::vector thread_ids; + bool sequence_mutex_unavailable; + size_t size; + size = GetCurrentThreadIDs (thread_ids, sequence_mutex_unavailable); + if (size && sequence_mutex_unavailable == false) + { + m_curr_pid = thread_ids.front(); + m_curr_pid_is_valid = eLazyBoolYes; + return m_curr_pid; + } + } } return LLDB_INVALID_PROCESS_ID; } bool GDBRemoteCommunicationClient::GetLaunchSuccess (std::string &error_str) { error_str.clear(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; if (response.GetChar() == 'E') { // A string the describes what failed when launching... error_str = response.GetStringRef().substr(1); } else { error_str.assign ("unknown error occurred launching process"); } } else { error_str.assign ("timed out waiting for app to launch"); } return false; } int GDBRemoteCommunicationClient::SendArgumentsPacket (const ProcessLaunchInfo &launch_info) { // Since we don't get the send argv0 separate from the executable path, we need to // make sure to use the actual executable path found in the launch_info... std::vector argv; FileSpec exe_file = launch_info.GetExecutableFile(); std::string exe_path; const char *arg = NULL; const Args &launch_args = launch_info.GetArguments(); if (exe_file) exe_path = exe_file.GetPath(false); else { arg = launch_args.GetArgumentAtIndex(0); if (arg) exe_path = arg; } if (!exe_path.empty()) { argv.push_back(exe_path.c_str()); for (uint32_t i=1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; ++i) { if (arg) argv.push_back(arg); } } if (!argv.empty()) { StreamString packet; packet.PutChar('A'); for (size_t i = 0, n = argv.size(); i < n; ++i) { arg = argv[i]; const int arg_len = strlen(arg); if (i > 0) packet.PutChar(','); packet.Printf("%i,%i,", arg_len * 2, (int)i); packet.PutBytesAsRawHex8 (arg, arg_len); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendEnvironmentPacket (char const *name_equal_value) { if (name_equal_value && name_equal_value[0]) { StreamString packet; bool send_hex_encoding = false; for (const char *p = name_equal_value; *p != '\0' && send_hex_encoding == false; ++p) { if (isprint(*p)) { switch (*p) { case '$': case '#': send_hex_encoding = true; break; default: break; } } else { // We have non printable characters, lets hex encode this... send_hex_encoding = true; } } StringExtractorGDBRemote response; if (send_hex_encoding) { if (m_supports_QEnvironmentHexEncoded) { packet.PutCString("QEnvironmentHexEncoded:"); packet.PutBytesAsRawHex8 (name_equal_value, strlen(name_equal_value)); if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironmentHexEncoded = false; } } } else if (m_supports_QEnvironment) { packet.Printf("QEnvironment:%s", name_equal_value); if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironment = false; } } } return -1; } int GDBRemoteCommunicationClient::SendLaunchArchPacket (char const *arch) { if (arch && arch[0]) { StreamString packet; packet.Printf("QLaunchArch:%s", arch); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendLaunchEventDataPacket (char const *data, bool *was_supported) { if (data && *data != '\0') { StreamString packet; packet.Printf("QSetProcessEvent:%s", data); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) { if (was_supported) *was_supported = true; return 0; } else if (response.IsUnsupportedResponse()) { if (was_supported) *was_supported = false; return -1; } else { uint8_t error = response.GetError(); if (was_supported) *was_supported = true; if (error) return error; } } } return -1; } bool GDBRemoteCommunicationClient::GetOSVersion (uint32_t &major, uint32_t &minor, uint32_t &update) { if (GetHostInfo ()) { if (m_os_version_major != UINT32_MAX) { major = m_os_version_major; minor = m_os_version_minor; update = m_os_version_update; return true; } } return false; } bool GDBRemoteCommunicationClient::GetOSBuildString (std::string &s) { if (GetHostInfo ()) { if (!m_os_build.empty()) { s = m_os_build; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetOSKernelDescription (std::string &s) { if (GetHostInfo ()) { if (!m_os_kernel.empty()) { s = m_os_kernel; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetHostname (std::string &s) { if (GetHostInfo ()) { if (!m_hostname.empty()) { s = m_hostname; return true; } } s.clear(); return false; } ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture () { if (GetHostInfo ()) return m_host_arch; return ArchSpec(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetProcessArchitecture () { if (m_qProcessInfo_is_valid == eLazyBoolCalculate) GetCurrentProcessInfo (); return m_process_arch; } bool GDBRemoteCommunicationClient::GetGDBServerVersion() { if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { m_gdb_server_name.clear(); m_gdb_server_version = 0; m_qGDBServerVersion_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse ("qGDBServerVersion", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { std::string name; std::string value; bool success = false; while (response.GetNameColonValue(name, value)) { if (name.compare("name") == 0) { success = true; m_gdb_server_name.swap(value); } else if (name.compare("version") == 0) { size_t dot_pos = value.find('.'); if (dot_pos != std::string::npos) value[dot_pos] = '\0'; const uint32_t version = StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0); if (version != UINT32_MAX) { success = true; m_gdb_server_version = version; } } } if (success) m_qGDBServerVersion_is_valid = eLazyBoolYes; } } } return m_qGDBServerVersion_is_valid == eLazyBoolYes; } void GDBRemoteCommunicationClient::MaybeEnableCompression (std::vector supported_compressions) { CompressionType avail_type = CompressionType::None; std::string avail_name; #if defined (HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzfse") { avail_type = CompressionType::LZFSE; avail_name = compression; break; } } } #endif #if defined (HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined (HAVE_LIBZ) if (avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined (HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lz4") { avail_type = CompressionType::LZ4; avail_name = compression; break; } } } #endif #if defined (HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzma") { avail_type = CompressionType::LZMA; avail_name = compression; break; } } } #endif if (avail_type != CompressionType::None) { StringExtractorGDBRemote response; std::string packet = "QEnableCompression:type:" + avail_name + ";"; if (SendPacketAndWaitForResponse (packet.c_str(), response, false) != PacketResult::Success) return; if (response.IsOKResponse()) { m_compression_type = avail_type; } } } const char * GDBRemoteCommunicationClient::GetGDBServerProgramName() { if (GetGDBServerVersion()) { if (!m_gdb_server_name.empty()) return m_gdb_server_name.c_str(); } return NULL; } uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { if (GetGDBServerVersion()) return m_gdb_server_version; return 0; } bool GDBRemoteCommunicationClient::GetDefaultThreadId (lldb::tid_t &tid) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC",response,false) != PacketResult::Success) return false; if (!response.IsNormalResponse()) return false; if (response.GetChar() == 'Q' && response.GetChar() == 'C') tid = response.GetHexMaxU32(true, -1); return true; } bool GDBRemoteCommunicationClient::GetHostInfo (bool force) { Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS)); if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { m_qHostInfo_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse ("qHostInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { std::string name; std::string value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; std::string distribution_id; uint32_t pointer_byte_size = 0; StringExtractor extractor; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; while (response.GetNameColonValue(name, value)) { if (name.compare("cputype") == 0) { // exception type in big endian hex cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0); if (cpu != LLDB_INVALID_CPUTYPE) ++num_keys_decoded; } else if (name.compare("cpusubtype") == 0) { // exception count in big endian hex sub = StringConvert::ToUInt32 (value.c_str(), 0, 0); if (sub != 0) ++num_keys_decoded; } else if (name.compare("arch") == 0) { arch_name.swap (value); ++num_keys_decoded; } else if (name.compare("triple") == 0) { extractor.GetStringRef ().swap (value); extractor.SetFilePos(0); extractor.GetHexByteString (triple); ++num_keys_decoded; } else if (name.compare ("distribution_id") == 0) { extractor.GetStringRef ().swap (value); extractor.SetFilePos (0); extractor.GetHexByteString (distribution_id); ++num_keys_decoded; } else if (name.compare("os_build") == 0) { extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (m_os_build); ++num_keys_decoded; } else if (name.compare("hostname") == 0) { extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (m_hostname); ++num_keys_decoded; } else if (name.compare("os_kernel") == 0) { extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (m_os_kernel); ++num_keys_decoded; } else if (name.compare("ostype") == 0) { os_name.swap (value); ++num_keys_decoded; } else if (name.compare("vendor") == 0) { vendor_name.swap(value); ++num_keys_decoded; } else if (name.compare("endian") == 0) { ++num_keys_decoded; if (value.compare("little") == 0) byte_order = eByteOrderLittle; else if (value.compare("big") == 0) byte_order = eByteOrderBig; else if (value.compare("pdp") == 0) byte_order = eByteOrderPDP; else --num_keys_decoded; } else if (name.compare("ptrsize") == 0) { pointer_byte_size = StringConvert::ToUInt32 (value.c_str(), 0, 0); if (pointer_byte_size != 0) ++num_keys_decoded; } else if (name.compare("os_version") == 0) { Args::StringToVersion (value.c_str(), m_os_version_major, m_os_version_minor, m_os_version_update); if (m_os_version_major != UINT32_MAX) ++num_keys_decoded; } else if (name.compare("watchpoint_exceptions_received") == 0) { ++num_keys_decoded; if (strcmp(value.c_str(),"before") == 0) m_watchpoints_trigger_after_instruction = eLazyBoolNo; else if (strcmp(value.c_str(),"after") == 0) m_watchpoints_trigger_after_instruction = eLazyBoolYes; else --num_keys_decoded; } else if (name.compare("default_packet_timeout") == 0) { m_default_packet_timeout = StringConvert::ToUInt32(value.c_str(), 0); if (m_default_packet_timeout > 0) { SetPacketTimeout(m_default_packet_timeout); ++num_keys_decoded; } } } if (num_keys_decoded > 0) m_qHostInfo_is_valid = eLazyBoolYes; if (triple.empty()) { if (arch_name.empty()) { if (cpu != LLDB_INVALID_CPUTYPE) { m_host_arch.SetArchitecture (eArchTypeMachO, cpu, sub); if (pointer_byte_size) { assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert (byte_order == m_host_arch.GetByteOrder()); } if (!os_name.empty() && vendor_name.compare("apple") == 0 && os_name.find("darwin") == 0) { switch (m_host_arch.GetMachine()) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::thumb: os_name = "ios"; break; default: os_name = "macosx"; break; } } if (!vendor_name.empty()) m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); if (!os_name.empty()) m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); } } else { std::string triple; triple += arch_name; if (!vendor_name.empty() || !os_name.empty()) { triple += '-'; if (vendor_name.empty()) triple += "unknown"; else triple += vendor_name; triple += '-'; if (os_name.empty()) triple += "unknown"; else triple += os_name; } m_host_arch.SetTriple (triple.c_str()); llvm::Triple &host_triple = m_host_arch.GetTriple(); if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin) { switch (m_host_arch.GetMachine()) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::thumb: host_triple.setOS(llvm::Triple::IOS); break; default: host_triple.setOS(llvm::Triple::MacOSX); break; } } if (pointer_byte_size) { assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert (byte_order == m_host_arch.GetByteOrder()); } } } else { m_host_arch.SetTriple (triple.c_str()); if (pointer_byte_size) { assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert (byte_order == m_host_arch.GetByteOrder()); } if (log) log->Printf ("GDBRemoteCommunicationClient::%s parsed host architecture as %s, triple as %s from triple text %s", __FUNCTION__, m_host_arch.GetArchitectureName () ? m_host_arch.GetArchitectureName () : "", m_host_arch.GetTriple ().getTriple ().c_str(), triple.c_str ()); } if (!distribution_id.empty ()) m_host_arch.SetDistributionId (distribution_id.c_str ()); } } } return m_qHostInfo_is_valid == eLazyBoolYes; } int GDBRemoteCommunicationClient::SendAttach ( lldb::pid_t pid, StringExtractorGDBRemote& response ) { if (pid != LLDB_INVALID_PROCESS_ID) { char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, pid); assert (packet_len < (int)sizeof(packet)); if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsErrorResponse()) return response.GetError(); return 0; } } return -1; } int GDBRemoteCommunicationClient::SendStdinNotification (const char* data, size_t data_len) { StreamString packet; packet.PutCString("I"); packet.PutBytesAsRawHex8(data, data_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { return 0; } return response.GetError(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetHostArchitecture () { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo (); return m_host_arch; } uint32_t GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout () { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo (); return m_default_packet_timeout; } addr_t GDBRemoteCommunicationClient::AllocateMemory (size_t size, uint32_t permissions) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, permissions & lldb::ePermissionsReadable ? "r" : "", permissions & lldb::ePermissionsWritable ? "w" : "", permissions & lldb::ePermissionsExecutable ? "x" : ""); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (!response.IsErrorResponse()) return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return LLDB_INVALID_ADDRESS; } bool GDBRemoteCommunicationClient::DeallocateMemory (addr_t addr) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (response.IsOKResponse()) return true; } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return false; } Error GDBRemoteCommunicationClient::Detach (bool keep_stopped) { Error error; if (keep_stopped) { if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { m_supports_detach_stay_stopped = eLazyBoolYes; } else { m_supports_detach_stay_stopped = eLazyBoolNo; } } if (m_supports_detach_stay_stopped == eLazyBoolNo) { error.SetErrorString("Stays stopped not supported by this target."); return error; } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse ("D1", 1, response, false); if (packet_result != PacketResult::Success) error.SetErrorString ("Sending extended disconnect packet failed."); } } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse ("D", 1, response, false); if (packet_result != PacketResult::Success) error.SetErrorString ("Sending disconnect packet failed."); } return error; } Error GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { Error error; region_info.Clear(); if (m_supports_memory_region_info != eLazyBoolNo) { m_supports_memory_region_info = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { std::string name; std::string value; addr_t addr_value; bool success = true; bool saw_permissions = false; while (success && response.GetNameColonValue(name, value)) { if (name.compare ("start") == 0) { addr_value = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16, &success); if (success) region_info.GetRange().SetRangeBase(addr_value); } else if (name.compare ("size") == 0) { addr_value = StringConvert::ToUInt64(value.c_str(), 0, 16, &success); if (success) region_info.GetRange().SetByteSize (addr_value); } else if (name.compare ("permissions") == 0 && region_info.GetRange().IsValid()) { saw_permissions = true; if (region_info.GetRange().Contains (addr)) { if (value.find('r') != std::string::npos) region_info.SetReadable (MemoryRegionInfo::eYes); else region_info.SetReadable (MemoryRegionInfo::eNo); if (value.find('w') != std::string::npos) region_info.SetWritable (MemoryRegionInfo::eYes); else region_info.SetWritable (MemoryRegionInfo::eNo); if (value.find('x') != std::string::npos) region_info.SetExecutable (MemoryRegionInfo::eYes); else region_info.SetExecutable (MemoryRegionInfo::eNo); } else { // The reported region does not contain this address -- we're looking at an unmapped page region_info.SetReadable (MemoryRegionInfo::eNo); region_info.SetWritable (MemoryRegionInfo::eNo); region_info.SetExecutable (MemoryRegionInfo::eNo); } } else if (name.compare ("error") == 0) { StringExtractorGDBRemote name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); error.SetErrorString(value.c_str()); } } // We got a valid address range back but no permissions -- which means this is an unmapped page if (region_info.GetRange().IsValid() && saw_permissions == false) { region_info.SetReadable (MemoryRegionInfo::eNo); region_info.SetWritable (MemoryRegionInfo::eNo); region_info.SetExecutable (MemoryRegionInfo::eNo); } } else { m_supports_memory_region_info = eLazyBoolNo; } } if (m_supports_memory_region_info == eLazyBoolNo) { error.SetErrorString("qMemoryRegionInfo is not supported"); } if (error.Fail()) region_info.Clear(); return error; } Error GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num) { Error error; if (m_supports_watchpoint_support_info == eLazyBoolYes) { num = m_num_supported_hardware_watchpoints; return error; } // Set num to 0 first. num = 0; if (m_supports_watchpoint_support_info != eLazyBoolNo) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { m_supports_watchpoint_support_info = eLazyBoolYes; std::string name; std::string value; while (response.GetNameColonValue(name, value)) { if (name.compare ("num") == 0) { num = StringConvert::ToUInt32(value.c_str(), 0, 0); m_num_supported_hardware_watchpoints = num; } } } else { m_supports_watchpoint_support_info = eLazyBoolNo; } } if (m_supports_watchpoint_support_info == eLazyBoolNo) { error.SetErrorString("qWatchpointSupportInfo is not supported"); } return error; } lldb_private::Error -GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after) +GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after, const ArchSpec &arch) { Error error(GetWatchpointSupportInfo(num)); if (error.Success()) - error = GetWatchpointsTriggerAfterInstruction(after); + error = GetWatchpointsTriggerAfterInstruction(after, arch); return error; } lldb_private::Error -GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after) +GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after, const ArchSpec &arch) { Error error; + llvm::Triple::ArchType atype = arch.GetMachine(); // we assume watchpoints will happen after running the relevant opcode // and we only want to override this behavior if we have explicitly // received a qHostInfo telling us otherwise if (m_qHostInfo_is_valid != eLazyBoolYes) - after = true; + { + // On targets like MIPS, watchpoint exceptions are always generated + // before the instruction is executed. The connected target may not + // support qHostInfo or qWatchpointSupportInfo packets. + if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel + || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) + after = false; + else + after = true; + } else + { + // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo + // if it is not calculated before. + if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && + (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel + || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) + m_watchpoints_trigger_after_instruction = eLazyBoolNo; + after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); + } return error; } int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDIN:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDOUT:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDERR:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse ("qGetWorkingDir", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) return false; if (response.IsErrorResponse()) return false; std::string cwd; response.GetHexByteString(cwd); working_dir.SetFile(cwd, false, GetHostArchitecture()); return !cwd.empty(); } return false; } int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { if (working_dir) { std::string path{working_dir.GetPath(false)}; StreamString packet; packet.PutCString("QSetWorkingDir:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetDisableASLR (bool enable) { char packet[32]; const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDisableASLR:%i", enable ? 1 : 0); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } int GDBRemoteCommunicationClient::SetDetachOnError (bool enable) { char packet[32]; const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDetachOnError:%i", enable ? 1 : 0); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { if (response.IsNormalResponse()) { std::string name; std::string value; StringExtractor extractor; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string vendor; std::string os_type; while (response.GetNameColonValue(name, value)) { if (name.compare("pid") == 0) { process_info.SetProcessID (StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); } else if (name.compare("ppid") == 0) { process_info.SetParentProcessID (StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); } else if (name.compare("uid") == 0) { process_info.SetUserID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); } else if (name.compare("euid") == 0) { process_info.SetEffectiveUserID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); } else if (name.compare("gid") == 0) { process_info.SetGroupID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); } else if (name.compare("egid") == 0) { process_info.SetEffectiveGroupID (StringConvert::ToUInt32 (value.c_str(), UINT32_MAX, 0)); } else if (name.compare("triple") == 0) { StringExtractor extractor; extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (value); process_info.GetArchitecture ().SetTriple (value.c_str()); } else if (name.compare("name") == 0) { StringExtractor extractor; // The process name from ASCII hex bytes since we can't // control the characters in a process name extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (value); process_info.GetExecutableFile().SetFile (value.c_str(), false); } else if (name.compare("cputype") == 0) { cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16); } else if (name.compare("cpusubtype") == 0) { sub = StringConvert::ToUInt32 (value.c_str(), 0, 16); } else if (name.compare("vendor") == 0) { vendor = value; } else if (name.compare("ostype") == 0) { os_type = value; } } if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { if (vendor == "apple") { process_info.GetArchitecture().SetArchitecture (eArchTypeMachO, cpu, sub); process_info.GetArchitecture().GetTriple().setVendorName (llvm::StringRef (vendor)); process_info.GetArchitecture().GetTriple().setOSName (llvm::StringRef (os_type)); } } if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; } return false; } bool GDBRemoteCommunicationClient::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) { process_info.Clear(); if (m_supports_qProcessInfoPID) { char packet[32]; const int packet_len = ::snprintf (packet, sizeof (packet), "qProcessInfoPID:%" PRIu64, pid); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { return DecodeProcessInfoResponse (response, process_info); } else { m_supports_qProcessInfoPID = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetCurrentProcessInfo (bool allow_lazy) { Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (allow_lazy) { if (m_qProcessInfo_is_valid == eLazyBoolYes) return true; if (m_qProcessInfo_is_valid == eLazyBoolNo) return false; } GetHostInfo (); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse ("qProcessInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { std::string name; std::string value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; uint32_t pointer_byte_size = 0; StringExtractor extractor; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { if (name.compare("cputype") == 0) { cpu = StringConvert::ToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16); if (cpu != LLDB_INVALID_CPUTYPE) ++num_keys_decoded; } else if (name.compare("cpusubtype") == 0) { sub = StringConvert::ToUInt32 (value.c_str(), 0, 16); if (sub != 0) ++num_keys_decoded; } else if (name.compare("triple") == 0) { StringExtractor extractor; extractor.GetStringRef().swap(value); extractor.SetFilePos(0); extractor.GetHexByteString (triple); ++num_keys_decoded; } else if (name.compare("ostype") == 0) { os_name.swap (value); ++num_keys_decoded; } else if (name.compare("vendor") == 0) { vendor_name.swap(value); ++num_keys_decoded; } else if (name.compare("endian") == 0) { ++num_keys_decoded; if (value.compare("little") == 0) byte_order = eByteOrderLittle; else if (value.compare("big") == 0) byte_order = eByteOrderBig; else if (value.compare("pdp") == 0) byte_order = eByteOrderPDP; else --num_keys_decoded; } else if (name.compare("ptrsize") == 0) { pointer_byte_size = StringConvert::ToUInt32 (value.c_str(), 0, 16); if (pointer_byte_size != 0) ++num_keys_decoded; } else if (name.compare("pid") == 0) { pid = StringConvert::ToUInt64(value.c_str(), 0, 16); if (pid != LLDB_INVALID_PROCESS_ID) ++num_keys_decoded; } } if (num_keys_decoded > 0) m_qProcessInfo_is_valid = eLazyBoolYes; if (pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; m_curr_pid = pid; } // Set the ArchSpec from the triple if we have it. if (!triple.empty ()) { m_process_arch.SetTriple (triple.c_str ()); if (pointer_byte_size) { assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); } } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) { llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); switch (triple.getObjectFormat()) { case llvm::Triple::MachO: m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); break; case llvm::Triple::ELF: m_process_arch.SetArchitecture (eArchTypeELF, cpu, sub); break; case llvm::Triple::COFF: m_process_arch.SetArchitecture (eArchTypeCOFF, cpu, sub); break; case llvm::Triple::UnknownObjectFormat: if (log) log->Printf("error: failed to determine target architecture"); return false; } if (pointer_byte_size) { assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert (byte_order == m_process_arch.GetByteOrder()); } m_process_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); m_process_arch.GetTriple().setOSName(llvm::StringRef (os_name)); m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); } return true; } } else { m_qProcessInfo_is_valid = eLazyBoolNo; } return false; } uint32_t GDBRemoteCommunicationClient::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { process_infos.Clear(); if (m_supports_qfProcessInfo) { StreamString packet; packet.PutCString ("qfProcessInfo"); if (!match_info.MatchAllProcesses()) { packet.PutChar (':'); const char *name = match_info.GetProcessInfo().GetName(); bool has_name_match = false; if (name && name[0]) { has_name_match = true; NameMatchType name_match_type = match_info.GetNameMatchType(); switch (name_match_type) { case eNameMatchIgnore: has_name_match = false; break; case eNameMatchEquals: packet.PutCString ("name_match:equals;"); break; case eNameMatchContains: packet.PutCString ("name_match:contains;"); break; case eNameMatchStartsWith: packet.PutCString ("name_match:starts_with;"); break; case eNameMatchEndsWith: packet.PutCString ("name_match:ends_with;"); break; case eNameMatchRegularExpression: packet.PutCString ("name_match:regex;"); break; } if (has_name_match) { packet.PutCString ("name:"); packet.PutBytesAsRawHex8(name, ::strlen(name)); packet.PutChar (';'); } } if (match_info.GetProcessInfo().ProcessIDIsValid()) packet.Printf("pid:%" PRIu64 ";",match_info.GetProcessInfo().GetProcessID()); if (match_info.GetProcessInfo().ParentProcessIDIsValid()) packet.Printf("parent_pid:%" PRIu64 ";",match_info.GetProcessInfo().GetParentProcessID()); if (match_info.GetProcessInfo().UserIDIsValid()) packet.Printf("uid:%u;",match_info.GetProcessInfo().GetUserID()); if (match_info.GetProcessInfo().GroupIDIsValid()) packet.Printf("gid:%u;",match_info.GetProcessInfo().GetGroupID()); if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) packet.Printf("euid:%u;",match_info.GetProcessInfo().GetEffectiveUserID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("egid:%u;",match_info.GetProcessInfo().GetEffectiveGroupID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("all_users:%u;",match_info.GetMatchAllUsers() ? 1 : 0); if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture(); const llvm::Triple &triple = match_arch.GetTriple(); packet.PutCString("triple:"); packet.PutCString(triple.getTriple().c_str()); packet.PutChar (';'); } } StringExtractorGDBRemote response; // Increase timeout as the first qfProcessInfo packet takes a long time // on Android. The value of 1min was arrived at empirically. GDBRemoteCommunication::ScopedTimeout timeout (*this, 60); if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success) { do { ProcessInstanceInfo process_info; if (!DecodeProcessInfoResponse (response, process_info)) break; process_infos.Append(process_info); response.GetStringRef().clear(); response.SetFilePos(0); } while (SendPacketAndWaitForResponse ("qsProcessInfo", strlen ("qsProcessInfo"), response, false) == PacketResult::Success); } else { m_supports_qfProcessInfo = false; return 0; } } return process_infos.GetSize(); } bool GDBRemoteCommunicationClient::GetUserName (uint32_t uid, std::string &name) { if (m_supports_qUserName) { char packet[32]; const int packet_len = ::snprintf (packet, sizeof (packet), "qUserName:%i", uid); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded user name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qUserName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name) { if (m_supports_qGroupName) { char packet[32]; const int packet_len = ::snprintf (packet, sizeof (packet), "qGroupName:%i", gid); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet, packet_len, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded group name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qGroupName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::SetNonStopMode (const bool enable) { // Form non-stop packet request char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); assert(packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; // Send to target if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) if (response.IsOKResponse()) return true; // Failed or not supported return false; } static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) { packet.Clear(); packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } } template T calculate_standard_deviation(const std::vector &v) { T sum = std::accumulate(std::begin(v), std::end(v), T(0)); T mean = sum / (T)v.size(); T accum = T(0); std::for_each (std::begin(v), std::end(v), [&](const T d) { T delta = d - mean; accum += delta * delta; }); T stdev = sqrt(accum / (v.size()-1)); return stdev; } void GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, bool json, Stream &strm) { uint32_t i; TimeValue start_time, end_time; uint64_t total_time_nsec; if (SendSpeedTestPacket (0, 0)) { StreamString packet; if (json) strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n \"results\" : [", num_packets); else strm.Printf("Testing sending %u packets of various sizes:\n", num_packets); strm.Flush(); uint32_t result_idx = 0; uint32_t send_size; std::vector packet_times; for (send_size = 0; send_size <= max_send; send_size ? send_size *= 2 : send_size = 4) { for (uint32_t recv_size = 0; recv_size <= max_recv; recv_size ? recv_size *= 2 : recv_size = 4) { MakeSpeedTestPacket (packet, send_size, recv_size); packet_times.clear(); // Test how long it takes to send 'num_packets' packets start_time = TimeValue::Now(); for (i=0; i(packet_times); if (json) { strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 ", \"standard_deviation_nsec\" : %9" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec, (uint64_t)standard_deviation); ++result_idx; } else { strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) in %" PRIu64 ".%9.9" PRIu64 " sec for %9.2f packets/sec (%10.6f ms per packet) with standard deviation of %10.6f ms\n", send_size, recv_size, total_time_nsec / TimeValue::NanoSecPerSec, total_time_nsec % TimeValue::NanoSecPerSec, packets_per_second, average_ms_per_packet, standard_deviation/(float)TimeValue::NanoSecPerMilliSec); } strm.Flush(); } } const uint64_t k_recv_amount = 4*1024*1024; // Receive amount in bytes const float k_recv_amount_mb = (float)k_recv_amount/(1024.0f*1024.0f); if (json) strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" : %" PRIu64 ",\n \"results\" : [", k_recv_amount); else strm.Printf("Testing receiving %2.1fMB of data using varying receive packet sizes:\n", k_recv_amount_mb); strm.Flush(); send_size = 0; result_idx = 0; for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { MakeSpeedTestPacket (packet, send_size, recv_size); // If we have a receive size, test how long it takes to receive 4MB of data if (recv_size > 0) { start_time = TimeValue::Now(); uint32_t bytes_read = 0; uint32_t packet_count = 0; while (bytes_read < k_recv_amount) { StringExtractorGDBRemote response; SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false); bytes_read += recv_size; ++packet_count; } end_time = TimeValue::Now(); total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); float mb_second = ((((float)k_recv_amount)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec) / (1024.0*1024.0); float packets_per_second = (((float)packet_count)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; float total_ms = (float)total_time_nsec/(float)TimeValue::NanoSecPerMilliSec; float average_ms_per_packet = total_ms / packet_count; if (json) { strm.Printf ("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, total_time_nsec); ++result_idx; } else { strm.Printf ("qSpeedTest(send=%-7u, recv=%-7u) %6u packets needed to receive %2.1fMB in %" PRIu64 ".%9.9" PRIu64 " sec for %f MB/sec for %9.2f packets/sec (%10.6f ms per packet)\n", send_size, recv_size, packet_count, k_recv_amount_mb, total_time_nsec / TimeValue::NanoSecPerSec, total_time_nsec % TimeValue::NanoSecPerSec, mb_second, packets_per_second, average_ms_per_packet); } strm.Flush(); } } if (json) strm.Printf("\n ]\n }\n}\n"); else strm.EOL(); } } bool GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t recv_size) { StreamString packet; packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } StringExtractorGDBRemote response; return SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success; } uint16_t GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid, const char *remote_accept_hostname) { pid = LLDB_INVALID_PROCESS_ID; StringExtractorGDBRemote response; StreamString stream; stream.PutCString("qLaunchGDBServer;"); std::string hostname; if (remote_accept_hostname && remote_accept_hostname[0]) hostname = remote_accept_hostname; else { if (HostInfo::GetHostname(hostname)) { // Make the GDB server we launch only accept connections from this host stream.Printf("host:%s;", hostname.c_str()); } else { // Make the GDB server we launch accept connections from any host since we can't figure out the hostname stream.Printf("host:*;"); } } const char *packet = stream.GetData(); int packet_len = stream.GetSize(); // give the process a few seconds to startup GDBRemoteCommunication::ScopedTimeout timeout (*this, 10); if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { std::string name; std::string value; uint16_t port = 0; while (response.GetNameColonValue(name, value)) { if (name.compare("port") == 0) port = StringConvert::ToUInt32(value.c_str(), 0, 0); else if (name.compare("pid") == 0) pid = StringConvert::ToUInt64(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); } return port; } return 0; } bool GDBRemoteCommunicationClient::KillSpawnedProcess (lldb::pid_t pid) { StreamString stream; stream.Printf ("qKillSpawnedProcess:%" PRId64 , pid); const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; } return false; } bool GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid) { if (m_curr_tid == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf (packet, sizeof(packet), "Hg-1"); else packet_len = ::snprintf (packet, sizeof(packet), "Hg%" PRIx64, tid); assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid = tid; return true; } } return false; } bool GDBRemoteCommunicationClient::SetCurrentThreadForRun (uint64_t tid) { if (m_curr_tid_run == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf (packet, sizeof(packet), "Hc-1"); else packet_len = ::snprintf (packet, sizeof(packet), "Hc%" PRIx64, tid); assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid_run = tid; return true; } } return false; } bool GDBRemoteCommunicationClient::GetStopReply (StringExtractorGDBRemote &response) { if (SendPacketAndWaitForResponse("?", 1, response, false) == PacketResult::Success) return response.IsNormalResponse(); return false; } bool GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtractorGDBRemote &response) { if (m_supports_qThreadStopInfo) { char packet[256]; int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); assert (packet_len < (int)sizeof(packet)); if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_qThreadStopInfo = false; else if (response.IsNormalResponse()) return true; else return false; } else { m_supports_qThreadStopInfo = false; } } return false; } uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, __FUNCTION__, insert ? "add" : "remove", addr); // Check if the stub is known not to support this breakpoint type if (!SupportsGDBStoppointPacket(type)) return UINT8_MAX; // Construct the breakpoint packet char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", insert ? 'Z' : 'z', type, addr, length); // Check we haven't overwritten the end of the packet buffer assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; // Try to send the breakpoint packet, and check that it was correctly sent if (SendPacketAndWaitForResponse(packet, packet_len, response, true) == PacketResult::Success) { // Receive and OK packet when the breakpoint successfully placed if (response.IsOKResponse()) return 0; // Error while setting breakpoint, send back specific error if (response.IsErrorResponse()) return response.GetError(); // Empty packet informs us that breakpoint is not supported if (response.IsUnsupportedResponse()) { // Disable this breakpoint type since it is unsupported switch (type) { case eBreakpointSoftware: m_supports_z0 = false; break; case eBreakpointHardware: m_supports_z1 = false; break; case eWatchpointWrite: m_supports_z2 = false; break; case eWatchpointRead: m_supports_z3 = false; break; case eWatchpointReadWrite: m_supports_z4 = false; break; case eStoppointInvalid: return UINT8_MAX; } } } // Signal generic failure return UINT8_MAX; } size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector &thread_ids, bool &sequence_mutex_unavailable) { Mutex::Locker locker; thread_ids.clear(); if (GetSequenceMutex (locker, "ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex")) { sequence_mutex_unavailable = false; StringExtractorGDBRemote response; PacketResult packet_result; for (packet_result = SendPacketAndWaitForResponseNoLock ("qfThreadInfo", strlen("qfThreadInfo"), response); packet_result == PacketResult::Success && response.IsNormalResponse(); packet_result = SendPacketAndWaitForResponseNoLock ("qsThreadInfo", strlen("qsThreadInfo"), response)) { char ch = response.GetChar(); if (ch == 'l') break; if (ch == 'm') { do { tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); if (tid != LLDB_INVALID_THREAD_ID) { thread_ids.push_back (tid); } ch = response.GetChar(); // Skip the command separator } while (ch == ','); // Make sure we got a comma separator } } } else { #if defined (LLDB_CONFIGURATION_DEBUG) // assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex"); #else Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending packet 'qfThreadInfo'"); #endif sequence_mutex_unavailable = true; } return thread_ids.size(); } lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { if (!IsRunning()) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, false) == PacketResult::Success) { if (response.IsNormalResponse()) return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } } return LLDB_INVALID_ADDRESS; } lldb_private::Error GDBRemoteCommunicationClient::RunShellCommand(const char *command, // Shouldn't be NULL const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); stream.PutHex32(timeout_sec); if (working_dir) { std::string path{working_dir.GetPath(false)}; stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); } const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return Error("malformed reply"); if (response.GetChar() != ',') return Error("malformed reply"); uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); if (exitcode == UINT32_MAX) return Error("unable to run remote process"); else if (status_ptr) *status_ptr = exitcode; if (response.GetChar() != ',') return Error("malformed reply"); uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); if (signo_ptr) *signo_ptr = signo; if (response.GetChar() != ',') return Error("malformed reply"); std::string output; response.GetEscapedBinaryData(output); if (command_output) command_output->assign(output); return Error(); } return Error("unable to send packet"); } Error GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_mkdir:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) return Error("failed to send '%s' packet", packet); if (response.GetChar() != 'F') return Error("invalid response to '%s' packet", packet); return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } Error GDBRemoteCommunicationClient::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_chmod:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); const char *packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) != PacketResult::Success) return Error("failed to send '%s' packet", packet); if (response.GetChar() != 'F') return Error("invalid response to '%s' packet", packet); return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } static uint64_t ParseHostIOPacketResponse (StringExtractorGDBRemote &response, uint64_t fail_result, Error &error) { response.SetFilePos(0); if (response.GetChar() != 'F') return fail_result; int32_t result = response.GetS32 (-2); if (result == -2) return fail_result; if (response.GetChar() == ',') { int result_errno = response.GetS32 (-2); if (result_errno != -2) error.SetError(result_errno, eErrorTypePOSIX); else error.SetError(-1, eErrorTypeGeneric); } else error.Clear(); return result; } lldb::user_id_t GDBRemoteCommunicationClient::OpenFile (const lldb_private::FileSpec& file_spec, uint32_t flags, mode_t mode, Error &error) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:open:"); if (path.empty()) return UINT64_MAX; stream.PutCStringAsRawHex8(path.c_str()); stream.PutChar(','); stream.PutHex32(flags); stream.PutChar(','); stream.PutHex32(mode); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { return ParseHostIOPacketResponse (response, UINT64_MAX, error); } return UINT64_MAX; } bool GDBRemoteCommunicationClient::CloseFile (lldb::user_id_t fd, Error &error) { lldb_private::StreamString stream; stream.Printf("vFile:close:%i", (int)fd); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { return ParseHostIOPacketResponse (response, -1, error) == 0; } return false; } // Extension of host I/O packets to get the file size. lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:size:"); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return UINT64_MAX; uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); return retcode; } return UINT64_MAX; } Error GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamString stream; stream.PutCString("vFile:mode:"); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat ("invalid response to '%s' packet", packet); } else { const uint32_t mode = response.GetS32(-1); if (static_cast(mode) == -1) { if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); else error.SetErrorToGenericError(); } else error.SetErrorToGenericError(); } else { file_permissions = mode & (S_IRWXU|S_IRWXG|S_IRWXO); } } } else { error.SetErrorStringWithFormat ("failed to send '%s' packet", packet); } return error; } uint64_t GDBRemoteCommunicationClient::ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Error &error) { lldb_private::StreamString stream; stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return 0; uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); if (retcode == UINT32_MAX) return retcode; const char next = (response.Peek() ? *response.Peek() : 0); if (next == ',') return 0; if (next == ';') { response.GetChar(); // skip the semicolon std::string buffer; if (response.GetEscapedBinaryData(buffer)) { const uint64_t data_to_write = std::min(dst_len, buffer.size()); if (data_to_write > 0) memcpy(dst, &buffer[0], data_to_write); return data_to_write; } } } return 0; } uint64_t GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, Error &error) { lldb_private::StreamGDBRemote stream; stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); stream.PutEscapedBytes(src, src_len); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat("write file failed"); return 0; } uint64_t bytes_written = response.GetU64(UINT64_MAX); if (bytes_written == UINT64_MAX) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } return 0; } return bytes_written; } else { error.SetErrorString ("failed to send vFile:pwrite packet"); } return 0; } Error GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, const FileSpec &dst) { std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:symlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(dst_path.c_str()); stream.PutChar(','); stream.PutCStringAsRawHex8(src_path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("symlink failed"); } } else { error.SetErrorString ("failed to send vFile:symlink packet"); } return error; } Error GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:unlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("unlink failed"); } } else { error.SetErrorString ("failed to send vFile:unlink packet"); } return error; } // Extension of host I/O packets to get whether a file exists. bool GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:exists:"); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; bool retcode = (response.GetChar() != '0'); return retcode; } return false; } bool GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_spec, uint64_t &high, uint64_t &low) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:MD5:"); stream.PutCStringAsRawHex8(path.c_str()); const char* packet = stream.GetData(); int packet_len = stream.GetSize(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, packet_len, response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; if (response.Peek() && *response.Peek() == 'x') return false; low = response.GetHexMaxU64(false, UINT64_MAX); high = response.GetHexMaxU64(false, UINT64_MAX); return true; } return false; } bool GDBRemoteCommunicationClient::AvoidGPackets (ProcessGDBRemote *process) { // Some targets have issues with g/G packets and we need to avoid using them if (m_avoid_g_packets == eLazyBoolCalculate) { if (process) { m_avoid_g_packets = eLazyBoolNo; const ArchSpec &arch = process->GetTarget().GetArchitecture(); if (arch.IsValid() && arch.GetTriple().getVendor() == llvm::Triple::Apple && arch.GetTriple().getOS() == llvm::Triple::IOS && arch.GetTriple().getArch() == llvm::Triple::aarch64) { m_avoid_g_packets = eLazyBoolYes; uint32_t gdb_server_version = GetGDBServerProgramVersion(); if (gdb_server_version != 0) { const char *gdb_server_name = GetGDBServerProgramName(); if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { if (gdb_server_version >= 310) m_avoid_g_packets = eLazyBoolNo; } } } } } return m_avoid_g_packets == eLazyBoolYes; } bool GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg, StringExtractorGDBRemote &response) { Mutex::Locker locker; if (GetSequenceMutex (locker, "Didn't get sequence mutex for p packet.")) { const bool thread_suffix_supported = GetThreadSuffixSupported(); if (thread_suffix_supported || SetCurrentThread(tid)) { char packet[64]; int packet_len = 0; if (thread_suffix_supported) packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, tid); else packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg); assert (packet_len < ((int)sizeof(packet) - 1)); return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success; } } return false; } bool GDBRemoteCommunicationClient::ReadAllRegisters (lldb::tid_t tid, StringExtractorGDBRemote &response) { Mutex::Locker locker; if (GetSequenceMutex (locker, "Didn't get sequence mutex for g packet.")) { const bool thread_suffix_supported = GetThreadSuffixSupported(); if (thread_suffix_supported || SetCurrentThread(tid)) { char packet[64]; int packet_len = 0; // Get all registers in one packet if (thread_suffix_supported) packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", tid); else packet_len = ::snprintf (packet, sizeof(packet), "g"); assert (packet_len < ((int)sizeof(packet) - 1)); return SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success; } } return false; } bool GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save_id) { save_id = 0; // Set to invalid save ID if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; m_supports_QSaveRegisterState = eLazyBoolYes; Mutex::Locker locker; if (GetSequenceMutex (locker, "Didn't get sequence mutex for QSaveRegisterState.")) { const bool thread_suffix_supported = GetThreadSuffixSupported(); if (thread_suffix_supported || SetCurrentThread(tid)) { char packet[256]; if (thread_suffix_supported) ::snprintf (packet, sizeof(packet), "QSaveRegisterState;thread:%4.4" PRIx64 ";", tid); else ::snprintf(packet, sizeof(packet), "QSaveRegisterState"); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) { // This packet isn't supported, don't try calling it again m_supports_QSaveRegisterState = eLazyBoolNo; } const uint32_t response_save_id = response.GetU32(0); if (response_save_id != 0) { save_id = response_save_id; return true; } } } } return false; } bool GDBRemoteCommunicationClient::RestoreRegisterState (lldb::tid_t tid, uint32_t save_id) { // We use the "m_supports_QSaveRegisterState" variable here because the // QSaveRegisterState and QRestoreRegisterState packets must both be supported in // order to be useful if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; Mutex::Locker locker; if (GetSequenceMutex (locker, "Didn't get sequence mutex for QRestoreRegisterState.")) { const bool thread_suffix_supported = GetThreadSuffixSupported(); if (thread_suffix_supported || SetCurrentThread(tid)) { char packet[256]; if (thread_suffix_supported) ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u;thread:%4.4" PRIx64 ";", save_id, tid); else ::snprintf (packet, sizeof(packet), "QRestoreRegisterState:%u" PRIx64 ";", save_id); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { return true; } else if (response.IsUnsupportedResponse()) { // This packet isn't supported, don't try calling this packet or // QSaveRegisterState again... m_supports_QSaveRegisterState = eLazyBoolNo; } } } } return false; } bool GDBRemoteCommunicationClient::GetModuleInfo (const FileSpec& module_file_spec, const lldb_private::ArchSpec& arch_spec, ModuleSpec &module_spec) { std::string module_path = module_file_spec.GetPath (false); if (module_path.empty ()) return false; StreamString packet; packet.PutCString("qModuleInfo:"); packet.PutCStringAsRawHex8(module_path.c_str()); packet.PutCString(";"); const auto& triple = arch_spec.GetTriple().getTriple(); packet.PutCStringAsRawHex8(triple.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) != PacketResult::Success) return false; if (response.IsErrorResponse () || response.IsUnsupportedResponse ()) return false; std::string name; std::string value; bool success; StringExtractor extractor; module_spec.Clear (); module_spec.GetFileSpec () = module_file_spec; while (response.GetNameColonValue (name, value)) { if (name == "uuid" || name == "md5") { extractor.GetStringRef ().swap (value); extractor.SetFilePos (0); extractor.GetHexByteString (value); module_spec.GetUUID().SetFromCString (value.c_str(), value.size() / 2); } else if (name == "triple") { extractor.GetStringRef ().swap (value); extractor.SetFilePos (0); extractor.GetHexByteString (value); module_spec.GetArchitecture().SetTriple (value.c_str ()); } else if (name == "file_offset") { const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); if (success) module_spec.SetObjectOffset (ival); } else if (name == "file_size") { const auto ival = StringConvert::ToUInt64 (value.c_str (), 0, 16, &success); if (success) module_spec.SetObjectSize (ival); } else if (name == "file_path") { extractor.GetStringRef ().swap (value); extractor.SetFilePos (0); extractor.GetHexByteString (value); module_spec.GetFileSpec() = FileSpec(value.c_str(), false, arch_spec); } } return true; } // query the target remote for extended information using the qXfer packet // // example: object='features', annex='target.xml', out= // return: 'true' on success // 'false' on failure (err set) bool GDBRemoteCommunicationClient::ReadExtFeature (const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string & out, lldb_private::Error & err) { std::stringstream output; StringExtractorGDBRemote chunk; uint64_t size = GetRemoteMaxPacketSize(); if (size == 0) size = 0x1000; size = size - 1; // Leave space for the 'm' or 'l' character in the response int offset = 0; bool active = true; // loop until all data has been read while ( active ) { // send query extended feature packet std::stringstream packet; packet << "qXfer:" << object.AsCString("") << ":read:" << annex.AsCString("") << ":" << std::hex << offset << "," << std::hex << size; GDBRemoteCommunication::PacketResult res = SendPacketAndWaitForResponse( packet.str().c_str(), chunk, false ); if ( res != GDBRemoteCommunication::PacketResult::Success ) { err.SetErrorString( "Error sending $qXfer packet" ); return false; } const std::string & str = chunk.GetStringRef( ); if ( str.length() == 0 ) { // should have some data in chunk err.SetErrorString( "Empty response from $qXfer packet" ); return false; } // check packet code switch ( str[0] ) { // last chunk case ( 'l' ): active = false; // fall through intentional // more chunks case ( 'm' ) : if ( str.length() > 1 ) output << &str[1]; offset += size; break; // unknown chunk default: err.SetErrorString( "Invalid continuation code from $qXfer packet" ); return false; } } out = output.str( ); err.Success( ); return true; } // Notify the target that gdb is prepared to serve symbol lookup requests. // packet: "qSymbol::" // reply: // OK The target does not need to look up any (more) symbols. // qSymbol: The target requests the value of symbol sym_name (hex encoded). // LLDB may provide the value by sending another qSymbol packet // in the form of"qSymbol::". void GDBRemoteCommunicationClient::ServeSymbolLookups(lldb_private::Process *process) { if (m_supports_qSymbol) { Mutex::Locker locker; if (GetSequenceMutex(locker, "GDBRemoteCommunicationClient::ServeSymbolLookups() failed due to not getting the sequence mutex")) { StreamString packet; packet.PutCString ("qSymbol::"); while (1) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponseNoLock(packet.GetData(), packet.GetSize(), response) == PacketResult::Success) { if (response.IsOKResponse()) { // We are done serving symbols requests return; } if (response.IsUnsupportedResponse()) { // qSymbol is not supported by the current GDB server we are connected to m_supports_qSymbol = false; return; } else { llvm::StringRef response_str(response.GetStringRef()); if (response_str.startswith("qSymbol:")) { response.SetFilePos(strlen("qSymbol:")); std::string symbol_name; if (response.GetHexByteString(symbol_name)) { if (symbol_name.empty()) return; addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; lldb_private::SymbolContextList sc_list; if (process->GetTarget().GetImages().FindSymbolsWithNameAndType(ConstString(symbol_name), eSymbolTypeAny, sc_list)) { const size_t num_scs = sc_list.GetSize(); for (size_t sc_idx=0; sc_idxGetType()) { case eSymbolTypeInvalid: case eSymbolTypeAbsolute: case eSymbolTypeUndefined: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeVariable: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeTrampoline: break; case eSymbolTypeCode: case eSymbolTypeResolver: case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeException: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: case eSymbolTypeReExported: symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); break; } } } } } // This is the normal path where our symbol lookup was successful and we want // to send a packet with the new symbol value and see if another lookup needs to be // done. // Change "packet" to contain the requested symbol value and name packet.Clear(); packet.PutCString("qSymbol:"); if (symbol_load_addr != LLDB_INVALID_ADDRESS) packet.Printf("%" PRIx64, symbol_load_addr); packet.PutCString(":"); packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); continue; // go back to the while loop and send "packet" and wait for another response } } } } } // If we make it here, the symbol request packet response wasn't valid or // our symbol lookup failed so we must abort return; } } } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (revision 287514) @@ -1,684 +1,684 @@ //===-- GDBRemoteCommunicationClient.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_GDBRemoteCommunicationClient_h_ #define liblldb_GDBRemoteCommunicationClient_h_ // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Core/StructuredData.h" #include "lldb/Target/Process.h" #include "GDBRemoteCommunication.h" namespace lldb_private { namespace process_gdb_remote { class GDBRemoteCommunicationClient : public GDBRemoteCommunication { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ GDBRemoteCommunicationClient(); ~GDBRemoteCommunicationClient(); //------------------------------------------------------------------ // After connecting, send the handshake to the server to make sure // we are communicating with it. //------------------------------------------------------------------ bool HandshakeWithServer (Error *error_ptr); PacketResult SendPacketAndWaitForResponse (const char *send_payload, StringExtractorGDBRemote &response, bool send_async); PacketResult SendPacketAndWaitForResponse (const char *send_payload, size_t send_length, StringExtractorGDBRemote &response, bool send_async); // For packets which specify a range of output to be returned, // return all of the output via a series of request packets of the form // 0, // , // *2, // *3, // ... // until a "$l..." packet is received, indicating the end. // (size is in hex; this format is used by a standard gdbserver to // return the given portion of the output specified by ; // for example, "qXfer:libraries-svr4:read::fff,1000" means // "return a chunk of the xml description file for shared // library load addresses, where the chunk starts at offset 0xfff // and continues for 0x1000 bytes"). // Concatenate the resulting server response packets together and // return in response_string. If any packet fails, the return value // indicates that failure and the returned string value is undefined. PacketResult SendPacketsAndConcatenateResponses (const char *send_payload_prefix, std::string &response_string); lldb::StateType SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process, const char *packet_payload, size_t packet_length, StringExtractorGDBRemote &response); bool SendvContPacket (ProcessGDBRemote *process, const char *payload, size_t packet_length, StringExtractorGDBRemote &response); bool GetThreadSuffixSupported () override; // This packet is usually sent first and the boolean return value // indicates if the packet was send and any response was received // even in the response is UNIMPLEMENTED. If the packet failed to // get a response, then false is returned. This quickly tells us // if we were able to connect and communicate with the remote GDB // server bool QueryNoAckModeSupported (); void GetListThreadsInStopReplySupported (); bool SendAsyncSignal (int signo); bool SendInterrupt (Mutex::Locker &locker, uint32_t seconds_to_wait_for_stop, bool &timed_out); lldb::pid_t GetCurrentProcessID (bool allow_lazy = true); bool GetLaunchSuccess (std::string &error_str); uint16_t LaunchGDBserverAndGetPort (lldb::pid_t &pid, const char *remote_accept_hostname); bool KillSpawnedProcess (lldb::pid_t pid); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'A' packet that delivers program /// arguments to the remote server. /// /// @param[in] argv /// A NULL terminated array of const C strings to use as the /// arguments. /// /// @return /// Zero if the response was "OK", a positive value if the /// the response was "Exx" where xx are two hex digits, or /// -1 if the call is unsupported or any other unexpected /// response was received. //------------------------------------------------------------------ int SendArgumentsPacket (const ProcessLaunchInfo &launch_info); //------------------------------------------------------------------ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the /// environment that will get used when launching an application /// in conjunction with the 'A' packet. This function can be called /// multiple times in a row in order to pass on the desired /// environment that the inferior should be launched with. /// /// @param[in] name_equal_value /// A NULL terminated C string that contains a single environment /// in the format "NAME=VALUE". /// /// @return /// Zero if the response was "OK", a positive value if the /// the response was "Exx" where xx are two hex digits, or /// -1 if the call is unsupported or any other unexpected /// response was received. //------------------------------------------------------------------ int SendEnvironmentPacket (char const *name_equal_value); int SendLaunchArchPacket (const char *arch); int SendLaunchEventDataPacket (const char *data, bool *was_supported = NULL); //------------------------------------------------------------------ /// Sends a "vAttach:PID" where PID is in hex. /// /// @param[in] pid /// A process ID for the remote gdb server to attach to. /// /// @param[out] response /// The response received from the gdb server. If the return /// value is zero, \a response will contain a stop reply /// packet. /// /// @return /// Zero if the attach was successful, or an error indicating /// an error code. //------------------------------------------------------------------ int SendAttach (lldb::pid_t pid, StringExtractorGDBRemote& response); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'I' packet that delivers stdin /// data to the remote process. /// /// @param[in] data /// A pointer to stdin data. /// /// @param[in] data_len /// The number of bytes available at \a data. /// /// @return /// Zero if the attach was successful, or an error indicating /// an error code. //------------------------------------------------------------------ int SendStdinNotification(const char* data, size_t data_len); //------------------------------------------------------------------ /// Sets the path to use for stdin/out/err for a process /// that will be launched with the 'A' packet. /// /// @param[in] path /// The path to use for stdin/out/err /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetSTDIN(const FileSpec &file_spec); int SetSTDOUT(const FileSpec &file_spec); int SetSTDERR(const FileSpec &file_spec); //------------------------------------------------------------------ /// Sets the disable ASLR flag to \a enable for a process that will /// be launched with the 'A' packet. /// /// @param[in] enable /// A boolean value indicating whether to disable ASLR or not. /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetDisableASLR (bool enable); //------------------------------------------------------------------ /// Sets the DetachOnError flag to \a enable for the process controlled by the stub. /// /// @param[in] enable /// A boolean value indicating whether to detach on error or not. /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetDetachOnError (bool enable); //------------------------------------------------------------------ /// Sets the working directory to \a path for a process that will /// be launched with the 'A' packet for non platform based /// connections. If this packet is sent to a GDB server that /// implements the platform, it will change the current working /// directory for the platform process. /// /// @param[in] working_dir /// The path to a directory to use when launching our process /// /// @return /// Zero if the for success, or an error code for failure. //------------------------------------------------------------------ int SetWorkingDir(const FileSpec &working_dir); //------------------------------------------------------------------ /// Gets the current working directory of a remote platform GDB /// server. /// /// @param[out] working_dir /// The current working directory on the remote platform. /// /// @return /// Boolean for success //------------------------------------------------------------------ bool GetWorkingDir(FileSpec &working_dir); lldb::addr_t AllocateMemory (size_t size, uint32_t permissions); bool DeallocateMemory (lldb::addr_t addr); Error Detach (bool keep_stopped); Error GetMemoryRegionInfo (lldb::addr_t addr, MemoryRegionInfo &range_info); Error GetWatchpointSupportInfo (uint32_t &num); Error - GetWatchpointSupportInfo (uint32_t &num, bool& after); + GetWatchpointSupportInfo (uint32_t &num, bool& after, const ArchSpec &arch); Error - GetWatchpointsTriggerAfterInstruction (bool &after); + GetWatchpointsTriggerAfterInstruction (bool &after, const ArchSpec &arch); const ArchSpec & GetHostArchitecture (); uint32_t GetHostDefaultPacketTimeout(); const ArchSpec & GetProcessArchitecture (); void GetRemoteQSupported(); bool GetVContSupported (char flavor); bool GetpPacketSupported (lldb::tid_t tid); bool GetxPacketSupported (); bool GetVAttachOrWaitSupported (); bool GetSyncThreadStateSupported(); void ResetDiscoverableSettings (bool did_exec); bool GetHostInfo (bool force = false); bool GetDefaultThreadId (lldb::tid_t &tid); bool GetOSVersion (uint32_t &major, uint32_t &minor, uint32_t &update); bool GetOSBuildString (std::string &s); bool GetOSKernelDescription (std::string &s); ArchSpec GetSystemArchitecture (); bool GetHostname (std::string &s); lldb::addr_t GetShlibInfoAddr(); bool GetSupportsThreadSuffix (); bool GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info); uint32_t FindProcesses (const ProcessInstanceInfoMatch &process_match_info, ProcessInstanceInfoList &process_infos); bool GetUserName (uint32_t uid, std::string &name); bool GetGroupName (uint32_t gid, std::string &name); bool HasFullVContSupport () { return GetVContSupported ('A'); } bool HasAnyVContSupport () { return GetVContSupported ('a'); } bool GetStopReply (StringExtractorGDBRemote &response); bool GetThreadStopInfo (lldb::tid_t tid, StringExtractorGDBRemote &response); bool SupportsGDBStoppointPacket (GDBStoppointType type) { switch (type) { case eBreakpointSoftware: return m_supports_z0; case eBreakpointHardware: return m_supports_z1; case eWatchpointWrite: return m_supports_z2; case eWatchpointRead: return m_supports_z3; case eWatchpointReadWrite: return m_supports_z4; default: return false; } } uint8_t SendGDBStoppointTypePacket (GDBStoppointType type, // Type of breakpoint or watchpoint bool insert, // Insert or remove? lldb::addr_t addr, // Address of breakpoint or watchpoint uint32_t length); // Byte Size of breakpoint or watchpoint bool SetNonStopMode (const bool enable); void TestPacketSpeed (const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, bool json, Stream &strm); // This packet is for testing the speed of the interface only. Both // the client and server need to support it, but this allows us to // measure the packet speed without any other work being done on the // other end and avoids any of that work affecting the packet send // and response times. bool SendSpeedTestPacket (uint32_t send_size, uint32_t recv_size); bool SetCurrentThread (uint64_t tid); bool SetCurrentThreadForRun (uint64_t tid); bool GetQXferAuxvReadSupported (); bool GetQXferLibrariesReadSupported (); bool GetQXferLibrariesSVR4ReadSupported (); uint64_t GetRemoteMaxPacketSize(); bool GetEchoSupported (); bool GetAugmentedLibrariesSVR4ReadSupported (); bool GetQXferFeaturesReadSupported (); LazyBool SupportsAllocDeallocMemory () // const { // Uncomment this to have lldb pretend the debug server doesn't respond to alloc/dealloc memory packets. // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo; return m_supports_alloc_dealloc_memory; } size_t GetCurrentThreadIDs (std::vector &thread_ids, bool &sequence_mutex_unavailable); bool GetInterruptWasSent () const { return m_interrupt_sent; } lldb::user_id_t OpenFile (const FileSpec& file_spec, uint32_t flags, mode_t mode, Error &error); bool CloseFile (lldb::user_id_t fd, Error &error); lldb::user_id_t GetFileSize (const FileSpec& file_spec); Error GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions); Error SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions); uint64_t ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Error &error); uint64_t WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, Error &error); Error CreateSymlink(const FileSpec &src, const FileSpec &dst); Error Unlink(const FileSpec &file_spec); Error MakeDirectory(const FileSpec &file_spec, uint32_t mode); bool GetFileExists (const FileSpec& file_spec); Error RunShellCommand(const char *command, // Shouldn't be NULL const FileSpec &working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish bool CalculateMD5 (const FileSpec& file_spec, uint64_t &high, uint64_t &low); std::string HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, StringExtractorGDBRemote &inputStringExtractor); bool ReadRegister(lldb::tid_t tid, uint32_t reg_num, StringExtractorGDBRemote &response); bool ReadAllRegisters (lldb::tid_t tid, StringExtractorGDBRemote &response); bool SaveRegisterState (lldb::tid_t tid, uint32_t &save_id); bool RestoreRegisterState (lldb::tid_t tid, uint32_t save_id); const char * GetGDBServerProgramName(); uint32_t GetGDBServerProgramVersion(); bool AvoidGPackets(ProcessGDBRemote *process); StructuredData::ObjectSP GetThreadsInfo(); bool GetThreadExtendedInfoSupported(); bool GetLoadedDynamicLibrariesInfosSupported(); bool GetModuleInfo (const FileSpec& module_file_spec, const ArchSpec& arch_spec, ModuleSpec &module_spec); bool ReadExtFeature (const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string & out, lldb_private::Error & err); void ServeSymbolLookups(lldb_private::Process *process); protected: PacketResult SendPacketAndWaitForResponseNoLock (const char *payload, size_t payload_length, StringExtractorGDBRemote &response); bool GetCurrentProcessInfo (bool allow_lazy_pid = true); bool GetGDBServerVersion(); // Given the list of compression types that the remote debug stub can support, // possibly enable compression if we find an encoding we can handle. void MaybeEnableCompression (std::vector supported_compressions); //------------------------------------------------------------------ // Classes that inherit from GDBRemoteCommunicationClient can see and modify these //------------------------------------------------------------------ LazyBool m_supports_not_sending_acks; LazyBool m_supports_thread_suffix; LazyBool m_supports_threads_in_stop_reply; LazyBool m_supports_vCont_all; LazyBool m_supports_vCont_any; LazyBool m_supports_vCont_c; LazyBool m_supports_vCont_C; LazyBool m_supports_vCont_s; LazyBool m_supports_vCont_S; LazyBool m_qHostInfo_is_valid; LazyBool m_curr_pid_is_valid; LazyBool m_qProcessInfo_is_valid; LazyBool m_qGDBServerVersion_is_valid; LazyBool m_supports_alloc_dealloc_memory; LazyBool m_supports_memory_region_info; LazyBool m_supports_watchpoint_support_info; LazyBool m_supports_detach_stay_stopped; LazyBool m_watchpoints_trigger_after_instruction; LazyBool m_attach_or_wait_reply; LazyBool m_prepare_for_reg_writing_reply; LazyBool m_supports_p; LazyBool m_supports_x; LazyBool m_avoid_g_packets; LazyBool m_supports_QSaveRegisterState; LazyBool m_supports_qXfer_auxv_read; LazyBool m_supports_qXfer_libraries_read; LazyBool m_supports_qXfer_libraries_svr4_read; LazyBool m_supports_qXfer_features_read; LazyBool m_supports_augmented_libraries_svr4_read; LazyBool m_supports_jThreadExtendedInfo; LazyBool m_supports_jLoadedDynamicLibrariesInfos; bool m_supports_qProcessInfoPID:1, m_supports_qfProcessInfo:1, m_supports_qUserName:1, m_supports_qGroupName:1, m_supports_qThreadStopInfo:1, m_supports_z0:1, m_supports_z1:1, m_supports_z2:1, m_supports_z3:1, m_supports_z4:1, m_supports_QEnvironment:1, m_supports_QEnvironmentHexEncoded:1, m_supports_qSymbol:1, m_supports_jThreadsInfo:1; lldb::pid_t m_curr_pid; lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc uint32_t m_num_supported_hardware_watchpoints; // If we need to send a packet while the target is running, the m_async_XXX // member variables take care of making this happen. Mutex m_async_mutex; Predicate m_async_packet_predicate; std::string m_async_packet; PacketResult m_async_result; StringExtractorGDBRemote m_async_response; int m_async_signal; // We were asked to deliver a signal to the inferior process. bool m_interrupt_sent; std::string m_partial_profile_data; std::map m_thread_id_to_used_usec_map; ArchSpec m_host_arch; ArchSpec m_process_arch; uint32_t m_os_version_major; uint32_t m_os_version_minor; uint32_t m_os_version_update; std::string m_os_build; std::string m_os_kernel; std::string m_hostname; std::string m_gdb_server_name; // from reply to qGDBServerVersion, empty if qGDBServerVersion is not supported uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if qGDBServerVersion is not supported uint32_t m_default_packet_timeout; uint64_t m_max_packet_size; // as returned by qSupported bool DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info); private: //------------------------------------------------------------------ // For GDBRemoteCommunicationClient only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationClient); }; } // namespace process_gdb_remote } // namespace lldb_private #endif // liblldb_GDBRemoteCommunicationClient_h_ Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 287513) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (revision 287514) @@ -1,5016 +1,5037 @@ //===-- ProcessGDBRemote.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/Config.h" // C Includes #include #include #ifndef LLDB_DISABLE_POSIX #include #include // for mmap #endif #include #include #include // C++ Includes #include #include #include #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Interpreter/Args.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" #include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Utility/PseudoTerminal.h" // Project includes #include "lldb/Host/Host.h" #include "Plugins/Process/Utility/GDBRemoteSignals.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" #include "Utility/StringExtractorGDBRemote.h" #include "GDBRemoteRegisterContext.h" #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "ThreadGDBRemote.h" #define DEBUGSERVER_BASENAME "debugserver" using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; namespace lldb { // Provide a function that can easily dump the packet history if we know a // ProcessGDBRemote * value (which we can get from logs or from debugging). // We need the function in the lldb namespace so it makes it into the final // executable since the LLDB shared library only exports stuff in the lldb // namespace. This allows you to attach with a debugger and call this // function and get the packet history dumped to a file. void DumpProcessGDBRemotePacketHistory (void *p, const char *path) { StreamFile strm; Error error (strm.GetFile().Open(path, File::eOpenOptionWrite | File::eOpenOptionCanCreate)); if (error.Success()) ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm); } } namespace { static PropertyDefinition g_properties[] = { { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, { "target-definition-file" , OptionValue::eTypeFileSpec , true, 0 , NULL, NULL, "The file that provides the description for remote target registers." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; enum { ePropertyPacketTimeout, ePropertyTargetDefinitionFile }; class PluginProperties : public Properties { public: static ConstString GetSettingName () { return ProcessGDBRemote::GetPluginNameStatic(); } PluginProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } virtual ~PluginProperties() { } uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); } bool SetPacketTimeout(uint64_t timeout) { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->SetPropertyAtIndexAsUInt64(NULL, idx, timeout); } FileSpec GetTargetDefinitionFile () const { const uint32_t idx = ePropertyTargetDefinitionFile; return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP & GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset (new PluginProperties ()); return g_settings_sp; } } // anonymous namespace end class ProcessGDBRemote::GDBLoadedModuleInfoList { public: class LoadedModuleInfo { public: enum e_data_point { e_has_name = 0, e_has_base , e_has_dynamic , e_has_link_map , e_num }; LoadedModuleInfo () { for (uint32_t i = 0; i < e_num; ++i) m_has[i] = false; }; void set_name (const std::string & name) { m_name = name; m_has[e_has_name] = true; } bool get_name (std::string & out) const { out = m_name; return m_has[e_has_name]; } void set_base (const lldb::addr_t base) { m_base = base; m_has[e_has_base] = true; } bool get_base (lldb::addr_t & out) const { out = m_base; return m_has[e_has_base]; } void set_link_map (const lldb::addr_t addr) { m_link_map = addr; m_has[e_has_link_map] = true; } bool get_link_map (lldb::addr_t & out) const { out = m_link_map; return m_has[e_has_link_map]; } void set_dynamic (const lldb::addr_t addr) { m_dynamic = addr; m_has[e_has_dynamic] = true; } bool get_dynamic (lldb::addr_t & out) const { out = m_dynamic; return m_has[e_has_dynamic]; } bool has_info (e_data_point datum) { assert (datum < e_num); return m_has[datum]; } protected: bool m_has[e_num]; std::string m_name; lldb::addr_t m_link_map; lldb::addr_t m_base; lldb::addr_t m_dynamic; }; GDBLoadedModuleInfoList () : m_list () , m_link_map (LLDB_INVALID_ADDRESS) {} void add (const LoadedModuleInfo & mod) { m_list.push_back (mod); } void clear () { m_list.clear (); } std::vector m_list; lldb::addr_t m_link_map; }; // TODO Randomly assigning a port is unsafe. We should get an unused // ephemeral port from the kernel and make sure we reserve it before passing // it to debugserver. #if defined (__APPLE__) #define LOW_PORT (IPPORT_RESERVED) #define HIGH_PORT (IPPORT_HIFIRSTAUTO) #else #define LOW_PORT (1024u) #define HIGH_PORT (49151u) #endif #if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) static bool rand_initialized = false; static inline uint16_t get_random_port () { if (!rand_initialized) { time_t seed = time(NULL); rand_initialized = true; srand(seed); } return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; } #endif ConstString ProcessGDBRemote::GetPluginNameStatic() { static ConstString g_name("gdb-remote"); return g_name; } const char * ProcessGDBRemote::GetPluginDescriptionStatic() { return "GDB Remote protocol based debugging plug-in."; } void ProcessGDBRemote::Terminate() { PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); } lldb::ProcessSP ProcessGDBRemote::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset (new ProcessGDBRemote (target, listener)); return process_sp; } bool ProcessGDBRemote::CanDebug (Target &target, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target.GetExecutableModulePointer(); if (exe_module) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); // We can't debug core files... switch (exe_objfile->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeSharedLibrary: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeJIT: return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeUnknown: break; } return exe_module->GetFileSpec().Exists(); } // However, if there is no executable module, we return true since we might be preparing to attach. return true; } //---------------------------------------------------------------------- // ProcessGDBRemote constructor //---------------------------------------------------------------------- ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : Process (target, listener), m_flags (0), m_gdb_comm (), m_debugserver_pid (LLDB_INVALID_PROCESS_ID), - m_last_stop_packet_mutex (Mutex::eMutexTypeNormal), + m_last_stop_packet_mutex (Mutex::eMutexTypeRecursive), m_register_info (), m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), m_thread_ids (), m_threads_info_sp (), m_continue_c_tids (), m_continue_C_tids (), m_continue_s_tids (), m_continue_S_tids (), m_max_memory_size (0), m_remote_stub_max_memory_size (0), m_addr_to_mmap_size (), m_thread_create_bp_sp (), m_waiting_for_attach (false), m_destroy_tried_resuming (false), m_command_sp (), m_breakpoint_pc_offset (0), m_initial_tid (LLDB_INVALID_THREAD_ID) { m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit"); const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_gdb_comm.SetPacketTimeout(timeout_seconds); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessGDBRemote::~ProcessGDBRemote() { // m_mach_process.UnregisterNotificationCallbacks (this); 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(); // The general Finalize is going to try to destroy the process and that SHOULD // shut down the async thread. However, if we don't kill it it will get stranded and // its connection will go away so when it wakes up it will crash. So kill it for sure here. StopAsyncThread(); KillDebugserverProcess(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } bool ProcessGDBRemote::ParsePythonTargetDefinition(const FileSpec &target_definition_fspec) { ScriptInterpreter *interpreter = GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); Error error; StructuredData::ObjectSP module_object_sp(interpreter->LoadPluginModule(target_definition_fspec, error)); if (module_object_sp) { StructuredData::DictionarySP target_definition_sp( interpreter->GetDynamicSettings(module_object_sp, &GetTarget(), "gdb-server-target-definition", error)); if (target_definition_sp) { StructuredData::ObjectSP target_object(target_definition_sp->GetValueForKey("host-info")); if (target_object) { if (auto host_info_dict = target_object->GetAsDictionary()) { StructuredData::ObjectSP triple_value = host_info_dict->GetValueForKey("triple"); if (auto triple_string_value = triple_value->GetAsString()) { std::string triple_string = triple_string_value->GetValue(); ArchSpec host_arch(triple_string.c_str()); if (!host_arch.IsCompatibleMatch(GetTarget().GetArchitecture())) { GetTarget().SetArchitecture(host_arch); } } } } m_breakpoint_pc_offset = 0; StructuredData::ObjectSP breakpoint_pc_offset_value = target_definition_sp->GetValueForKey("breakpoint-pc-offset"); if (breakpoint_pc_offset_value) { if (auto breakpoint_pc_int_value = breakpoint_pc_offset_value->GetAsInteger()) m_breakpoint_pc_offset = breakpoint_pc_int_value->GetValue(); } if (m_register_info.SetRegisterInfo(*target_definition_sp, GetTarget().GetArchitecture()) > 0) { return true; } } } return false; } static size_t SplitCommaSeparatedRegisterNumberString(const llvm::StringRef &comma_separated_regiter_numbers, std::vector ®nums, int base) { regnums.clear(); std::pair value_pair; value_pair.second = comma_separated_regiter_numbers; do { value_pair = value_pair.second.split(','); if (!value_pair.first.empty()) { uint32_t reg = StringConvert::ToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, base); if (reg != LLDB_INVALID_REGNUM) regnums.push_back (reg); } } while (!value_pair.second.empty()); return regnums.size(); } void ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) { if (!force && m_register_info.GetNumRegisters() > 0) return; m_register_info.Clear(); // Check if qHostInfo specified a specific packet timeout for this connection. // If so then lets update our setting so the user knows what the timeout is // and can see it. const uint32_t host_packet_timeout = m_gdb_comm.GetHostDefaultPacketTimeout(); if (host_packet_timeout) { GetGlobalPluginProperties()->SetPacketTimeout(host_packet_timeout); } // Register info search order: // 1 - Use the target definition python file if one is specified. // 2 - If the target definition doesn't have any of the info from the target.xml (registers) then proceed to read the target.xml. // 3 - Fall back on the qRegisterInfo packets. FileSpec target_definition_fspec = GetGlobalPluginProperties()->GetTargetDefinitionFile (); if (target_definition_fspec) { // See if we can get register definitions from a python file if (ParsePythonTargetDefinition (target_definition_fspec)) return; } if (GetGDBServerRegisterInfo ()) return; char packet[128]; uint32_t reg_offset = 0; uint32_t reg_num = 0; for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; response_type == StringExtractorGDBRemote::eResponse; ++reg_num) { const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); assert (packet_len < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false) == GDBRemoteCommunication::PacketResult::Success) { response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { std::string name; std::string value; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // formate { LLDB_INVALID_REGNUM, // GCC reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num reg_num, // GDB reg num reg_num // native register number }, NULL, NULL }; while (response.GetNameColonValue(name, value)) { if (name.compare("name") == 0) { reg_name.SetCString(value.c_str()); } else if (name.compare("alt-name") == 0) { alt_name.SetCString(value.c_str()); } else if (name.compare("bitsize") == 0) { reg_info.byte_size = StringConvert::ToUInt32(value.c_str(), 0, 0) / CHAR_BIT; } else if (name.compare("offset") == 0) { uint32_t offset = StringConvert::ToUInt32(value.c_str(), UINT32_MAX, 0); if (reg_offset != offset) { reg_offset = offset; } } else if (name.compare("encoding") == 0) { const Encoding encoding = Args::StringToEncoding (value.c_str()); if (encoding != eEncodingInvalid) reg_info.encoding = encoding; } else if (name.compare("format") == 0) { Format format = eFormatInvalid; if (Args::StringToFormat (value.c_str(), format, NULL).Success()) reg_info.format = format; else if (value.compare("binary") == 0) reg_info.format = eFormatBinary; else if (value.compare("decimal") == 0) reg_info.format = eFormatDecimal; else if (value.compare("hex") == 0) reg_info.format = eFormatHex; else if (value.compare("float") == 0) reg_info.format = eFormatFloat; else if (value.compare("vector-sint8") == 0) reg_info.format = eFormatVectorOfSInt8; else if (value.compare("vector-uint8") == 0) reg_info.format = eFormatVectorOfUInt8; else if (value.compare("vector-sint16") == 0) reg_info.format = eFormatVectorOfSInt16; else if (value.compare("vector-uint16") == 0) reg_info.format = eFormatVectorOfUInt16; else if (value.compare("vector-sint32") == 0) reg_info.format = eFormatVectorOfSInt32; else if (value.compare("vector-uint32") == 0) reg_info.format = eFormatVectorOfUInt32; else if (value.compare("vector-float32") == 0) reg_info.format = eFormatVectorOfFloat32; else if (value.compare("vector-uint128") == 0) reg_info.format = eFormatVectorOfUInt128; } else if (name.compare("set") == 0) { set_name.SetCString(value.c_str()); } else if (name.compare("gcc") == 0) { reg_info.kinds[eRegisterKindGCC] = StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("dwarf") == 0) { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); } else if (name.compare("generic") == 0) { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister (value.c_str()); } else if (name.compare("container-regs") == 0) { SplitCommaSeparatedRegisterNumberString(value, value_regs, 16); } else if (name.compare("invalidate-regs") == 0) { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 16); } } reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); } else { break; // ensure exit before reg_num is incremented } } else { break; } } if (m_register_info.GetNumRegisters() > 0) { m_register_info.Finalize(GetTarget().GetArchitecture()); return; } // We didn't get anything if the accumulated reg_num is zero. See if we are // debugging ARM and fill with a hard coded register set until we can get an // updated debugserver down on the devices. // On the other hand, if the accumulated reg_num is positive, see if we can // add composite registers to the existing primordial ones. bool from_scratch = (m_register_info.GetNumRegisters() == 0); const ArchSpec &target_arch = GetTarget().GetArchitecture(); const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); // Use the process' architecture instead of the host arch, if available ArchSpec remote_arch; if (remote_process_arch.IsValid ()) remote_arch = remote_process_arch; else remote_arch = remote_host_arch; if (!target_arch.IsValid()) { if (remote_arch.IsValid() && remote_arch.GetMachine() == llvm::Triple::arm && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_register_info.HardcodeARMRegisters(from_scratch); } else if (target_arch.GetMachine() == llvm::Triple::arm) { m_register_info.HardcodeARMRegisters(from_scratch); } // At this point, we can finalize our register info. m_register_info.Finalize (GetTarget().GetArchitecture()); } Error ProcessGDBRemote::WillLaunch (Module* module) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithID (lldb::pid_t pid) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) { return WillLaunchOrAttach (); } Error ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error (WillLaunchOrAttach ()); if (error.Fail()) return error; error = ConnectToDebugserver (remote_url); if (error.Fail()) return error; StartAsyncThread (); lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid == LLDB_INVALID_PROCESS_ID) { // We don't have a valid process ID, so note that we are connected // and could now request to launch or attach, or get remote process // listings... SetPrivateState (eStateConnected); } else { // We have a valid process SetID (pid); GetThreadList(); StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); if (!m_target.GetArchitecture().IsValid()) { if (m_gdb_comm.GetProcessArchitecture().IsValid()) { m_target.SetArchitecture(m_gdb_comm.GetProcessArchitecture()); } else { m_target.SetArchitecture(m_gdb_comm.GetHostArchitecture()); } } const StateType state = SetThreadStopInfo (response); if (state == eStateStopped) { SetPrivateState (state); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but state was not stopped: %s", pid, remote_url, StateAsCString (state)); } else error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but no stop reply packet was received", pid, remote_url); } if (log) log->Printf ("ProcessGDBRemote::%s pid %" PRIu64 ": normalizing target architecture initial triple: %s (GetTarget().GetArchitecture().IsValid() %s, m_gdb_comm.GetHostArchitecture().IsValid(): %s)", __FUNCTION__, GetID (), GetTarget ().GetArchitecture ().GetTriple ().getTriple ().c_str (), GetTarget ().GetArchitecture ().IsValid () ? "true" : "false", m_gdb_comm.GetHostArchitecture ().IsValid () ? "true" : "false"); if (error.Success() && !GetTarget().GetArchitecture().IsValid() && m_gdb_comm.GetHostArchitecture().IsValid()) { // Prefer the *process'* architecture over that of the *host*, if available. if (m_gdb_comm.GetProcessArchitecture().IsValid()) GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); else GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); } if (log) log->Printf ("ProcessGDBRemote::%s pid %" PRIu64 ": normalized target architecture triple: %s", __FUNCTION__, GetID (), GetTarget ().GetArchitecture ().GetTriple ().getTriple ().c_str ()); if (error.Success()) - SetUnixSignals(std::make_shared(GetTarget().GetPlatform()->GetUnixSignals())); + { + PlatformSP platform_sp = GetTarget().GetPlatform(); + if (platform_sp && platform_sp->IsConnected()) + SetUnixSignals(platform_sp->GetUnixSignals()); + else + SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture())); + } return error; } Error ProcessGDBRemote::WillLaunchOrAttach () { Error error; m_stdio_communication.Clear (); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Error ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; if (log) log->Printf ("ProcessGDBRemote::%s() entered", __FUNCTION__); uint32_t launch_flags = launch_info.GetFlags().Get(); FileSpec stdin_file_spec{}; FileSpec stdout_file_spec{}; FileSpec stderr_file_spec{}; FileSpec working_dir = launch_info.GetWorkingDirectory(); const FileAction *file_action; file_action = launch_info.GetFileActionForFD (STDIN_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdin_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stdout_file_spec = file_action->GetFileSpec(); } file_action = launch_info.GetFileActionForFD (STDERR_FILENO); if (file_action) { if (file_action->GetAction() == FileAction::eFileActionOpen) stderr_file_spec = file_action->GetFileSpec(); } if (log) { if (stdin_file_spec || stdout_file_spec || stderr_file_spec) log->Printf ("ProcessGDBRemote::%s provided with STDIO paths via launch_info: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); else log->Printf ("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); } const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; if (stdin_file_spec || disable_stdio) { // the inferior will be reading stdin from the specified file // or stdio is completely disabled m_stdin_forward = false; } else { m_stdin_forward = true; } // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); ObjectFile * object_file = exe_module->GetObjectFile(); if (object_file) { // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { error = LaunchAndConnectToDebugserver (launch_info); } if (error.Success()) { lldb_utility::PseudoTerminal pty; const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; PlatformSP platform_sp (m_target.GetPlatform()); if (disable_stdio) { // set to /dev/null unless redirected to a file above if (!stdin_file_spec) stdin_file_spec.SetFile("/dev/null", false); if (!stdout_file_spec) stdout_file_spec.SetFile("/dev/null", false); if (!stderr_file_spec) stderr_file_spec.SetFile("/dev/null", false); } else if (platform_sp && platform_sp->IsHost()) { // If the debugserver is local and we aren't disabling STDIO, lets use // a pseudo terminal to instead of relying on the 'O' packets for stdio // since 'O' packets can really slow down debugging if the inferior // does a lot of output. if ((!stdin_file_spec || !stdout_file_spec || !stderr_file_spec) && pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) { FileSpec slave_name{pty.GetSlaveName(NULL, 0), false}; if (!stdin_file_spec) stdin_file_spec = slave_name; if (!stdout_file_spec) stdout_file_spec = slave_name; if (!stderr_file_spec) stderr_file_spec = slave_name; } if (log) log->Printf ("ProcessGDBRemote::%s adjusted STDIO paths for local platform (IsHost() is true) using slave: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); } if (log) log->Printf ("ProcessGDBRemote::%s final STDIO paths after all adjustments: stdin=%s, stdout=%s, stderr=%s", __FUNCTION__, stdin_file_spec ? stdin_file_spec.GetCString() : "", stdout_file_spec ? stdout_file_spec.GetCString() : "", stderr_file_spec ? stderr_file_spec.GetCString() : ""); if (stdin_file_spec) m_gdb_comm.SetSTDIN(stdin_file_spec); if (stdout_file_spec) m_gdb_comm.SetSTDOUT(stdout_file_spec); if (stderr_file_spec) m_gdb_comm.SetSTDERR(stderr_file_spec); m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); m_gdb_comm.SetDetachOnError (launch_flags & eLaunchFlagDetachOnError); m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName()); const char * launch_event_data = launch_info.GetLaunchEventData(); if (launch_event_data != NULL && *launch_event_data != '\0') m_gdb_comm.SendLaunchEventDataPacket (launch_event_data); if (working_dir) { m_gdb_comm.SetWorkingDir (working_dir); } // Send the environment and the program + arguments after we connect const Args &environment = launch_info.GetEnvironmentEntries(); if (environment.GetArgumentCount()) { size_t num_environment_entries = environment.GetArgumentCount(); for (size_t i=0; iPrintf("failed to connect to debugserver: %s", error.AsCString()); KillDebugserverProcess (); return error; } StringExtractorGDBRemote response; if (m_gdb_comm.GetStopReply(response)) { SetLastStopPacket(response); // '?' Packets must be handled differently in non-stop mode if (GetTarget().GetNonStopModeEnabled()) HandleStopReplySequence(); const ArchSpec &process_arch = m_gdb_comm.GetProcessArchitecture(); if (process_arch.IsValid()) { m_target.MergeArchitecture(process_arch); } else { const ArchSpec &host_arch = m_gdb_comm.GetHostArchitecture(); if (host_arch.IsValid()) m_target.MergeArchitecture(host_arch); } SetPrivateState (SetThreadStopInfo (response)); if (!disable_stdio) { if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) SetSTDIOFileDescriptor (pty.ReleaseMasterFileDescriptor()); } } } else { if (log) log->Printf("failed to connect to debugserver: %s", error.AsCString()); } } else { // Set our user ID to an invalid process ID. SetID(LLDB_INVALID_PROCESS_ID); error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", exe_module->GetFileSpec().GetFilename().AsCString(), exe_module->GetArchitecture().GetArchitectureName()); } return error; } Error ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) { Error error; // Only connect if we have a valid connect URL Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (connect_url && connect_url[0]) { if (log) log->Printf("ProcessGDBRemote::%s Connecting to %s", __FUNCTION__, connect_url); std::unique_ptr conn_ap(new ConnectionFileDescriptor()); if (conn_ap.get()) { const uint32_t max_retry_count = 50; uint32_t retry_count = 0; while (!m_gdb_comm.IsConnected()) { if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) { m_gdb_comm.SetConnection (conn_ap.release()); break; } else if (error.WasInterrupted()) { // If we were interrupted, don't keep retrying. break; } retry_count++; if (retry_count >= max_retry_count) break; usleep (100000); } } } if (!m_gdb_comm.IsConnected()) { if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Start the communications read thread so all incoming data can be // parsed into packets and queued as they arrive. if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.StartReadThread(); // We always seem to be able to open a connection to a local port // so we need to make sure we can then send data to it. If we can't // then we aren't actually connected to anything, so try and do the // handshake with the remote GDB server and make sure that goes // alright. if (!m_gdb_comm.HandshakeWithServer (&error)) { m_gdb_comm.Disconnect(); if (error.Success()) error.SetErrorString("not connected to remote gdb server"); return error; } // Send $QNonStop:1 packet on startup if required if (GetTarget().GetNonStopModeEnabled()) GetTarget().SetNonStopModeEnabled (m_gdb_comm.SetNonStopMode(true)); m_gdb_comm.GetEchoSupported (); m_gdb_comm.GetThreadSuffixSupported (); m_gdb_comm.GetListThreadsInStopReplySupported (); m_gdb_comm.GetHostInfo (); m_gdb_comm.GetVContSupported ('c'); m_gdb_comm.GetVAttachOrWaitSupported(); // Ask the remote server for the default thread id if (GetTarget().GetNonStopModeEnabled()) m_gdb_comm.GetDefaultThreadId(m_initial_tid); size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); for (size_t idx = 0; idx < num_cmds; idx++) { StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse (GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); } return error; } void ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DidLaunch()"); if (GetID() != LLDB_INVALID_PROCESS_ID) { BuildDynamicRegisterInfo (false); // See if the GDB server supports the qHostInfo information // See if the GDB server supports the qProcessInfo packet, if so // prefer that over the Host information as it will be more specific // to our process. const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); if (remote_process_arch.IsValid()) { process_arch = remote_process_arch; if (log) log->Printf ("ProcessGDBRemote::%s gdb-remote had process architecture, using %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } else { process_arch = m_gdb_comm.GetHostArchitecture(); if (log) log->Printf ("ProcessGDBRemote::%s gdb-remote did not have process architecture, using gdb-remote host architecture %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } if (process_arch.IsValid()) { const ArchSpec &target_arch = GetTarget().GetArchitecture(); if (target_arch.IsValid()) { if (log) log->Printf ("ProcessGDBRemote::%s analyzing target arch, currently %s %s", __FUNCTION__, target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "", target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : ""); // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared libraries // it has, so we really need to take the remote host architecture as our // defacto architecture in this case. if (process_arch.GetMachine() == llvm::Triple::arm && process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture (process_arch); if (log) log->Printf ("ProcessGDBRemote::%s remote process is ARM/Apple, setting target arch to %s %s", __FUNCTION__, process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "", process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : ""); } else { // Fill in what is missing in the triple const llvm::Triple &remote_triple = process_arch.GetTriple(); llvm::Triple new_target_triple = target_arch.GetTriple(); if (new_target_triple.getVendorName().size() == 0) { new_target_triple.setVendor (remote_triple.getVendor()); if (new_target_triple.getOSName().size() == 0) { new_target_triple.setOS (remote_triple.getOS()); if (new_target_triple.getEnvironmentName().size() == 0) new_target_triple.setEnvironment (remote_triple.getEnvironment()); } ArchSpec new_target_arch = target_arch; new_target_arch.SetTriple(new_target_triple); GetTarget().SetArchitecture(new_target_arch); } } if (log) log->Printf ("ProcessGDBRemote::%s final target arch after adjustments for remote architecture: %s %s", __FUNCTION__, target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "", target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : ""); } else { // The target doesn't have a valid architecture yet, set it from // the architecture we got from the remote GDB server GetTarget().SetArchitecture (process_arch); } } } } void ProcessGDBRemote::DidLaunch () { ArchSpec process_arch; DidLaunchOrAttach (process_arch); } Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; if (log) log->Printf ("ProcessGDBRemote::%s()", __FUNCTION__); // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) { // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { error = LaunchAndConnectToDebugserver (attach_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "unable to launch " DEBUGSERVER_BASENAME; SetExitStatus (-1, error_string); } } if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } } return error; } Error ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) { Error error; // Clear out and clean up from any current state Clear(); if (process_name && process_name[0]) { // Make sure we aren't already connected? if (!m_gdb_comm.IsConnected()) { error = LaunchAndConnectToDebugserver (attach_info); if (error.Fail()) { const char *error_string = error.AsCString(); if (error_string == NULL) error_string = "unable to launch " DEBUGSERVER_BASENAME; SetExitStatus (-1, error_string); } } if (error.Success()) { StreamString packet; m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); if (attach_info.GetWaitForLaunch()) { if (!m_gdb_comm.GetVAttachOrWaitSupported()) { packet.PutCString ("vAttachWait"); } else { if (attach_info.GetIgnoreExisting()) packet.PutCString("vAttachWait"); else packet.PutCString ("vAttachOrWait"); } } else packet.PutCString("vAttachName"); packet.PutChar(';'); packet.PutBytesAsRawHex8(process_name, strlen(process_name), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); } } return error; } void ProcessGDBRemote::DidExit () { // When we exit, disconnect from the GDB server communications m_gdb_comm.Disconnect(); } void ProcessGDBRemote::DidAttach (ArchSpec &process_arch) { // If you can figure out what the architecture is, fill it in here. process_arch.Clear(); DidLaunchOrAttach (process_arch); } Error ProcessGDBRemote::WillResume () { m_continue_c_tids.clear(); m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); return Error(); } Error ProcessGDBRemote::DoResume () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::Resume()"); Listener listener ("gdb-remote.resume-packet-sent"); if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) { listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); const size_t num_threads = GetThreadList().GetSize(); StreamString continue_packet; bool continue_packet_error = false; if (m_gdb_comm.HasAnyVContSupport ()) { if (!GetTarget().GetNonStopModeEnabled() && (m_continue_c_tids.size() == num_threads || (m_continue_c_tids.empty() && m_continue_C_tids.empty() && m_continue_s_tids.empty() && m_continue_S_tids.empty()))) { // All threads are continuing, just send a "c" packet continue_packet.PutCString ("c"); } else { continue_packet.PutCString ("vCont"); if (!m_continue_c_tids.empty()) { if (m_gdb_comm.GetVContSupported ('c')) { for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_C_tids.empty()) { if (m_gdb_comm.GetVContSupported ('C')) { for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_s_tids.empty()) { if (m_gdb_comm.GetVContSupported ('s')) { for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); } else continue_packet_error = true; } if (!continue_packet_error && !m_continue_S_tids.empty()) { if (m_gdb_comm.GetVContSupported ('S')) { for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); } else continue_packet_error = true; } if (continue_packet_error) continue_packet.GetString().clear(); } } else continue_packet_error = true; if (continue_packet_error) { // Either no vCont support, or we tried to use part of the vCont // packet that wasn't supported by the remote GDB server. // We need to try and make a simple packet that can do our continue const size_t num_continue_c_tids = m_continue_c_tids.size(); const size_t num_continue_C_tids = m_continue_C_tids.size(); const size_t num_continue_s_tids = m_continue_s_tids.size(); const size_t num_continue_S_tids = m_continue_S_tids.size(); if (num_continue_c_tids > 0) { if (num_continue_c_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); continue_packet.PutChar ('c'); continue_packet_error = false; } else if (num_continue_c_tids == 1 && num_continue_C_tids == 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { // Only one thread is continuing m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); continue_packet.PutChar ('c'); continue_packet_error = false; } } if (continue_packet_error && num_continue_C_tids > 0) { if ((num_continue_C_tids + num_continue_c_tids) == num_threads && num_continue_C_tids > 0 && num_continue_s_tids == 0 && num_continue_S_tids == 0 ) { const int continue_signo = m_continue_C_tids.front().second; // Only one thread is continuing if (num_continue_C_tids > 1) { // More that one thread with a signal, yet we don't have // vCont support and we are being asked to resume each // thread with a signal, we need to make sure they are // all the same signal, or we can't issue the continue // accurately with the current support... if (num_continue_C_tids > 1) { continue_packet_error = false; for (size_t i=1; i 0) { if (num_continue_s_tids == num_threads) { // All threads are resuming... m_gdb_comm.SetCurrentThreadForRun (-1); // If in Non-Stop-Mode use vCont when stepping if (GetTarget().GetNonStopModeEnabled()) { if (m_gdb_comm.GetVContSupported('s')) continue_packet.PutCString("vCont;s"); else continue_packet.PutChar('s'); } else continue_packet.PutChar('s'); continue_packet_error = false; } else if (num_continue_c_tids == 0 && num_continue_C_tids == 0 && num_continue_s_tids == 1 && num_continue_S_tids == 0 ) { // Only one thread is stepping m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); continue_packet.PutChar ('s'); continue_packet_error = false; } } if (!continue_packet_error && num_continue_S_tids > 0) { if (num_continue_S_tids == num_threads) { const int step_signo = m_continue_S_tids.front().second; // Are all threads trying to step with the same signal? continue_packet_error = false; if (num_continue_S_tids > 1) { for (size_t i=1; iPrintf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); return error; } m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); if (listener.WaitForEvent (&timeout, event_sp) == false) { error.SetErrorString("Resume timed out."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Resume timed out."); } else if (event_sp->BroadcasterIs (&m_async_broadcaster)) { error.SetErrorString ("Broadcast continue, but the async thread was killed before we got an ack back."); if (log) log->Printf ("ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back."); return error; } } } return error; } void ProcessGDBRemote::HandleStopReplySequence () { while(true) { // Send vStopped StringExtractorGDBRemote response; m_gdb_comm.SendPacketAndWaitForResponse("vStopped", response, false); // OK represents end of signal list if (response.IsOKResponse()) break; // If not OK or a normal packet we have a problem if (!response.IsNormalResponse()) break; SetLastStopPacket(response); } } void ProcessGDBRemote::ClearThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); } size_t ProcessGDBRemote::UpdateThreadIDsFromStopReplyThreadsValue (std::string &value) { m_thread_ids.clear(); size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); return m_thread_ids.size(); } bool ProcessGDBRemote::UpdateThreadIDList () { Mutex::Locker locker(m_thread_list_real.GetMutex()); if (m_threads_info_sp) { // If we have the JSON threads info, we can get the thread list from that StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray(); if (thread_infos && thread_infos->GetSize() > 0) { m_thread_ids.clear(); thread_infos->ForEach([this](StructuredData::Object* object) -> bool { StructuredData::Dictionary *thread_dict = object->GetAsDictionary(); if (thread_dict) { // Set the thread stop info from the JSON dictionary SetThreadStopInfo (thread_dict); lldb::tid_t tid = LLDB_INVALID_THREAD_ID; if (thread_dict->GetValueForKeyAsInteger("tid", tid)) m_thread_ids.push_back(tid); } return true; // Keep iterating through all thread_info objects }); } if (!m_thread_ids.empty()) return true; } else { // See if we can get the thread IDs from the current stop reply packets // that might contain a "threads" key/value pair // Lock the thread stack while we access it Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote &stop_info = m_stop_packet_stack[i]; const std::string &stop_info_str = stop_info.GetStringRef(); const size_t threads_pos = stop_info_str.find(";threads:"); if (threads_pos != std::string::npos) { const size_t start = threads_pos + strlen(";threads:"); const size_t end = stop_info_str.find(';', start); if (end != std::string::npos) { std::string value = stop_info_str.substr(start, end - start); if (UpdateThreadIDsFromStopReplyThreadsValue(value)) return true; } } } } bool sequence_mutex_unavailable = false; m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable); if (sequence_mutex_unavailable) { return false; // We just didn't get the list } return true; } bool ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); size_t num_thread_ids = m_thread_ids.size(); // The "m_thread_ids" thread ID list should always be updated after each stop // reply packet, but in case it isn't, update it here. if (num_thread_ids == 0) { if (!UpdateThreadIDList ()) return false; num_thread_ids = m_thread_ids.size(); } ThreadList old_thread_list_copy(old_thread_list); if (num_thread_ids > 0) { for (size_t i=0; iGetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Making new thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, static_cast(thread_sp.get()), thread_sp->GetID()); } else { if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) log->Printf( "ProcessGDBRemote::%s Found old thread: %p for thread ID: 0x%" PRIx64 ".\n", __FUNCTION__, static_cast(thread_sp.get()), thread_sp->GetID()); } new_thread_list.AddThread(thread_sp); } } // Whatever that is left in old_thread_list_copy are not // present in new_thread_list. Remove non-existent threads from internal id table. size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); for (size_t i=0; iGetProtocolID(); m_thread_id_to_index_id_map.erase(old_thread_id); } } return true; } bool ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread) { // See if we got thread stop infos for all threads via the "jThreadsInfo" packet if (m_threads_info_sp) { StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray(); if (thread_infos) { lldb::tid_t tid; const size_t n = thread_infos->GetSize(); for (size_t i=0; iGetItemAtIndex(i)->GetAsDictionary(); if (thread_dict) { if (thread_dict->GetValueForKeyAsInteger("tid", tid, LLDB_INVALID_THREAD_ID)) { if (tid == thread->GetID()) return SetThreadStopInfo(thread_dict); } } } } } // Fall back to using the qThreadStopInfo packet StringExtractorGDBRemote stop_packet; if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet)) return SetThreadStopInfo (stop_packet) == eStateStopped; return false; } ThreadSP ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map, uint8_t signo, const std::string &thread_name, const std::string &reason, const std::string &description, uint32_t exc_type, const std::vector &exc_data, addr_t thread_dispatch_qaddr, bool queue_vars_valid, // Set to true if queue_name, queue_kind and queue_serial are valid std::string &queue_name, QueueKind queue_kind, uint64_t queue_serial) { ThreadSP thread_sp; if (tid != LLDB_INVALID_THREAD_ID) { // Scope for "locker" below { // m_thread_list_real does have its own mutex, but we need to // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) // and the m_thread_list_real.AddThread(...) so it doesn't change on us Mutex::Locker locker (m_thread_list_real.GetMutex ()); thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); if (!thread_sp) { // Create the thread if we need to thread_sp.reset (new ThreadGDBRemote (*this, tid)); m_thread_list_real.AddThread(thread_sp); } } if (thread_sp) { ThreadGDBRemote *gdb_thread = static_cast (thread_sp.get()); gdb_thread->GetRegisterContext()->InvalidateIfNeeded(true); for (const auto &pair : expedited_register_map) { StringExtractor reg_value_extractor; reg_value_extractor.GetStringRef() = pair.second; gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor); } // Clear the stop info just in case we don't set it to anything thread_sp->SetStopInfo (StopInfoSP()); thread_sp->SetName (thread_name.empty() ? NULL : thread_name.c_str()); gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); // Check if the GDB server was able to provide the queue name, kind and serial number if (queue_vars_valid) gdb_thread->SetQueueInfo(std::move(queue_name), queue_kind, queue_serial); else gdb_thread->ClearQueueInfo(); if (exc_type != 0) { const size_t exc_data_size = exc_data.size(); thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, exc_type, exc_data_size, exc_data_size >= 1 ? exc_data[0] : 0, exc_data_size >= 2 ? exc_data[1] : 0, exc_data_size >= 3 ? exc_data[2] : 0)); } else { bool handled = false; bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); handled = true; } else if (reason.compare("breakpoint") == 0) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. handled = true; if (bp_site_sp->ValidForThisThread (thread_sp.get())) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } } else if (reason.compare("trap") == 0) { // Let the trap just use the standard signal stop reason below... } else if (reason.compare("watchpoint") == 0) { StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); + addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); if (wp_sp) { wp_sp->SetHardwareIndex(wp_index); watch_id = wp_sp->GetID(); } } if (watch_id == LLDB_INVALID_WATCH_ID) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("failed to find watchpoint"); } - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason.compare("exception") == 0) { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); handled = true; } else if (reason.compare("exec") == 0) { did_exec = true; thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); handled = true; } } if (!handled && signo && did_exec == false) { if (signo == SIGTRAP) { // Currently we are going to assume SIGTRAP means we are either // hitting a breakpoint or hardware single stepping. handled = true; addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset; lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp) { // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. if (bp_site_sp->ValidForThisThread (thread_sp.get())) { if(m_breakpoint_pc_offset != 0) thread_sp->GetRegisterContext()->SetPC(pc); thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); } else { StopInfoSP invalid_stop_info_sp; thread_sp->SetStopInfo (invalid_stop_info_sp); } } else { // If we were stepping then assume the stop was the result of the trace. If we were // not stepping then report the SIGTRAP. // FIXME: We are still missing the case where we single step over a trap instruction. if (thread_sp->GetTemporaryResumeState() == eStateStepping) thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); else thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str())); } } if (!handled) thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str())); } if (!description.empty()) { lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); if (stop_info_sp) { const char *stop_info_desc = stop_info_sp->GetDescription(); if (!stop_info_desc || !stop_info_desc[0]) stop_info_sp->SetDescription (description.c_str()); } else { thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); } } } } } return thread_sp; } StateType ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict) { static ConstString g_key_tid("tid"); static ConstString g_key_name("name"); static ConstString g_key_reason("reason"); static ConstString g_key_metype("metype"); static ConstString g_key_medata("medata"); static ConstString g_key_qaddr("qaddr"); static ConstString g_key_queue_name("qname"); static ConstString g_key_queue_kind("qkind"); static ConstString g_key_queue_serial("qserial"); static ConstString g_key_registers("registers"); static ConstString g_key_memory("memory"); static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; uint8_t signo = 0; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; ExpeditedRegisterMap expedited_register_map; bool queue_vars_valid = false; std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial = 0; // Iterate through all of the thread dictionary key/value pairs from the structured data dictionary thread_dict->ForEach([this, &tid, &expedited_register_map, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr, &queue_vars_valid, &queue_name, &queue_kind, &queue_serial] (ConstString key, StructuredData::Object* object) -> bool { if (key == g_key_tid) { // thread in big endian hex tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID); } else if (key == g_key_metype) { // exception type in big endian hex exc_type = object->GetIntegerValue(0); } else if (key == g_key_medata) { // exception data in big endian hex StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([&exc_data](StructuredData::Object* object) -> bool { exc_data.push_back(object->GetIntegerValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_name) { thread_name = std::move(object->GetStringValue()); } else if (key == g_key_qaddr) { thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS); } else if (key == g_key_queue_name) { queue_vars_valid = true; queue_name = std::move(object->GetStringValue()); } else if (key == g_key_queue_kind) { std::string queue_kind_str = object->GetStringValue(); if (queue_kind_str == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (queue_kind_str == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key == g_key_queue_serial) { queue_serial = object->GetIntegerValue(0); if (queue_serial != 0) queue_vars_valid = true; } else if (key == g_key_reason) { reason = std::move(object->GetStringValue()); } else if (key == g_key_description) { description = std::move(object->GetStringValue()); } else if (key == g_key_registers) { StructuredData::Dictionary *registers_dict = object->GetAsDictionary(); if (registers_dict) { registers_dict->ForEach([&expedited_register_map](ConstString key, StructuredData::Object* object) -> bool { const uint32_t reg = StringConvert::ToUInt32 (key.GetCString(), UINT32_MAX, 10); if (reg != UINT32_MAX) expedited_register_map[reg] = std::move(object->GetStringValue()); return true; // Keep iterating through all array items }); } } else if (key == g_key_memory) { StructuredData::Array *array = object->GetAsArray(); if (array) { array->ForEach([this](StructuredData::Object* object) -> bool { StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary(); if (mem_cache_dict) { lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS; if (mem_cache_dict->GetValueForKeyAsInteger("address", mem_cache_addr)) { if (mem_cache_addr != LLDB_INVALID_ADDRESS) { StringExtractor bytes; if (mem_cache_dict->GetValueForKeyAsString("bytes", bytes.GetStringRef())) { bytes.SetFilePos(0); const size_t byte_size = bytes.GetStringRef().size()/2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } } return true; // Keep iterating through all array items }); } } return true; // Keep iterating through all dictionary key/value pairs }); SetThreadStopInfo (tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, queue_name, queue_kind, queue_serial); return eStateExited; } StateType ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) { stop_packet.SetFilePos (0); const char stop_type = stop_packet.GetChar(); switch (stop_type) { case 'T': case 'S': { // This is a bit of a hack, but is is required. If we did exec, we // need to clear our thread lists and also know to rebuild our dynamic // register info before we lookup and threads and populate the expedited // register values so we need to know this right away so we can cleanup // and update our registers. const uint32_t stop_id = GetStopID(); if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers if (GetID() == LLDB_INVALID_PROCESS_ID) { lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); if (pid != LLDB_INVALID_PROCESS_ID) SetID (pid); } BuildDynamicRegisterInfo (true); } // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; const uint8_t signo = stop_packet.GetHexU8(); std::string key; std::string value; std::string thread_name; std::string reason; std::string description; uint32_t exc_type = 0; std::vector exc_data; addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; bool queue_vars_valid = false; // says if locals below that start with "queue_" are valid std::string queue_name; QueueKind queue_kind = eQueueKindUnknown; uint64_t queue_serial = 0; ExpeditedRegisterMap expedited_register_map; while (stop_packet.GetNameColonValue(key, value)) { if (key.compare("metype") == 0) { // exception type in big endian hex exc_type = StringConvert::ToUInt32 (value.c_str(), 0, 16); } else if (key.compare("medata") == 0) { // exception data in big endian hex exc_data.push_back(StringConvert::ToUInt64 (value.c_str(), 0, 16)); } else if (key.compare("thread") == 0) { // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); } else if (key.compare("threads") == 0) { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); // A comma separated list of all threads in the current // process that includes the thread for this stop reply // packet size_t comma_pos; lldb::tid_t tid; while ((comma_pos = value.find(',')) != std::string::npos) { value[comma_pos] = '\0'; // thread in big endian hex tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); value.erase(0, comma_pos + 1); } tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); if (tid != LLDB_INVALID_THREAD_ID) m_thread_ids.push_back (tid); } else if (key.compare("hexname") == 0) { StringExtractor name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); thread_name.swap (value); } else if (key.compare("name") == 0) { thread_name.swap (value); } else if (key.compare("qaddr") == 0) { thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16); } else if (key.compare("qname") == 0) { queue_vars_valid = true; StringExtractor name_extractor; // Swap "value" over into "name_extractor" name_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value name_extractor.GetHexByteString (value); queue_name.swap (value); } else if (key.compare("qkind") == 0) { if (value == "serial") { queue_vars_valid = true; queue_kind = eQueueKindSerial; } else if (value == "concurrent") { queue_vars_valid = true; queue_kind = eQueueKindConcurrent; } } else if (key.compare("qserial") == 0) { queue_serial = StringConvert::ToUInt64 (value.c_str(), 0, 0); if (queue_serial != 0) queue_vars_valid = true; } else if (key.compare("reason") == 0) { reason.swap(value); } else if (key.compare("description") == 0) { StringExtractor desc_extractor; // Swap "value" over into "name_extractor" desc_extractor.GetStringRef().swap(value); // Now convert the HEX bytes into a string value desc_extractor.GetHexByteString (value); description.swap(value); } else if (key.compare("memory") == 0) { // Expedited memory. GDB servers can choose to send back expedited memory // that can populate the L1 memory cache in the process so that things like // the frame pointer backchain can be expedited. This will help stack // backtracing be more efficient by not having to send as many memory read // requests down the remote GDB server. // Key/value pair format: memory:=; // is a number whose base will be interpreted by the prefix: // "0x[0-9a-fA-F]+" for hex // "0[0-7]+" for octal // "[1-9]+" for decimal // is native endian ASCII hex bytes just like the register values llvm::StringRef value_ref(value); std::pair pair; pair = value_ref.split('='); if (!pair.first.empty() && !pair.second.empty()) { std::string addr_str(pair.first.str()); const lldb::addr_t mem_cache_addr = StringConvert::ToUInt64(addr_str.c_str(), LLDB_INVALID_ADDRESS, 0); if (mem_cache_addr != LLDB_INVALID_ADDRESS) { StringExtractor bytes; bytes.GetStringRef() = std::move(pair.second.str()); const size_t byte_size = bytes.GetStringRef().size()/2; DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0)); const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0); if (bytes_copied == byte_size) m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp); } } } + else if (key.compare("watch") == 0 || key.compare("rwatch") == 0 || key.compare("awatch") == 0) + { + // Support standard GDB remote stop reply packet 'TAAwatch:addr' + lldb::addr_t wp_addr = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_ADDRESS, 16); + WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr); + uint32_t wp_index = LLDB_INVALID_INDEX32; + + if (wp_sp) + wp_index = wp_sp->GetHardwareIndex(); + + reason = "watchpoint"; + StreamString ostr; + ostr.Printf("%" PRIu64 " %" PRIu32, wp_addr, wp_index); + description = ostr.GetString().c_str(); + } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) { uint32_t reg = StringConvert::ToUInt32 (key.c_str(), UINT32_MAX, 16); if (reg != UINT32_MAX) expedited_register_map[reg] = std::move(value); } } + if (tid == LLDB_INVALID_THREAD_ID) + { + // A thread id may be invalid if the response is old style 'S' packet which does not provide the + // thread information. So update the thread list and choose the first one. + UpdateThreadIDList (); + + if (!m_thread_ids.empty ()) + { + tid = m_thread_ids.front (); + } + } + ThreadSP thread_sp = SetThreadStopInfo (tid, expedited_register_map, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid, queue_name, queue_kind, queue_serial); - // If the response is old style 'S' packet which does not provide us with thread information - // then update the thread list and choose the first one. - if (!thread_sp) - { - UpdateThreadIDList (); - - if (!m_thread_ids.empty ()) - { - Mutex::Locker locker (m_thread_list_real.GetMutex ()); - thread_sp = m_thread_list_real.FindThreadByProtocolID (m_thread_ids.front (), false); - } - } - return eStateStopped; } break; case 'W': case 'X': // process exited return eStateExited; default: break; } return eStateInvalid; } void ProcessGDBRemote::RefreshStateAfterStop () { Mutex::Locker locker(m_thread_list_real.GetMutex()); m_thread_ids.clear(); // Set the thread stop info. It might have a "threads" key whose value is // a list of all thread IDs in the current process, so m_thread_ids might // get set. // Scope for the lock { // Lock the thread stack while we access it Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); // Get the number of stop packets on the stack int nItems = m_stop_packet_stack.size(); // Iterate over them for (int i = 0; i < nItems; i++) { // Get the thread stop info StringExtractorGDBRemote stop_info = m_stop_packet_stack[i]; // Process thread stop info SetThreadStopInfo(stop_info); } // Clear the thread stop stack m_stop_packet_stack.clear(); } // Check to see if SetThreadStopInfo() filled in m_thread_ids? if (m_thread_ids.empty()) { // No, we need to fetch the thread list manually UpdateThreadIDList(); } // If we have queried for a default thread id if (m_initial_tid != LLDB_INVALID_THREAD_ID) { m_thread_list.SetSelectedThreadByID(m_initial_tid); m_initial_tid = LLDB_INVALID_THREAD_ID; } // Fetch the threads via an efficient packet that gets stop infos for all threads // only if we have more than one thread if (m_thread_ids.size() > 1) m_threads_info_sp = m_gdb_comm.GetThreadsInfo(); // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list_real.RefreshStateAfterStop(); } Error ProcessGDBRemote::DoHalt (bool &caused_stop) { Error error; bool timed_out = false; Mutex::Locker locker; if (m_public_state.GetValue() == eStateAttaching) { // We are being asked to halt during an attach. We need to just close // our file handle and debugserver will go away, and we can be done... m_gdb_comm.Disconnect(); } else { if (!m_gdb_comm.SendInterrupt (locker, 2, timed_out)) { if (timed_out) error.SetErrorString("timed out sending interrupt packet"); else error.SetErrorString("unknown error sending interrupt packet"); } caused_stop = m_gdb_comm.GetInterruptWasSent (); } return error; } Error ProcessGDBRemote::DoDetach(bool keep_stopped) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); error = m_gdb_comm.Detach (keep_stopped); if (log) { if (error.Success()) log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully"); else log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : ""); } if (!error.Success()) return error; // Sleep for one second to let the process get all detached... StopAsyncThread (); SetPrivateState (eStateDetached); ResumePrivateStateThread(); //KillDebugserverProcess (); return error; } Error ProcessGDBRemote::DoDestroy () { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoDestroy()"); // There is a bug in older iOS debugservers where they don't shut down the process // they are debugging properly. If the process is sitting at a breakpoint or an exception, // this can cause problems with restarting. So we check to see if any of our threads are stopped // at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN // destroy it again. // // Note, we don't have a good way to test the version of debugserver, but I happen to know that // the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of // the debugservers with this bug are equal. There really should be a better way to test this! // // We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and // get called here to destroy again and we're still at a breakpoint or exception, then we should // just do the straight-forward kill. // // And of course, if we weren't able to stop the process by the time we get here, it isn't // necessary (or helpful) to do any of this. if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) { PlatformSP platform_sp = GetTarget().GetPlatform(); // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. if (platform_sp && platform_sp->GetName() && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) { if (m_destroy_tried_resuming) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Tried resuming to destroy once already, not doing it again."); } else { // At present, the plans are discarded and the breakpoints disabled Process::Destroy, // but we really need it to happen here and it doesn't matter if we do it twice. m_thread_list.DiscardThreadPlans(); DisableAllBreakpointSites(); bool stop_looks_like_crash = false; ThreadList &threads = GetThreadList(); { Mutex::Locker locker(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason == eStopReasonBreakpoint || reason == eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", thread_sp->GetProtocolID(), stop_info_sp->GetDescription()); stop_looks_like_crash = true; break; } } } if (stop_looks_like_crash) { if (log) log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); m_destroy_tried_resuming = true; // If we are going to run again before killing, it would be good to suspend all the threads // before resuming so they won't get into more trouble. Sadly, for the threads stopped with // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do // have to run the risk of letting those threads proceed a bit. { Mutex::Locker locker(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; i++) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); StopReason reason = eStopReasonInvalid; if (stop_info_sp) reason = stop_info_sp->GetStopReason(); if (reason != eStopReasonBreakpoint && reason != eStopReasonException) { if (log) log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: 0x%4.4" PRIx64 " before running.", thread_sp->GetProtocolID()); thread_sp->SetResumeState(eStateSuspended); } } } Resume (); return Destroy(false); } } } } // Interrupt if our inferior is running... int exit_status = SIGABRT; std::string exit_string; if (m_gdb_comm.IsConnected()) { if (m_public_state.GetValue() != eStateAttaching) { StringExtractorGDBRemote response; bool send_async = true; GDBRemoteCommunication::ScopedTimeout (m_gdb_comm, 3); if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async) == GDBRemoteCommunication::PacketResult::Success) { char packet_cmd = response.GetChar(0); if (packet_cmd == 'W' || packet_cmd == 'X') { #if defined(__APPLE__) // For Native processes on Mac OS X, we launch through the Host Platform, then hand the process off // to debugserver, which becomes the parent process through "PT_ATTACH". Then when we go to kill // the process on Mac OS X we call ptrace(PT_KILL) to kill it, then we call waitpid which returns // with no error and the correct status. But amusingly enough that doesn't seem to actually reap // the process, but instead it is left around as a Zombie. Probably the kernel is in the process of // switching ownership back to lldb which was the original parent, and gets confused in the handoff. // Anyway, so call waitpid here to finally reap it. PlatformSP platform_sp(GetTarget().GetPlatform()); if (platform_sp && platform_sp->IsHost()) { int status; ::pid_t reap_pid; reap_pid = waitpid (GetID(), &status, WNOHANG); if (log) log->Printf ("Reaped pid: %d, status: %d.\n", reap_pid, status); } #endif SetLastStopPacket (response); ClearThreadIDList (); exit_status = response.GetHexU8(); } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - got unexpected response to k packet: %s", response.GetStringRef().c_str()); exit_string.assign("got unexpected response to k packet: "); exit_string.append(response.GetStringRef()); } } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); exit_string.assign("failed to send the k packet"); } } else { if (log) log->Printf ("ProcessGDBRemote::DoDestroy - killed or interrupted while attaching"); exit_string.assign ("killed or interrupted while attaching."); } } else { // If we missed setting the exit status on the way out, do it here. // NB set exit status can be called multiple times, the first one sets the status. exit_string.assign("destroying when not connected to debugserver"); } SetExitStatus(exit_status, exit_string.c_str()); StopAsyncThread (); KillDebugserverProcess (); return error; } void ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) { const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; if (did_exec) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec"); m_thread_list_real.Clear(); m_thread_list.Clear(); BuildDynamicRegisterInfo (true); m_gdb_comm.ResetDiscoverableSettings (did_exec); } // Scope the lock { // Lock the thread stack while we access it Mutex::Locker stop_stack_lock(m_last_stop_packet_mutex); // Add this stop packet to the stop packet stack // This stack will get popped and examined when we switch to the // Stopped state m_stop_packet_stack.push_back(response); } } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessGDBRemote::IsAlive () { return m_gdb_comm.IsConnected() && m_private_state.GetValue() != eStateExited; } addr_t ProcessGDBRemote::GetImageInfoAddress() { // request the link map address via the $qShlibInfoAddr packet lldb::addr_t addr = m_gdb_comm.GetShlibInfoAddr(); // the loaded module list can also provides a link map address if (addr == LLDB_INVALID_ADDRESS) { GDBLoadedModuleInfoList list; if (GetLoadedModuleList (list).Success()) addr = list.m_link_map; } return addr; } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) { GetMaxMemorySize (); if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } char packet[64]; int packet_len; bool binary_memory_read = m_gdb_comm.GetxPacketSupported(); if (binary_memory_read) { packet_len = ::snprintf (packet, sizeof(packet), "x0x%" PRIx64 ",0x%" PRIx64, (uint64_t)addr, (uint64_t)size); } else { packet_len = ::snprintf (packet, sizeof(packet), "m%" PRIx64 ",%" PRIx64, (uint64_t)addr, (uint64_t)size); } assert (packet_len + 1 < (int)sizeof(packet)); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsNormalResponse()) { error.Clear(); if (binary_memory_read) { // The lower level GDBRemoteCommunication packet receive layer has already de-quoted any // 0x7d character escaping that was present in the packet size_t data_received_size = response.GetBytesLeft(); if (data_received_size > size) { // Don't write past the end of BUF if the remote debug server gave us too // much data for some reason. data_received_size = size; } memcpy (buf, response.GetStringRef().data(), data_received_size); return data_received_size; } else { return response.GetHexBytes(buf, size, '\xdd'); } } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support reading memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } size_t ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) { GetMaxMemorySize (); if (size > m_max_memory_size) { // Keep memory read sizes down to a sane limit. This function will be // called multiple times in order to complete the task by // lldb_private::Process so it is ok to do this. size = m_max_memory_size; } StreamString packet; packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); packet.PutBytesAsRawHex8(buf, size, lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, true) == GDBRemoteCommunication::PacketResult::Success) { if (response.IsOKResponse()) { error.Clear(); return size; } else if (response.IsErrorResponse()) error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); else if (response.IsUnsupportedResponse()) error.SetErrorStringWithFormat("GDB server does not support writing memory"); else error.SetErrorStringWithFormat("unexpected response to GDB server memory write packet '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); } else { error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str()); } return 0; } lldb::addr_t ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) { Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_EXPRESSIONS)); addr_t allocated_addr = LLDB_INVALID_ADDRESS; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: case eLazyBoolYes: allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes) return allocated_addr; case eLazyBoolNo: // Call mmap() to create memory in the inferior.. unsigned prot = 0; if (permissions & lldb::ePermissionsReadable) prot |= eMmapProtRead; if (permissions & lldb::ePermissionsWritable) prot |= eMmapProtWrite; if (permissions & lldb::ePermissionsExecutable) prot |= eMmapProtExec; if (InferiorCallMmap(this, allocated_addr, 0, size, prot, eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) m_addr_to_mmap_size[allocated_addr] = size; else { allocated_addr = LLDB_INVALID_ADDRESS; if (log) log->Printf ("ProcessGDBRemote::%s no direct stub support for memory allocation, and InferiorCallMmap also failed - is stub missing register context save/restore capability?", __FUNCTION__); } break; } if (allocated_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); else error.Clear(); return allocated_addr; } Error ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, MemoryRegionInfo ®ion_info) { Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) { Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); return error; } Error ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) { - Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after)); + Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after, GetTarget().GetArchitecture())); return error; } Error ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) { Error error; LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); switch (supported) { case eLazyBoolCalculate: // We should never be deallocating memory without allocating memory // first so we should never get eLazyBoolCalculate error.SetErrorString ("tried to deallocate memory without ever allocating memory"); break; case eLazyBoolYes: if (!m_gdb_comm.DeallocateMemory (addr)) error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); break; case eLazyBoolNo: // Call munmap() to deallocate memory in the inferior.. { MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); if (pos != m_addr_to_mmap_size.end() && InferiorCallMunmap(this, addr, pos->second)) m_addr_to_mmap_size.erase (pos); else error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); } break; } return error; } //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ size_t ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) { if (m_stdio_communication.IsConnected()) { ConnectionStatus status; m_stdio_communication.Write(src, src_len, status, NULL); } else if (m_stdin_forward) { m_gdb_comm.SendStdinNotification(src, src_len); } return 0; } Error ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) { Error error; assert(bp_site != NULL); // Get logging info Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); user_id_t site_id = bp_site->GetID(); // Get the breakpoint address const addr_t addr = bp_site->GetLoadAddress(); // Log that a breakpoint was requested if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); // Breakpoint already exists and is enabled if (bp_site->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); return error; } // Get the software breakpoint trap opcode size const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode(bp_site); // SupportsGDBStoppointPacket() simply checks a boolean, indicating if this breakpoint type // is supported by the remote stub. These are set to true by default, and later set to false // only after we receive an unimplemented response when sending a breakpoint packet. This means // initially that unless we were specifically instructed to use a hardware breakpoint, LLDB will // attempt to set a software breakpoint. HardwareRequired() also queries a boolean variable which // indicates if the user specifically asked for hardware breakpoints. If true then we will // skip over software breakpoints. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware) && (!bp_site->HardwareRequired())) { // Try to send off a software breakpoint packet ($Z0) if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); return error; } // SendGDBStoppointTypePacket() will return an error if it was unable to set this // breakpoint. We need to differentiate between a error specific to placing this breakpoint // or if we have learned that this breakpoint type is unsupported. To do this, we // must test the support boolean for this breakpoint type to see if it now indicates that // this breakpoint type is unsupported. If they are still supported then we should return // with the error code. If they are now unsupported, then we would like to fall through // and try another form of breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) return error; // We reach here when software breakpoints have been found to be unsupported. For future // calls to set a breakpoint, we will not attempt to set a breakpoint with a type that is // known not to be supported. if (log) log->Printf("Software breakpoints are unsupported"); // So we will fall through and try a hardware breakpoint } // The process of setting a hardware breakpoint is much the same as above. We check the // supported boolean for this breakpoint type, and if it is thought to be supported then we // will try to set this breakpoint with a hardware breakpoint. if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Try to send off a hardware breakpoint packet ($Z1) if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0) { // The breakpoint was placed successfully bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eHardware); return error; } // Check if the error was something other then an unsupported breakpoint type if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) { // Unable to set this hardware breakpoint error.SetErrorString("failed to set hardware breakpoint (hardware breakpoint resources might be exhausted or unavailable)"); return error; } // We will reach here when the stub gives an unsupported response to a hardware breakpoint if (log) log->Printf("Hardware breakpoints are unsupported"); // Finally we will falling through to a #trap style breakpoint } // Don't fall through when hardware breakpoints were specifically requested if (bp_site->HardwareRequired()) { error.SetErrorString("hardware breakpoints are not supported"); return error; } // As a last resort we want to place a manual breakpoint. An instruction // is placed into the process memory using memory write packets. return EnableSoftwareBreakpoint(bp_site); } Error ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) { Error error; assert (bp_site != NULL); addr_t addr = bp_site->GetLoadAddress(); user_id_t site_id = bp_site->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); if (bp_site->IsEnabled()) { const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); BreakpointSite::Type bp_type = bp_site->GetType(); switch (bp_type) { case BreakpointSite::eSoftware: error = DisableSoftwareBreakpoint (bp_site); break; case BreakpointSite::eHardware: if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, false, addr, bp_op_size)) error.SetErrorToGenericError(); break; case BreakpointSite::eExternal: { GDBStoppointType stoppoint_type; if (bp_site->IsHardware()) stoppoint_type = eBreakpointHardware; else stoppoint_type = eBreakpointSoftware; if (m_gdb_comm.SendGDBStoppointTypePacket(stoppoint_type, false, addr, bp_op_size)) error.SetErrorToGenericError(); } break; } if (error.Success()) bp_site->SetEnabled(false); } else { if (log) log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); return error; } if (error.Success()) error.SetErrorToGenericError(); return error; } // Pre-requisite: wp != NULL. static GDBStoppointType GetGDBStoppointType (Watchpoint *wp) { assert(wp); bool watch_read = wp->WatchpointRead(); bool watch_write = wp->WatchpointWrite(); // watch_read and watch_write cannot both be false. assert(watch_read || watch_write); if (watch_read && watch_write) return eWatchpointReadWrite; else if (watch_read) return eWatchpointRead; else // Must be watch_write, then. return eWatchpointWrite; } Error ProcessGDBRemote::EnableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); addr_t addr = wp->GetLoadAddress(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); if (wp->IsEnabled()) { if (log) log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); return error; } GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SupportsGDBStoppointPacket (type)) { if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(true, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } else error.SetErrorString("watchpoints not supported"); } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } Error ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) { Error error; if (wp) { user_id_t watchID = wp->GetID(); Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); addr_t addr = wp->GetLoadAddress(); if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); if (!wp->IsEnabled()) { if (log) log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); // See also 'class WatchpointSentry' within StopInfo.cpp. // This disabling attempt might come from the user-supplied actions, we'll route it in order for // the watchpoint object to intelligently process this action. wp->SetEnabled(false, notify); return error; } if (wp->IsHardware()) { GDBStoppointType type = GetGDBStoppointType(wp); // Pass down an appropriate z/Z packet... if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) { wp->SetEnabled(false, notify); return error; } else error.SetErrorString("sending gdb watchpoint packet failed"); } // TODO: clear software watchpoints if we implement them } else { error.SetErrorString("Watchpoint argument was NULL."); } if (error.Success()) error.SetErrorToGenericError(); return error; } void ProcessGDBRemote::Clear() { m_flags = 0; m_thread_list_real.Clear(); m_thread_list.Clear(); } Error ProcessGDBRemote::DoSignal (int signo) { Error error; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); if (!m_gdb_comm.SendAsyncSignal (signo)) error.SetErrorStringWithFormat("failed to send signal %i", signo); return error; } Error ProcessGDBRemote::LaunchAndConnectToDebugserver (const ProcessInfo &process_info) { Error error; if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) { // If we locate debugserver, keep that located version around static FileSpec g_debugserver_file_spec; ProcessLaunchInfo debugserver_launch_info; // Make debugserver run in its own session so signals generated by // special terminal key sequences (^C) don't affect debugserver. debugserver_launch_info.SetLaunchInSeparateProcessGroup(true); debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); debugserver_launch_info.SetUserID(process_info.GetUserID()); #if defined (__APPLE__) && (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) // On iOS, still do a local connection using a random port const char *hostname = "127.0.0.1"; uint16_t port = get_random_port (); #else // Set hostname being NULL to do the reverse connect where debugserver // will bind to port zero and it will communicate back to us the port // that we will connect to const char *hostname = NULL; uint16_t port = 0; #endif error = m_gdb_comm.StartDebugserverProcess (hostname, port, debugserver_launch_info, port); if (error.Success ()) m_debugserver_pid = debugserver_launch_info.GetProcessID(); else m_debugserver_pid = LLDB_INVALID_PROCESS_ID; if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) StartAsyncThread (); if (error.Fail()) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf("failed to start debugserver process: %s", error.AsCString()); return error; } if (m_gdb_comm.IsConnected()) { // Finish the connection process by doing the handshake without connecting (send NULL URL) ConnectToDebugserver (NULL); } else { StreamString connect_url; connect_url.Printf("connect://%s:%u", hostname, port); error = ConnectToDebugserver (connect_url.GetString().c_str()); } } return error; } bool ProcessGDBRemote::MonitorDebugserverProcess ( void *callback_baton, lldb::pid_t debugserver_pid, bool exited, // True if the process did exit int signo, // Zero for no signal int exit_status // Exit value of process if signal is zero ) { // The baton is a "ProcessGDBRemote *". Now this class might be gone // and might not exist anymore, so we need to carefully try to get the // target for this process first since we have a race condition when // we are done running between getting the notice that the inferior // process has died and the debugserver that was debugging this process. // In our test suite, we are also continually running process after // process, so we must be very careful to make sure: // 1 - process object hasn't been deleted already // 2 - that a new process object hasn't been recreated in its place // "debugserver_pid" argument passed in is the process ID for // debugserver that we are tracking... Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton; // Get a shared pointer to the target that has a matching process pointer. // This target could be gone, or the target could already have a new process // object inside of it TargetSP target_sp (Debugger::FindTargetWithProcess(process)); if (log) log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status); if (target_sp) { // We found a process in a target that matches, but another thread // might be in the process of launching a new process that will // soon replace it, so get a shared pointer to the process so we // can keep it alive. ProcessSP process_sp (target_sp->GetProcessSP()); // Now we have a shared pointer to the process that can't go away on us // so we now make sure it was the same as the one passed in, and also make // sure that our previous "process *" didn't get deleted and have a new // "process *" created in its place with the same pointer. To verify this // we make sure the process has our debugserver process ID. If we pass all // of these tests, then we are sure that this process is the one we were // looking for. if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid) { // Sleep for a half a second to make sure our inferior process has // time to set its exit status before we set it incorrectly when // both the debugserver and the inferior process shut down. usleep (500000); // If our process hasn't yet exited, debugserver might have died. // If the process did exit, the we are reaping it. const StateType state = process->GetState(); if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && state != eStateInvalid && state != eStateUnloaded && state != eStateExited && state != eStateDetached) { char error_str[1024]; if (signo) { const char *signal_cstr = process->GetUnixSignals()->GetSignalAsCString(signo); if (signal_cstr) ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); else ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); } else { ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); } process->SetExitStatus (-1, error_str); } // Debugserver has exited we need to let our ProcessGDBRemote // know that it no longer has a debugserver instance process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } return true; } void ProcessGDBRemote::KillDebugserverProcess () { m_gdb_comm.Disconnect(); if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) { Host::Kill (m_debugserver_pid, SIGINT); m_debugserver_pid = LLDB_INVALID_PROCESS_ID; } } void ProcessGDBRemote::Initialize() { static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); }); } void ProcessGDBRemote::DebuggerInitialize (Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin (debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString ("Properties for the gdb-remote process plug-in."), is_global_setting); } } bool ProcessGDBRemote::StartAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). m_async_thread = ThreadLauncher::LaunchThread("", ProcessGDBRemote::AsyncThread, this, NULL); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was already running.", __FUNCTION__); return m_async_thread.IsJoinable(); } void ProcessGDBRemote::StopAsyncThread () { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); // This will shut down the async thread. m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread m_async_thread.Join(nullptr); m_async_thread.Reset(); } else if (log) log->Printf("ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } bool ProcessGDBRemote::HandleNotifyPacket (StringExtractorGDBRemote &packet) { // get the packet at a string const std::string &pkt = packet.GetStringRef(); // skip %stop: StringExtractorGDBRemote stop_info(pkt.c_str() + 5); // pass as a thread stop info packet SetLastStopPacket(stop_info); // check for more stop reasons HandleStopReplySequence(); // if the process is stopped then we need to fake a resume // so that we can stop properly with the new break. This // is possible due to SetPrivateState() broadcasting the // state change as a side effect. if (GetPrivateState() == lldb::StateType::eStateStopped) { SetPrivateState(lldb::StateType::eStateRunning); } // since we have some stopped packets we can halt the process SetPrivateState(lldb::StateType::eStateStopped); return true; } thread_result_t ProcessGDBRemote::AsyncThread (void *arg) { ProcessGDBRemote *process = (ProcessGDBRemote*) arg; Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); Listener listener ("ProcessGDBRemote::AsyncThread"); EventSP event_sp; const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit | GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify); bool done = false; while (!done) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); if (listener.WaitForEvent (NULL, event_sp)) { const uint32_t event_type = event_sp->GetType(); if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); switch (event_type) { case eBroadcastBitAsyncContinue: { const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); if (continue_packet) { const char *continue_cstr = (const char *)continue_packet->GetBytes (); const size_t continue_cstr_len = continue_packet->GetByteSize (); if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); if (::strstr (continue_cstr, "vAttach") == NULL) process->SetPrivateState(eStateRunning); StringExtractorGDBRemote response; // If in Non-Stop-Mode if (process->GetTarget().GetNonStopModeEnabled()) { // send the vCont packet if (!process->GetGDBRemote().SendvContPacket(process, continue_cstr, continue_cstr_len, response)) { // Something went wrong done = true; break; } } // If in All-Stop-Mode else { StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. // The thread ID list might be contained within the "response", or the stop reply packet that // caused the stop. So clear it now before we give the stop reply packet to the process // using the process->SetLastStopPacket()... process->ClearThreadIDList (); switch (stop_state) { case eStateStopped: case eStateCrashed: case eStateSuspended: process->SetLastStopPacket (response); process->SetPrivateState (stop_state); break; case eStateExited: { process->SetLastStopPacket (response); process->ClearThreadIDList(); response.SetFilePos(1); int exit_status = response.GetHexU8(); const char *desc_cstr = NULL; StringExtractor extractor; std::string desc_string; if (response.GetBytesLeft() > 0 && response.GetChar('-') == ';') { std::string desc_token; while (response.GetNameColonValue (desc_token, desc_string)) { if (desc_token == "description") { extractor.GetStringRef().swap(desc_string); extractor.SetFilePos(0); extractor.GetHexByteString (desc_string); desc_cstr = desc_string.c_str(); } } } process->SetExitStatus(exit_status, desc_cstr); done = true; break; } case eStateInvalid: process->SetExitStatus(-1, "lost connection"); break; default: process->SetPrivateState (stop_state); break; } // switch(stop_state) } // else // if in All-stop-mode } // if (continue_packet) } // case eBroadcastBitAysncContinue break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); done = true; break; default: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } else if (event_sp->BroadcasterIs (&process->m_gdb_comm)) { switch (event_type) { case Communication::eBroadcastBitReadThreadDidExit: process->SetExitStatus (-1, "lost connection"); done = true; break; case GDBRemoteCommunication::eBroadcastBitGdbReadThreadGotNotify: { lldb_private::Event *event = event_sp.get(); const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event); StringExtractorGDBRemote notify((const char*)continue_packet->GetBytes()); // Hand this over to the process to handle process->HandleNotifyPacket(notify); break; } default: if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); done = true; break; } } } else { if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); done = true; } } } if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); return NULL; } //uint32_t //ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList &matches, std::vector &pids) //{ // // If we are planning to launch the debugserver remotely, then we need to fire up a debugserver // // process and ask it for the list of processes. But if we are local, we can let the Host do it. // if (m_local_debugserver) // { // return Host::ListProcessesMatchingName (name, matches, pids); // } // else // { // // FIXME: Implement talking to the remote debugserver. // return 0; // } // //} // bool ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { // I don't think I have to do anything here, just make sure I notice the new thread when it starts to // run so I can stop it if that's what I want to do. Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log) log->Printf("Hit New Thread Notification breakpoint."); return false; } bool ProcessGDBRemote::StartNoticingNewThreads() { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Enabled noticing new thread breakpoint."); m_thread_create_bp_sp->SetEnabled(true); } else { PlatformSP platform_sp (m_target.GetPlatform()); if (platform_sp) { m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(m_target); if (m_thread_create_bp_sp) { if (log && log->GetVerbose()) log->Printf("Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); m_thread_create_bp_sp->SetCallback (ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); } else { if (log) log->Printf("Failed to create new thread notification breakpoint."); } } } return m_thread_create_bp_sp.get() != NULL; } bool ProcessGDBRemote::StopNoticingNewThreads() { Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf ("Disabling new thread notification breakpoint."); if (m_thread_create_bp_sp) m_thread_create_bp_sp->SetEnabled(false); return true; } DynamicLoader * ProcessGDBRemote::GetDynamicLoader () { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); return m_dyld_ap.get(); } Error ProcessGDBRemote::SendEventData(const char *data) { int return_value; bool was_supported; Error error; return_value = m_gdb_comm.SendLaunchEventDataPacket (data, &was_supported); if (return_value != 0) { if (!was_supported) error.SetErrorString("Sending events is not supported for this process."); else error.SetErrorStringWithFormat("Error sending event data: %d.", return_value); } return error; } const DataBufferSP ProcessGDBRemote::GetAuxvData() { DataBufferSP buf; if (m_gdb_comm.GetQXferAuxvReadSupported()) { std::string response_string; if (m_gdb_comm.SendPacketsAndConcatenateResponses("qXfer:auxv:read::", response_string) == GDBRemoteCommunication::PacketResult::Success) buf.reset(new DataBufferHeap(response_string.c_str(), response_string.length())); } return buf; } StructuredData::ObjectSP ProcessGDBRemote::GetExtendedInfoForThread (lldb::tid_t tid) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetThreadExtendedInfoSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); SystemRuntime *runtime = GetSystemRuntime(); if (runtime) { runtime->AddThreadExtendedInfoPacketHints (args_dict); } args_dict->GetAsDictionary()->AddIntegerItem ("thread", tid); StreamString packet; packet << "jThreadExtendedInfo:"; args_dict->Dump (packet); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { object_sp = StructuredData::ParseJSON (response.GetStringRef()); } } } } return object_sp; } StructuredData::ObjectSP ProcessGDBRemote::GetLoadedDynamicLibrariesInfos (lldb::addr_t image_list_address, lldb::addr_t image_count) { StructuredData::ObjectSP object_sp; if (m_gdb_comm.GetLoadedDynamicLibrariesInfosSupported()) { StructuredData::ObjectSP args_dict(new StructuredData::Dictionary()); args_dict->GetAsDictionary()->AddIntegerItem ("image_list_address", image_list_address); args_dict->GetAsDictionary()->AddIntegerItem ("image_count", image_count); StreamString packet; packet << "jGetLoadedDynamicLibrariesInfos:"; args_dict->Dump (packet); // FIXME the final character of a JSON dictionary, '}', is the escape // character in gdb-remote binary mode. lldb currently doesn't escape // these characters in its packet output -- so we add the quoted version // of the } character here manually in case we talk to a debugserver which // un-escapes the characters at packet read time. packet << (char) (0x7d ^ 0x20); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == GDBRemoteCommunication::PacketResult::Success) { StringExtractorGDBRemote::ResponseType response_type = response.GetResponseType(); if (response_type == StringExtractorGDBRemote::eResponse) { if (!response.Empty()) { // The packet has already had the 0x7d xor quoting stripped out at the // GDBRemoteCommunication packet receive level. object_sp = StructuredData::ParseJSON (response.GetStringRef()); } } } } return object_sp; } // Establish the largest memory read/write payloads we should use. // If the remote stub has a max packet size, stay under that size. // // If the remote stub's max packet size is crazy large, use a // reasonable largeish default. // // If the remote stub doesn't advertise a max packet size, use a // conservative default. void ProcessGDBRemote::GetMaxMemorySize() { const uint64_t reasonable_largeish_default = 128 * 1024; const uint64_t conservative_default = 512; if (m_max_memory_size == 0) { uint64_t stub_max_size = m_gdb_comm.GetRemoteMaxPacketSize(); if (stub_max_size != UINT64_MAX && stub_max_size != 0) { // Save the stub's claimed maximum packet size m_remote_stub_max_memory_size = stub_max_size; // Even if the stub says it can support ginormous packets, // don't exceed our reasonable largeish default packet size. if (stub_max_size > reasonable_largeish_default) { stub_max_size = reasonable_largeish_default; } m_max_memory_size = stub_max_size; } else { m_max_memory_size = conservative_default; } } } void ProcessGDBRemote::SetUserSpecifiedMaxMemoryTransferSize (uint64_t user_specified_max) { if (user_specified_max != 0) { GetMaxMemorySize (); if (m_remote_stub_max_memory_size != 0) { if (m_remote_stub_max_memory_size < user_specified_max) { m_max_memory_size = m_remote_stub_max_memory_size; // user specified a packet size too big, go as big // as the remote stub says we can go. } else { m_max_memory_size = user_specified_max; // user's packet size is good } } else { m_max_memory_size = user_specified_max; // user's packet size is probably fine } } } bool ProcessGDBRemote::GetModuleSpec(const FileSpec& module_file_spec, const ArchSpec& arch, ModuleSpec &module_spec) { Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PLATFORM); if (!m_gdb_comm.GetModuleInfo (module_file_spec, arch, module_spec)) { if (log) log->Printf ("ProcessGDBRemote::%s - failed to get module info for %s:%s", __FUNCTION__, module_file_spec.GetPath ().c_str (), arch.GetTriple ().getTriple ().c_str ()); return false; } if (log) { StreamString stream; module_spec.Dump (stream); log->Printf ("ProcessGDBRemote::%s - got module info for (%s:%s) : %s", __FUNCTION__, module_file_spec.GetPath ().c_str (), arch.GetTriple ().getTriple ().c_str (), stream.GetString ().c_str ()); } return true; } namespace { typedef std::vector stringVec; typedef std::vector GDBServerRegisterVec; struct RegisterSetInfo { ConstString name; }; typedef std::map RegisterSetMap; struct GdbServerTargetInfo { std::string arch; std::string osabi; stringVec includes; RegisterSetMap reg_set_map; XMLNode feature_node; }; bool ParseRegisters (XMLNode feature_node, GdbServerTargetInfo &target_info, GDBRemoteDynamicRegisterInfo &dyn_reg_info) { if (!feature_node) return false; uint32_t prev_reg_num = 0; uint32_t reg_offset = 0; feature_node.ForEachChildElementWithName("reg", [&target_info, &dyn_reg_info, &prev_reg_num, ®_offset](const XMLNode ®_node) -> bool { std::string gdb_group; std::string gdb_type; ConstString reg_name; ConstString alt_name; ConstString set_name; std::vector value_regs; std::vector invalidate_regs; bool encoding_set = false; bool format_set = false; RegisterInfo reg_info = { NULL, // Name NULL, // Alt name 0, // byte size reg_offset, // offset eEncodingUint, // encoding eFormatHex, // formate { LLDB_INVALID_REGNUM, // GCC reg num LLDB_INVALID_REGNUM, // DWARF reg num LLDB_INVALID_REGNUM, // generic reg num prev_reg_num, // GDB reg num prev_reg_num // native register number }, NULL, NULL }; reg_node.ForEachAttribute([&target_info, &gdb_group, &gdb_type, ®_name, &alt_name, &set_name, &value_regs, &invalidate_regs, &encoding_set, &format_set, ®_info, &prev_reg_num, ®_offset](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") { reg_name.SetString(value); } else if (name == "bitsize") { reg_info.byte_size = StringConvert::ToUInt32(value.data(), 0, 0) / CHAR_BIT; } else if (name == "type") { gdb_type = value.str(); } else if (name == "group") { gdb_group = value.str(); } else if (name == "regnum") { const uint32_t regnum = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); if (regnum != LLDB_INVALID_REGNUM) { reg_info.kinds[eRegisterKindGDB] = regnum; reg_info.kinds[eRegisterKindLLDB] = regnum; prev_reg_num = regnum; } } else if (name == "offset") { reg_offset = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); } else if (name == "altname") { alt_name.SetString(value); } else if (name == "encoding") { encoding_set = true; reg_info.encoding = Args::StringToEncoding (value.data(), eEncodingUint); } else if (name == "format") { format_set = true; Format format = eFormatInvalid; if (Args::StringToFormat (value.data(), format, NULL).Success()) reg_info.format = format; else if (value == "vector-sint8") reg_info.format = eFormatVectorOfSInt8; else if (value == "vector-uint8") reg_info.format = eFormatVectorOfUInt8; else if (value == "vector-sint16") reg_info.format = eFormatVectorOfSInt16; else if (value == "vector-uint16") reg_info.format = eFormatVectorOfUInt16; else if (value == "vector-sint32") reg_info.format = eFormatVectorOfSInt32; else if (value == "vector-uint32") reg_info.format = eFormatVectorOfUInt32; else if (value == "vector-float32") reg_info.format = eFormatVectorOfFloat32; else if (value == "vector-uint128") reg_info.format = eFormatVectorOfUInt128; } else if (name == "group_id") { const uint32_t set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); RegisterSetMap::const_iterator pos = target_info.reg_set_map.find(set_id); if (pos != target_info.reg_set_map.end()) set_name = pos->second.name; } else if (name == "gcc_regnum") { reg_info.kinds[eRegisterKindGCC] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "dwarf_regnum") { reg_info.kinds[eRegisterKindDWARF] = StringConvert::ToUInt32(value.data(), LLDB_INVALID_REGNUM, 0); } else if (name == "generic") { reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister(value.data()); } else if (name == "value_regnums") { SplitCommaSeparatedRegisterNumberString(value, value_regs, 0); } else if (name == "invalidate_regnums") { SplitCommaSeparatedRegisterNumberString(value, invalidate_regs, 0); } else { printf("unhandled attribute %s = %s\n", name.data(), value.data()); } return true; // Keep iterating through all attributes }); if (!gdb_type.empty() && !(encoding_set || format_set)) { if (gdb_type.find("int") == 0) { reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } else if (gdb_type == "data_ptr" || gdb_type == "code_ptr") { reg_info.format = eFormatAddressInfo; reg_info.encoding = eEncodingUint; } else if (gdb_type == "i387_ext" || gdb_type == "float") { reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } } // Only update the register set name if we didn't get a "reg_set" attribute. // "set_name" will be empty if we didn't have a "reg_set" attribute. if (!set_name && !gdb_group.empty()) set_name.SetCString(gdb_group.c_str()); reg_info.byte_offset = reg_offset; assert (reg_info.byte_size != 0); reg_offset += reg_info.byte_size; if (!value_regs.empty()) { value_regs.push_back(LLDB_INVALID_REGNUM); reg_info.value_regs = value_regs.data(); } if (!invalidate_regs.empty()) { invalidate_regs.push_back(LLDB_INVALID_REGNUM); reg_info.invalidate_regs = invalidate_regs.data(); } ++prev_reg_num; dyn_reg_info.AddRegister(reg_info, reg_name, alt_name, set_name); return true; // Keep iterating through all "reg" elements }); return true; } } // namespace {} // query the target of gdb-remote for extended target information // return: 'true' on success // 'false' on failure bool ProcessGDBRemote::GetGDBServerRegisterInfo () { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return false; // redirect libxml2's error handler since the default prints to stdout GDBRemoteCommunicationClient & comm = m_gdb_comm; // check that we have extended feature read support if ( !comm.GetQXferFeaturesReadSupported( ) ) return false; // request the target xml file std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature(ConstString("features"), ConstString("target.xml"), raw, lldberr)) { return false; } XMLDocument xml_document; if (xml_document.ParseMemory(raw.c_str(), raw.size(), "target.xml")) { GdbServerTargetInfo target_info; XMLNode target_node = xml_document.GetRootElement("target"); if (target_node) { XMLNode feature_node; target_node.ForEachChildElement([&target_info, this, &feature_node](const XMLNode &node) -> bool { llvm::StringRef name = node.GetName(); if (name == "architecture") { node.GetElementText(target_info.arch); } else if (name == "osabi") { node.GetElementText(target_info.osabi); } else if (name == "xi:include" || name == "include") { llvm::StringRef href = node.GetAttributeValue("href"); if (!href.empty()) target_info.includes.push_back(href.str()); } else if (name == "feature") { feature_node = node; } else if (name == "groups") { node.ForEachChildElementWithName("group", [&target_info](const XMLNode &node) -> bool { uint32_t set_id = UINT32_MAX; RegisterSetInfo set_info; node.ForEachAttribute([&set_id, &set_info](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "id") set_id = StringConvert::ToUInt32(value.data(), UINT32_MAX, 0); if (name == "name") set_info.name = ConstString(value); return true; // Keep iterating through all attributes }); if (set_id != UINT32_MAX) target_info.reg_set_map[set_id] = set_info; return true; // Keep iterating through all "group" elements }); } return true; // Keep iterating through all children of the target_node }); if (feature_node) { ParseRegisters(feature_node, target_info, this->m_register_info); } for (const auto &include : target_info.includes) { // request register file std::string xml_data; if (!comm.ReadExtFeature(ConstString("features"), ConstString(include), xml_data, lldberr)) continue; XMLDocument include_xml_document; include_xml_document.ParseMemory(xml_data.data(), xml_data.size(), include.c_str()); XMLNode include_feature_node = include_xml_document.GetRootElement("feature"); if (include_feature_node) { ParseRegisters(include_feature_node, target_info, this->m_register_info); } } this->m_register_info.Finalize(GetTarget().GetArchitecture()); } } return m_register_info.GetNumRegisters() > 0; } Error ProcessGDBRemote::GetLoadedModuleList (GDBLoadedModuleInfoList & list) { // Make sure LLDB has an XML parser it can use first if (!XMLDocument::XMLEnabled()) return Error (0, ErrorType::eErrorTypeGeneric); Log *log = GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS); if (log) log->Printf ("ProcessGDBRemote::%s", __FUNCTION__); GDBRemoteCommunicationClient & comm = m_gdb_comm; // check that we have extended feature read support if (comm.GetQXferLibrariesSVR4ReadSupported ()) { list.clear (); // request the loaded library list std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature (ConstString ("libraries-svr4"), ConstString (""), raw, lldberr)) return Error (0, ErrorType::eErrorTypeGeneric); // parse the xml file in memory if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list-svr4"); if (!root_element) return Error(); // main link map structure llvm::StringRef main_lm = root_element.GetAttributeValue("main-lm"); if (!main_lm.empty()) { list.m_link_map = StringConvert::ToUInt64(main_lm.data(), LLDB_INVALID_ADDRESS, 0); } root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool { GDBLoadedModuleInfoList::LoadedModuleInfo module; library.ForEachAttribute([log, &module](const llvm::StringRef &name, const llvm::StringRef &value) -> bool { if (name == "name") module.set_name (value.str()); else if (name == "lm") { // the address of the link_map struct. module.set_link_map(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_addr") { // the displacement as read from the field 'l_addr' of the link_map struct. module.set_base(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } else if (name == "l_ld") { // the memory address of the libraries PT_DYAMIC section. module.set_dynamic(StringConvert::ToUInt64(value.data(), LLDB_INVALID_ADDRESS, 0)); } return true; // Keep iterating over all properties of "library" }); if (log) { std::string name; lldb::addr_t lm=0, base=0, ld=0; module.get_name (name); module.get_link_map (lm); module.get_base (base); module.get_dynamic (ld); log->Printf ("found (link_map:0x08%" PRIx64 ", base:0x08%" PRIx64 ", ld:0x08%" PRIx64 ", name:'%s')", lm, base, ld, name.c_str()); } list.add (module); return true; // Keep iterating over all "library" elements in the root node }); if (log) log->Printf ("found %" PRId32 " modules in total", (int) list.m_list.size()); } else if (comm.GetQXferLibrariesReadSupported ()) { list.clear (); // request the loaded library list std::string raw; lldb_private::Error lldberr; if (!comm.ReadExtFeature (ConstString ("libraries"), ConstString (""), raw, lldberr)) return Error (0, ErrorType::eErrorTypeGeneric); if (log) log->Printf ("parsing: %s", raw.c_str()); XMLDocument doc; if (!doc.ParseMemory(raw.c_str(), raw.size(), "noname.xml")) return Error (0, ErrorType::eErrorTypeGeneric); XMLNode root_element = doc.GetRootElement("library-list"); if (!root_element) return Error(); root_element.ForEachChildElementWithName("library", [log, &list](const XMLNode &library) -> bool { GDBLoadedModuleInfoList::LoadedModuleInfo module; llvm::StringRef name = library.GetAttributeValue("name"); module.set_name(name.str()); // The base address of a given library will be the address of its // first section. Most remotes send only one section for Windows // targets for example. const XMLNode §ion = library.FindFirstChildElementWithName("section"); llvm::StringRef address = section.GetAttributeValue("address"); module.set_base(StringConvert::ToUInt64(address.data(), LLDB_INVALID_ADDRESS, 0)); if (log) { std::string name; lldb::addr_t base = 0; module.get_name (name); module.get_base (base); log->Printf ("found (base:0x%" PRIx64 ", name:'%s')", base, name.c_str()); } list.add (module); return true; // Keep iterating over all "library" elements in the root node }); if (log) log->Printf ("found %" PRId32 " modules in total", (int) list.m_list.size()); } else { return Error (0, ErrorType::eErrorTypeGeneric); } return Error(); } lldb::ModuleSP ProcessGDBRemote::LoadModuleAtAddress (const FileSpec &file, lldb::addr_t base_addr) { Target &target = m_process->GetTarget(); ModuleList &modules = target.GetImages(); ModuleSP module_sp; bool changed = false; ModuleSpec module_spec (file, target.GetArchitecture()); if ((module_sp = modules.FindFirstModule (module_spec))) { module_sp->SetLoadAddress (target, base_addr, true, changed); } else if ((module_sp = target.GetSharedModule (module_spec))) { module_sp->SetLoadAddress (target, base_addr, true, changed); } return module_sp; } size_t ProcessGDBRemote::LoadModules () { using lldb_private::process_gdb_remote::ProcessGDBRemote; // request a list of loaded libraries from GDBServer GDBLoadedModuleInfoList module_list; if (GetLoadedModuleList (module_list).Fail()) return 0; // get a list of all the modules ModuleList new_modules; for (GDBLoadedModuleInfoList::LoadedModuleInfo & modInfo : module_list.m_list) { std::string mod_name; lldb::addr_t mod_base; bool valid = true; valid &= modInfo.get_name (mod_name); valid &= modInfo.get_base (mod_base); if (!valid) continue; // hack (cleaner way to get file name only?) (win/unix compat?) size_t marker = mod_name.rfind ('/'); if (marker == std::string::npos) marker = 0; else marker += 1; FileSpec file (mod_name.c_str()+marker, true); lldb::ModuleSP module_sp = LoadModuleAtAddress (file, mod_base); if (module_sp.get()) new_modules.Append (module_sp); } if (new_modules.GetSize() > 0) { Target & target = m_target; new_modules.ForEach ([&target](const lldb::ModuleSP module_sp) -> bool { lldb_private::ObjectFile * obj = module_sp->GetObjectFile (); if (!obj) return true; if (obj->GetType () != ObjectFile::Type::eTypeExecutable) return true; lldb::ModuleSP module_copy_sp = module_sp; target.SetExecutableModule (module_copy_sp, false); return false; }); ModuleList &loaded_modules = m_process->GetTarget().GetImages(); loaded_modules.AppendIfNeeded (new_modules); m_process->GetTarget().ModulesDidLoad (new_modules); } return new_modules.GetSize(); } Error ProcessGDBRemote::GetFileLoadAddress(const FileSpec& file, bool& is_loaded, lldb::addr_t& load_addr) { is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; std::string file_path = file.GetPath(false); if (file_path.empty ()) return Error("Empty file name specified"); StreamString packet; packet.PutCString("qFileLoadAddress:"); packet.PutCStringAsRawHex8(file_path.c_str()); StringExtractorGDBRemote response; if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), response, false) != GDBRemoteCommunication::PacketResult::Success) return Error("Sending qFileLoadAddress packet failed"); if (response.IsErrorResponse()) { if (response.GetError() == 1) { // The file is not loaded into the inferior is_loaded = false; load_addr = LLDB_INVALID_ADDRESS; return Error(); } return Error("Fetching file load address from remote server returned an error"); } if (response.IsNormalResponse()) { is_loaded = true; load_addr = response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); return Error(); } return Error("Unknown error happened during sending the load address packet"); } void ProcessGDBRemote::ModulesDidLoad (ModuleList &module_list) { // We must call the lldb_private::Process::ModulesDidLoad () first before we do anything Process::ModulesDidLoad (module_list); // After loading shared libraries, we can ask our remote GDB server if // it needs any symbols. m_gdb_comm.ServeSymbolLookups(this); } class CommandObjectProcessGDBRemoteSpeedTest: public CommandObjectParsed { public: CommandObjectProcessGDBRemoteSpeedTest(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet speed-test", "Tests packet speeds of various sizes to determine the performance characteristics of the GDB remote connection. ", NULL), m_option_group (interpreter), m_num_packets (LLDB_OPT_SET_1, false, "count", 'c', 0, eArgTypeCount, "The number of packets to send of each varying size (default is 1000).", 1000), m_max_send (LLDB_OPT_SET_1, false, "max-send", 's', 0, eArgTypeCount, "The maximum number of bytes to send in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), m_max_recv (LLDB_OPT_SET_1, false, "max-receive", 'r', 0, eArgTypeCount, "The maximum number of bytes to receive in a packet. Sizes increase in powers of 2 while the size is less than or equal to this option value. (default 1024).", 1024), m_json (LLDB_OPT_SET_1, false, "json", 'j', "Print the output as JSON data for easy parsing.", false, true) { m_option_group.Append (&m_num_packets, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_max_send, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_max_recv, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_json, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessGDBRemoteSpeedTest () { } Options * GetOptions () override { return &m_option_group; } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamSP output_stream_sp (m_interpreter.GetDebugger().GetAsyncOutputStream()); result.SetImmediateOutputStream (output_stream_sp); const uint32_t num_packets = (uint32_t)m_num_packets.GetOptionValue().GetCurrentValue(); const uint64_t max_send = m_max_send.GetOptionValue().GetCurrentValue(); const uint64_t max_recv = m_max_recv.GetOptionValue().GetCurrentValue(); const bool json = m_json.GetOptionValue().GetCurrentValue(); if (output_stream_sp) process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, *output_stream_sp); else { process->GetGDBRemote().TestPacketSpeed (num_packets, max_send, max_recv, json, result.GetOutputStream()); } result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus (eReturnStatusFailed); return false; } protected: OptionGroupOptions m_option_group; OptionGroupUInt64 m_num_packets; OptionGroupUInt64 m_max_send; OptionGroupUInt64 m_max_recv; OptionGroupBoolean m_json; }; class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet history", "Dumps the packet history buffer. ", NULL) { } ~CommandObjectProcessGDBRemotePacketHistory () { } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { process->GetGDBRemote().DumpHistory(result.GetOutputStream()); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } else { result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); } result.SetStatus (eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketXferSize : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketXferSize(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet xfer-size", "Maximum size that lldb will try to read/write one one chunk.", NULL) { } ~CommandObjectProcessGDBRemotePacketXferSize () { } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat ("'%s' takes an argument to specify the max amount to be transferred when reading/writing", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const char *packet_size = command.GetArgumentAtIndex(0); errno = 0; uint64_t user_specified_max = strtoul (packet_size, NULL, 10); if (errno == 0 && user_specified_max != 0) { process->SetUserSpecifiedMaxMemoryTransferSize (user_specified_max); result.SetStatus (eReturnStatusSuccessFinishResult); return true; } } result.SetStatus (eReturnStatusFailed); return false; } }; class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed { private: public: CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, "process plugin packet send", "Send a custom packet through the GDB remote protocol and print the answer. " "The packet header and footer will automatically be added to the packet prior to sending and stripped from the result.", NULL) { } ~CommandObjectProcessGDBRemotePacketSend () { } bool DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendErrorWithFormat ("'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { for (size_t i=0; iGetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); std::string &response_str = response.GetStringRef(); if (strstr(packet_cstr, "qGetProfileData") != NULL) { response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); } if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } } return true; } }; class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw { private: public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw (interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol and print the response." "The argument passed to this command will be hex encoded into a valid 'qRcmd' packet, sent and the response will be printed.", NULL) { } ~CommandObjectProcessGDBRemotePacketMonitor () { } bool DoExecute (const char *command, CommandReturnObject &result) override { if (command == NULL || command[0] == '\0') { result.AppendErrorWithFormat ("'%s' takes a command string argument", m_cmd_name.c_str()); result.SetStatus (eReturnStatusFailed); return false; } ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { StreamString packet; packet.PutCString("qRcmd,"); packet.PutBytesAsRawHex8(command, strlen(command)); const char *packet_cstr = packet.GetString().c_str(); bool send_async = true; StringExtractorGDBRemote response; process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); result.SetStatus (eReturnStatusSuccessFinishResult); Stream &output_strm = result.GetOutputStream(); output_strm.Printf (" packet: %s\n", packet_cstr); const std::string &response_str = response.GetStringRef(); if (response_str.empty()) output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); else output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); } return true; } }; class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword { private: public: CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin packet", "Commands that deal with GDB remote packets.", NULL) { LoadSubCommand ("history", CommandObjectSP (new CommandObjectProcessGDBRemotePacketHistory (interpreter))); LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter))); LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter))); LoadSubCommand ("xfer-size", CommandObjectSP (new CommandObjectProcessGDBRemotePacketXferSize (interpreter))); LoadSubCommand ("speed-test", CommandObjectSP (new CommandObjectProcessGDBRemoteSpeedTest (interpreter))); } ~CommandObjectProcessGDBRemotePacket () { } }; class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword { public: CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "process plugin", "A set of commands for operating on a ProcessGDBRemote process.", "process plugin []") { LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter))); } ~CommandObjectMultiwordProcessGDBRemote () { } }; CommandObject * ProcessGDBRemote::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); } Index: vendor/lldb/dist/source/Target/StopInfo.cpp =================================================================== --- vendor/lldb/dist/source/Target/StopInfo.cpp (revision 287513) +++ vendor/lldb/dist/source/Target/StopInfo.cpp (revision 287514) @@ -1,1216 +1,1233 @@ //===-- StopInfo.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/StopInfo.h" // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "lldb/Core/Log.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/ClangUserExpression.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/Process.h" #include "lldb/Target/UnixSignals.h" using namespace lldb; using namespace lldb_private; StopInfo::StopInfo (Thread &thread, uint64_t value) : m_thread_wp (thread.shared_from_this()), m_stop_id (thread.GetProcess()->GetStopID()), m_resume_id (thread.GetProcess()->GetResumeID()), m_value (value), m_description (), m_override_should_notify (eLazyBoolCalculate), m_override_should_stop (eLazyBoolCalculate), m_extended_info() { } bool StopInfo::IsValid () const { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetStopID() == m_stop_id; return false; } void StopInfo::MakeStopInfoValid () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { m_stop_id = thread_sp->GetProcess()->GetStopID(); m_resume_id = thread_sp->GetProcess()->GetResumeID(); } } bool StopInfo::HasTargetRunSinceMe () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState(); if (ret_type == eStateRunning) { return true; } else if (ret_type == eStateStopped) { // This is a little tricky. We want to count "run and stopped again before you could // ask this question as a "TRUE" answer to HasTargetRunSinceMe. But we don't want to // include any running of the target done for expressions. So we track both resumes, // and resumes caused by expressions, and check if there are any resumes NOT caused // by expressions. uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID(); uint32_t last_user_expression_id = thread_sp->GetProcess()->GetLastUserExpressionResumeID (); if (curr_resume_id == m_resume_id) { return false; } else if (curr_resume_id > last_user_expression_id) { return true; } } } return false; } //---------------------------------------------------------------------- // StopInfoBreakpoint //---------------------------------------------------------------------- namespace lldb_private { class StopInfoBreakpoint : public StopInfo { public: StopInfoBreakpoint (Thread &thread, break_id_t break_id) : StopInfo (thread, break_id), m_should_stop (false), m_should_stop_is_valid (false), m_should_perform_action (true), m_address (LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), m_was_one_shot (false) { StoreBPInfo(); } StopInfoBreakpoint (Thread &thread, break_id_t break_id, bool should_stop) : StopInfo (thread, break_id), m_should_stop (should_stop), m_should_stop_is_valid (true), m_should_perform_action (true), m_address (LLDB_INVALID_ADDRESS), m_break_id(LLDB_INVALID_BREAK_ID), m_was_one_shot (false) { StoreBPInfo(); } void StoreBPInfo () { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { if (bp_site_sp->GetNumberOfOwners() == 1) { BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0); if (bp_loc_sp) { m_break_id = bp_loc_sp->GetBreakpoint().GetID(); m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot(); } } m_address = bp_site_sp->GetLoadAddress(); } } } virtual ~StopInfoBreakpoint () { } virtual bool IsValidForOperatingSystemThread (Thread &thread) { ProcessSP process_sp (thread.GetProcess()); if (process_sp) { BreakpointSiteSP bp_site_sp (process_sp->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) return bp_site_sp->ValidForThisThread (&thread); } return false; } virtual StopReason GetStopReason () const { return eStopReasonBreakpoint; } virtual bool ShouldStopSynchronous (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { if (!m_should_stop_is_valid) { // Only check once if we should stop at a breakpoint BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); bp_site_sp->BumpHitCounts(); m_should_stop = bp_site_sp->ShouldStop (&context); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value); m_should_stop = true; } m_should_stop_is_valid = true; } return m_should_stop; } return false; } virtual bool DoShouldNotify (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { bool all_internal = true; for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++) { if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) { all_internal = false; break; } } return all_internal == false; } } return true; } virtual const char * GetDescription () { if (m_description.empty()) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); if (bp_site_sp) { StreamString strm; // If we have just hit an internal breakpoint, and it has a kind description, print that instead of the // full breakpoint printing: if (bp_site_sp->IsInternal()) { size_t num_owners = bp_site_sp->GetNumberOfOwners(); for (size_t idx = 0; idx < num_owners; idx++) { const char *kind = bp_site_sp->GetOwnerAtIndex(idx)->GetBreakpoint().GetBreakpointKind(); if (kind != NULL) { m_description.assign (kind); return kind; } } } strm.Printf("breakpoint "); bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief); m_description.swap (strm.GetString()); } else { StreamString strm; if (m_break_id != LLDB_INVALID_BREAK_ID) { BreakpointSP break_sp = thread_sp->GetProcess()->GetTarget().GetBreakpointByID(m_break_id); if (break_sp) { if (break_sp->IsInternal()) { const char *kind = break_sp->GetBreakpointKind(); if (kind) strm.Printf ("internal %s breakpoint(%d).", kind, m_break_id); else strm.Printf ("internal breakpoint(%d).", m_break_id); } else { strm.Printf ("breakpoint %d.", m_break_id); } } else { if (m_was_one_shot) strm.Printf ("one-shot breakpoint %d", m_break_id); else strm.Printf ("breakpoint %d which has been deleted.", m_break_id); } } else if (m_address == LLDB_INVALID_ADDRESS) strm.Printf("breakpoint site %" PRIi64 " which has been deleted - unknown address", m_value); else strm.Printf("breakpoint site %" PRIi64 " which has been deleted - was at 0x%" PRIx64, m_value, m_address); m_description.swap (strm.GetString()); } } } return m_description.c_str(); } protected: bool ShouldStop (Event *event_ptr) { // This just reports the work done by PerformAction or the synchronous stop. It should // only ever get called after they have had a chance to run. assert (m_should_stop_is_valid); return m_should_stop; } virtual void PerformAction (Event *event_ptr) { if (!m_should_perform_action) return; m_should_perform_action = false; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { Log *log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS | LIBLLDB_LOG_STEP); if (!thread_sp->IsValid()) { // This shouldn't ever happen, but just in case, don't do more harm. if (log) { log->Printf ("PerformAction got called with an invalid thread."); } m_should_stop = true; m_should_stop_is_valid = true; return; } BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value)); std::unordered_set precondition_breakpoints; if (bp_site_sp) { size_t num_owners = bp_site_sp->GetNumberOfOwners(); if (num_owners == 0) { m_should_stop = true; } else { // We go through each location, and test first its precondition - this overrides everything. Note, // we only do this once per breakpoint - not once per location... // Then check the condition. If the condition says to stop, // then we run the callback for that location. If that callback says to stop as well, then // we set m_should_stop to true; we are going to stop. // But we still want to give all the breakpoints whose conditions say we are going to stop a // chance to run their callbacks. // Of course if any callback restarts the target by putting "continue" in the callback, then // we're going to restart, without running the rest of the callbacks. And in this case we will // end up not stopping even if another location said we should stop. But that's better than not // running all the callbacks. m_should_stop = false; ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); Process *process = exe_ctx.GetProcessPtr(); if (process->GetModIDRef().IsLastResumeForUserExpression()) { // If we are in the middle of evaluating an expression, don't run asynchronous breakpoint commands or // expressions. That could lead to infinite recursion if the command or condition re-calls the function // with this breakpoint. // TODO: We can keep a list of the breakpoints we've seen while running expressions in the nested // PerformAction calls that can arise when the action runs a function that hits another breakpoint, // and only stop running commands when we see the same breakpoint hit a second time. m_should_stop_is_valid = true; if (log) log->Printf ("StopInfoBreakpoint::PerformAction - Hit a breakpoint while running an expression," " not running commands to avoid recursion."); bool ignoring_breakpoints = process->GetIgnoreBreakpointsInExpressions(); if (ignoring_breakpoints) { m_should_stop = false; // Internal breakpoints will always stop. for (size_t j = 0; j < num_owners; j++) { lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); if (bp_loc_sp->GetBreakpoint().IsInternal()) { m_should_stop = true; break; } } } else { m_should_stop = true; } if (log) log->Printf ("StopInfoBreakpoint::PerformAction - in expression, continuing: %s.", m_should_stop ? "true" : "false"); process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf("Warning: hit breakpoint while " "running function, skipping commands and conditions to prevent recursion."); return; } StoppointCallbackContext context (event_ptr, exe_ctx, false); // Let's copy the breakpoint locations out of the site and store them in a local list. That way if // one of the breakpoint actions changes the site, then we won't be operating on a bad list. // For safety's sake let's also grab an extra reference to the breakpoint owners of the locations we're // going to examine, since the locations are going to have to get back to their breakpoints, and the // locations don't keep their owners alive. I'm just sticking the BreakpointSP's in a vector since // I'm only really using it to locally increment their retain counts. BreakpointLocationCollection site_locations; std::vector location_owners; for (size_t j = 0; j < num_owners; j++) { BreakpointLocationSP loc(bp_site_sp->GetOwnerAtIndex(j)); site_locations.Add(loc); location_owners.push_back(loc->GetBreakpoint().shared_from_this()); } for (size_t j = 0; j < num_owners; j++) { lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j); // If another action disabled this breakpoint or its location, then don't run the actions. if (!bp_loc_sp->IsEnabled() || !bp_loc_sp->GetBreakpoint().IsEnabled()) continue; // The breakpoint site may have many locations associated with it, not all of them valid for // this thread. Skip the ones that aren't: if (!bp_loc_sp->ValidForThisThread(thread_sp.get())) { if (log) { StreamString s; bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief); log->Printf ("Breakpoint %s hit on thread 0x%llx but it was not for this thread, continuing.", s.GetData(), static_cast(thread_sp->GetID())); } continue; } // First run the precondition, but since the precondition is per breakpoint, only run it once // per breakpoint. std::pair::iterator, bool> result = precondition_breakpoints.insert(bp_loc_sp->GetBreakpoint().GetID()); if (!result.second) continue; bool precondition_result = bp_loc_sp->GetBreakpoint().EvaluatePrecondition(context); if (!precondition_result) continue; // Next run the condition for the breakpoint. If that says we should stop, then we'll run // the callback for the breakpoint. If the callback says we shouldn't stop that will win. if (bp_loc_sp->GetConditionText() != NULL) { Error condition_error; bool condition_says_stop = bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error); if (!condition_error.Success()) { Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP error_sp = debugger.GetAsyncErrorStream (); error_sp->Printf ("Stopped due to an error evaluating condition of breakpoint "); bp_loc_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief); error_sp->Printf (": \"%s\"", bp_loc_sp->GetConditionText()); error_sp->EOL(); const char *err_str = condition_error.AsCString(""); if (log) log->Printf("Error evaluating condition: \"%s\"\n", err_str); error_sp->PutCString (err_str); error_sp->EOL(); error_sp->Flush(); } else { if (log) { StreamString s; bp_loc_sp->GetDescription(&s, eDescriptionLevelBrief); log->Printf ("Condition evaluated for breakpoint %s on thread 0x%llx conditon_says_stop: %i.", s.GetData(), static_cast(thread_sp->GetID()), condition_says_stop); } if (!condition_says_stop) { // We don't want to increment the hit count of breakpoints if the condition fails. // We've already bumped it by the time we get here, so undo the bump: bp_loc_sp->UndoBumpHitCount(); continue; } } } bool callback_says_stop; // FIXME: For now the callbacks have to run in async mode - the first time we restart we need // to get out of there. So set it here. // When we figure out how to nest breakpoint hits then this will change. Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger(); bool old_async = debugger.GetAsyncExecution(); debugger.SetAsyncExecution (true); callback_says_stop = bp_loc_sp->InvokeCallback (&context); debugger.SetAsyncExecution (old_async); if (callback_says_stop) m_should_stop = true; // If we are going to stop for this breakpoint, then remove the breakpoint. if (callback_says_stop && bp_loc_sp && bp_loc_sp->GetBreakpoint().IsOneShot()) { thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID (bp_loc_sp->GetBreakpoint().GetID()); } // Also make sure that the callback hasn't continued the target. // If it did, when we'll set m_should_start to false and get out of here. if (HasTargetRunSinceMe ()) { m_should_stop = false; break; } } } // We've figured out what this stop wants to do, so mark it as valid so we don't compute it again. m_should_stop_is_valid = true; } else { m_should_stop = true; m_should_stop_is_valid = true; Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log_process) log_process->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value); } if (log) log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop); } } private: bool m_should_stop; bool m_should_stop_is_valid; bool m_should_perform_action; // Since we are trying to preserve the "state" of the system even if we run functions // etc. behind the users backs, we need to make sure we only REALLY perform the action once. lldb::addr_t m_address; // We use this to capture the breakpoint site address when we create the StopInfo, // in case somebody deletes it between the time the StopInfo is made and the // description is asked for. lldb::break_id_t m_break_id; bool m_was_one_shot; }; //---------------------------------------------------------------------- // StopInfoWatchpoint //---------------------------------------------------------------------- class StopInfoWatchpoint : public StopInfo { public: // Make sure watchpoint is properly disabled and subsequently enabled while performing watchpoint actions. class WatchpointSentry { public: WatchpointSentry(Process *p, Watchpoint *w): process(p), watchpoint(w) { if (process && watchpoint) { const bool notify = false; watchpoint->TurnOnEphemeralMode(); process->DisableWatchpoint(watchpoint, notify); } } ~WatchpointSentry() { if (process && watchpoint) { if (!watchpoint->IsDisabledDuringEphemeralMode()) { const bool notify = false; process->EnableWatchpoint(watchpoint, notify); } watchpoint->TurnOffEphemeralMode(); } } private: Process *process; Watchpoint *watchpoint; }; - StopInfoWatchpoint (Thread &thread, break_id_t watch_id) : + StopInfoWatchpoint (Thread &thread, break_id_t watch_id, lldb::addr_t watch_hit_addr) : StopInfo(thread, watch_id), m_should_stop(false), - m_should_stop_is_valid(false) + m_should_stop_is_valid(false), + m_watch_hit_addr(watch_hit_addr) { } virtual ~StopInfoWatchpoint () { } virtual StopReason GetStopReason () const { return eStopReasonWatchpoint; } virtual const char * GetDescription () { if (m_description.empty()) { StreamString strm; strm.Printf("watchpoint %" PRIi64, m_value); m_description.swap (strm.GetString()); } return m_description.c_str(); } protected: virtual bool ShouldStopSynchronous (Event *event_ptr) { // ShouldStop() method is idempotent and should not affect hit count. // See Process::RunPrivateStateThread()->Process()->HandlePrivateEvent() // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()-> // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()-> // StopInfoWatchpoint::ShouldStop() and // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()-> // StopInfoWatchpoint::PerformAction(). if (m_should_stop_is_valid) return m_should_stop; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue())); if (wp_sp) { // Check if we should stop at a watchpoint. ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); m_should_stop = wp_sp->ShouldStop (&context); } else { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log) log->Printf ("Process::%s could not find watchpoint location id: %" PRId64 "...", __FUNCTION__, GetValue()); m_should_stop = true; } } m_should_stop_is_valid = true; return m_should_stop; } bool ShouldStop (Event *event_ptr) { // This just reports the work done by PerformAction or the synchronous stop. It should // only ever get called after they have had a chance to run. assert (m_should_stop_is_valid); return m_should_stop; } virtual void PerformAction (Event *event_ptr) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS); // We're going to calculate if we should stop or not in some way during the course of // this code. Also by default we're going to stop, so set that here. m_should_stop = true; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue())); if (wp_sp) { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); Process* process = exe_ctx.GetProcessPtr(); // This sentry object makes sure the current watchpoint is disabled while performing watchpoint actions, // and it is then enabled after we are finished. WatchpointSentry sentry(process, wp_sp.get()); { // check if this process is running on an architecture where watchpoints trigger // before the associated instruction runs. if so, disable the WP, single-step and then // re-enable the watchpoint if (process) { uint32_t num; bool wp_triggers_after; if (process->GetWatchpointSupportInfo(num, wp_triggers_after).Success()) { if (!wp_triggers_after) { StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo(); assert (stored_stop_info_sp.get() == this); ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over false, // abort_other_plans true)); // stop_other_threads new_plan_sp->SetIsMasterPlan (true); new_plan_sp->SetOkayToDiscard (false); new_plan_sp->SetPrivate (true); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); process->ResumeSynchronous(NULL); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); thread_sp->SetStopInfo(stored_stop_info_sp); } } } } + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle this case, + * server emulates the instruction at PC and finds the base address of the load/store + * instruction and appends it in the description of the stop-info packet. If watchpoint + * is not set on this address by user then this do not stop. + */ + if (m_watch_hit_addr != LLDB_INVALID_ADDRESS) + { + WatchpointSP wp_hit_sp = thread_sp->CalculateTarget()->GetWatchpointList().FindByAddress(m_watch_hit_addr); + if (!wp_hit_sp) + m_should_stop = false; + } + if (m_should_stop && wp_sp->GetConditionText() != NULL) { // We need to make sure the user sees any parse errors in their condition, so we'll hook the // constructor errors up to the debugger's Async I/O. ExpressionResults result_code; EvaluateExpressionOptions expr_options; expr_options.SetUnwindOnError(true); expr_options.SetIgnoreBreakpoints(true); ValueObjectSP result_value_sp; Error error; result_code = ClangUserExpression::Evaluate (exe_ctx, expr_options, wp_sp->GetConditionText(), NULL, result_value_sp, error); if (result_code == eExpressionCompleted) { if (result_value_sp) { Scalar scalar_value; if (result_value_sp->ResolveValue (scalar_value)) { if (scalar_value.ULongLong(1) == 0) { // We have been vetoed. This takes precedence over querying // the watchpoint whether it should stop (aka ignore count and // friends). See also StopInfoWatchpoint::ShouldStop() as well // as Process::ProcessEventData::DoOnRemoval(). m_should_stop = false; } else m_should_stop = true; if (log) log->Printf("Condition successfully evaluated, result is %s.\n", m_should_stop ? "true" : "false"); } else { m_should_stop = true; if (log) log->Printf("Failed to get an integer result from the expression."); } } } else { Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP error_sp = debugger.GetAsyncErrorStream (); error_sp->Printf ("Stopped due to an error evaluating condition of watchpoint "); wp_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief); error_sp->Printf (": \"%s\"", wp_sp->GetConditionText()); error_sp->EOL(); const char *err_str = error.AsCString(""); if (log) log->Printf("Error evaluating condition: \"%s\"\n", err_str); error_sp->PutCString (err_str); error_sp->EOL(); error_sp->Flush(); // If the condition fails to be parsed or run, we should stop. m_should_stop = true; } } // If the condition says to stop, we run the callback to further decide whether to stop. if (m_should_stop) { StoppointCallbackContext context (event_ptr, exe_ctx, false); bool stop_requested = wp_sp->InvokeCallback (&context); // Also make sure that the callback hasn't continued the target. // If it did, when we'll set m_should_stop to false and get out of here. if (HasTargetRunSinceMe ()) m_should_stop = false; if (m_should_stop && !stop_requested) { // We have been vetoed by the callback mechanism. m_should_stop = false; } } // Finally, if we are going to stop, print out the new & old values: if (m_should_stop) { wp_sp->CaptureWatchedValue(exe_ctx); Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger(); StreamSP output_sp = debugger.GetAsyncOutputStream (); wp_sp->DumpSnapshots(output_sp.get()); output_sp->EOL(); output_sp->Flush(); } } else { Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); if (log_process) log_process->Printf ("Process::%s could not find watchpoint id: %" PRId64 "...", __FUNCTION__, m_value); } if (log) log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop); m_should_stop_is_valid = true; } } private: bool m_should_stop; bool m_should_stop_is_valid; + lldb::addr_t m_watch_hit_addr; }; //---------------------------------------------------------------------- // StopInfoUnixSignal //---------------------------------------------------------------------- class StopInfoUnixSignal : public StopInfo { public: StopInfoUnixSignal (Thread &thread, int signo, const char *description) : StopInfo (thread, signo) { SetDescription (description); } virtual ~StopInfoUnixSignal () { } virtual StopReason GetStopReason () const { return eStopReasonSignal; } virtual bool ShouldStopSynchronous (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value); return false; } virtual bool ShouldStop (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) return thread_sp->GetProcess()->GetUnixSignals()->GetShouldStop(m_value); return false; } // If should stop returns false, check if we should notify of this event virtual bool DoShouldNotify (Event *event_ptr) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { bool should_notify = thread_sp->GetProcess()->GetUnixSignals()->GetShouldNotify(m_value); if (should_notify) { StreamString strm; strm.Printf ("thread %d received signal: %s", thread_sp->GetIndexID(), thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(m_value)); Process::ProcessEventData::AddRestartedReason(event_ptr, strm.GetData()); } return should_notify; } return true; } virtual void WillResume (lldb::StateType resume_state) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { if (thread_sp->GetProcess()->GetUnixSignals()->GetShouldSuppress(m_value) == false) thread_sp->SetResumeSignal(m_value); } } virtual const char * GetDescription () { if (m_description.empty()) { ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) { StreamString strm; const char *signal_name = thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString(m_value); if (signal_name) strm.Printf("signal %s", signal_name); else strm.Printf("signal %" PRIi64, m_value); m_description.swap (strm.GetString()); } } return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoTrace //---------------------------------------------------------------------- class StopInfoTrace : public StopInfo { public: StopInfoTrace (Thread &thread) : StopInfo (thread, LLDB_INVALID_UID) { } virtual ~StopInfoTrace () { } virtual StopReason GetStopReason () const { return eStopReasonTrace; } virtual const char * GetDescription () { if (m_description.empty()) return "trace"; else return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoException //---------------------------------------------------------------------- class StopInfoException : public StopInfo { public: StopInfoException (Thread &thread, const char *description) : StopInfo (thread, LLDB_INVALID_UID) { if (description) SetDescription (description); } virtual ~StopInfoException () { } virtual StopReason GetStopReason () const { return eStopReasonException; } virtual const char * GetDescription () { if (m_description.empty()) return "exception"; else return m_description.c_str(); } }; //---------------------------------------------------------------------- // StopInfoThreadPlan //---------------------------------------------------------------------- class StopInfoThreadPlan : public StopInfo { public: StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp, ClangExpressionVariableSP &expression_variable_sp) : StopInfo (plan_sp->GetThread(), LLDB_INVALID_UID), m_plan_sp (plan_sp), m_return_valobj_sp (return_valobj_sp), m_expression_variable_sp (expression_variable_sp) { } virtual ~StopInfoThreadPlan () { } virtual StopReason GetStopReason () const { return eStopReasonPlanComplete; } virtual const char * GetDescription () { if (m_description.empty()) { StreamString strm; m_plan_sp->GetDescription (&strm, eDescriptionLevelBrief); m_description.swap (strm.GetString()); } return m_description.c_str(); } ValueObjectSP GetReturnValueObject() { return m_return_valobj_sp; } ClangExpressionVariableSP GetExpressionVariable() { return m_expression_variable_sp; } protected: virtual bool ShouldStop (Event *event_ptr) { if (m_plan_sp) return m_plan_sp->ShouldStop(event_ptr); else return StopInfo::ShouldStop(event_ptr); } private: ThreadPlanSP m_plan_sp; ValueObjectSP m_return_valobj_sp; ClangExpressionVariableSP m_expression_variable_sp; }; class StopInfoExec : public StopInfo { public: StopInfoExec (Thread &thread) : StopInfo (thread, LLDB_INVALID_UID), m_performed_action (false) { } virtual ~StopInfoExec () { } virtual StopReason GetStopReason () const { return eStopReasonExec; } virtual const char * GetDescription () { return "exec"; } protected: virtual void PerformAction (Event *event_ptr) { // Only perform the action once if (m_performed_action) return; m_performed_action = true; ThreadSP thread_sp (m_thread_wp.lock()); if (thread_sp) thread_sp->GetProcess()->DidExec(); } bool m_performed_action; }; } // namespace lldb_private StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id) { return StopInfoSP (new StopInfoBreakpoint (thread, break_id)); } StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id, bool should_stop) { return StopInfoSP (new StopInfoBreakpoint (thread, break_id, should_stop)); } StopInfoSP -StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id) +StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id, lldb::addr_t watch_hit_addr) { - return StopInfoSP (new StopInfoWatchpoint (thread, watch_id)); + return StopInfoSP (new StopInfoWatchpoint (thread, watch_id, watch_hit_addr)); } StopInfoSP StopInfo::CreateStopReasonWithSignal (Thread &thread, int signo, const char *description) { return StopInfoSP (new StopInfoUnixSignal (thread, signo, description)); } StopInfoSP StopInfo::CreateStopReasonToTrace (Thread &thread) { return StopInfoSP (new StopInfoTrace (thread)); } StopInfoSP StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp, ClangExpressionVariableSP expression_variable_sp) { return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp, expression_variable_sp)); } StopInfoSP StopInfo::CreateStopReasonWithException (Thread &thread, const char *description) { return StopInfoSP (new StopInfoException (thread, description)); } StopInfoSP StopInfo::CreateStopReasonWithExec (Thread &thread) { return StopInfoSP (new StopInfoExec (thread)); } ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) { if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { StopInfoThreadPlan *plan_stop_info = static_cast(stop_info_sp.get()); return plan_stop_info->GetReturnValueObject(); } else return ValueObjectSP(); } ClangExpressionVariableSP StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp) { if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { StopInfoThreadPlan *plan_stop_info = static_cast(stop_info_sp.get()); return plan_stop_info->GetExpressionVariable(); } else return ClangExpressionVariableSP(); } Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdData.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdData.cpp (revision 287513) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdData.cpp (revision 287514) @@ -1,1829 +1,1829 @@ //===-- MICmdCmdData.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdDataEvaluateExpression implementation. // CMICmdCmdDataDisassemble implementation. // CMICmdCmdDataReadMemoryBytes implementation. // CMICmdCmdDataReadMemory implementation. // CMICmdCmdDataListRegisterNames implementation. // CMICmdCmdDataListRegisterValues implementation. // CMICmdCmdDataListRegisterChanged implementation. // CMICmdCmdDataWriteMemoryBytes implementation. // CMICmdCmdDataWriteMemory implementation. // CMICmdCmdDataInfoLine implementation. // Third Party Headers: #include // For PRIx64 #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBStream.h" // In-house headers: #include "MICmdCmdData.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" #include "MICmnLLDBDebugger.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBProxySBValue.h" #include "MICmdArgValNumber.h" #include "MICmdArgValString.h" #include "MICmdArgValThreadGrp.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValOptionShort.h" #include "MICmdArgValListOfN.h" #include "MICmdArgValConsume.h" #include "MICmnLLDBDebugSessionInfoVarObj.h" #include "MICmnLLDBUtilSBValue.h" //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataEvaluateExpression constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataEvaluateExpression::CMICmdCmdDataEvaluateExpression(void) : m_bExpressionValid(true) , m_bEvaluatedExpression(true) , m_strValue("??") , m_bCompositeVarType(false) , m_bFoundInvalidChar(false) , m_cExpressionInvalidChar(0x00) , m_constStrArgThread("thread") , m_constStrArgFrame("frame") , m_constStrArgExpr("expr") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-evaluate-expression"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataEvaluateExpression::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataEvaluateExpression destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataEvaluateExpression::~CMICmdCmdDataEvaluateExpression(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataEvaluateExpression::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, false, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgFrame, false, false, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgExpr, true, true, true, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataEvaluateExpression::Execute(void) { CMICMDBASE_GETOPTION(pArgExpr, String, m_constStrArgExpr); const CMIUtilString &rExpression(pArgExpr->GetValue()); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); lldb::SBThread thread = sbProcess.GetSelectedThread(); m_bExpressionValid = (thread.GetNumFrames() > 0); if (!m_bExpressionValid) return MIstatus::success; lldb::SBFrame frame = thread.GetSelectedFrame(); lldb::SBValue value = frame.EvaluateExpression(rExpression.c_str()); if (!value.IsValid() || value.GetError().Fail()) value = frame.FindVariable(rExpression.c_str()); const CMICmnLLDBUtilSBValue utilValue(value, true); if (!utilValue.IsValid() || utilValue.IsValueUnknown()) { m_bEvaluatedExpression = false; return MIstatus::success; } if (!utilValue.HasName()) { if (HaveInvalidCharacterInExpression(rExpression, m_cExpressionInvalidChar)) { m_bFoundInvalidChar = true; return MIstatus::success; } m_strValue = rExpression; return MIstatus::success; } if (rExpression.IsQuoted()) { m_strValue = rExpression.Trim('\"'); return MIstatus::success; } MIuint64 nNumber = 0; if (CMICmnLLDBProxySBValue::GetValueAsUnsigned(value, nNumber) == MIstatus::success) { const lldb::ValueType eValueType = value.GetValueType(); MIunused(eValueType); m_strValue = utilValue.GetValue().Escape().AddSlashes(); return MIstatus::success; } // Composite type i.e. struct m_bCompositeVarType = true; const MIuint nChild = value.GetNumChildren(); for (MIuint i = 0; i < nChild; i++) { lldb::SBValue member = value.GetChildAtIndex(i); const bool bValid = member.IsValid(); CMIUtilString strType(MIRSRC(IDS_WORD_UNKNOWNTYPE_BRKTS)); if (bValid) { const CMIUtilString strValue( CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted(member, CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Natural)); const char *pTypeName = member.GetName(); if (pTypeName != nullptr) strType = pTypeName; // MI print "{variable = 1, variable2 = 3, variable3 = 5}" const bool bNoQuotes = true; const CMICmnMIValueConst miValueConst(strValue, bNoQuotes); const bool bUseSpaces = true; const CMICmnMIValueResult miValueResult(strType, miValueConst, bUseSpaces); m_miValueTuple.Add(miValueResult, bUseSpaces); } } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataEvaluateExpression::Acknowledge(void) { if (m_bExpressionValid) { if (m_bEvaluatedExpression) { if (m_bCompositeVarType) { const CMICmnMIValueConst miValueConst(m_miValueTuple.GetString()); const CMICmnMIValueResult miValueResult("value", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } if (m_bFoundInvalidChar) { const CMICmnMIValueConst miValueConst( CMIUtilString::Format("Invalid character '%c' in expression", m_cExpressionInvalidChar)); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } const CMICmnMIValueConst miValueConst(m_strValue); const CMICmnMIValueResult miValueResult("value", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } const CMICmnMIValueConst miValueConst("Could not evaluate expression"); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } const CMICmnMIValueConst miValueConst("Invalid expression"); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataEvaluateExpression::CreateSelf(void) { return new CMICmdCmdDataEvaluateExpression(); } //++ ------------------------------------------------------------------------------------ // Details: Examine the expression string to see if it contains invalid characters. // Type: Method. // Args: vrExpr - (R) Expression string given to *this command. // vrwInvalidChar - (W) True = Invalid character found, false = nothing found. // Return: bool - True = Invalid character found, false = nothing found. // Throws: None. //-- bool CMICmdCmdDataEvaluateExpression::HaveInvalidCharacterInExpression(const CMIUtilString &vrExpr, char &vrwInvalidChar) { static const std::string strInvalidCharacters(";#\\"); const size_t nInvalidCharacterOffset = vrExpr.find_first_of(strInvalidCharacters); const bool bFoundInvalidCharInExpression = (nInvalidCharacterOffset != CMIUtilString::npos); vrwInvalidChar = bFoundInvalidCharInExpression ? vrExpr[nInvalidCharacterOffset] : 0x00; return bFoundInvalidCharInExpression; } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataDisassemble constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataDisassemble::CMICmdCmdDataDisassemble(void) : m_constStrArgThread("thread") , m_constStrArgAddrStart("s") , m_constStrArgAddrEnd("e") , m_constStrArgConsume("--") , m_constStrArgMode("mode") , m_miValueList(true) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-disassemble"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataDisassemble::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataDisassemble destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataDisassemble::~CMICmdCmdDataDisassemble(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataDisassemble::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, true, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValOptionShort(m_constStrArgAddrStart, true, true, CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1))); bOk = bOk && m_setCmdArgs.Add( *(new CMICmdArgValOptionShort(m_constStrArgAddrEnd, true, true, CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValConsume(m_constStrArgConsume, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgMode, true, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataDisassemble::Execute(void) { CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); CMICMDBASE_GETOPTION(pArgAddrStart, OptionShort, m_constStrArgAddrStart); CMICMDBASE_GETOPTION(pArgAddrEnd, OptionShort, m_constStrArgAddrEnd); CMICMDBASE_GETOPTION(pArgMode, Number, m_constStrArgMode); // Retrieve the --thread option's thread ID (only 1) MIuint64 nThreadId = UINT64_MAX; if (pArgThread->GetFound() && !pArgThread->GetExpectedOption(nThreadId)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgThread.c_str())); return MIstatus::failure; } CMIUtilString strAddrStart; if (!pArgAddrStart->GetExpectedOption(strAddrStart)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str())); return MIstatus::failure; } MIint64 nAddrStart = 0; if (!strAddrStart.ExtractNumber(nAddrStart)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str())); return MIstatus::failure; } CMIUtilString strAddrEnd; if (!pArgAddrEnd->GetExpectedOption(strAddrEnd)) { SetError( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgAddrEnd.c_str())); return MIstatus::failure; } MIint64 nAddrEnd = 0; if (!strAddrEnd.ExtractNumber(nAddrEnd)) { SetError( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgAddrEnd.c_str())); return MIstatus::failure; } const MIuint nDisasmMode = pArgMode->GetValue(); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); lldb::addr_t lldbStartAddr = static_cast(nAddrStart); lldb::SBInstructionList instructions = sbTarget.ReadInstructions(lldb::SBAddress(lldbStartAddr, sbTarget), nAddrEnd - nAddrStart); const MIuint nInstructions = instructions.GetSize(); // Calculate the offset of first instruction so that we can generate offset starting at 0 lldb::addr_t start_offset = 0; if(nInstructions > 0) start_offset = instructions.GetInstructionAtIndex(0).GetAddress().GetOffset(); for (size_t i = 0; i < nInstructions; i++) { const char *pUnknown = "??"; lldb::SBInstruction instrt = instructions.GetInstructionAtIndex(i); const char *pStrMnemonic = instrt.GetMnemonic(sbTarget); pStrMnemonic = (pStrMnemonic != nullptr) ? pStrMnemonic : pUnknown; const char *pStrComment = instrt.GetComment(sbTarget); CMIUtilString strComment; if (pStrComment != nullptr && *pStrComment != '\0') strComment = CMIUtilString::Format("; %s", pStrComment); lldb::SBAddress address = instrt.GetAddress(); lldb::addr_t addr = address.GetLoadAddress(sbTarget); const char *pFnName = address.GetFunction().GetName(); pFnName = (pFnName != nullptr) ? pFnName : pUnknown; lldb::addr_t addrOffSet = address.GetOffset() - start_offset; const char *pStrOperands = instrt.GetOperands(sbTarget); pStrOperands = (pStrOperands != nullptr) ? pStrOperands : pUnknown; const size_t instrtSize = instrt.GetByteSize(); // MI "{address=\"0x%016" PRIx64 "\",func-name=\"%s\",offset=\"%lld\",inst=\"%s %s\"}" const CMICmnMIValueConst miValueConst(CMIUtilString::Format("0x%016" PRIx64, addr)); const CMICmnMIValueResult miValueResult("address", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult); const CMICmnMIValueConst miValueConst2(pFnName); const CMICmnMIValueResult miValueResult2("func-name", miValueConst2); miValueTuple.Add(miValueResult2); const CMICmnMIValueConst miValueConst3(CMIUtilString::Format("%lld", addrOffSet)); const CMICmnMIValueResult miValueResult3("offset", miValueConst3); miValueTuple.Add(miValueResult3); const CMICmnMIValueConst miValueConst4(CMIUtilString::Format("%d", instrtSize)); const CMICmnMIValueResult miValueResult4("size", miValueConst4); miValueTuple.Add(miValueResult4); const CMICmnMIValueConst miValueConst5(CMIUtilString::Format("%s %s%s", pStrMnemonic, pStrOperands, strComment.Escape(true).c_str())); const CMICmnMIValueResult miValueResult5("inst", miValueConst5); miValueTuple.Add(miValueResult5); if (nDisasmMode == 1) { lldb::SBLineEntry lineEntry = address.GetLineEntry(); const MIuint nLine = lineEntry.GetLine(); const char *pFileName = lineEntry.GetFileSpec().GetFilename(); pFileName = (pFileName != nullptr) ? pFileName : pUnknown; // MI "src_and_asm_line={line=\"%u\",file=\"%s\",line_asm_insn=[ ]}" const CMICmnMIValueConst miValueConst(CMIUtilString::Format("0x%u", nLine)); const CMICmnMIValueResult miValueResult("line", miValueConst); CMICmnMIValueTuple miValueTuple2(miValueResult); const CMICmnMIValueConst miValueConst2(pFileName); const CMICmnMIValueResult miValueResult2("file", miValueConst2); miValueTuple2.Add(miValueResult2); const CMICmnMIValueList miValueList(miValueTuple); const CMICmnMIValueResult miValueResult3("line_asm_insn", miValueList); miValueTuple2.Add(miValueResult3); const CMICmnMIValueResult miValueResult4("src_and_asm_line", miValueTuple2); m_miValueList.Add(miValueResult4); } else { m_miValueList.Add(miValueTuple); } } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataDisassemble::Acknowledge(void) { const CMICmnMIValueResult miValueResult("asm_insns", m_miValueList); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataDisassemble::CreateSelf(void) { return new CMICmdCmdDataDisassemble(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataReadMemoryBytes constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataReadMemoryBytes::CMICmdCmdDataReadMemoryBytes(void) : m_constStrArgThread("thread") , m_constStrArgFrame("frame") , m_constStrArgByteOffset("o") , m_constStrArgAddrExpr("address") , m_constStrArgNumBytes("count") , m_pBufferMemory(nullptr) , m_nAddrStart(0) , m_nAddrNumBytesToRead(0) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-read-memory-bytes"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataReadMemoryBytes::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataReadMemoryBytes destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataReadMemoryBytes::~CMICmdCmdDataReadMemoryBytes(void) { if (m_pBufferMemory != nullptr) { delete[] m_pBufferMemory; m_pBufferMemory = nullptr; } } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataReadMemoryBytes::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, true, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgFrame, false, true, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionShort(m_constStrArgByteOffset, false, true, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgAddrExpr, true, true, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgNumBytes, true, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdDataReadMemoryBytes::Execute(void) { CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame); CMICMDBASE_GETOPTION(pArgAddrOffset, OptionShort, m_constStrArgByteOffset); CMICMDBASE_GETOPTION(pArgAddrExpr, String, m_constStrArgAddrExpr); CMICMDBASE_GETOPTION(pArgNumBytes, Number, m_constStrArgNumBytes); // get the --thread option value MIuint64 nThreadId = UINT64_MAX; if (pArgThread->GetFound() && !pArgThread->GetExpectedOption(nThreadId)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), m_cmdData.strMiCmd.c_str(), m_constStrArgThread.c_str())); return MIstatus::failure; } // get the --frame option value MIuint64 nFrame = UINT64_MAX; if (pArgFrame->GetFound() && !pArgFrame->GetExpectedOption(nFrame)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), m_cmdData.strMiCmd.c_str(), m_constStrArgFrame.c_str())); return MIstatus::failure; } // get the -o option value MIuint64 nAddrOffset = 0; if (pArgAddrOffset->GetFound() && !pArgAddrOffset->GetExpectedOption(nAddrOffset)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), m_cmdData.strMiCmd.c_str(), m_constStrArgByteOffset.c_str())); return MIstatus::failure; } CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); if (!sbProcess.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } lldb::SBThread thread = (nThreadId != UINT64_MAX) ? sbProcess.GetThreadByIndexID(nThreadId) : sbProcess.GetSelectedThread(); if (!thread.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } lldb::SBFrame frame = (nFrame != UINT64_MAX) ? thread.GetFrameAtIndex(nFrame) : thread.GetSelectedFrame(); if (!frame.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FRAME_INVALID), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } const CMIUtilString &rAddrExpr = pArgAddrExpr->GetValue(); lldb::SBValue addrExprValue = frame.EvaluateExpression(rAddrExpr.c_str()); lldb::SBError error = addrExprValue.GetError(); if (error.Fail()) { SetError(error.GetCString()); return MIstatus::failure; } else if (!addrExprValue.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID), rAddrExpr.c_str())); return MIstatus::failure; } MIuint64 nAddrStart = 0; if (!CMICmnLLDBProxySBValue::GetValueAsUnsigned(addrExprValue, nAddrStart)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID), rAddrExpr.c_str())); return MIstatus::failure; } nAddrStart += nAddrOffset; const MIuint64 nAddrNumBytes = pArgNumBytes->GetValue(); m_pBufferMemory = new unsigned char[nAddrNumBytes]; if (m_pBufferMemory == nullptr) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE), m_cmdData.strMiCmd.c_str(), nAddrNumBytes)); return MIstatus::failure; } const MIuint64 nReadBytes = sbProcess.ReadMemory(static_cast(nAddrStart), (void *)m_pBufferMemory, nAddrNumBytes, error); if (nReadBytes != nAddrNumBytes) { SetError( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_READ_WHOLE_BLK), m_cmdData.strMiCmd.c_str(), nAddrNumBytes, nAddrStart)); return MIstatus::failure; } if (error.Fail()) { lldb::SBStream err; const bool bOk = error.GetDescription(err); MIunused(bOk); SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES), m_cmdData.strMiCmd.c_str(), nAddrNumBytes, nAddrStart, err.GetData())); return MIstatus::failure; } m_nAddrStart = nAddrStart; m_nAddrNumBytesToRead = nAddrNumBytes; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataReadMemoryBytes::Acknowledge(void) { // MI: memory=[{begin=\"0x%016" PRIx64 "\",offset=\"0x%016" PRIx64" \",end=\"0x%016" PRIx64 "\",contents=\" \" }]" const CMICmnMIValueConst miValueConst(CMIUtilString::Format("0x%016" PRIx64, m_nAddrStart)); const CMICmnMIValueResult miValueResult("begin", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult); const MIuint64 nAddrOffset = 0; const CMICmnMIValueConst miValueConst2(CMIUtilString::Format("0x%016" PRIx64, nAddrOffset)); const CMICmnMIValueResult miValueResult2("offset", miValueConst2); miValueTuple.Add(miValueResult2); const CMICmnMIValueConst miValueConst3(CMIUtilString::Format("0x%016" PRIx64, m_nAddrStart + m_nAddrNumBytesToRead)); const CMICmnMIValueResult miValueResult3("end", miValueConst3); miValueTuple.Add(miValueResult3); // MI: contents=\" \" CMIUtilString strContent; strContent.reserve((m_nAddrNumBytesToRead << 1) + 1); for (MIuint64 i = 0; i < m_nAddrNumBytesToRead; i++) { strContent += CMIUtilString::Format("%02hhx", m_pBufferMemory[i]); } const CMICmnMIValueConst miValueConst4(strContent); const CMICmnMIValueResult miValueResult4("contents", miValueConst4); miValueTuple.Add(miValueResult4); const CMICmnMIValueList miValueList(miValueTuple); const CMICmnMIValueResult miValueResult5("memory", miValueList); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult5); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataReadMemoryBytes::CreateSelf(void) { return new CMICmdCmdDataReadMemoryBytes(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataReadMemory constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataReadMemory::CMICmdCmdDataReadMemory(void) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-read-memory"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataReadMemory::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataReadMemory destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataReadMemory::~CMICmdCmdDataReadMemory(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataReadMemory::Execute(void) { // Do nothing - command deprecated use "data-read-memory-bytes" command return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataReadMemory::Acknowledge(void) { // Command CMICmdCmdSupportListFeatures sends "data-read-memory-bytes" which causes this command not to be called const CMICmnMIValueConst miValueConst(MIRSRC(IDS_CMD_ERR_NOT_IMPLEMENTED_DEPRECATED)); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataReadMemory::CreateSelf(void) { return new CMICmdCmdDataReadMemory(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataListRegisterNames constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterNames::CMICmdCmdDataListRegisterNames(void) : m_constStrArgThreadGroup("thread-group") , m_constStrArgRegNo("regno") , m_miValueList(true) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-list-register-names"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataListRegisterNames::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataReadMemoryBytes destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterNames::~CMICmdCmdDataListRegisterNames(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterNames::ParseArgs(void) { bool bOk = m_setCmdArgs.Add( *(new CMICmdArgValOptionLong(m_constStrArgThreadGroup, false, false, CMICmdArgValListBase::eArgValType_ThreadGrp, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValListOfN(m_constStrArgRegNo, false, false, CMICmdArgValListBase::eArgValType_Number))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterNames::Execute(void) { CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); if (!sbProcess.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo(pArgRegNo->GetExpectedOptions()); if (!rVecRegNo.empty()) { // List of required registers CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin(); while (it != rVecRegNo.end()) { const CMICmdArgValNumber *pRegNo = static_cast(*it); const MIuint nRegIndex = pRegNo->GetValue(); lldb::SBValue regValue = GetRegister(nRegIndex); if (regValue.IsValid()) { const CMICmnMIValueConst miValueConst(CMICmnLLDBUtilSBValue(regValue).GetName()); m_miValueList.Add(miValueConst); } // Next ++it; } } else { // List of all registers lldb::SBThread thread = sbProcess.GetSelectedThread(); lldb::SBFrame frame = thread.GetSelectedFrame(); lldb::SBValueList registers = frame.GetRegisters(); const MIuint nRegisters = registers.GetSize(); for (MIuint i = 0; i < nRegisters; i++) { lldb::SBValue value = registers.GetValueAtIndex(i); const MIuint nRegChildren = value.GetNumChildren(); for (MIuint j = 0; j < nRegChildren; j++) { lldb::SBValue regValue = value.GetChildAtIndex(j); if (regValue.IsValid()) { const CMICmnMIValueConst miValueConst(CMICmnLLDBUtilSBValue(regValue).GetName()); const bool bOk = m_miValueList.Add(miValueConst); if (!bOk) return MIstatus::failure; } } } } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterNames::Acknowledge(void) { const CMICmnMIValueResult miValueResult("register-names", m_miValueList); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataListRegisterNames::CreateSelf(void) { return new CMICmdCmdDataListRegisterNames(); } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Method. // Args: None. // Return: lldb::SBValue - LLDB SBValue object. // Throws: None. //-- lldb::SBValue CMICmdCmdDataListRegisterNames::GetRegister(const MIuint vRegisterIndex) const { lldb::SBThread thread = CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread(); lldb::SBFrame frame = thread.GetSelectedFrame(); lldb::SBValueList registers = frame.GetRegisters(); const MIuint nRegisters = registers.GetSize(); MIuint nRegisterIndex(vRegisterIndex); for (MIuint i = 0; i < nRegisters; i++) { lldb::SBValue value = registers.GetValueAtIndex(i); const MIuint nRegChildren = value.GetNumChildren(); if (nRegisterIndex >= nRegChildren) { nRegisterIndex -= nRegChildren; continue; } lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex); if (value2.IsValid()) { return value2; } } return lldb::SBValue(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataListRegisterValues constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterValues::CMICmdCmdDataListRegisterValues(void) : m_constStrArgThread("thread") , m_constStrArgSkip("skip-unavailable") , m_constStrArgFormat("fmt") , m_constStrArgRegNo("regno") , m_miValueList(true) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-list-register-values"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataListRegisterValues::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataListRegisterValues destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterValues::~CMICmdCmdDataListRegisterValues(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterValues::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, false, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgSkip, false, false))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgFormat, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValListOfN(m_constStrArgRegNo, false, true, CMICmdArgValListBase::eArgValType_Number))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterValues::Execute(void) { CMICMDBASE_GETOPTION(pArgFormat, String, m_constStrArgFormat); CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo); const CMIUtilString &rStrFormat(pArgFormat->GetValue()); if (rStrFormat.length() != 1) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE), m_cmdData.strMiCmd.c_str(), rStrFormat.c_str())); return MIstatus::failure; } const CMICmnLLDBDebugSessionInfoVarObj::varFormat_e eFormat = CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForChar(rStrFormat[0]); if (eFormat == CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE), m_cmdData.strMiCmd.c_str(), rStrFormat.c_str())); return MIstatus::failure; } CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); if (!sbProcess.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo(pArgRegNo->GetExpectedOptions()); if (!rVecRegNo.empty()) { // List of required registers CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin(); while (it != rVecRegNo.end()) { const CMICmdArgValNumber *pRegNo = static_cast(*it); const MIuint nRegIndex = pRegNo->GetValue(); lldb::SBValue regValue = GetRegister(nRegIndex); if (regValue.IsValid()) { const bool bOk = AddToOutput(nRegIndex, regValue, eFormat); if (!bOk) return MIstatus::failure; } // Next ++it; } } else { // No register numbers are provided. Output all registers. lldb::SBThread thread = sbProcess.GetSelectedThread(); lldb::SBFrame frame = thread.GetSelectedFrame(); lldb::SBValueList registers = frame.GetRegisters(); const MIuint nRegisters = registers.GetSize(); MIuint nRegIndex = 0; for (MIuint i = 0; i < nRegisters; i++) { lldb::SBValue value = registers.GetValueAtIndex(i); const MIuint nRegChildren = value.GetNumChildren(); for (MIuint j = 0; j < nRegChildren; j++) { lldb::SBValue regValue = value.GetChildAtIndex(j); if (regValue.IsValid()) { const bool bOk = AddToOutput(nRegIndex, regValue, eFormat); if (!bOk) return MIstatus::failure; } // Next ++nRegIndex; } } } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterValues::Acknowledge(void) { const CMICmnMIValueResult miValueResult("register-values", m_miValueList); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataListRegisterValues::CreateSelf(void) { return new CMICmdCmdDataListRegisterValues(); } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Method. // Args: None. // Return: lldb::SBValue - LLDB SBValue object. // Throws: None. //-- lldb::SBValue CMICmdCmdDataListRegisterValues::GetRegister(const MIuint vRegisterIndex) const { lldb::SBThread thread = CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread(); lldb::SBFrame frame = thread.GetSelectedFrame(); lldb::SBValueList registers = frame.GetRegisters(); const MIuint nRegisters = registers.GetSize(); MIuint nRegisterIndex(vRegisterIndex); for (MIuint i = 0; i < nRegisters; i++) { lldb::SBValue value = registers.GetValueAtIndex(i); const MIuint nRegChildren = value.GetNumChildren(); if (nRegisterIndex >= nRegChildren) { nRegisterIndex -= nRegChildren; continue; } lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex); if (value2.IsValid()) { return value2; } } return lldb::SBValue(); } //++ ------------------------------------------------------------------------------------ // Details: Adds the register value to the output list. // Type: Method. // Args: Value of the register, its index and output format. // Return: None // Throws: None. //-- bool CMICmdCmdDataListRegisterValues::AddToOutput(const MIuint vnIndex, const lldb::SBValue &vrValue, CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat) { const CMICmnMIValueConst miValueConst(CMIUtilString::Format("%u", vnIndex)); const CMICmnMIValueResult miValueResult("number", miValueConst); CMICmnMIValueTuple miValueTuple(miValueResult); const CMIUtilString strRegValue(CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted(vrValue, veVarFormat)); const CMICmnMIValueConst miValueConst2(strRegValue); const CMICmnMIValueResult miValueResult2("value", miValueConst2); bool bOk = miValueTuple.Add(miValueResult2); return bOk && m_miValueList.Add(miValueTuple); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataListRegisterChanged constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterChanged::CMICmdCmdDataListRegisterChanged(void) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-list-changed-registers"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataListRegisterChanged::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataListRegisterChanged destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataListRegisterChanged::~CMICmdCmdDataListRegisterChanged(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterChanged::Execute(void) { // Do nothing return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataListRegisterChanged::Acknowledge(void) { const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED)); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataListRegisterChanged::CreateSelf(void) { return new CMICmdCmdDataListRegisterChanged(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataWriteMemoryBytes constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataWriteMemoryBytes::CMICmdCmdDataWriteMemoryBytes(void) : m_constStrArgThread("thread") , m_constStrArgAddr("address") , m_constStrArgContents("contents") , m_constStrArgCount("count") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-write-memory-bytes"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataWriteMemoryBytes::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataWriteMemoryBytes destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataWriteMemoryBytes::~CMICmdCmdDataWriteMemoryBytes(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemoryBytes::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, false, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgAddr, true, true, false, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgContents, true, true, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgCount, false, true, false, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemoryBytes::Execute(void) { // Do nothing - not reproduceable (yet) in Eclipse // CMICMDBASE_GETOPTION( pArgOffset, OptionShort, m_constStrArgOffset ); // CMICMDBASE_GETOPTION( pArgAddr, String, m_constStrArgAddr ); // CMICMDBASE_GETOPTION( pArgNumber, String, m_constStrArgNumber ); // CMICMDBASE_GETOPTION( pArgContents, String, m_constStrArgContents ); // // Numbers extracts as string types as they could be hex numbers // '&' is not recognised and so has to be removed return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemoryBytes::Acknowledge(void) { const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED)); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataWriteMemoryBytes::CreateSelf(void) { return new CMICmdCmdDataWriteMemoryBytes(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataWriteMemory constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataWriteMemory::CMICmdCmdDataWriteMemory(void) : m_constStrArgThread("thread") , m_constStrArgOffset("o") , m_constStrArgAddr("address") , m_constStrArgD("d") , m_constStrArgNumber("a number") , m_constStrArgContents("contents") , m_nAddr(0) , m_nCount(0) , m_pBufferMemory(nullptr) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-write-memory"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataWriteMemory::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataWriteMemory destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataWriteMemory::~CMICmdCmdDataWriteMemory(void) { if (m_pBufferMemory != nullptr) { delete[] m_pBufferMemory; m_pBufferMemory = nullptr; } } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemory::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValOptionLong(m_constStrArgThread, false, false, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValOptionShort(m_constStrArgOffset, false, true, CMICmdArgValListBase::eArgValType_Number, 1))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgAddr, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgD, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgNumber, true, true))); bOk = bOk && m_setCmdArgs.Add(*(new CMICmdArgValNumber(m_constStrArgContents, true, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemory::Execute(void) { CMICMDBASE_GETOPTION(pArgOffset, OptionShort, m_constStrArgOffset); CMICMDBASE_GETOPTION(pArgAddr, Number, m_constStrArgAddr); CMICMDBASE_GETOPTION(pArgNumber, Number, m_constStrArgNumber); CMICMDBASE_GETOPTION(pArgContents, Number, m_constStrArgContents); MIuint nAddrOffset = 0; if (pArgOffset->GetFound() && !pArgOffset->GetExpectedOption(nAddrOffset)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_INVALID), m_cmdData.strMiCmd.c_str(), m_constStrArgAddr.c_str())); return MIstatus::failure; } m_nAddr = pArgAddr->GetValue(); m_nCount = pArgNumber->GetValue(); const MIuint64 nValue = pArgContents->GetValue(); m_pBufferMemory = new unsigned char[m_nCount]; if (m_pBufferMemory == nullptr) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE), m_cmdData.strMiCmd.c_str(), m_nCount)); return MIstatus::failure; } *m_pBufferMemory = static_cast(nValue); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); lldb::SBError error; lldb::addr_t addr = static_cast(m_nAddr + nAddrOffset); const size_t nBytesWritten = sbProcess.WriteMemory(addr, (const void *)m_pBufferMemory, (size_t)m_nCount, error); if (nBytesWritten != static_cast(m_nCount)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_WRITE_WHOLEBLK), m_cmdData.strMiCmd.c_str(), m_nCount, addr)); return MIstatus::failure; } if (error.Fail()) { lldb::SBStream err; const bool bOk = error.GetDescription(err); MIunused(bOk); SetError( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES), m_cmdData.strMiCmd.c_str(), m_nCount, addr, err.GetData())); return MIstatus::failure; } return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataWriteMemory::Acknowledge(void) { const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataWriteMemory::CreateSelf(void) { return new CMICmdCmdDataWriteMemory(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataInfoLine constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataInfoLine::CMICmdCmdDataInfoLine(void) : m_constStrArgLocation("location") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "data-info-line"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdDataInfoLine::CreateSelf; } //++ ------------------------------------------------------------------------------------ // Details: CMICmdCmdDataInfoLine destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine(void) { } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataInfoLine::ParseArgs(void) { bool bOk = m_setCmdArgs.Add(*(new CMICmdArgValString(m_constStrArgLocation, true, true))); return (bOk && ParseValidateCmdOptions()); } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this function. // The command is likely to communicate with the LLDB SBDebugger in here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataInfoLine::Execute(void) { CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgLocation); const CMIUtilString &strLocation(pArgLocation->GetValue()); CMIUtilString strCmdOptionsLocation; if (strLocation.at(0) == '*') { // Parse argument: // *0x12345 // ^^^^^^^ -- address const CMIUtilString strAddress(strLocation.c_str() + 1); strCmdOptionsLocation = CMIUtilString::Format("--address %s", strAddress.c_str()); } else { const size_t nLineStartPos = strLocation.rfind(':'); if ((nLineStartPos == std::string::npos) || (nLineStartPos == 0) || (nLineStartPos == strLocation.length() - 1)) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_LOCATION_FORMAT), m_cmdData.strMiCmd.c_str(), strLocation.c_str()) .c_str()); return MIstatus::failure; } // Parse argument: // hello.cpp:5 // ^^^^^^^^^ -- file // ^ -- line const CMIUtilString strFile(strLocation.substr(0, nLineStartPos).c_str()); const CMIUtilString strLine(strLocation.substr(nLineStartPos + 1).c_str()); strCmdOptionsLocation = CMIUtilString::Format("--file \"%s\" --line %s", strFile.AddSlashes().c_str(), strLine.c_str()); } const CMIUtilString strCmd(CMIUtilString::Format("target modules lookup -v %s", strCmdOptionsLocation.c_str())); CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand(strCmd.c_str(), m_lldbResult); MIunused(rtn); return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdDataInfoLine::Acknowledge(void) { if (m_lldbResult.GetErrorSize() > 0) { const CMICmnMIValueConst miValueConst(m_lldbResult.GetError()); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } else if (m_lldbResult.GetOutputSize() > 0) { CMIUtilString::VecString_t vecLines; const CMIUtilString strLldbMsg(m_lldbResult.GetOutput()); const MIuint nLines(strLldbMsg.SplitLines(vecLines)); for (MIuint i = 0; i < nLines; ++i) { // String looks like: // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] const CMIUtilString &rLine(vecLines[i]); // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] // ^^^^^^^^^ -- property const size_t nPropertyStartPos = rLine.find_first_not_of(' '); const size_t nPropertyEndPos = rLine.find(':'); const size_t nPropertyLen = nPropertyEndPos - nPropertyStartPos; const CMIUtilString strProperty(rLine.substr(nPropertyStartPos, nPropertyLen).c_str()); // Skip all except LineEntry if (!CMIUtilString::Compare(strProperty, "LineEntry")) continue; // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] // ^^^^^^^^^^^^^^^^^^ -- start address const size_t nStartAddressStartPos = rLine.find('['); const size_t nStartAddressEndPos = rLine.find('-'); const size_t nStartAddressLen = nStartAddressEndPos - nStartAddressStartPos - 1; const CMIUtilString strStartAddress(rLine.substr(nStartAddressStartPos + 1, nStartAddressLen).c_str()); const CMICmnMIValueConst miValueConst(strStartAddress); const CMICmnMIValueResult miValueResult("start", miValueConst); CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] // ^^^^^^^^^^^^^^^^^^ -- end address const size_t nEndAddressEndPos = rLine.find(')'); const size_t nEndAddressLen = nEndAddressEndPos - nStartAddressEndPos - 1; const CMIUtilString strEndAddress(rLine.substr(nStartAddressEndPos + 1, nEndAddressLen).c_str()); const CMICmnMIValueConst miValueConst2(strEndAddress); const CMICmnMIValueResult miValueResult2("end", miValueConst2); bool bOk = miRecordResult.Add(miValueResult2); if (!bOk) return MIstatus::failure; // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] // ^^^^^^^^^^^^^ -- file // ^ -- line // ^ -- column (optional) const size_t nFileStartPos = rLine.find_first_not_of(' ', nEndAddressEndPos + 2); const size_t nFileOrLineEndPos = rLine.rfind(':'); const size_t nFileOrLineStartPos = rLine.rfind(':', nFileOrLineEndPos - 1); const size_t nFileEndPos = nFileStartPos < nFileOrLineStartPos ? nFileOrLineStartPos : nFileOrLineEndPos; const size_t nFileLen = nFileEndPos - nFileStartPos; const CMIUtilString strFile(rLine.substr(nFileStartPos, nFileLen).c_str()); const CMICmnMIValueConst miValueConst3(strFile); const CMICmnMIValueResult miValueResult3("file", miValueConst3); bOk = miRecordResult.Add(miValueResult3); if (!bOk) return MIstatus::failure; // LineEntry: \[0x0000000100000f37-0x0000000100000f45\): /path/to/file:3[:1] // ^ -- line const size_t nLineStartPos = nFileEndPos + 1; const size_t nLineEndPos = rLine.find(':', nLineStartPos); - const size_t nLineLen = nLineEndPos != std::string::npos ? nLineEndPos - nLineStartPos - 1 + const size_t nLineLen = nLineEndPos != std::string::npos ? nLineEndPos - nLineStartPos : std::string::npos; const CMIUtilString strLine(rLine.substr(nLineStartPos, nLineLen).c_str()); const CMICmnMIValueConst miValueConst4(strLine); const CMICmnMIValueResult miValueResult4("line", miValueConst4); bOk = miRecordResult.Add(miValueResult4); if (!bOk) return MIstatus::failure; // MI print "%s^done,start=\"%d\",end=\"%d\"",file=\"%s\",line=\"%d\" m_miResultRecord = miRecordResult; return MIstatus::success; } } // MI print "%s^error,msg=\"Command '-data-info-line'. Error: The LineEntry is absent or has an unknown format.\"" const CMICmnMIValueConst miValueConst(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), "The LineEntry is absent or has an unknown format.")); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult(m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ ------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase * CMICmdCmdDataInfoLine::CreateSelf(void) { return new CMICmdCmdDataInfoLine(); }