//===-- DynamicLoaderFreeBSDKernel.cpp
//------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/OperatingSystem.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlanRunToAddress.h"
#include "lldb/Utility/DataBuffer.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"

#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"

#include "DynamicLoaderFreeBSDKernel.h"
#include <memory>
#include <mutex>

using namespace lldb;
using namespace lldb_private;

LLDB_PLUGIN_DEFINE(DynamicLoaderFreeBSDKernel)

void DynamicLoaderFreeBSDKernel::Initialize() {
  PluginManager::RegisterPlugin(GetPluginNameStatic(),
                                GetPluginDescriptionStatic(), CreateInstance,
                                DebuggerInit);
}

void DynamicLoaderFreeBSDKernel::Terminate() {
  PluginManager::UnregisterPlugin(CreateInstance);
}

llvm::StringRef DynamicLoaderFreeBSDKernel::GetPluginDescriptionStatic() {
  return "The Dynamic Loader Plugin For FreeBSD Kernel";
}

static bool is_kernel(Module *module) {
  if (!module)
    return false;

  ObjectFile *objfile = module->GetObjectFile();
  if (!objfile)
    return false;
  if (objfile->GetType() != ObjectFile::eTypeExecutable)
    return false;
  if (objfile->GetStrata() != ObjectFile::eStrataUnknown &&
      objfile->GetStrata() != ObjectFile::eStrataKernel)
    return false;

  return true;
}

static bool is_kmod(Module *module) {
  if (!module)
    return false;
  if (!module->GetObjectFile())
    return false;
  ObjectFile *objfile = module->GetObjectFile();
  if (objfile->GetType() != ObjectFile::eTypeObjectFile &&
      objfile->GetType() != ObjectFile::eTypeSharedLibrary)
    return false;

  return true;
}

static bool is_reloc(Module *module) {
  if (!module)
    return false;
  if (!module->GetObjectFile())
    return false;
  ObjectFile *objfile = module->GetObjectFile();
  if (objfile->GetType() != ObjectFile::eTypeObjectFile)
    return false;

  return true;
}

// Instantiate Function of the FreeBSD Kernel Dynamic Loader Plugin called when
// Register the Plugin
DynamicLoader *
DynamicLoaderFreeBSDKernel::CreateInstance(lldb_private::Process *process,
                                           bool force) {
  // Check the environment when the plugin is not force loaded
  Module *exec = process->GetTarget().GetExecutableModulePointer();
  if (exec && !is_kernel(exec)) {
    return nullptr;
  }
  if (!force) {
    // Check if the target is kernel
    const llvm::Triple &triple_ref =
        process->GetTarget().GetArchitecture().GetTriple();
    if (!triple_ref.isOSFreeBSD()) {
      return nullptr;
    }
  }

  // At this point we have checked the target is a FreeBSD kernel and all we
  // have to do is to find the kernel address
  const addr_t kernel_address = FindFreeBSDKernel(process);

  if (CheckForKernelImageAtAddress(process, kernel_address).IsValid())
    return new DynamicLoaderFreeBSDKernel(process, kernel_address);

  return nullptr;
}

addr_t
DynamicLoaderFreeBSDKernel::FindFreeBSDKernel(lldb_private::Process *process) {
  addr_t kernel_addr = process->GetImageInfoAddress();
  if (kernel_addr == LLDB_INVALID_ADDRESS)
    kernel_addr = FindKernelAtLoadAddress(process);
  return kernel_addr;
}

// Get the kernel address if the kernel is not loaded with a slide
addr_t DynamicLoaderFreeBSDKernel::FindKernelAtLoadAddress(
    lldb_private::Process *process) {
  Module *exe_module = process->GetTarget().GetExecutableModulePointer();

  if (!is_kernel(exe_module))
    return LLDB_INVALID_ADDRESS;

  ObjectFile *exe_objfile = exe_module->GetObjectFile();

  if (!exe_objfile->GetBaseAddress().IsValid())
    return LLDB_INVALID_ADDRESS;

  if (CheckForKernelImageAtAddress(
          process, exe_objfile->GetBaseAddress().GetFileAddress())
          .IsValid())
    return exe_objfile->GetBaseAddress().GetFileAddress();

  return LLDB_INVALID_ADDRESS;
}

// Read ELF header from memry and return
bool DynamicLoaderFreeBSDKernel::ReadELFHeader(Process *process,
                                               lldb::addr_t addr,
                                               llvm::ELF::Elf32_Ehdr &header,
                                               bool *read_error) {
  Status error;
  if (read_error)
    *read_error = false;

  if (process->ReadMemory(addr, &header, sizeof(header), error) !=
      sizeof(header)) {
    if (read_error)
      *read_error = true;
    return false;
  }

  if (!header.checkMagic())
    return false;

  return true;
}

// Check the correctness of Kernel and return UUID
lldb_private::UUID DynamicLoaderFreeBSDKernel::CheckForKernelImageAtAddress(
    Process *process, lldb::addr_t addr, bool *read_error) {
  Log *log = GetLog(LLDBLog::DynamicLoader);

  if (addr == LLDB_INVALID_ADDRESS) {
    if (read_error)
      *read_error = true;
    return UUID();
  }

  LLDB_LOGF(log,
            "DynamicLoaderFreeBSDKernel::CheckForKernelImageAtAddress: "
            "looking for kernel binary at 0x%" PRIx64,
            addr);

  llvm::ELF::Elf32_Ehdr header;
  if (!ReadELFHeader(process, addr, header)) {
    *read_error = true;
    return UUID();
  }

  // Check header type
  if (header.e_type != llvm::ELF::ET_EXEC)
    return UUID();

  ModuleSP memory_module_sp =
      process->ReadModuleFromMemory(FileSpec("temp_freebsd_kernel"), addr);

  if (!memory_module_sp.get()) {
    *read_error = true;
    return UUID();
  }

  ObjectFile *exe_objfile = memory_module_sp->GetObjectFile();
  if (exe_objfile == nullptr) {
    LLDB_LOGF(log,
              "DynamicLoaderFreeBSDKernel::CheckForKernelImageAtAddress "
              "found a binary at 0x%" PRIx64
              " but could not create an object file from memory",
              addr);
    return UUID();
  }

  // In here, I should check is_kernel for memory_module_sp
  // However, the ReadModuleFromMemory reads wrong section so that this check
  // will failed
  ArchSpec kernel_arch(llvm::ELF::convertEMachineToArchName(header.e_machine));

  if (!process->GetTarget().GetArchitecture().IsCompatibleMatch(kernel_arch))
    process->GetTarget().SetArchitecture(kernel_arch);

  std::string uuid_str;
  if (memory_module_sp->GetUUID().IsValid()) {
    uuid_str = "with UUID ";
    uuid_str += memory_module_sp->GetUUID().GetAsString();
  } else {
    uuid_str = "and no LC_UUID found in load commands ";
  }
  LLDB_LOGF(log,
            "DynamicLoaderFreeBSDKernel::CheckForKernelImageAtAddress: "
            "kernel binary image found at 0x%" PRIx64 " with arch '%s' %s",
            addr, kernel_arch.GetTriple().str().c_str(), uuid_str.c_str());

  return memory_module_sp->GetUUID();
}

void DynamicLoaderFreeBSDKernel::DebuggerInit(
    lldb_private::Debugger &debugger) {}

DynamicLoaderFreeBSDKernel::DynamicLoaderFreeBSDKernel(Process *process,
                                                       addr_t kernel_address)
    : DynamicLoader(process), m_process(process),
      m_linker_file_list_struct_addr(LLDB_INVALID_ADDRESS),
      m_linker_file_head_addr(LLDB_INVALID_ADDRESS),
      m_kernel_load_address(kernel_address), m_mutex() {
  process->SetCanRunCode(false);
}

DynamicLoaderFreeBSDKernel::~DynamicLoaderFreeBSDKernel() { Clear(true); }

void DynamicLoaderFreeBSDKernel::Update() {
  LoadKernelModules();
  SetNotificationBreakPoint();
}

