https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/67106
>From a05c9b5c082395d0bade0bdef3faf7f00035c96d Mon Sep 17 00:00:00 2001 From: SHENG-YI HONG <aokbl...@freebsd.org> Date: Sat, 30 Sep 2023 00:28:38 +0800 Subject: [PATCH] Add DynamicLoader Plugin Fore FreeBSD Kernel coredump This patch add dynamicloader plguin for freebsd kernel coredump on lldb. The implementation is by parsing linker_files structure and get all loaded kernel modules. This patch was part of FreeBSD's participation in Google Summer of Code 2023 --- .../Plugins/DynamicLoader/CMakeLists.txt | 1 + .../FreeBSD-Kernel/CMakeLists.txt | 13 + .../DynamicLoaderFreeBSDKernel.cpp | 789 ++++++++++++++++++ .../DynamicLoaderFreeBSDKernel.h | 171 ++++ .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 43 +- .../FreeBSDKernel/ProcessFreeBSDKernel.cpp | 4 +- 6 files changed, 1014 insertions(+), 7 deletions(-) create mode 100644 lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/CMakeLists.txt create mode 100644 lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp create mode 100644 lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt index f357fea02efbe68..30607159acdc088 100644 --- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(Darwin-Kernel) +add_subdirectory(FreeBSD-Kernel) add_subdirectory(MacOSX-DYLD) add_subdirectory(POSIX-DYLD) add_subdirectory(Static) diff --git a/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/CMakeLists.txt b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/CMakeLists.txt new file mode 100644 index 000000000000000..76daf0a327cf97b --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginDynamicLoaderFreeBSDKernel PLUGIN + DynamicLoaderFreeBSDKernel.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbHost + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + lldbPluginObjectFileELF + ) diff --git a/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp new file mode 100644 index 000000000000000..bbb83ff0a118400 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp @@ -0,0 +1,789 @@ +//===-- 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/LocateSymbolFile.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()) { + Stream &s = target.GetDebugger().GetOutputStream(); + 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 (Symbols::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) { + Stream &s = target.GetDebugger().GetOutputStream(); + s.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 (ObjectFileELF *memory_objfile_elf = + 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()) { + Stream &s = target.GetDebugger().GetOutputStream(); + 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()); + } + } + s.Flush(); + } + + 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 %lu 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) { + if (m_kld_name_to_uuid.find(image_info.GetName()) != + m_kld_name_to_uuid.end()) + image_info.SetUUID(m_kld_name_to_uuid[image_info.GetName()]); + 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() { + Status error("shared object cannot be loaded into kernel"); + return error; +} diff --git a/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h new file mode 100644 index 000000000000000..d8656e9c49dfe25 --- /dev/null +++ b/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h @@ -0,0 +1,171 @@ +//===-- DynamicLoaderFreeBSDKernel.h -----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_FREEBSD_KERNEL_DYNAMICLOADERFREEBSDKERNEL_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_FREEBSD_KERNEL_DYNAMICLOADERFREEBSDKERNEL_H + +#include <mutex> +#include <string> +#include <vector> + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" +#include "llvm/BinaryFormat/ELF.h" + +class DynamicLoaderFreeBSDKernel : public lldb_private::DynamicLoader { +public: + DynamicLoaderFreeBSDKernel(lldb_private::Process *process, + lldb::addr_t kernel_addr); + + ~DynamicLoaderFreeBSDKernel() override; + + // Static Functions + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "freebsd-kernel"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + static void DebuggerInit(lldb_private::Debugger &debugger); + + static lldb::addr_t FindFreeBSDKernel(lldb_private::Process *process); + + // Hooks for time point that after attach to some proccess + void DidAttach() override; + + void DidLaunch() override; + + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Status CanLoadImage() override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + class KModImageInfo { + public: + KModImageInfo() + : m_module_sp(), m_memory_module_sp(), m_uuid(), m_name(), m_path() {} + + void Clear() { + m_load_address = LLDB_INVALID_ADDRESS; + m_name.clear(); + m_uuid.Clear(); + m_module_sp.reset(); + m_memory_module_sp.reset(); + m_stop_id = UINT32_MAX; + m_path.clear(); + } + + void SetLoadAddress(lldb::addr_t load_address) { + m_load_address = load_address; + } + + lldb::addr_t GetLoadAddress() const { return m_load_address; } + + void SetUUID(const lldb_private::UUID uuid) { m_uuid = uuid; } + + lldb_private::UUID GetUUID() const { return m_uuid; } + + void SetName(const char *name) { m_name = name; } + + std::string GetName() const { return m_name; } + + void SetPath(const char *path) { m_path = path; } + + std::string GetPath() const { return m_path; } + + void SetModule(lldb::ModuleSP module) { m_module_sp = module; } + + lldb::ModuleSP GetModule() { return m_module_sp; } + + void SetIsKernel(bool is_kernel) { m_is_kernel = is_kernel; } + + bool IsKernel() const { return m_is_kernel; }; + + void SetStopID(uint32_t stop_id) { m_stop_id = stop_id; } + + uint32_t GetStopID() { return m_stop_id; } + + bool IsLoaded() const { return m_stop_id != UINT32_MAX; }; + + bool ReadMemoryModule(lldb_private::Process *process); + + bool LoadImageUsingMemoryModule(lldb_private::Process *process); + + bool LoadImageUsingFileAddress(lldb_private::Process *process); + + using collection_type = std::vector<KModImageInfo>; + + private: + lldb::ModuleSP m_module_sp; + lldb::ModuleSP m_memory_module_sp; + lldb::addr_t m_load_address = LLDB_INVALID_ADDRESS; + lldb_private::UUID m_uuid; + bool m_is_kernel = false; + std::string m_name; + std::string m_path; + uint32_t m_stop_id = UINT32_MAX; + }; + + void PrivateInitialize(lldb_private::Process *process); + + void Clear(bool clear_process); + + void Update(); + + void LoadKernelModules(); + + void ReadAllKmods(); + + bool ReadAllKmods(lldb_private::Address linker_files_head_address, + KModImageInfo::collection_type &kmods_list); + + bool ReadKmodsListHeader(); + + bool ParseKmods(lldb_private::Address linker_files_head_address); + + void SetNotificationBreakPoint(); + + static lldb_private::UUID + CheckForKernelImageAtAddress(lldb_private::Process *process, + lldb::addr_t address, + bool *read_error = nullptr); + + static lldb::addr_t FindKernelAtLoadAddress(lldb_private::Process *process); + + static bool ReadELFHeader(lldb_private::Process *process, + lldb::addr_t address, llvm::ELF::Elf32_Ehdr &header, + bool *read_error = nullptr); + + lldb_private::Process *m_process; + lldb_private::Address m_linker_file_list_struct_addr; + lldb_private::Address m_linker_file_head_addr; + lldb::addr_t m_kernel_load_address; + KModImageInfo m_kernel_image_info; + KModImageInfo::collection_type m_linker_files_list; + std::recursive_mutex m_mutex; + std::unordered_map<std::string, lldb_private::UUID> m_kld_name_to_uuid; + +private: + DynamicLoaderFreeBSDKernel(const DynamicLoaderFreeBSDKernel &) = delete; + + const DynamicLoaderFreeBSDKernel & + operator=(const DynamicLoaderFreeBSDKernel &) = delete; +}; + +#endif diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 2da971dff895b4a..43ab87f08e19251 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -935,6 +935,16 @@ lldb_private::Address ObjectFileELF::GetEntryPointAddress() { } Address ObjectFileELF::GetBaseAddress() { + if (GetType() == ObjectFile::eTypeObjectFile) { + for (SectionHeaderCollIter I = std::next(m_section_headers.begin()); + I != m_section_headers.end(); ++I) { + const ELFSectionHeaderInfo &header = *I; + if (header.sh_flags & SHF_ALLOC) + return Address(GetSectionList()->FindSectionByID(SectionIndex(I)), 0); + } + return LLDB_INVALID_ADDRESS; + } + for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) { const ELFProgramHeader &H = EnumPHdr.value(); if (H.p_type != PT_LOAD) @@ -1764,7 +1774,12 @@ class VMAddressProvider { VMRange GetVMRange(const ELFSectionHeader &H) { addr_t Address = H.sh_addr; addr_t Size = H.sh_flags & SHF_ALLOC ? H.sh_size : 0; - if (ObjectType == ObjectFile::Type::eTypeObjectFile && Segments.empty() && (H.sh_flags & SHF_ALLOC)) { + + // When this is a debug file for relocatable file, the address is all zero + // and thus needs to use accumulate method + if ((ObjectType == ObjectFile::Type::eTypeObjectFile || + (ObjectType == ObjectFile::Type::eTypeDebugInfo && H.sh_addr == 0)) && + Segments.empty() && (H.sh_flags & SHF_ALLOC)) { NextVMAddress = llvm::alignTo(NextVMAddress, std::max<addr_t>(H.sh_addralign, 1)); Address = NextVMAddress; @@ -3454,10 +3469,28 @@ ObjectFile::Strata ObjectFileELF::CalculateStrata() { 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; + { + SectionList *section_list = GetSectionList(); + if (section_list) { + static ConstString loader_section_name(".interp"); + SectionSP loader_section = + section_list->FindSectionByName(loader_section_name); + if (loader_section) { + char buffer[256]; + size_t read_size = + ReadSectionData(loader_section.get(), 0, buffer, sizeof(buffer)); + + // We compare the content of .interp section + // It will contains \0 when counting read_size, so the size needs to + // decrease by one + llvm::StringRef loader_name(buffer, read_size - 1); + llvm::StringRef freebsd_kernel_loader_name("/red/herring"); + if (loader_name.equals(freebsd_kernel_loader_name)) + return eStrataKernel; + } + } + return eStrataUser; + } case llvm::ELF::ET_DYN: // 3 - Shared object file diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp index e3707365a9c3f19..601f5df43dbba4e 100644 --- a/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp +++ b/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp @@ -10,7 +10,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Target/DynamicLoader.h" -#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h" #include "ProcessFreeBSDKernel.h" #include "ThreadFreeBSDKernel.h" @@ -262,7 +262,7 @@ Status ProcessFreeBSDKernel::DoLoadCore() { DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() { if (m_dyld_up.get() == nullptr) m_dyld_up.reset(DynamicLoader::FindPlugin( - this, DynamicLoaderStatic::GetPluginNameStatic())); + this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic())); return m_dyld_up.get(); } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits