| //===-- 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/FileSystem.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 "llvm/Support/Error.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; | |
| ObjectFile::Type expected_type; | |
| switch (module->GetArchitecture().GetMachine()) { | |
| case llvm::Triple::x86: | |
| case llvm::Triple::x86_64: | |
| case llvm::Triple::arm: | |
| case llvm::Triple::aarch64: | |
| case llvm::Triple::riscv64: | |
| expected_type = ObjectFile::eTypeExecutable; | |
| break; | |
| case llvm::Triple::ppc64: | |
| case llvm::Triple::ppc64le: | |
| expected_type = ObjectFile::eTypeSharedLibrary; | |
| break; | |
| default: | |
| return false; | |
| } | |
| if (objfile->GetType() != expected_type) | |
| 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; | |
| ObjectFile *objfile = module->GetObjectFile(); | |
| if (!objfile) | |
| return false; | |
| switch (module->GetArchitecture().GetMachine()) { | |
| case llvm::Triple::x86_64: | |
| return objfile->GetType() == ObjectFile::eTypeObjectFile; | |
| case llvm::Triple::x86: | |
| case llvm::Triple::arm: | |
| case llvm::Triple::aarch64: | |
| case llvm::Triple::riscv64: | |
| case llvm::Triple::ppc64: | |
| case llvm::Triple::ppc64le: | |
| return objfile->GetType() == ObjectFile::eTypeSharedLibrary; | |
| default: | |
| return false; | |
| } | |
| } | |
| static bool set_debug_file(const ModuleSP &module, | |
| llvm::StringRef target_path) { | |
| if (!module || module->GetSymbolFileFileSpec()) | |
| return false; | |
| FileSpec local_file = module->GetFileSpec(); | |
| FileSpec target_file(target_path); | |
| if (!target_file.IsAbsolute()) | |
| return false; | |
| std::string local_path = local_file.GetPath(); | |
| std::string normalized_target_path = target_file.GetPath(); | |
| llvm::StringRef local_sysroot(local_path); | |
| if (!local_sysroot.consume_back(normalized_target_path)) | |
| return false; | |
| std::string debug_path = local_sysroot.str(); | |
| debug_path += "/usr/lib/debug"; | |
| debug_path += normalized_target_path; | |
| debug_path += ".debug"; | |
| FileSpec debug_file(debug_path); | |
| if (!FileSystem::Instance().Exists(debug_file)) | |
| return false; | |
| module->SetSymbolFileFileSpec(debug_file); | |
| return true; | |
| } | |
| static bool is_reloc(Module *module) { | |
| if (!module) | |
| return false; | |
| ObjectFile *objfile = module->GetObjectFile(); | |
| if (!objfile) | |
| return false; | |
| switch (module->GetArchitecture().GetMachine()) { | |
| case llvm::Triple::x86_64: | |
| return objfile->GetType() == ObjectFile::eTypeObjectFile; | |
| default: | |
| return false; | |
| } | |
| } | |
| // 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; | |
| } | |
| } | |
| // ProcessFreeBSDKernelCore explicitly selects this plugin after libkvm has | |
| // established the kernel's section load addresses. Some architectures do | |
| // not map the ELF file and program headers at the kernel's load address, so | |
| // use the supplied kernel module instead of requiring an in-memory header. | |
| if (force && exec) { | |
| Address base_address = exec->GetObjectFile()->GetBaseAddress(); | |
| addr_t kernel_address = base_address.GetLoadAddress(&process->GetTarget()); | |
| if (kernel_address == LLDB_INVALID_ADDRESS) | |
| kernel_address = base_address.GetFileAddress(); | |
| return new DynamicLoaderFreeBSDKernel(process, kernel_address); | |
| } | |
| // 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) || | |
| error.Fail()) { | |
| 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); | |
| bool local_read_error; | |
| if (!read_error) | |
| read_error = &local_read_error; | |
| *read_error = false; | |
| if (addr == LLDB_INVALID_ADDRESS) { | |
| *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(); | |
| } | |
| uint16_t expected_type; | |
| switch (header.e_machine) { | |
| case llvm::ELF::EM_386: | |
| case llvm::ELF::EM_X86_64: | |
| case llvm::ELF::EM_ARM: | |
| case llvm::ELF::EM_AARCH64: | |
| case llvm::ELF::EM_RISCV: | |
| expected_type = llvm::ELF::ET_EXEC; | |
| break; | |
| case llvm::ELF::EM_PPC64: | |
| expected_type = llvm::ELF::ET_DYN; | |
| break; | |
| default: | |
| return UUID(); | |
| } | |
| if (header.e_type != expected_type) | |
| return UUID(); | |
| llvm::Expected<ModuleSP> memory_module_sp_or_err = | |
| process->ReadModuleFromMemory(FileSpec("temp_freebsd_kernel"), addr); | |
| if (auto err = memory_module_sp_or_err.takeError()) { | |
| LLDB_LOG_ERROR(log, std::move(err), | |
| "DynamicLoaderFreeBSDKernel::CheckForKernelImageAtAddress: " | |
| "Failed to read module in memory -- {0}"); | |
| *read_error = true; | |
| return UUID(); | |
| } | |
| ModuleSP memory_module_sp = *memory_module_sp_or_err; | |
| 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_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) && | |
| error.Success()) | |
| size_to_read = sizeof(llvm::ELF::Elf64_Ehdr) + | |
| elf_eheader.e_phnum * elf_eheader.e_phentsize; | |
| } | |
| } | |
| llvm::Expected<ModuleSP> memory_module_sp_or_err = | |
| process->ReadModuleFromMemory(file_spec, m_load_address, size_to_read); | |
| if (auto err = memory_module_sp_or_err.takeError()) { | |
| LLDB_LOG_ERROR(log, std::move(err), | |
| "KextImageInfo::ReadMemoryModule: Failed to read module " | |
| "from memory -- {0}"); | |
| return false; | |
| } | |
| memory_module_sp = *memory_module_sp_or_err; | |
| 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) && | |
| error.Success()) { | |
| 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 && !IsKernel()) | |
| set_debug_file(m_module_sp, GetPath()); | |
| 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); | |
| ++num_load_sections; | |
| } 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 | |
| llvm::Expected<lldb::addr_t> address = m_process->ReadPointerFromMemory( | |
| m_linker_file_list_struct_addr.GetLoadAddress(&m_process->GetTarget())); | |
| if (!address) { | |
| llvm::consumeError(address.takeError()); | |
| m_linker_file_list_struct_addr.Clear(); | |
| return false; | |
| } | |
| m_linker_file_head_addr = Address(*address); | |
| 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) { | |
| llvm::Expected<lldb::addr_t> kld_filename_addr = | |
| m_process->ReadPointerFromMemory(current_kld + kld_off_filename); | |
| if (!kld_filename_addr) { | |
| llvm::consumeError(kld_filename_addr.takeError()); | |
| return false; | |
| } | |
| llvm::Expected<lldb::addr_t> kld_pathname_addr = | |
| m_process->ReadPointerFromMemory(current_kld + kld_off_pathname); | |
| if (!kld_pathname_addr) { | |
| llvm::consumeError(kld_pathname_addr.takeError()); | |
| 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; | |
| llvm::Expected<lldb::addr_t> kld_load_addr_or_err = | |
| m_process->ReadPointerFromMemory(current_kld + kld_off_address); | |
| if (!kld_load_addr_or_err) { | |
| llvm::consumeError(kld_load_addr_or_err.takeError()); | |
| return false; | |
| } | |
| kld_load_addr = *kld_load_addr_or_err; | |
| 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); | |
| llvm::Expected<lldb::addr_t> next_kld = | |
| m_process->ReadPointerFromMemory(current_kld + kld_off_next); | |
| if (kmod_info.GetName() == "kernel") | |
| kmods_list.pop_back(); | |
| if (!next_kld) { | |
| llvm::consumeError(next_kld.takeError()); | |
| return false; | |
| } | |
| current_kld = *next_kld; | |
| } | |
| 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().empty()) | |
| kernel_name = module_sp->GetObjectFile()->GetFileSpec().GetFilename(); | |
| 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"); | |
| } |