// Create in memory Module at the load address
bool DynamicLoaderFreeBSDKernel::KModImageInfo::ReadMemoryModule(
    lldb_private::Process *process) {
  Log *log = GetLog(LLDBLog::DynamicLoader);
  if (m_memory_module_sp)
    return true;
  if (m_load_address == LLDB_INVALID_ADDRESS)
    return false;

  FileSpec file_spec(m_name);

  ModuleSP memory_module_sp;

  llvm::ELF::Elf32_Ehdr elf_eheader;
  size_t size_to_read = 512;

  if (ReadELFHeader(process, m_load_address, elf_eheader)) {
    if (elf_eheader.e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32) {
      size_to_read = sizeof(llvm::ELF::Elf32_Ehdr) +
                     elf_eheader.e_phnum * elf_eheader.e_phentsize;
    } else if (elf_eheader.e_ident[llvm::ELF::EI_CLASS] ==
               llvm::ELF::ELFCLASS64) {
      llvm::ELF::Elf64_Ehdr elf_eheader;
      Status error;
      if (process->ReadMemory(m_load_address, &elf_eheader, sizeof(elf_eheader),
                              error) == sizeof(elf_eheader))
        size_to_read = sizeof(llvm::ELF::Elf64_Ehdr) +
                       elf_eheader.e_phnum * elf_eheader.e_phentsize;
    }
  }

  memory_module_sp =
      process->ReadModuleFromMemory(file_spec, m_load_address, size_to_read);

  if (!memory_module_sp)
    return false;

  bool this_is_kernel = is_kernel(memory_module_sp.get());

  if (!m_uuid.IsValid() && memory_module_sp->GetUUID().IsValid())
    m_uuid = memory_module_sp->GetUUID();

  m_memory_module_sp = memory_module_sp;
  m_is_kernel = this_is_kernel;

  // The kernel binary is from memory
  if (this_is_kernel) {
    LLDB_LOGF(log, "KextImageInfo::ReadMemoryModule read the kernel binary out "
                   "of memory");

    if (memory_module_sp->GetArchitecture().IsValid())
      process->GetTarget().SetArchitecture(memory_module_sp->GetArchitecture());
  }

  return true;
}

