//===-- DynamicLoaderDarwinKernel.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_DynamicLoaderDarwinKernel_h_
#define liblldb_DynamicLoaderDarwinKernel_h_

// C Includes
// C++ Includes
#include <mutex>
#include <string>
#include <vector>

// Other libraries and framework includes
// Project includes
#include "lldb/Target/DynamicLoader.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/TimeValue.h"
#include "lldb/Core/UUID.h"
#include "lldb/Target/Process.h"

class DynamicLoaderDarwinKernel : public lldb_private::DynamicLoader
{
public:
    DynamicLoaderDarwinKernel(lldb_private::Process *process, lldb::addr_t kernel_addr);

    ~DynamicLoaderDarwinKernel() override;

    //------------------------------------------------------------------
    // Static Functions
    //------------------------------------------------------------------
    static void
    Initialize();

    static void
    Terminate();

    static lldb_private::ConstString
    GetPluginNameStatic();

    static const char *
    GetPluginDescriptionStatic();

    static lldb_private::DynamicLoader *
    CreateInstance (lldb_private::Process *process, bool force);

    static void
    DebuggerInitialize (lldb_private::Debugger &debugger);

    static lldb::addr_t
    SearchForDarwinKernel (lldb_private::Process *process);

    //------------------------------------------------------------------
    /// Called after attaching a process.
    ///
    /// Allow DynamicLoader plug-ins to execute some code after
    /// attaching to a process.
    //------------------------------------------------------------------
    void
    DidAttach() override;

    void
    DidLaunch() override;

    lldb::ThreadPlanSP
    GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
                                 bool stop_others) override;

    lldb_private::Error
    CanLoadImage() override;

    //------------------------------------------------------------------
    // PluginInterface protocol
    //------------------------------------------------------------------
    lldb_private::ConstString
    GetPluginName() override;

    uint32_t
    GetPluginVersion() override;