bool DynamicLoaderFreeBSDKernel::KModImageInfo::LoadImageUsingMemoryModule(
    lldb_private::Process *process) {
  Log *log = GetLog(LLDBLog::DynamicLoader);

  if (IsLoaded())
    return true;

  Target &target = process->GetTarget();

  if (IsKernel() && m_uuid.IsValid()) {
    lldb::StreamUP s = target.GetDebugger().GetAsyncOutputStream();
    s->Printf("Kernel UUID: %s\n", m_uuid.GetAsString().c_str());
    s->Printf("Load Address: 0x%" PRIx64 "\n", m_load_address);
  }

  // Test if the module is loaded into the taget,
  // maybe the module is loaded manually by user by doing target module add
  // So that we have to create the module manually
  if (!m_module_sp) {
    const ModuleList &target_images = target.GetImages();
    m_module_sp = target_images.FindModule(m_uuid);

    // Search in the file system
    if (!m_module_sp) {
      ModuleSpec module_spec(FileSpec(GetPath()), target.GetArchitecture());
      if (IsKernel()) {
        Status error;
        if (PluginManager::DownloadObjectAndSymbolFile(module_spec, error,
                                                       true)) {
          if (FileSystem::Instance().Exists(module_spec.GetFileSpec()))
            m_module_sp = std::make_shared<Module>(module_spec.GetFileSpec(),
                                                   target.GetArchitecture());
        }
      }

      if (!m_module_sp)
        m_module_sp = target.GetOrCreateModule(module_spec, true);
      if (IsKernel() && !m_module_sp) {
        target.GetDebugger().GetAsyncOutputStream()->Printf(
            "WARNING: Unable to locate kernel binary on the debugger "
            "system.\n");
      }
    }

    if (m_module_sp) {
      // If the file is not kernel or kmod, the target should be loaded once and
      // don't reload again
      if (!IsKernel() && !is_kmod(m_module_sp.get())) {
        ModuleSP existing_module_sp = target.GetImages().FindModule(m_uuid);
        if (existing_module_sp &&
            existing_module_sp->IsLoadedInTarget(&target)) {
          LLDB_LOGF(log,
                    "'%s' with UUID %s is not a kmod or kernel, and is "
                    "already registered in target, not loading.",
                    m_name.c_str(), m_uuid.GetAsString().c_str());
          return true;
        }
      }
      m_uuid = m_module_sp->GetUUID();

      // or append to the images
      target.GetImages().AppendIfNeeded(m_module_sp, false);
    }
  }

  // If this file is relocatable kernel module(x86_64), adjust it's
  // section(PT_LOAD segment) and return Because the kernel module's load
  // address is the text section. lldb cannot create full memory module upon
  // relocatable file So what we do is to set the load address only.
  if (is_kmod(m_module_sp.get()) && is_reloc(m_module_sp.get())) {
    m_stop_id = process->GetStopID();
    bool changed = false;
    m_module_sp->SetLoadAddress(target, m_load_address, true, changed);
    return true;
  }

  if (m_module_sp)
    ReadMemoryModule(process);

  // Calculate the slides of in memory module
  if (!m_memory_module_sp || !m_module_sp) {
    m_module_sp.reset();
    return false;
  }

  ObjectFile *ondisk_object_file = m_module_sp->GetObjectFile();
  ObjectFile *memory_object_file = m_memory_module_sp->GetObjectFile();

  if (!ondisk_object_file || !memory_object_file)
    m_module_sp.reset();

  // Find the slide address
  addr_t fixed_slide = LLDB_INVALID_ADDRESS;
  if (llvm::dyn_cast<ObjectFileELF>(memory_object_file)) {
    addr_t load_address = memory_object_file->GetBaseAddress().GetFileAddress();

    if (load_address != LLDB_INVALID_ADDRESS &&
        m_load_address != load_address) {
      fixed_slide = m_load_address - load_address;
      LLDB_LOGF(log,
                "kmod %s in-memory LOAD vmaddr is not correct, using a "
                "fixed slide of 0x%" PRIx64,
                m_name.c_str(), fixed_slide);
    }
  }

  SectionList *ondisk_section_list = ondisk_object_file->GetSectionList();
  SectionList *memory_section_list = memory_object_file->GetSectionList();

  if (memory_section_list && ondisk_object_file) {
    const uint32_t num_ondisk_sections = ondisk_section_list->GetSize();
    uint32_t num_load_sections = 0;

    for (uint32_t section_idx = 0; section_idx < num_ondisk_sections;
         ++section_idx) {
      SectionSP on_disk_section_sp =
          ondisk_section_list->GetSectionAtIndex(section_idx);

      if (!on_disk_section_sp)
        continue;
      if (fixed_slide != LLDB_INVALID_ADDRESS) {
        target.SetSectionLoadAddress(on_disk_section_sp,
                                     on_disk_section_sp->GetFileAddress() +
                                         fixed_slide);

      } else {
        const Section *memory_section =
            memory_section_list
                ->FindSectionByName(on_disk_section_sp->GetName())
                .get();
        if (memory_section) {
          target.SetSectionLoadAddress(on_disk_section_sp,
                                       memory_section->GetFileAddress());
          ++num_load_sections;
        }
      }
    }

    if (num_load_sections)
      m_stop_id = process->GetStopID();
    else
      m_module_sp.reset();
  } else {
    m_module_sp.reset();
  }

  if (IsLoaded() && m_module_sp && IsKernel()) {
    lldb::StreamUP s = target.GetDebugger().GetAsyncOutputStream();
    ObjectFile *kernel_object_file = m_module_sp->GetObjectFile();
    if (kernel_object_file) {
      addr_t file_address =
          kernel_object_file->GetBaseAddress().GetFileAddress();
      if (m_load_address != LLDB_INVALID_ADDRESS &&
          file_address != LLDB_INVALID_ADDRESS) {
        s->Printf("Kernel slide 0x%" PRIx64 " in memory.\n",
                  m_load_address - file_address);
        s->Printf("Loaded kernel file %s\n",
                  m_module_sp->GetFileSpec().GetPath().c_str());
      }
    }
  }

  return IsLoaded();
}

// This function is work for kernel file, others it wil reset load address and
// return false
bool DynamicLoaderFreeBSDKernel::KModImageInfo::LoadImageUsingFileAddress(
    lldb_private::Process *process) {
  if (IsLoaded())
    return true;

  if (m_module_sp) {
    bool changed = false;
    if (m_module_sp->SetLoadAddress(process->GetTarget(), 0, true, changed))
      m_stop_id = process->GetStopID();
  }

  return false;
}

// Get the head of found_list
bool DynamicLoaderFreeBSDKernel::ReadKmodsListHeader() {
  std::lock_guard<decltype(m_mutex)> guard(m_mutex);

  if (m_linker_file_list_struct_addr.IsValid()) {
    // Get tqh_first struct element from linker_files
    Status error;
    addr_t address = m_process->ReadPointerFromMemory(
        m_linker_file_list_struct_addr.GetLoadAddress(&m_process->GetTarget()),
        error);
    if (address != LLDB_INVALID_ADDRESS && error.Success()) {
      m_linker_file_head_addr = Address(address);
    } else {
      m_linker_file_list_struct_addr.Clear();
      return false;
    }

    if (!m_linker_file_head_addr.IsValid() ||
        m_linker_file_head_addr.GetFileAddress() == 0) {
      m_linker_file_list_struct_addr.Clear();
      return false;
    }
  }
  return true;
}

// Parse Kmod info in found_list
bool DynamicLoaderFreeBSDKernel::ParseKmods(Address linker_files_head_addr) {
  std::lock_guard<decltype(m_mutex)> guard(m_mutex);
  KModImageInfo::collection_type linker_files_list;
  Log *log = GetLog(LLDBLog::DynamicLoader);

  if (!ReadAllKmods(linker_files_head_addr, linker_files_list))
    return false;
  LLDB_LOGF(
      log,
      "Kmod-changed breakpoint hit, there are %zu kernel modules currently.\n",
      linker_files_list.size());

  ModuleList &modules = m_process->GetTarget().GetImages();
  ModuleList remove_modules;
  ModuleList add_modules;

  for (ModuleSP module : modules.Modules()) {
    if (is_kernel(module.get()))
      continue;
    if (is_kmod(module.get()))
      remove_modules.AppendIfNeeded(module);
  }

  m_process->GetTarget().ModulesDidUnload(remove_modules, false);

  for (KModImageInfo &image_info : linker_files_list) {
    auto it = m_kld_name_to_uuid.find(image_info.GetName());
    if (it != m_kld_name_to_uuid.end())
      image_info.SetUUID(it->second);
    bool failed_to_load = false;
    if (!image_info.LoadImageUsingMemoryModule(m_process)) {
      image_info.LoadImageUsingFileAddress(m_process);
      failed_to_load = true;
    } else {
      m_linker_files_list.push_back(image_info);
      m_kld_name_to_uuid[image_info.GetName()] = image_info.GetUUID();
    }

    if (!failed_to_load)
      add_modules.AppendIfNeeded(image_info.GetModule());
  }
  m_process->GetTarget().ModulesDidLoad(add_modules);
  return true;
}