protected:
    void
    PrivateInitialize (lldb_private::Process *process);

    void
    PrivateProcessStateChanged (lldb_private::Process *process,
                                lldb::StateType state);

    void
    UpdateIfNeeded();

    void
    LoadKernelModuleIfNeeded ();

    void
    Clear (bool clear_process);

    void
    PutToLog (lldb_private::Log *log) const;

    static bool
    BreakpointHitCallback (void *baton,
                           lldb_private::StoppointCallbackContext *context,
                           lldb::user_id_t break_id,
                           lldb::user_id_t break_loc_id);

    bool
    BreakpointHit (lldb_private::StoppointCallbackContext *context,
                   lldb::user_id_t break_id,
                   lldb::user_id_t break_loc_id);
    uint32_t
    GetAddrByteSize()
    {
        return m_kernel.GetAddressByteSize();
    }

    static lldb::ByteOrder
    GetByteOrderFromMagic (uint32_t magic);

    enum
    {
        KERNEL_MODULE_MAX_NAME = 64u,
        // Versions less than 2 didn't have an entry size,
        // they had a 64 bit name, 16 byte UUID, 8 byte addr,
        // 8 byte size, 8 byte version, 4 byte load tag, and
        // 4 byte flags
        KERNEL_MODULE_ENTRY_SIZE_VERSION_1 = 64u + 16u + 8u + 8u + 8u + 4u + 4u
    };

    // class KextImageInfo represents a single kext or kernel binary image.
    // The class was designed to hold the information from the OSKextLoadedKextSummary
    // structure (in libkern/libkern/OSKextLibPrivate.h from xnu).  The kernel maintains 
    // a list of loded kexts in memory (the OSKextLoadedKextSummaryHeader structure, 
    // which points to an array of OSKextLoadedKextSummary's).
    //
    // A KextImageInfos may have -
    // 
    // 1. The load address, name, UUID, and size of a kext/kernel binary in memory
    //    (read straight out of the kernel's list-of-kexts loaded)
    // 2. A ModuleSP based on a MemoryModule read out of the kernel's memory 
    //    (very unlikely to have any symbolic information)
    // 3. A ModuleSP for an on-disk copy of the kext binary, possibly with debug info
    //    or a dSYM
    //
    // For performance reasons, the developer may prefer that lldb not load the kexts out
    // of memory at the start of a kernel session.  But we should build up / maintain a 
    // list of kexts that the kernel has told us about so we can relocate a kext module
    // later if the user explicitly adds it to the target.

    class KextImageInfo
    {
    public:
        KextImageInfo () :
            m_name (),
            m_module_sp (),
            m_memory_module_sp (),
            m_load_process_stop_id (UINT32_MAX),
            m_uuid (),
            m_load_address (LLDB_INVALID_ADDRESS),
            m_size (0),
            m_kernel_image (false)
        { }

        void
        Clear ()
        {
            m_load_address = LLDB_INVALID_ADDRESS;
            m_size = 0;
            m_name.clear ();
            m_uuid.Clear();
            m_module_sp.reset();
            m_memory_module_sp.reset();
            m_load_process_stop_id = UINT32_MAX;
        }

        bool
        LoadImageAtFileAddress (lldb_private::Process *process);

        bool
        LoadImageUsingMemoryModule (lldb_private::Process *process);

        bool
        IsLoaded ()
        {
            return m_load_process_stop_id != UINT32_MAX;
        }

        void
        SetLoadAddress (lldb::addr_t load_addr);     // Address of the Mach-O header for this binary

        lldb::addr_t 
        GetLoadAddress () const;                     // Address of the Mach-O header for this binary

        lldb_private::UUID
        GetUUID () const;

        void
        SetUUID (const lldb_private::UUID &uuid);

        void
        SetName (const char *);

        std::string
        GetName () const;

        void
        SetModule (lldb::ModuleSP module);

        lldb::ModuleSP
        GetModule ();

        // try to fill in m_memory_module_sp from memory based on the m_load_address
        bool
        ReadMemoryModule (lldb_private::Process *process); 

        bool
        IsKernel () const;            // true if this is the mach_kernel; false if this is a kext

        void
        SetIsKernel (bool is_kernel);

        uint64_t 
        GetSize () const;

        void
        SetSize (uint64_t size);

        uint32_t
        GetProcessStopId () const;    // the stop-id when this binary was first noticed

        void
        SetProcessStopId (uint32_t stop_id);

        bool
        operator== (const KextImageInfo &rhs);

        uint32_t
        GetAddressByteSize ();        // as determined by Mach-O header

        lldb::ByteOrder
        GetByteOrder();               // as determined by Mach-O header

        lldb_private::ArchSpec
        GetArchitecture () const;     // as determined by Mach-O header

        void
        PutToLog (lldb_private::Log *log) const;

        typedef std::vector<KextImageInfo> collection;
        typedef collection::iterator iterator;
        typedef collection::const_iterator const_iterator;

    private:
        std::string              m_name;
        lldb::ModuleSP           m_module_sp;
        lldb::ModuleSP           m_memory_module_sp;
        uint32_t                 m_load_process_stop_id; // the stop-id when this module was added to the Target
        lldb_private::UUID       m_uuid;                 // UUID for this dylib if it has one, else all zeros
        lldb::addr_t             m_load_address;
        uint64_t                 m_size;
        bool                     m_kernel_image;         // true if this is the kernel, false if this is a kext
    };

    struct OSKextLoadedKextSummaryHeader
    {
        uint32_t version;
        uint32_t entry_size;
        uint32_t entry_count;
        lldb::addr_t image_infos_addr;

        OSKextLoadedKextSummaryHeader() :
            version (0),
            entry_size (0),
            entry_count (0),
            image_infos_addr (LLDB_INVALID_ADDRESS)
        {
        }

        uint32_t
        GetSize()
        {
            switch (version)
            {
                case 0: return 0;   // Can't know the size without a valid version
                case 1: return 8;   // Version 1 only had a version + entry_count
                default: break;
            }
            // Version 2 and above has version, entry_size, entry_count, and reserved
            return 16;
        }

        void
        Clear()
        {
            version = 0;
            entry_size = 0;
            entry_count = 0;
            image_infos_addr = LLDB_INVALID_ADDRESS;
        }

        bool
        IsValid() const
        {
            return version >= 1 || version <= 2;
        }
    };

    void
    RegisterNotificationCallbacks();

    void
    UnregisterNotificationCallbacks();

    void
    SetNotificationBreakpointIfNeeded ();

    bool
    ReadAllKextSummaries ();

    bool
    ReadKextSummaryHeader ();

    bool
    ParseKextSummaries (const lldb_private::Address &kext_summary_addr,
                        uint32_t count);

    void
    UpdateImageInfosHeaderAndLoadCommands(KextImageInfo::collection &image_infos,
                                          uint32_t infos_count,
                                          bool update_executable);

    uint32_t
    ReadKextSummaries (const lldb_private::Address &kext_summary_addr,
                       uint32_t image_infos_count,
                       KextImageInfo::collection &image_infos);

    static lldb::addr_t
    SearchForKernelAtSameLoadAddr (lldb_private::Process *process);

    static lldb::addr_t
    SearchForKernelWithDebugHints (lldb_private::Process *process);

    static lldb::addr_t
    SearchForKernelNearPC (lldb_private::Process *process);

    static lldb::addr_t
    SearchForKernelViaExhaustiveSearch (lldb_private::Process *process);

    static lldb_private::UUID
    CheckForKernelImageAtAddress (lldb::addr_t addr, lldb_private::Process *process);

    lldb::addr_t  m_kernel_load_address;
    KextImageInfo m_kernel;                 // Info about the current kernel image being used

    lldb_private::Address          m_kext_summary_header_ptr_addr;
    lldb_private::Address          m_kext_summary_header_addr;
    OSKextLoadedKextSummaryHeader  m_kext_summary_header;
    KextImageInfo::collection      m_known_kexts;
    mutable std::recursive_mutex m_mutex;
    lldb::user_id_t                m_break_id;

private:
    DISALLOW_COPY_AND_ASSIGN (DynamicLoaderDarwinKernel);
};

#endif // liblldb_DynamicLoaderDarwinKernel_h_