// Read all kmod from a given arrays of list
bool DynamicLoaderFreeBSDKernel::ReadAllKmods(
    Address linker_files_head_addr,
    KModImageInfo::collection_type &kmods_list) {

  // Get offset of next member and load address symbol
  static ConstString kld_off_address_symbol_name("kld_off_address");
  static ConstString kld_off_next_symbol_name("kld_off_next");
  static ConstString kld_off_filename_symbol_name("kld_off_filename");
  static ConstString kld_off_pathname_symbol_name("kld_off_pathname");
  const Symbol *kld_off_address_symbol =
      m_kernel_image_info.GetModule()->FindFirstSymbolWithNameAndType(
          kld_off_address_symbol_name, eSymbolTypeData);
  const Symbol *kld_off_next_symbol =
      m_kernel_image_info.GetModule()->FindFirstSymbolWithNameAndType(
          kld_off_next_symbol_name, eSymbolTypeData);
  const Symbol *kld_off_filename_symbol =
      m_kernel_image_info.GetModule()->FindFirstSymbolWithNameAndType(
          kld_off_filename_symbol_name, eSymbolTypeData);
  const Symbol *kld_off_pathname_symbol =
      m_kernel_image_info.GetModule()->FindFirstSymbolWithNameAndType(
          kld_off_pathname_symbol_name, eSymbolTypeData);

  if (!kld_off_address_symbol || !kld_off_next_symbol ||
      !kld_off_filename_symbol || !kld_off_pathname_symbol)
    return false;

  Status error;
  const int32_t kld_off_address = m_process->ReadSignedIntegerFromMemory(
      kld_off_address_symbol->GetAddress().GetLoadAddress(
          &m_process->GetTarget()),
      4, 0, error);
  if (error.Fail())
    return false;
  const int32_t kld_off_next = m_process->ReadSignedIntegerFromMemory(
      kld_off_next_symbol->GetAddress().GetLoadAddress(&m_process->GetTarget()),
      4, 0, error);
  if (error.Fail())
    return false;
  const int32_t kld_off_filename = m_process->ReadSignedIntegerFromMemory(
      kld_off_filename_symbol->GetAddress().GetLoadAddress(
          &m_process->GetTarget()),
      4, 0, error);
  if (error.Fail())
    return false;

  const int32_t kld_off_pathname = m_process->ReadSignedIntegerFromMemory(
      kld_off_pathname_symbol->GetAddress().GetLoadAddress(
          &m_process->GetTarget()),
      4, 0, error);
  if (error.Fail())
    return false;

  // Parse KMods
  addr_t kld_load_addr(LLDB_INVALID_ADDRESS);
  char kld_filename[255];
  char kld_pathname[255];
  addr_t current_kld =
      linker_files_head_addr.GetLoadAddress(&m_process->GetTarget());

  while (current_kld != 0) {
    addr_t kld_filename_addr =
        m_process->ReadPointerFromMemory(current_kld + kld_off_filename, error);
    if (error.Fail())
      return false;
    addr_t kld_pathname_addr =
        m_process->ReadPointerFromMemory(current_kld + kld_off_pathname, error);
    if (error.Fail())
      return false;

    m_process->ReadCStringFromMemory(kld_filename_addr, kld_filename,
                                     sizeof(kld_filename), error);
    if (error.Fail())
      return false;
    m_process->ReadCStringFromMemory(kld_pathname_addr, kld_pathname,
                                     sizeof(kld_pathname), error);
    if (error.Fail())
      return false;
    kld_load_addr =
        m_process->ReadPointerFromMemory(current_kld + kld_off_address, error);
    if (error.Fail())
      return false;

    kmods_list.emplace_back();
    KModImageInfo &kmod_info = kmods_list.back();
    kmod_info.SetName(kld_filename);
    kmod_info.SetLoadAddress(kld_load_addr);
    kmod_info.SetPath(kld_pathname);

    current_kld =
        m_process->ReadPointerFromMemory(current_kld + kld_off_next, error);
    if (kmod_info.GetName() == "kernel")
      kmods_list.pop_back();
    if (error.Fail())
      return false;
  }

  return true;
}

// Read all kmods
void DynamicLoaderFreeBSDKernel::ReadAllKmods() {
  std::lock_guard<decltype(m_mutex)> guard(m_mutex);

  if (ReadKmodsListHeader()) {
    if (m_linker_file_head_addr.IsValid()) {
      if (!ParseKmods(m_linker_file_head_addr))
        m_linker_files_list.clear();
    }
  }
}

// Load all Kernel Modules
void DynamicLoaderFreeBSDKernel::LoadKernelModules() {
  Log *log = GetLog(LLDBLog::DynamicLoader);
  LLDB_LOGF(log, "DynamicLoaderFreeBSDKernel::LoadKernelModules "
                 "Start loading Kernel Module");

  // Initialize Kernel Image Information at the first time
  if (m_kernel_image_info.GetLoadAddress() == LLDB_INVALID_ADDRESS) {
    ModuleSP module_sp = m_process->GetTarget().GetExecutableModule();
    if (is_kernel(module_sp.get())) {
      m_kernel_image_info.SetModule(module_sp);
      m_kernel_image_info.SetIsKernel(true);
    }

    // Set name for kernel
    llvm::StringRef kernel_name("freebsd_kernel");
    module_sp = m_kernel_image_info.GetModule();
    if (module_sp.get() && module_sp->GetObjectFile() &&
        !module_sp->GetObjectFile()->GetFileSpec().GetFilename().IsEmpty())
      kernel_name = module_sp->GetObjectFile()
                        ->GetFileSpec()
                        .GetFilename()
                        .GetStringRef();
    m_kernel_image_info.SetName(kernel_name.data());

    if (m_kernel_image_info.GetLoadAddress() == LLDB_INVALID_ADDRESS) {
      m_kernel_image_info.SetLoadAddress(m_kernel_load_address);
    }

    // Build In memory Module
    if (m_kernel_image_info.GetLoadAddress() != LLDB_INVALID_ADDRESS) {
      // If the kernel is not loaded in the memory, use file to load
      if (!m_kernel_image_info.LoadImageUsingMemoryModule(m_process))
        m_kernel_image_info.LoadImageUsingFileAddress(m_process);
    }
  }

  LoadOperatingSystemPlugin(false);

  if (!m_kernel_image_info.IsLoaded() || !m_kernel_image_info.GetModule()) {
    m_kernel_image_info.Clear();
    return;
  }

  static ConstString modlist_symbol_name("linker_files");

  const Symbol *symbol =
      m_kernel_image_info.GetModule()->FindFirstSymbolWithNameAndType(
          modlist_symbol_name, lldb::eSymbolTypeData);

  if (symbol) {
    m_linker_file_list_struct_addr = symbol->GetAddress();
    ReadAllKmods();
  } else {
    LLDB_LOGF(log, "DynamicLoaderFreeBSDKernel::LoadKernelModules "
                   "cannot file modlist symbol");
  }
}

// Update symbol when use kldload by setting callback function on kldload
void DynamicLoaderFreeBSDKernel::SetNotificationBreakPoint() {}

// Hook called when attach to a process
void DynamicLoaderFreeBSDKernel::DidAttach() {
  PrivateInitialize(m_process);
  Update();
}

// Hook called after attach to a process
void DynamicLoaderFreeBSDKernel::DidLaunch() {
  PrivateInitialize(m_process);
  Update();
}

// Clear all member except kernel address
void DynamicLoaderFreeBSDKernel::Clear(bool clear_process) {
  std::lock_guard<decltype(m_mutex)> guard(m_mutex);
  if (clear_process)
    m_process = nullptr;
  m_linker_file_head_addr.Clear();
  m_linker_file_list_struct_addr.Clear();
  m_kernel_image_info.Clear();
  m_linker_files_list.clear();
}

// Reinitialize class
void DynamicLoaderFreeBSDKernel::PrivateInitialize(Process *process) {
  Clear(true);
  m_process = process;
}

ThreadPlanSP DynamicLoaderFreeBSDKernel::GetStepThroughTrampolinePlan(
    lldb_private::Thread &thread, bool stop_others) {
  Log *log = GetLog(LLDBLog::Step);
  LLDB_LOGF(log, "DynamicLoaderFreeBSDKernel::GetStepThroughTrampolinePlan is "
                 "not yet implemented.");
  return {};
}

Status DynamicLoaderFreeBSDKernel::CanLoadImage() {
  return Status::FromErrorString("shared object cannot be loaded into kernel");
}
