diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/DynamicLoader')
16 files changed, 5136 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp new file mode 100644 index 000000000000..e504e6cbf692 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.cpp @@ -0,0 +1,788 @@ +//===-- 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()) { + 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 (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) { + 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 (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 %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) { + 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/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.h new file mode 100644 index 000000000000..d8656e9c49df --- /dev/null +++ b/contrib/llvm-project/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/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp new file mode 100644 index 000000000000..96c94535c623 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.cpp @@ -0,0 +1,600 @@ +//===-- DynamicLoaderHexagonDYLD.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/BreakpointLocation.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "DynamicLoaderHexagonDYLD.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderHexagonDYLD) + +// Aidan 21/05/2014 +// +// Notes about hexagon dynamic loading: +// +// When we connect to a target we find the dyld breakpoint address. We put +// a +// breakpoint there with a callback 'RendezvousBreakpointHit()'. +// +// It is possible to find the dyld structure address from the ELF symbol +// table, +// but in the case of the simulator it has not been initialized before the +// target calls dlinit(). +// +// We can only safely parse the dyld structure after we hit the dyld +// breakpoint +// since at that time we know dlinit() must have been called. +// + +// Find the load address of a symbol +static lldb::addr_t findSymbolAddress(Process *proc, ConstString findName) { + assert(proc != nullptr); + + ModuleSP module = proc->GetTarget().GetExecutableModule(); + assert(module.get() != nullptr); + + ObjectFile *exe = module->GetObjectFile(); + assert(exe != nullptr); + + lldb_private::Symtab *symtab = exe->GetSymtab(); + assert(symtab != nullptr); + + for (size_t i = 0; i < symtab->GetNumSymbols(); i++) { + const Symbol *sym = symtab->SymbolAtIndex(i); + assert(sym != nullptr); + ConstString symName = sym->GetName(); + + if (ConstString::Compare(findName, symName) == 0) { + Address addr = sym->GetAddress(); + return addr.GetLoadAddress(&proc->GetTarget()); + } + } + return LLDB_INVALID_ADDRESS; +} + +void DynamicLoaderHexagonDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderHexagonDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderHexagonDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in Hexagon processes."; +} + +DynamicLoader *DynamicLoaderHexagonDYLD::CreateInstance(Process *process, + bool force) { + bool create = force; + if (!create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getArch() == llvm::Triple::hexagon) + create = true; + } + + if (create) + return new DynamicLoaderHexagonDYLD(process); + return nullptr; +} + +DynamicLoaderHexagonDYLD::DynamicLoaderHexagonDYLD(Process *process) + : DynamicLoader(process), m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS), + m_dyld_bid(LLDB_INVALID_BREAK_ID) {} + +DynamicLoaderHexagonDYLD::~DynamicLoaderHexagonDYLD() { + if (m_dyld_bid != LLDB_INVALID_BREAK_ID) { + m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid); + m_dyld_bid = LLDB_INVALID_BREAK_ID; + } +} + +void DynamicLoaderHexagonDYLD::DidAttach() { + ModuleSP executable; + addr_t load_offset; + + executable = GetTargetExecutable(); + + // Find the difference between the desired load address in the elf file and + // the real load address in memory + load_offset = ComputeLoadOffset(); + + // Check that there is a valid executable + if (executable.get() == nullptr) + return; + + // Disable JIT for hexagon targets because its not supported + m_process->SetCanJIT(false); + + // Enable Interpreting of function call expressions + m_process->SetCanInterpretFunctionCalls(true); + + // Add the current executable to the module list + ModuleList module_list; + module_list.Append(executable); + + // Map the loaded sections of this executable + if (load_offset != LLDB_INVALID_ADDRESS) + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true); + + // AD: confirm this? + // Load into LLDB all of the currently loaded executables in the stub + LoadAllCurrentModules(); + + // AD: confirm this? + // Callback for the target to give it the loaded module list + m_process->GetTarget().ModulesDidLoad(module_list); + + // Try to set a breakpoint at the rendezvous breakpoint. DidLaunch uses + // ProbeEntry() instead. That sets a breakpoint, at the dyld breakpoint + // address, with a callback so that when hit, the dyld structure can be + // parsed. + if (!SetRendezvousBreakpoint()) { + // fail + } +} + +void DynamicLoaderHexagonDYLD::DidLaunch() {} + +/// Checks to see if the target module has changed, updates the target +/// accordingly and returns the target executable module. +ModuleSP DynamicLoaderHexagonDYLD::GetTargetExecutable() { + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + // There is no executable + if (!executable.get()) + return executable; + + // The target executable file does not exits + if (!FileSystem::Instance().Exists(executable->GetFileSpec())) + return executable; + + // Prep module for loading + ModuleSpec module_spec(executable->GetFileSpec(), + executable->GetArchitecture()); + ModuleSP module_sp(new Module(module_spec)); + + // Check if the executable has changed and set it to the target executable if + // they differ. + if (module_sp.get() && module_sp->GetUUID().IsValid() && + executable->GetUUID().IsValid()) { + // if the executable has changed ?? + if (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } else if (executable->FileHasChanged()) + executable.reset(); + + if (executable.get()) + return executable; + + // TODO: What case is this code used? + executable = target.GetOrCreateModule(module_spec, true /* notify */); + if (executable.get() != target.GetExecutableModulePointer()) { + // Don't load dependent images since we are in dyld where we will know and + // find out about all images that are loaded + target.SetExecutableModule(executable, eLoadDependentsNo); + } + + return executable; +} + +// AD: Needs to be updated? +Status DynamicLoaderHexagonDYLD::CanLoadImage() { return Status(); } + +void DynamicLoaderHexagonDYLD::UpdateLoadedSections(ModuleSP module, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + Target &target = m_process->GetTarget(); + const SectionList *sections = GetSectionListFromModule(module); + + assert(sections && "SectionList missing from loaded module."); + + m_loaded_modules[module] = link_map_addr; + + const size_t num_sections = sections->GetSize(); + + for (unsigned i = 0; i < num_sections; ++i) { + SectionSP section_sp(sections->GetSectionAtIndex(i)); + lldb::addr_t new_load_addr = section_sp->GetFileAddress() + base_addr; + + // AD: 02/05/14 + // since our memory map starts from address 0, we must not ignore + // sections that load to address 0. This violates the reference + // ELF spec, however is used for Hexagon. + + // If the file address of the section is zero then this is not an + // allocatable/loadable section (property of ELF sh_addr). Skip it. + // if (new_load_addr == base_addr) + // continue; + + target.SetSectionLoadAddress(section_sp, new_load_addr); + } +} + +/// Removes the loaded sections from the target in \p module. +/// +/// \param module The module to traverse. +void DynamicLoaderHexagonDYLD::UnloadSections(const ModuleSP module) { + Target &target = m_process->GetTarget(); + const SectionList *sections = GetSectionListFromModule(module); + + assert(sections && "SectionList missing from unloaded module."); + + m_loaded_modules.erase(module); + + const size_t num_sections = sections->GetSize(); + for (size_t i = 0; i < num_sections; ++i) { + SectionSP section_sp(sections->GetSectionAtIndex(i)); + target.SetSectionUnloaded(section_sp); + } +} + +// Place a breakpoint on <_rtld_debug_state> +bool DynamicLoaderHexagonDYLD::SetRendezvousBreakpoint() { + Log *log = GetLog(LLDBLog::DynamicLoader); + + // This is the original code, which want to look in the rendezvous structure + // to find the breakpoint address. Its backwards for us, since we can easily + // find the breakpoint address, since it is exported in our executable. We + // however know that we cant read the Rendezvous structure until we have hit + // the breakpoint once. + const ConstString dyldBpName("_rtld_debug_state"); + addr_t break_addr = findSymbolAddress(m_process, dyldBpName); + + Target &target = m_process->GetTarget(); + + // Do not try to set the breakpoint if we don't know where to put it + if (break_addr == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "Unable to locate _rtld_debug_state breakpoint address"); + + return false; + } + + // Save the address of the rendezvous structure + m_rendezvous.SetBreakAddress(break_addr); + + // If we haven't set the breakpoint before then set it + if (m_dyld_bid == LLDB_INVALID_BREAK_ID) { + Breakpoint *dyld_break = + target.CreateBreakpoint(break_addr, true, false).get(); + dyld_break->SetCallback(RendezvousBreakpointHit, this, true); + dyld_break->SetBreakpointKind("shared-library-event"); + m_dyld_bid = dyld_break->GetID(); + + // Make sure our breakpoint is at the right address. + assert(target.GetBreakpointByID(m_dyld_bid) + ->FindLocationByAddress(break_addr) + ->GetBreakpoint() + .GetID() == m_dyld_bid); + + if (log && dyld_break == nullptr) + LLDB_LOGF(log, "Failed to create _rtld_debug_state breakpoint"); + + // check we have successfully set bp + return (dyld_break != nullptr); + } else + // rendezvous already set + return true; +} + +// We have just hit our breakpoint at <_rtld_debug_state> +bool DynamicLoaderHexagonDYLD::RendezvousBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + LLDB_LOGF(log, "Rendezvous breakpoint hit!"); + + DynamicLoaderHexagonDYLD *dyld_instance = nullptr; + dyld_instance = static_cast<DynamicLoaderHexagonDYLD *>(baton); + + // if the dyld_instance is still not valid then try to locate it on the + // symbol table + if (!dyld_instance->m_rendezvous.IsValid()) { + Process *proc = dyld_instance->m_process; + + const ConstString dyldStructName("_rtld_debug"); + addr_t structAddr = findSymbolAddress(proc, dyldStructName); + + if (structAddr != LLDB_INVALID_ADDRESS) { + dyld_instance->m_rendezvous.SetRendezvousAddress(structAddr); + + LLDB_LOGF(log, "Found _rtld_debug structure @ 0x%08" PRIx64, structAddr); + } else { + LLDB_LOGF(log, "Unable to resolve the _rtld_debug structure"); + } + } + + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +/// Helper method for RendezvousBreakpointHit. Updates LLDB's current set +/// of loaded modules. +void DynamicLoaderHexagonDYLD::RefreshModules() { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (!m_rendezvous.Resolve()) + return; + + HexagonDYLDRendezvous::iterator I; + HexagonDYLDRendezvous::iterator E; + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + if (m_rendezvous.ModulesDidLoad()) { + ModuleList new_modules; + + E = m_rendezvous.loaded_end(); + for (I = m_rendezvous.loaded_begin(); I != E; ++I) { + FileSpec file(I->path); + FileSystem::Instance().Resolve(file); + ModuleSP module_sp = + LoadModuleAtAddress(file, I->link_addr, I->base_addr, true); + if (module_sp.get()) { + loaded_modules.AppendIfNeeded(module_sp); + new_modules.Append(module_sp); + } + + if (log) { + LLDB_LOGF(log, "Target is loading '%s'", I->path.c_str()); + if (!module_sp.get()) + LLDB_LOGF(log, "LLDB failed to load '%s'", I->path.c_str()); + else + LLDB_LOGF(log, "LLDB successfully loaded '%s'", I->path.c_str()); + } + } + m_process->GetTarget().ModulesDidLoad(new_modules); + } + + if (m_rendezvous.ModulesDidUnload()) { + ModuleList old_modules; + + E = m_rendezvous.unloaded_end(); + for (I = m_rendezvous.unloaded_begin(); I != E; ++I) { + FileSpec file(I->path); + FileSystem::Instance().Resolve(file); + ModuleSpec module_spec(file); + ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec); + + if (module_sp.get()) { + old_modules.Append(module_sp); + UnloadSections(module_sp); + } + + LLDB_LOGF(log, "Target is unloading '%s'", I->path.c_str()); + } + loaded_modules.Remove(old_modules); + m_process->GetTarget().ModulesDidUnload(old_modules, false); + } +} + +// AD: This is very different to the Static Loader code. +// It may be wise to look over this and its relation to stack +// unwinding. +ThreadPlanSP +DynamicLoaderHexagonDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + ThreadPlanSP thread_plan_sp; + + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *sym = context.symbol; + + if (sym == nullptr || !sym->IsTrampoline()) + return thread_plan_sp; + + const ConstString sym_name = + sym->GetMangled().GetName(Mangled::ePreferMangled); + if (!sym_name) + return thread_plan_sp; + + SymbolContextList target_symbols; + Target &target = thread.GetProcess()->GetTarget(); + const ModuleList &images = target.GetImages(); + + images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); + if (target_symbols.GetSize() == 0) + return thread_plan_sp; + + typedef std::vector<lldb::addr_t> AddressVector; + AddressVector addrs; + for (const SymbolContext &context : target_symbols) { + AddressRange range; + context.GetAddressRange(eSymbolContextEverything, 0, false, range); + lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); + if (addr != LLDB_INVALID_ADDRESS) + addrs.push_back(addr); + } + + if (addrs.size() > 0) { + AddressVector::iterator start = addrs.begin(); + AddressVector::iterator end = addrs.end(); + + llvm::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp = + std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop); + } + + return thread_plan_sp; +} + +/// Helper for the entry breakpoint callback. Resolves the load addresses +/// of all dependent modules. +void DynamicLoaderHexagonDYLD::LoadAllCurrentModules() { + HexagonDYLDRendezvous::iterator I; + HexagonDYLDRendezvous::iterator E; + ModuleList module_list; + + if (!m_rendezvous.Resolve()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF( + log, + "DynamicLoaderHexagonDYLD::%s unable to resolve rendezvous address", + __FUNCTION__); + return; + } + + // The rendezvous class doesn't enumerate the main module, so track that + // ourselves here. + ModuleSP executable = GetTargetExecutable(); + m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + const char *module_path = I->path.c_str(); + FileSpec file(module_path); + ModuleSP module_sp = + LoadModuleAtAddress(file, I->link_addr, I->base_addr, true); + if (module_sp.get()) { + module_list.Append(module_sp); + } else { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, + "DynamicLoaderHexagonDYLD::%s failed loading module %s at " + "0x%" PRIx64, + __FUNCTION__, module_path, I->base_addr); + } + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +/// Computes a value for m_load_offset returning the computed address on +/// success and LLDB_INVALID_ADDRESS on failure. +addr_t DynamicLoaderHexagonDYLD::ComputeLoadOffset() { + // Here we could send a GDB packet to know the load offset + // + // send: $qOffsets#4b + // get: Text=0;Data=0;Bss=0 + // + // Currently qOffsets is not supported by pluginProcessGDBRemote + // + return 0; +} + +// Here we must try to read the entry point directly from the elf header. This +// is possible if the process is not relocatable or dynamically linked. +// +// an alternative is to look at the PC if we can be sure that we have connected +// when the process is at the entry point. +// I dont think that is reliable for us. +addr_t DynamicLoaderHexagonDYLD::GetEntryPoint() { + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + // check we have a valid process + if (m_process == nullptr) + return LLDB_INVALID_ADDRESS; + // Get the current executable module + Module &module = *(m_process->GetTarget().GetExecutableModule().get()); + // Get the object file (elf file) for this module + lldb_private::ObjectFile &object = *(module.GetObjectFile()); + // Check if the file is executable (ie, not shared object or relocatable) + if (object.IsExecutable()) { + // Get the entry point address for this object + lldb_private::Address entry = object.GetEntryPointAddress(); + // Return the entry point address + return entry.GetFileAddress(); + } + // No idea so back out + return LLDB_INVALID_ADDRESS; +} + +const SectionList *DynamicLoaderHexagonDYLD::GetSectionListFromModule( + const ModuleSP module) const { + SectionList *sections = nullptr; + if (module.get()) { + ObjectFile *obj_file = module->GetObjectFile(); + if (obj_file) { + sections = obj_file->GetSectionList(); + } + } + return sections; +} + +static int ReadInt(Process *process, addr_t addr) { + Status error; + int value = (int)process->ReadUnsignedIntegerFromMemory( + addr, sizeof(uint32_t), 0, error); + if (error.Fail()) + return -1; + else + return value; +} + +lldb::addr_t +DynamicLoaderHexagonDYLD::GetThreadLocalData(const lldb::ModuleSP module, + const lldb::ThreadSP thread, + lldb::addr_t tls_file_addr) { + auto it = m_loaded_modules.find(module); + if (it == m_loaded_modules.end()) + return LLDB_INVALID_ADDRESS; + + addr_t link_map = it->second; + if (link_map == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + const HexagonDYLDRendezvous::ThreadInfo &metadata = + m_rendezvous.GetThreadInfo(); + if (!metadata.valid) + return LLDB_INVALID_ADDRESS; + + // Get the thread pointer. + addr_t tp = thread->GetThreadPointer(); + if (tp == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + // Find the module's modid. + int modid = ReadInt(m_process, link_map + metadata.modid_offset); + if (modid == -1) + return LLDB_INVALID_ADDRESS; + + // Lookup the DTV structure for this thread. + addr_t dtv_ptr = tp + metadata.dtv_offset; + addr_t dtv = ReadPointer(dtv_ptr); + if (dtv == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + // Find the TLS block for this module. + addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid; + addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset); + + Module *mod = module.get(); + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, + "DynamicLoaderHexagonDYLD::Performed TLS lookup: " + "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64 + ", modid=%i, tls_block=0x%" PRIx64, + mod->GetObjectName().AsCString(""), link_map, tp, modid, tls_block); + + if (tls_block == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + else + return tls_block + tls_file_addr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h new file mode 100644 index 000000000000..54711f5e6bb3 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/DynamicLoaderHexagonDYLD.h @@ -0,0 +1,134 @@ +//===-- DynamicLoaderHexagonDYLD.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_HEXAGON_DYLD_DYNAMICLOADERHEXAGONDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_HEXAGON_DYLD_DYNAMICLOADERHEXAGONDYLD_H + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +#include "HexagonDYLDRendezvous.h" + +class DynamicLoaderHexagonDYLD : public lldb_private::DynamicLoader { +public: + DynamicLoaderHexagonDYLD(lldb_private::Process *process); + + ~DynamicLoaderHexagonDYLD() override; + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "hexagon-dyld"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + // DynamicLoader protocol + + void DidAttach() override; + + void DidLaunch() override; + + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Status CanLoadImage() override; + + lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module, + const lldb::ThreadSP thread, + lldb::addr_t tls_file_addr) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + /// Runtime linker rendezvous structure. + HexagonDYLDRendezvous m_rendezvous; + + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + + /// Rendezvous breakpoint. + lldb::break_id_t m_dyld_bid; + + /// Loaded module list. (link map for each module) + std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>> + m_loaded_modules; + + /// Enables a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + bool SetRendezvousBreakpoint(); + + /// Callback routine which updates the current list of loaded modules based + /// on the information supplied by the runtime linker. + static bool RendezvousBreakpointHit( + void *baton, lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set + /// of loaded modules. + void RefreshModules(); + + /// Updates the load address of every allocatable section in \p module. + /// + /// \param module The module to traverse. + /// + /// \param link_map_addr The virtual address of the link map for the @p + /// module. + /// + /// \param base_addr The virtual base address \p module is loaded at. + void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + + /// Removes the loaded sections from the target in \p module. + /// + /// \param module The module to traverse. + void UnloadSections(const lldb::ModuleSP module) override; + + /// Callback routine invoked when we hit the breakpoint on process entry. + /// + /// This routine is responsible for resolving the load addresses of all + /// dependent modules required by the inferior and setting up the rendezvous + /// breakpoint. + static bool + EntryBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + /// Helper for the entry breakpoint callback. Resolves the load addresses + /// of all dependent modules. + void LoadAllCurrentModules(); + + /// Computes a value for m_load_offset returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t ComputeLoadOffset(); + + /// Computes a value for m_entry_point returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t GetEntryPoint(); + + /// Checks to see if the target module has changed, updates the target + /// accordingly and returns the target executable module. + lldb::ModuleSP GetTargetExecutable(); + + /// return the address of the Rendezvous breakpoint + lldb::addr_t FindRendezvousBreakpointAddress(); + +private: + const lldb_private::SectionList * + GetSectionListFromModule(const lldb::ModuleSP module) const; +}; + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_HEXAGON_DYLD_DYNAMICLOADERHEXAGONDYLD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp new file mode 100644 index 000000000000..d2bf2586cd82 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.cpp @@ -0,0 +1,370 @@ +//===-- HexagonDYLDRendezvous.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/Core/Module.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "HexagonDYLDRendezvous.h" + +using namespace lldb; +using namespace lldb_private; + +/// Locates the address of the rendezvous structure. Returns the address on +/// success and LLDB_INVALID_ADDRESS on failure. +static addr_t ResolveRendezvousAddress(Process *process) { + addr_t info_location; + addr_t info_addr; + Status error; + + info_location = process->GetImageInfoAddress(); + + if (info_location == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + info_addr = process->ReadPointerFromMemory(info_location, error); + if (error.Fail()) + return LLDB_INVALID_ADDRESS; + + if (info_addr == 0) + return LLDB_INVALID_ADDRESS; + + return info_addr; +} + +HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process) + : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(), + m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() { + m_thread_info.valid = false; + m_thread_info.dtv_offset = 0; + m_thread_info.dtv_slot_size = 0; + m_thread_info.modid_offset = 0; + m_thread_info.tls_offset = 0; + + // Cache a copy of the executable path + if (m_process) { + Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer(); + if (exe_mod) + exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX); + } +} + +bool HexagonDYLDRendezvous::Resolve() { + const size_t word_size = 4; + Rendezvous info; + size_t address_size; + size_t padding; + addr_t info_addr; + addr_t cursor; + + address_size = m_process->GetAddressByteSize(); + padding = address_size - word_size; + + if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) + cursor = info_addr = ResolveRendezvousAddress(m_process); + else + cursor = info_addr = m_rendezvous_addr; + + if (cursor == LLDB_INVALID_ADDRESS) + return false; + + if (!(cursor = ReadWord(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadPointer(cursor + padding, &info.map_addr))) + return false; + + if (!(cursor = ReadPointer(cursor, &info.brk))) + return false; + + if (!(cursor = ReadWord(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadPointer(cursor + padding, &info.ldbase))) + return false; + + // The rendezvous was successfully read. Update our internal state. + m_rendezvous_addr = info_addr; + m_previous = m_current; + m_current = info; + + return UpdateSOEntries(); +} + +void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) { + m_rendezvous_addr = addr; +} + +bool HexagonDYLDRendezvous::IsValid() { + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +bool HexagonDYLDRendezvous::UpdateSOEntries() { + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + // When the previous and current states are consistent this is the first time + // we have been asked to update. Just take a snapshot of the currently + // loaded modules. + if (m_previous.state == eConsistent && m_current.state == eConsistent) + return TakeSnapshot(m_soentries); + + // If we are about to add or remove a shared object clear out the current + // state and take a snapshot of the currently loaded images. + if (m_current.state == eAdd || m_current.state == eDelete) { + // this is a fudge so that we can clear the assert below. + m_previous.state = eConsistent; + // We hit this assert on the 2nd run of this function after running the + // calc example + assert(m_previous.state == eConsistent); + m_soentries.clear(); + m_added_soentries.clear(); + m_removed_soentries.clear(); + return TakeSnapshot(m_soentries); + } + assert(m_current.state == eConsistent); + + // Otherwise check the previous state to determine what to expect and update + // accordingly. + if (m_previous.state == eAdd) + return UpdateSOEntriesForAddition(); + else if (m_previous.state == eDelete) + return UpdateSOEntriesForDeletion(); + + return false; +} + +bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() { + SOEntry entry; + iterator pos; + + assert(m_previous.state == eAdd); + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. On Linux this is + // indicated by an empty path in the entry. On FreeBSD it is the name of + // the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + if (!llvm::is_contained(m_soentries, entry)) { + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + return true; +} + +bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() { + SOEntryList entry_list; + iterator pos; + + assert(m_previous.state == eDelete); + + if (!TakeSnapshot(entry_list)) + return false; + + for (iterator I = begin(); I != end(); ++I) { + if (!llvm::is_contained(entry_list, *I)) + m_removed_soentries.push_back(*I); + } + + m_soentries = entry_list; + return true; +} + +bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) { + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. On Linux this is + // indicated by an empty path in the entry. On FreeBSD it is the name of + // the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + entry_list.push_back(entry); + } + + return true; +} + +addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, + size_t size) { + Status error; + + *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error); + if (error.Fail()) + return 0; + + return addr + size; +} + +addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) { + Status error; + + *dst = m_process->ReadPointerFromMemory(addr, error); + if (error.Fail()) + return 0; + + return addr + m_process->GetAddressByteSize(); +} + +std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) { + std::string str; + Status error; + size_t size; + char c; + + if (addr == LLDB_INVALID_ADDRESS) + return std::string(); + + for (;;) { + size = m_process->ReadMemory(addr, &c, 1, error); + if (size != 1 || error.Fail()) + return std::string(); + if (c == 0) + break; + else { + str.push_back(c); + addr++; + } + } + + return str; +} + +bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, + SOEntry &entry) { + entry.clear(); + entry.link_addr = addr; + + if (!(addr = ReadPointer(addr, &entry.base_addr))) + return false; + + if (!(addr = ReadPointer(addr, &entry.path_addr))) + return false; + + if (!(addr = ReadPointer(addr, &entry.dyn_addr))) + return false; + + if (!(addr = ReadPointer(addr, &entry.next))) + return false; + + if (!(addr = ReadPointer(addr, &entry.prev))) + return false; + + entry.path = ReadStringFromMemory(entry.path_addr); + + return true; +} + +bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field, + uint32_t &value) { + Target &target = m_process->GetTarget(); + + SymbolContextList list; + target.GetImages().FindSymbolsWithNameAndType(ConstString(name), + eSymbolTypeAny, list); + if (list.IsEmpty()) + return false; + + Address address = list[0].symbol->GetAddress(); + addr_t addr = address.GetLoadAddress(&target); + if (addr == LLDB_INVALID_ADDRESS) + return false; + + Status error; + value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory( + addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error); + if (error.Fail()) + return false; + + if (field == eSize) + value /= 8; // convert bits to bytes + + return true; +} + +const HexagonDYLDRendezvous::ThreadInfo & +HexagonDYLDRendezvous::GetThreadInfo() { + if (!m_thread_info.valid) { + bool ok = true; + + ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset, + m_thread_info.dtv_offset); + ok &= + FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size); + ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset, + m_thread_info.modid_offset); + ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset, + m_thread_info.tls_offset); + + if (ok) + m_thread_info.valid = true; + } + + return m_thread_info; +} + +void HexagonDYLDRendezvous::DumpToLog(Log *log) const { + int state = GetState(); + + if (!log) + return; + + log->PutCString("HexagonDYLDRendezvous:"); + LLDB_LOGF(log, " Address: %" PRIx64, GetRendezvousAddress()); + LLDB_LOGF(log, " Version: %" PRIu64, GetVersion()); + LLDB_LOGF(log, " Link : %" PRIx64, GetLinkMapAddress()); + LLDB_LOGF(log, " Break : %" PRIx64, GetBreakAddress()); + LLDB_LOGF(log, " LDBase : %" PRIx64, GetLDBase()); + LLDB_LOGF(log, " State : %s", + (state == eConsistent) + ? "consistent" + : (state == eAdd) ? "add" + : (state == eDelete) ? "delete" : "unknown"); + + iterator I = begin(); + iterator E = end(); + + if (I != E) + log->PutCString("HexagonDYLDRendezvous SOEntries:"); + + for (int i = 1; I != E; ++I, ++i) { + LLDB_LOGF(log, "\n SOEntry [%d] %s", i, I->path.c_str()); + LLDB_LOGF(log, " Base : %" PRIx64, I->base_addr); + LLDB_LOGF(log, " Path : %" PRIx64, I->path_addr); + LLDB_LOGF(log, " Dyn : %" PRIx64, I->dyn_addr); + LLDB_LOGF(log, " Next : %" PRIx64, I->next); + LLDB_LOGF(log, " Prev : %" PRIx64, I->prev); + } +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h new file mode 100644 index 000000000000..c34e02ed5678 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Hexagon-DYLD/HexagonDYLDRendezvous.h @@ -0,0 +1,244 @@ +//===-- HexagonDYLDRendezvous.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_HEXAGON_DYLD_HEXAGONDYLDRENDEZVOUS_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_HEXAGON_DYLD_HEXAGONDYLDRENDEZVOUS_H + +#include <limits.h> +#include <list> +#include <map> +#include <string> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +class Process; +} + +/// \class HexagonDYLDRendezvous +/// Interface to the runtime linker. +/// +/// A structure is present in a processes memory space which is updated by the +/// runtime liker each time a module is loaded or unloaded. This class +/// provides an interface to this structure and maintains a consistent +/// snapshot of the currently loaded modules. +class HexagonDYLDRendezvous { + + // This structure is used to hold the contents of the debug rendezvous + // information (struct r_debug) as found in the inferiors memory. Note that + // the layout of this struct is not binary compatible, it is simply large + // enough to hold the information on both 32 and 64 bit platforms. + struct Rendezvous { + uint64_t version = 0; + lldb::addr_t map_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t brk = LLDB_INVALID_ADDRESS; + uint64_t state = 0; + lldb::addr_t ldbase = 0; + + Rendezvous() = default; + }; + +public: + // Various metadata supplied by the inferior's threading library to describe + // the per-thread state. + struct ThreadInfo { + bool valid; // whether we read valid metadata + uint32_t dtv_offset; // offset of DTV pointer within pthread + uint32_t dtv_slot_size; // size of one DTV slot + uint32_t modid_offset; // offset of module ID within link_map + uint32_t tls_offset; // offset of TLS pointer within DTV slot + }; + + HexagonDYLDRendezvous(lldb_private::Process *process); + + /// Update the internal snapshot of runtime linker rendezvous and recompute + /// the currently loaded modules. + /// + /// This method should be called once one start up, then once each time the + /// runtime linker enters the function given by GetBreakAddress(). + /// + /// \returns true on success and false on failure. + /// + /// \see GetBreakAddress(). + bool Resolve(); + + /// \returns true if this rendezvous has been located in the inferiors + /// address space and false otherwise. + bool IsValid(); + + /// \returns the address of the rendezvous structure in the inferiors + /// address space. + lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; } + + /// Provide the dyld structure address + void SetRendezvousAddress(lldb::addr_t); + + /// \returns the version of the rendezvous protocol being used. + uint64_t GetVersion() const { return m_current.version; } + + /// \returns address in the inferiors address space containing the linked + /// list of shared object descriptors. + lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; } + + /// A breakpoint should be set at this address and Resolve called on each + /// hit. + /// + /// \returns the address of a function called by the runtime linker each + /// time a module is loaded/unloaded, or about to be loaded/unloaded. + /// + /// \see Resolve() + lldb::addr_t GetBreakAddress() const { return m_current.brk; } + + /// In hexagon it is possible that we can know the dyld breakpoint without + /// having to find it from the rendezvous structure + /// + void SetBreakAddress(lldb::addr_t addr) { m_current.brk = addr; } + + /// Returns the current state of the rendezvous structure. + uint64_t GetState() const { return m_current.state; } + + /// \returns the base address of the runtime linker in the inferiors address + /// space. + lldb::addr_t GetLDBase() const { return m_current.ldbase; } + + /// \returns the thread layout metadata from the inferiors thread library. + const ThreadInfo &GetThreadInfo(); + + /// \returns true if modules have been loaded into the inferior since the + /// last call to Resolve(). + bool ModulesDidLoad() const { return !m_added_soentries.empty(); } + + /// \returns true if modules have been unloaded from the inferior since the + /// last call to Resolve(). + bool ModulesDidUnload() const { return !m_removed_soentries.empty(); } + + void DumpToLog(lldb_private::Log *log) const; + + /// Constants describing the state of the rendezvous. + /// + /// \see GetState(). + enum RendezvousState { + eConsistent = 0, + eAdd, + eDelete, + }; + + /// Structure representing the shared objects currently loaded into the + /// inferior process. + /// + /// This object is a rough analogue to the struct link_map object which + /// actually lives in the inferiors memory. + struct SOEntry { + lldb::addr_t link_addr; ///< Address of this link_map. + lldb::addr_t base_addr; ///< Base address of the loaded object. + lldb::addr_t path_addr; ///< String naming the shared object. + lldb::addr_t dyn_addr; ///< Dynamic section of shared object. + lldb::addr_t next; ///< Address of next so_entry. + lldb::addr_t prev; ///< Address of previous so_entry. + std::string path; ///< File name of shared object. + + SOEntry() { clear(); } + + bool operator==(const SOEntry &entry) { return this->path == entry.path; } + + void clear() { + link_addr = 0; + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + path.clear(); + } + }; + +protected: + typedef std::list<SOEntry> SOEntryList; + +public: + typedef SOEntryList::const_iterator iterator; + + /// Iterators over all currently loaded modules. + iterator begin() const { return m_soentries.begin(); } + iterator end() const { return m_soentries.end(); } + + /// Iterators over all modules loaded into the inferior since the last call + /// to Resolve(). + iterator loaded_begin() const { return m_added_soentries.begin(); } + iterator loaded_end() const { return m_added_soentries.end(); } + + /// Iterators over all modules unloaded from the inferior since the last + /// call to Resolve(). + iterator unloaded_begin() const { return m_removed_soentries.begin(); } + iterator unloaded_end() const { return m_removed_soentries.end(); } + +protected: + lldb_private::Process *m_process; + + // Cached copy of executable pathname + char m_exe_path[PATH_MAX]; + + /// Location of the r_debug structure in the inferiors address space. + lldb::addr_t m_rendezvous_addr; + + /// Current and previous snapshots of the rendezvous structure. + Rendezvous m_current; + Rendezvous m_previous; + + /// List of SOEntry objects corresponding to the current link map state. + SOEntryList m_soentries; + + /// List of SOEntry's added to the link map since the last call to + /// Resolve(). + SOEntryList m_added_soentries; + + /// List of SOEntry's removed from the link map since the last call to + /// Resolve(). + SOEntryList m_removed_soentries; + + /// Threading metadata read from the inferior. + ThreadInfo m_thread_info; + + /// Reads an unsigned integer of \p size bytes from the inferior's address + /// space starting at \p addr. + /// + /// \returns addr + size if the read was successful and false otherwise. + lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size); + + /// Reads an address from the inferior's address space starting at \p addr. + /// + /// \returns addr + target address size if the read was successful and + /// 0 otherwise. + lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst); + + /// Reads a null-terminated C string from the memory location starting at @p + /// addr. + std::string ReadStringFromMemory(lldb::addr_t addr); + + /// Reads an SOEntry starting at \p addr. + bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry); + + /// Updates the current set of SOEntries, the set of added entries, and the + /// set of removed entries. + bool UpdateSOEntries(); + + bool UpdateSOEntriesForAddition(); + + bool UpdateSOEntriesForDeletion(); + + /// Reads the current list of shared objects according to the link map + /// supplied by the runtime linker. + bool TakeSnapshot(SOEntryList &entry_list); + + enum PThreadField { eSize, eNElem, eOffset }; + + bool FindMetadata(const char *name, PThreadField field, uint32_t &value); +}; + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_HEXAGON_DYLD_HEXAGONDYLDRENDEZVOUS_H diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp new file mode 100644 index 000000000000..1a9c4593b1b4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,792 @@ +//===-- DYLDRendezvous.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/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" + +#include "llvm/Support/Path.h" + +#include "DYLDRendezvous.h" + +using namespace lldb; +using namespace lldb_private; + +const char *DYLDRendezvous::StateToCStr(RendezvousState state) { + switch (state) { + case DYLDRendezvous::eConsistent: + return "eConsistent"; + case DYLDRendezvous::eAdd: + return "eAdd"; + case DYLDRendezvous::eDelete: + return "eDelete"; + } + return "<invalid RendezvousState>"; +} + +const char *DYLDRendezvous::ActionToCStr(RendezvousAction action) { + switch (action) { + case DYLDRendezvous::RendezvousAction::eTakeSnapshot: + return "eTakeSnapshot"; + case DYLDRendezvous::RendezvousAction::eAddModules: + return "eAddModules"; + case DYLDRendezvous::RendezvousAction::eRemoveModules: + return "eRemoveModules"; + case DYLDRendezvous::RendezvousAction::eNoAction: + return "eNoAction"; + } + return "<invalid RendezvousAction>"; +} + +DYLDRendezvous::DYLDRendezvous(Process *process) + : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), + m_executable_interpreter(false), m_current(), m_previous(), + m_loaded_modules(), m_soentries(), m_added_soentries(), + m_removed_soentries() { + m_thread_info.valid = false; + UpdateExecutablePath(); +} + +addr_t DYLDRendezvous::ResolveRendezvousAddress() { + Log *log = GetLog(LLDBLog::DynamicLoader); + addr_t info_location; + addr_t info_addr; + Status error; + + if (!m_process) { + LLDB_LOGF(log, "%s null process provided", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + // Try to get it from our process. This might be a remote process and might + // grab it via some remote-specific mechanism. + info_location = m_process->GetImageInfoAddress(); + LLDB_LOGF(log, "%s info_location = 0x%" PRIx64, __FUNCTION__, info_location); + + // If the process fails to return an address, fall back to seeing if the + // local object file can help us find it. + if (info_location == LLDB_INVALID_ADDRESS) { + Target *target = &m_process->GetTarget(); + if (target) { + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(target); + + if (addr.IsValid()) { + info_location = addr.GetLoadAddress(target); + LLDB_LOGF(log, + "%s resolved via direct object file approach to 0x%" PRIx64, + __FUNCTION__, info_location); + } else { + const Symbol *_r_debug = + target->GetExecutableModule()->FindFirstSymbolWithNameAndType( + ConstString("_r_debug")); + if (_r_debug) { + info_addr = _r_debug->GetAddress().GetLoadAddress(target); + if (info_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, + "%s resolved by finding symbol '_r_debug' whose value is " + "0x%" PRIx64, + __FUNCTION__, info_addr); + m_executable_interpreter = true; + return info_addr; + } + } + LLDB_LOGF(log, + "%s FAILED - direct object file approach did not yield a " + "valid address", + __FUNCTION__); + } + } + } + + if (info_location == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "%s FAILED - invalid info address", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + + LLDB_LOGF(log, "%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64, + __FUNCTION__, m_process->GetAddressByteSize(), info_location); + + info_addr = m_process->ReadPointerFromMemory(info_location, error); + if (error.Fail()) { + LLDB_LOGF(log, "%s FAILED - could not read from the info location: %s", + __FUNCTION__, error.AsCString()); + return LLDB_INVALID_ADDRESS; + } + + if (info_addr == 0) { + LLDB_LOGF(log, + "%s FAILED - the rendezvous address contained at 0x%" PRIx64 + " returned a null value", + __FUNCTION__, info_location); + return LLDB_INVALID_ADDRESS; + } + + return info_addr; +} + +void DYLDRendezvous::UpdateExecutablePath() { + if (m_process) { + Log *log = GetLog(LLDBLog::DynamicLoader); + Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer(); + if (exe_mod) { + m_exe_file_spec = exe_mod->GetPlatformFileSpec(); + LLDB_LOGF(log, "DYLDRendezvous::%s exe module executable path set: '%s'", + __FUNCTION__, m_exe_file_spec.GetPath().c_str()); + } else { + LLDB_LOGF(log, + "DYLDRendezvous::%s cannot cache exe module path: null " + "executable module pointer", + __FUNCTION__); + } + } +} + +void DYLDRendezvous::Rendezvous::DumpToLog(Log *log, const char *label) { + LLDB_LOGF(log, "%s Rendezvous: version = %" PRIu64 ", map_addr = 0x%16.16" + PRIx64 ", brk = 0x%16.16" PRIx64 ", state = %" PRIu64 + " (%s), ldbase = 0x%16.16" PRIx64, label ? label : "", version, + map_addr, brk, state, StateToCStr((RendezvousState)state), ldbase); +} + +bool DYLDRendezvous::Resolve() { + Log *log = GetLog(LLDBLog::DynamicLoader); + + const size_t word_size = 4; + Rendezvous info; + size_t address_size; + size_t padding; + addr_t info_addr; + addr_t cursor; + + address_size = m_process->GetAddressByteSize(); + padding = address_size - word_size; + LLDB_LOGF(log, + "DYLDRendezvous::%s address size: %" PRIu64 ", padding %" PRIu64, + __FUNCTION__, uint64_t(address_size), uint64_t(padding)); + + if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) + cursor = info_addr = + ResolveRendezvousAddress(); + else + cursor = info_addr = m_rendezvous_addr; + LLDB_LOGF(log, "DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, + cursor); + + if (cursor == LLDB_INVALID_ADDRESS) + return false; + + if (!(cursor = ReadWord(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadPointer(cursor + padding, &info.map_addr))) + return false; + + if (!(cursor = ReadPointer(cursor, &info.brk))) + return false; + + if (!(cursor = ReadWord(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadPointer(cursor + padding, &info.ldbase))) + return false; + + // The rendezvous was successfully read. Update our internal state. + m_rendezvous_addr = info_addr; + m_previous = m_current; + m_current = info; + + m_previous.DumpToLog(log, "m_previous"); + m_current.DumpToLog(log, "m_current "); + + if (m_current.map_addr == 0) + return false; + + if (UpdateSOEntriesFromRemote()) + return true; + + return UpdateSOEntries(); +} + +bool DYLDRendezvous::IsValid() { + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +DYLDRendezvous::RendezvousAction DYLDRendezvous::GetAction() const { + // If we have a core file, we will read the current rendezvous state + // from the core file's memory into m_current which can be in an inconsistent + // state, so we can't rely on its state to determine what we should do. We + // always need it to load all of the shared libraries one time when we attach + // to a core file. + if (IsCoreFile()) + return eTakeSnapshot; + + switch (m_current.state) { + + case eConsistent: + switch (m_previous.state) { + // When the previous and current states are consistent this is the first + // time we have been asked to update. Just take a snapshot of the + // currently loaded modules. + case eConsistent: + return eTakeSnapshot; + // If we are about to add or remove a shared object clear out the current + // state and take a snapshot of the currently loaded images. + case eAdd: + return eAddModules; + case eDelete: + return eRemoveModules; + } + break; + + case eAdd: + // If the main executable or a shared library defines a publicly visible + // symbol named "_r_debug", then it will cause problems once the executable + // that contains the symbol is loaded into the process. The correct + // "_r_debug" structure is currently found by LLDB by looking through + // the .dynamic section in the main executable and finding the DT_DEBUG tag + // entry. + // + // An issue comes up if someone defines another publicly visible "_r_debug" + // struct in their program. Sample code looks like: + // + // #include <link.h> + // r_debug _r_debug; + // + // If code like this is in an executable or shared library, this creates a + // new "_r_debug" structure and it causes problems once the executable is + // loaded due to the way symbol lookups happen in linux: the shared library + // list from _r_debug.r_map will be searched for a symbol named "_r_debug" + // and the first match will be the new version that is used. The dynamic + // loader is always last in this list. So at some point the dynamic loader + // will start updating the copy of "_r_debug" that gets found first. The + // issue is that LLDB will only look at the copy that is pointed to by the + // DT_DEBUG entry, or the initial version from the ld.so binary. + // + // Steps that show the problem are: + // + // - LLDB finds the "_r_debug" structure via the DT_DEBUG entry in the + // .dynamic section and this points to the "_r_debug" in ld.so + // - ld.so uodates its copy of "_r_debug" with "state = eAdd" before it + // loads the dependent shared libraries for the main executable and + // any dependencies of all shared libraries from the executable's list + // and ld.so code calls the debugger notification function + // that LLDB has set a breakpoint on. + // - LLDB hits the breakpoint and the breakpoint has a callback function + // where we read the _r_debug.state (eAdd) state and we do nothing as the + // "eAdd" state indicates that the shared libraries are about to be added. + // - ld.so finishes loading the main executable and any dependent shared + // libraries and it will update the "_r_debug.state" member with a + // "eConsistent", but it now updates the "_r_debug" in the a.out program + // and it calls the debugger notification function. + // - lldb hits the notification breakpoint and checks the ld.so copy of + // "_r_debug.state" which still has a state of "eAdd", but LLDB needs to see a + // "eConsistent" state to trigger the shared libraries to get loaded into + // the debug session, but LLDB the ld.so _r_debug.state which still + // contains "eAdd" and doesn't do anyhing and library load is missed. + // The "_r_debug" in a.out has the state set correctly to "eConsistent" + // but LLDB is still looking at the "_r_debug" from ld.so. + // + // So if we detect two "eAdd" states in a row, we assume this is the issue + // and we now load shared libraries correctly and will emit a log message + // in the "log enable lldb dyld" log channel which states there might be + // multiple "_r_debug" structs causing problems. + // + // The correct solution is that no one should be adding a duplicate + // publicly visible "_r_debug" symbols to their binaries, but we have + // programs that are doing this already and since it can be done, we should + // be able to work with this and keep debug sessions working as expected. + // + // If a user includes the <link.h> file, they can just use the existing + // "_r_debug" structure as it is defined in this header file as "extern + // struct r_debug _r_debug;" and no local copies need to be made. + if (m_previous.state == eAdd) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "DYLDRendezvous::GetAction() found two eAdd states in a " + "row, check process for multiple \"_r_debug\" symbols. " + "Returning eAddModules to ensure shared libraries get loaded " + "correctly"); + return eAddModules; + } + return eNoAction; + case eDelete: + return eNoAction; + } + + return eNoAction; +} + +bool DYLDRendezvous::UpdateSOEntriesFromRemote() { + const auto action = GetAction(); + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "{0} action = {1}", LLVM_PRETTY_FUNCTION, ActionToCStr(action)); + + if (action == eNoAction) + return false; + + m_added_soentries.clear(); + m_removed_soentries.clear(); + if (action == eTakeSnapshot) { + // We already have the loaded list from the previous update so no need to + // find all the modules again. + if (!m_loaded_modules.m_list.empty()) + return true; + } + + llvm::Expected<LoadedModuleInfoList> module_list = + m_process->GetLoadedModuleList(); + if (!module_list) { + llvm::consumeError(module_list.takeError()); + return false; + } + + switch (action) { + case eTakeSnapshot: + m_soentries.clear(); + return SaveSOEntriesFromRemote(*module_list); + case eAddModules: + return AddSOEntriesFromRemote(*module_list); + case eRemoveModules: + return RemoveSOEntriesFromRemote(*module_list); + case eNoAction: + return false; + } + llvm_unreachable("Fully covered switch above!"); +} + +bool DYLDRendezvous::UpdateSOEntries() { + m_added_soentries.clear(); + m_removed_soentries.clear(); + const auto action = GetAction(); + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "{0} action = {1}", LLVM_PRETTY_FUNCTION, ActionToCStr(action)); + switch (action) { + case eTakeSnapshot: + m_soentries.clear(); + return TakeSnapshot(m_soentries); + case eAddModules: + return AddSOEntries(); + case eRemoveModules: + return RemoveSOEntries(); + case eNoAction: + return false; + } + llvm_unreachable("Fully covered switch above!"); +} + +bool DYLDRendezvous::FillSOEntryFromModuleInfo( + LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry) { + addr_t link_map_addr; + addr_t base_addr; + addr_t dyn_addr; + std::string name; + + if (!modInfo.get_link_map(link_map_addr) || !modInfo.get_base(base_addr) || + !modInfo.get_dynamic(dyn_addr) || !modInfo.get_name(name)) + return false; + + entry.link_addr = link_map_addr; + entry.base_addr = base_addr; + entry.dyn_addr = dyn_addr; + + entry.file_spec.SetFile(name, FileSpec::Style::native); + + UpdateBaseAddrIfNecessary(entry, name); + + // not needed if we're using ModuleInfos + entry.next = 0; + entry.prev = 0; + entry.path_addr = 0; + + return true; +} + +bool DYLDRendezvous::SaveSOEntriesFromRemote( + const LoadedModuleInfoList &module_list) { + for (auto const &modInfo : module_list.m_list) { + SOEntry entry; + if (!FillSOEntryFromModuleInfo(modInfo, entry)) + return false; + + // Only add shared libraries and not the executable. + if (!SOEntryIsMainExecutable(entry)) { + UpdateFileSpecIfNecessary(entry); + m_soentries.push_back(entry); + } + } + + m_loaded_modules = module_list; + return true; +} + +bool DYLDRendezvous::AddSOEntriesFromRemote( + const LoadedModuleInfoList &module_list) { + for (auto const &modInfo : module_list.m_list) { + bool found = false; + for (auto const &existing : m_loaded_modules.m_list) { + if (modInfo == existing) { + found = true; + break; + } + } + + if (found) + continue; + + SOEntry entry; + if (!FillSOEntryFromModuleInfo(modInfo, entry)) + return false; + + // Only add shared libraries and not the executable. + if (!SOEntryIsMainExecutable(entry)) { + UpdateFileSpecIfNecessary(entry); + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + m_loaded_modules = module_list; + return true; +} + +bool DYLDRendezvous::RemoveSOEntriesFromRemote( + const LoadedModuleInfoList &module_list) { + for (auto const &existing : m_loaded_modules.m_list) { + bool found = false; + for (auto const &modInfo : module_list.m_list) { + if (modInfo == existing) { + found = true; + break; + } + } + + if (found) + continue; + + SOEntry entry; + if (!FillSOEntryFromModuleInfo(existing, entry)) + return false; + + // Only add shared libraries and not the executable. + if (!SOEntryIsMainExecutable(entry)) { + auto pos = llvm::find(m_soentries, entry); + if (pos == m_soentries.end()) + return false; + + m_soentries.erase(pos); + m_removed_soentries.push_back(entry); + } + } + + m_loaded_modules = module_list; + return true; +} + +bool DYLDRendezvous::AddSOEntries() { + SOEntry entry; + iterator pos; + + assert(m_previous.state == eAdd); + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + if (SOEntryIsMainExecutable(entry)) + continue; + + UpdateFileSpecIfNecessary(entry); + + if (!llvm::is_contained(m_soentries, entry)) { + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + return true; +} + +bool DYLDRendezvous::RemoveSOEntries() { + SOEntryList entry_list; + iterator pos; + + assert(m_previous.state == eDelete); + + if (!TakeSnapshot(entry_list)) + return false; + + for (iterator I = begin(); I != end(); ++I) { + if (!llvm::is_contained(entry_list, *I)) + m_removed_soentries.push_back(*I); + } + + m_soentries = entry_list; + return true; +} + +bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) { + // On some systes the executable is indicated by an empty path in the entry. + // On others it is the full path to the executable. + + auto triple = m_process->GetTarget().GetArchitecture().GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + return entry.file_spec == m_exe_file_spec; + case llvm::Triple::Linux: + if (triple.isAndroid()) + return entry.file_spec == m_exe_file_spec; + // If we are debugging ld.so, then all SOEntries should be treated as + // libraries, including the "main" one (denoted by an empty string). + if (!entry.file_spec && m_executable_interpreter) + return false; + return !entry.file_spec; + default: + return false; + } +} + +bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) { + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + // Clear previous entries since we are about to obtain an up to date list. + entry_list.clear(); + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + if (SOEntryIsMainExecutable(entry)) + continue; + + UpdateFileSpecIfNecessary(entry); + + entry_list.push_back(entry); + } + + return true; +} + +addr_t DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size) { + Status error; + + *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error); + if (error.Fail()) + return 0; + + return addr + size; +} + +addr_t DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) { + Status error; + + *dst = m_process->ReadPointerFromMemory(addr, error); + if (error.Fail()) + return 0; + + return addr + m_process->GetAddressByteSize(); +} + +std::string DYLDRendezvous::ReadStringFromMemory(addr_t addr) { + std::string str; + Status error; + + if (addr == LLDB_INVALID_ADDRESS) + return std::string(); + + m_process->ReadCStringFromMemory(addr, str, error); + + return str; +} + +// Returns true if the load bias reported by the linker is incorrect for the +// given entry. This function is used to handle cases where we want to work +// around a bug in the system linker. +static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) { + // On Android L (API 21, 22) the load address of the "/system/bin/linker" + // isn't filled in correctly. + unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor(); + return target.GetArchitecture().GetTriple().isAndroid() && + (os_major == 21 || os_major == 22) && + (file_path == "/system/bin/linker" || + file_path == "/system/bin/linker64"); +} + +void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry, + std::string const &file_path) { + // If the load bias reported by the linker is incorrect then fetch the load + // address of the file from the proc file system. + if (isLoadBiasIncorrect(m_process->GetTarget(), file_path)) { + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + bool is_loaded = false; + Status error = + m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr); + if (error.Success() && is_loaded) + entry.base_addr = load_addr; + } +} + +void DYLDRendezvous::UpdateFileSpecIfNecessary(SOEntry &entry) { + // Updates filename if empty. It is useful while debugging ld.so, + // when the link map returns empty string for the main executable. + if (!entry.file_spec) { + MemoryRegionInfo region; + Status region_status = + m_process->GetMemoryRegionInfo(entry.dyn_addr, region); + if (!region.GetName().IsEmpty()) + entry.file_spec.SetFile(region.GetName().AsCString(), + FileSpec::Style::native); + } +} + +bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) { + entry.clear(); + + entry.link_addr = addr; + + if (!(addr = ReadPointer(addr, &entry.base_addr))) + return false; + + // mips adds an extra load offset field to the link map struct on FreeBSD and + // NetBSD (need to validate other OSes). + // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57 + const ArchSpec &arch = m_process->GetTarget().GetArchitecture(); + if ((arch.GetTriple().getOS() == llvm::Triple::FreeBSD || + arch.GetTriple().getOS() == llvm::Triple::NetBSD) && + arch.IsMIPS()) { + addr_t mips_l_offs; + if (!(addr = ReadPointer(addr, &mips_l_offs))) + return false; + if (mips_l_offs != 0 && mips_l_offs != entry.base_addr) + return false; + } + + if (!(addr = ReadPointer(addr, &entry.path_addr))) + return false; + + if (!(addr = ReadPointer(addr, &entry.dyn_addr))) + return false; + + if (!(addr = ReadPointer(addr, &entry.next))) + return false; + + if (!(addr = ReadPointer(addr, &entry.prev))) + return false; + + std::string file_path = ReadStringFromMemory(entry.path_addr); + entry.file_spec.SetFile(file_path, FileSpec::Style::native); + + UpdateBaseAddrIfNecessary(entry, file_path); + + return true; +} + +bool DYLDRendezvous::FindMetadata(const char *name, PThreadField field, + uint32_t &value) { + Target &target = m_process->GetTarget(); + + SymbolContextList list; + target.GetImages().FindSymbolsWithNameAndType(ConstString(name), + eSymbolTypeAny, list); + if (list.IsEmpty()) + return false; + + Address address = list[0].symbol->GetAddress(); + address.SetOffset(address.GetOffset() + field * sizeof(uint32_t)); + + // Read from target memory as this allows us to try process memory and + // fallback to reading from read only sections from the object files. Here we + // are reading read only data from libpthread.so to find data in the thread + // specific area for the data we want and this won't be saved into process + // memory due to it being read only. + Status error; + value = + target.ReadUnsignedIntegerFromMemory(address, sizeof(uint32_t), 0, error); + if (error.Fail()) + return false; + + if (field == eSize) + value /= 8; // convert bits to bytes + + return true; +} + +const DYLDRendezvous::ThreadInfo &DYLDRendezvous::GetThreadInfo() { + if (!m_thread_info.valid) { + bool ok = true; + + ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset, + m_thread_info.dtv_offset); + ok &= + FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size); + ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset, + m_thread_info.modid_offset); + ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset, + m_thread_info.tls_offset); + + if (ok) + m_thread_info.valid = true; + } + + return m_thread_info; +} + +void DYLDRendezvous::DumpToLog(Log *log) const { + int state = GetState(); + + if (!log) + return; + + log->PutCString("DYLDRendezvous:"); + LLDB_LOGF(log, " Address: %" PRIx64, GetRendezvousAddress()); + LLDB_LOGF(log, " Version: %" PRIu64, GetVersion()); + LLDB_LOGF(log, " Link : %" PRIx64, GetLinkMapAddress()); + LLDB_LOGF(log, " Break : %" PRIx64, GetBreakAddress()); + LLDB_LOGF(log, " LDBase : %" PRIx64, GetLDBase()); + LLDB_LOGF(log, " State : %s", + (state == eConsistent) + ? "consistent" + : (state == eAdd) ? "add" + : (state == eDelete) ? "delete" : "unknown"); + + iterator I = begin(); + iterator E = end(); + + if (I != E) + log->PutCString("DYLDRendezvous SOEntries:"); + + for (int i = 1; I != E; ++I, ++i) { + LLDB_LOGF(log, "\n SOEntry [%d] %s", i, I->file_spec.GetPath().c_str()); + LLDB_LOGF(log, " Base : %" PRIx64, I->base_addr); + LLDB_LOGF(log, " Path : %" PRIx64, I->path_addr); + LLDB_LOGF(log, " Dyn : %" PRIx64, I->dyn_addr); + LLDB_LOGF(log, " Next : %" PRIx64, I->next); + LLDB_LOGF(log, " Prev : %" PRIx64, I->prev); + } +} + +bool DYLDRendezvous::IsCoreFile() const { + return !m_process->IsLiveDebugSession(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h new file mode 100644 index 000000000000..b8bdf78fbdfa --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -0,0 +1,369 @@ +//===-- DYLDRendezvous.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_POSIX_DYLD_DYLDRENDEZVOUS_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H + +#include <list> +#include <string> + +#include "lldb/Utility/FileSpec.h" +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +#include "lldb/Core/LoadedModuleInfoList.h" + +using lldb_private::LoadedModuleInfoList; + +namespace lldb_private { +class Log; +class Process; +} + +/// \class DYLDRendezvous +/// Interface to the runtime linker. +/// +/// A structure is present in a processes memory space which is updated by the +/// dynamic linker each time a module is loaded or unloaded. This class +/// provides an interface to this structure and maintains a consistent +/// snapshot of the currently loaded modules. +/// +/// In the dynamic loader sources, this structure has a type of "r_debug" and +/// the name of the structure us "_r_debug". The structure looks like: +/// +/// struct r_debug { +/// // Version number for this protocol. +/// int r_version; +/// // Head of the chain of loaded objects. +/// struct link_map *r_map; +/// // The address the debugger should set a breakpoint at in order to get +/// // notified when shared libraries are added or removed +/// uintptr_t r_brk; +/// // This state value describes the mapping change taking place when the +/// // 'r_brk' address is called. +/// enum { +/// RT_CONSISTENT, // Mapping change is complete. +/// RT_ADD, // Beginning to add a new object. +/// RT_DELETE, // Beginning to remove an object mapping. +/// } r_state; +/// // Base address the linker is loaded at. +/// uintptr_t r_ldbase; +/// }; +/// +/// The dynamic linker then defines a global variable using this type named +/// "_r_debug": +/// +/// r_debug _r_debug; +/// +/// The DYLDRendezvous class defines a local version of this structure named +/// DYLDRendezvous::Rendezvous. See the definition inside the class definition +/// for DYLDRendezvous. +/// +/// This structure can be located by looking through the .dynamic section in +/// the main executable and finding the DT_DEBUG tag entry. This value starts +/// out with a value of zero when the program first is initially loaded, but +/// the address of the "_r_debug" structure from ld.so is filled in by the +/// dynamic loader during program initialization code in ld.so prior to loading +/// or unloading and shared libraries. +/// +/// The dynamic loader will update this structure as shared libraries are +/// loaded and will call a specific function that LLDB knows to set a +/// breakpoint on (from _r_debug.r_brk) so LLDB will find out when shared +/// libraries are loaded or unloaded. Each time this breakpoint is hit, LLDB +/// looks at the contents of this structure and the contents tell LLDB what +/// needs to be done. +/// +/// Currently we expect the "state" in this structure to change as things +/// happen. +/// +/// When any shared libraries are loaded the following happens: +/// - _r_debug.r_map is updated with the new shared libraries. This is a +/// doubly linked list of "link_map *" entries. +/// - _r_debug.r_state is set to RT_ADD and the debugger notification +/// function is called notifying the debugger that shared libraries are +/// about to be added, but are not yet ready for use. +/// - Once the the shared libraries are fully loaded, _r_debug.r_state is set +/// to RT_CONSISTENT and the debugger notification function is called again +/// notifying the debugger that shared libraries are ready for use. +/// DYLDRendezvous must remember that the previous state was RT_ADD when it +/// receives a RT_CONSISTENT in order to know to add libraries +/// +/// When any shared libraries are unloaded the following happens: +/// - _r_debug.r_map is updated and the unloaded libraries are removed. +/// - _r_debug.r_state is set to RT_DELETE and the debugger notification +/// function is called notifying the debugger that shared libraries are +/// about to be removed. +/// - Once the the shared libraries are removed _r_debug.r_state is set to +/// RT_CONSISTENT and the debugger notification function is called again +/// notifying the debugger that shared libraries have been removed. +/// DYLDRendezvous must remember that the previous state was RT_DELETE when +/// it receives a RT_CONSISTENT in order to know to remove libraries +/// +class DYLDRendezvous { + + // This structure is used to hold the contents of the debug rendezvous + // information (struct r_debug) as found in the inferiors memory. Note that + // the layout of this struct is not binary compatible, it is simply large + // enough to hold the information on both 32 and 64 bit platforms. + struct Rendezvous { + uint64_t version = 0; + lldb::addr_t map_addr = 0; + lldb::addr_t brk = 0; + uint64_t state = 0; + lldb::addr_t ldbase = 0; + + Rendezvous() = default; + + void DumpToLog(lldb_private::Log *log, const char *label); + }; + + /// Locates the address of the rendezvous structure. It updates + /// m_executable_interpreter if address is extracted from _r_debug. + /// + /// \returns address on success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t ResolveRendezvousAddress(); + +public: + // Various metadata supplied by the inferior's threading library to describe + // the per-thread state. + struct ThreadInfo { + bool valid; // whether we read valid metadata + uint32_t dtv_offset; // offset of DTV pointer within pthread + uint32_t dtv_slot_size; // size of one DTV slot + uint32_t modid_offset; // offset of module ID within link_map + uint32_t tls_offset; // offset of TLS pointer within DTV slot + }; + + DYLDRendezvous(lldb_private::Process *process); + + /// Update the cached executable path. + void UpdateExecutablePath(); + + /// Update the internal snapshot of runtime linker rendezvous and recompute + /// the currently loaded modules. + /// + /// This method should be called once one start up, then once each time the + /// runtime linker enters the function given by GetBreakAddress(). + /// + /// \returns true on success and false on failure. + /// + /// \see GetBreakAddress(). + bool Resolve(); + + /// \returns true if this rendezvous has been located in the inferiors + /// address space and false otherwise. + bool IsValid(); + + /// \returns the address of the rendezvous structure in the inferiors + /// address space. + lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; } + + /// \returns the version of the rendezvous protocol being used. + uint64_t GetVersion() const { return m_current.version; } + + /// \returns address in the inferiors address space containing the linked + /// list of shared object descriptors. + lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; } + + /// A breakpoint should be set at this address and Resolve called on each + /// hit. + /// + /// \returns the address of a function called by the runtime linker each + /// time a module is loaded/unloaded, or about to be loaded/unloaded. + /// + /// \see Resolve() + lldb::addr_t GetBreakAddress() const { return m_current.brk; } + + /// Returns the current state of the rendezvous structure. + uint64_t GetState() const { return m_current.state; } + + /// \returns the base address of the runtime linker in the inferiors address + /// space. + lldb::addr_t GetLDBase() const { return m_current.ldbase; } + + /// \returns the thread layout metadata from the inferiors thread library. + const ThreadInfo &GetThreadInfo(); + + /// \returns true if modules have been loaded into the inferior since the + /// last call to Resolve(). + bool ModulesDidLoad() const { return !m_added_soentries.empty(); } + + /// \returns true if modules have been unloaded from the inferior since the + /// last call to Resolve(). + bool ModulesDidUnload() const { return !m_removed_soentries.empty(); } + + void DumpToLog(lldb_private::Log *log) const; + + /// Constants describing the state of the rendezvous. + /// + /// These values are defined to match the r_debug.r_state enum from the + /// actual dynamic loader sources. + /// + /// \see GetState(). + enum RendezvousState { + eConsistent, // RT_CONSISTENT + eAdd, // RT_ADD + eDelete // RT_DELETE + }; + + /// Structure representing the shared objects currently loaded into the + /// inferior process. + /// + /// This object is a rough analogue to the struct link_map object which + /// actually lives in the inferiors memory. + struct SOEntry { + lldb::addr_t link_addr; ///< Address of this link_map. + lldb::addr_t base_addr; ///< Base address of the loaded object. + lldb::addr_t path_addr; ///< String naming the shared object. + lldb::addr_t dyn_addr; ///< Dynamic section of shared object. + lldb::addr_t next; ///< Address of next so_entry. + lldb::addr_t prev; ///< Address of previous so_entry. + lldb_private::FileSpec file_spec; ///< File spec of shared object. + + SOEntry() { clear(); } + + bool operator==(const SOEntry &entry) { + return file_spec == entry.file_spec; + } + + void clear() { + link_addr = 0; + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + file_spec.Clear(); + } + }; + +protected: + typedef std::list<SOEntry> SOEntryList; + +public: + typedef SOEntryList::const_iterator iterator; + + /// Iterators over all currently loaded modules. + iterator begin() const { return m_soentries.begin(); } + iterator end() const { return m_soentries.end(); } + + /// Iterators over all modules loaded into the inferior since the last call + /// to Resolve(). + iterator loaded_begin() const { return m_added_soentries.begin(); } + iterator loaded_end() const { return m_added_soentries.end(); } + + /// Iterators over all modules unloaded from the inferior since the last + /// call to Resolve(). + iterator unloaded_begin() const { return m_removed_soentries.begin(); } + iterator unloaded_end() const { return m_removed_soentries.end(); } + +protected: + lldb_private::Process *m_process; + + // Cached copy of executable file spec + lldb_private::FileSpec m_exe_file_spec; + + /// Location of the r_debug structure in the inferiors address space. + lldb::addr_t m_rendezvous_addr; + + // True if the main program is the dynamic linker/loader/program interpreter. + bool m_executable_interpreter; + + /// Current and previous snapshots of the rendezvous structure. + Rendezvous m_current; + Rendezvous m_previous; + + /// List of currently loaded SO modules + LoadedModuleInfoList m_loaded_modules; + + /// List of SOEntry objects corresponding to the current link map state. + SOEntryList m_soentries; + + /// List of SOEntry's added to the link map since the last call to + /// Resolve(). + SOEntryList m_added_soentries; + + /// List of SOEntry's removed from the link map since the last call to + /// Resolve(). + SOEntryList m_removed_soentries; + + /// Threading metadata read from the inferior. + ThreadInfo m_thread_info; + + /// Reads an unsigned integer of \p size bytes from the inferior's address + /// space starting at \p addr. + /// + /// \returns addr + size if the read was successful and false otherwise. + lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size); + + /// Reads an address from the inferior's address space starting at \p addr. + /// + /// \returns addr + target address size if the read was successful and + /// 0 otherwise. + lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst); + + /// Reads a null-terminated C string from the memory location starting at @p + /// addr. + std::string ReadStringFromMemory(lldb::addr_t addr); + + /// Reads an SOEntry starting at \p addr. + bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry); + + /// Updates the current set of SOEntries, the set of added entries, and the + /// set of removed entries. + bool UpdateSOEntries(); + + /// Same as UpdateSOEntries but it gets the list of loaded modules from the + /// remote debug server (faster when supported). + bool UpdateSOEntriesFromRemote(); + + bool FillSOEntryFromModuleInfo( + LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry); + + bool SaveSOEntriesFromRemote(const LoadedModuleInfoList &module_list); + + bool AddSOEntriesFromRemote(const LoadedModuleInfoList &module_list); + + bool RemoveSOEntriesFromRemote(const LoadedModuleInfoList &module_list); + + bool AddSOEntries(); + + bool RemoveSOEntries(); + + void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path); + + void UpdateFileSpecIfNecessary(SOEntry &entry); + + bool SOEntryIsMainExecutable(const SOEntry &entry); + + /// Reads the current list of shared objects according to the link map + /// supplied by the runtime linker. + bool TakeSnapshot(SOEntryList &entry_list); + + enum PThreadField { eSize, eNElem, eOffset }; + + bool FindMetadata(const char *name, PThreadField field, uint32_t &value); + + bool IsCoreFile() const; + + enum RendezvousAction { + eNoAction, + eTakeSnapshot, + eAddModules, + eRemoveModules + }; + + static const char *StateToCStr(RendezvousState state); + static const char *ActionToCStr(RendezvousAction action); + + /// Returns the current action to be taken given the current and previous + /// state + RendezvousAction GetAction() const; +}; + +#endif diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp new file mode 100644 index 000000000000..51e4b3e6728f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -0,0 +1,883 @@ +//===-- DynamicLoaderPOSIXDYLD.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 +// +//===----------------------------------------------------------------------===// + +// Main header include +#include "DynamicLoaderPOSIXDYLD.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" + +#include <memory> +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE_ADV(DynamicLoaderPOSIXDYLD, DynamicLoaderPosixDYLD) + +void DynamicLoaderPOSIXDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderPOSIXDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in POSIX processes."; +} + +DynamicLoader *DynamicLoaderPOSIXDYLD::CreateInstance(Process *process, + bool force) { + bool create = force; + if (!create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::FreeBSD || + triple_ref.getOS() == llvm::Triple::Linux || + triple_ref.getOS() == llvm::Triple::NetBSD || + triple_ref.getOS() == llvm::Triple::OpenBSD) + create = true; + } + + if (create) + return new DynamicLoaderPOSIXDYLD(process); + return nullptr; +} + +DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process) + : DynamicLoader(process), m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), m_entry_point(LLDB_INVALID_ADDRESS), + m_auxv(), m_dyld_bid(LLDB_INVALID_BREAK_ID), + m_vdso_base(LLDB_INVALID_ADDRESS), + m_interpreter_base(LLDB_INVALID_ADDRESS), m_initial_modules_added(false) { +} + +DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() { + if (m_dyld_bid != LLDB_INVALID_BREAK_ID) { + m_process->GetTarget().RemoveBreakpointByID(m_dyld_bid); + m_dyld_bid = LLDB_INVALID_BREAK_ID; + } +} + +void DynamicLoaderPOSIXDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64, __FUNCTION__, + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + m_auxv = std::make_unique<AuxVector>(m_process->GetAuxvData()); + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " reloaded auxv data", + __FUNCTION__, m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + + ModuleSP executable_sp = GetTargetExecutable(); + ResolveExecutableModule(executable_sp); + m_rendezvous.UpdateExecutablePath(); + + // find the main process load offset + addr_t load_offset = ComputeLoadOffset(); + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " executable '%s', load_offset 0x%" PRIx64, + __FUNCTION__, + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID, + executable_sp ? executable_sp->GetFileSpec().GetPath().c_str() + : "<null executable>", + load_offset); + + EvalSpecialModulesStatus(); + + // if we dont have a load address we cant re-base + bool rebase_exec = load_offset != LLDB_INVALID_ADDRESS; + + // if we have a valid executable + if (executable_sp.get()) { + lldb_private::ObjectFile *obj = executable_sp->GetObjectFile(); + if (obj) { + // don't rebase if the module already has a load address + Target &target = m_process->GetTarget(); + Address addr = obj->GetImageInfoAddress(&target); + if (addr.GetLoadAddress(&target) != LLDB_INVALID_ADDRESS) + rebase_exec = false; + } + } else { + // no executable, nothing to re-base + rebase_exec = false; + } + + // if the target executable should be re-based + if (rebase_exec) { + ModuleList module_list; + + module_list.Append(executable_sp); + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " added executable '%s' to module load list", + __FUNCTION__, + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID, + executable_sp->GetFileSpec().GetPath().c_str()); + + UpdateLoadedSections(executable_sp, LLDB_INVALID_ADDRESS, load_offset, + true); + + LoadAllCurrentModules(); + + m_process->GetTarget().ModulesDidLoad(module_list); + if (log) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s told the target about the " + "modules that loaded:", + __FUNCTION__); + for (auto module_sp : module_list.Modules()) { + LLDB_LOGF(log, "-- [module] %s (pid %" PRIu64 ")", + module_sp ? module_sp->GetFileSpec().GetPath().c_str() + : "<null>", + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + } + } + } + + if (executable_sp.get()) { + if (!SetRendezvousBreakpoint()) { + // If we cannot establish rendezvous breakpoint right now we'll try again + // at entry point. + ProbeEntry(); + } + } +} + +void DynamicLoaderPOSIXDYLD::DidLaunch() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderPOSIXDYLD::%s()", __FUNCTION__); + + ModuleSP executable; + addr_t load_offset; + + m_auxv = std::make_unique<AuxVector>(m_process->GetAuxvData()); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + EvalSpecialModulesStatus(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset, true); + + LLDB_LOGF(log, "DynamicLoaderPOSIXDYLD::%s about to call ProbeEntry()", + __FUNCTION__); + + if (!SetRendezvousBreakpoint()) { + // If we cannot establish rendezvous breakpoint right now we'll try again + // at entry point. + ProbeEntry(); + } + + LoadVDSO(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +Status DynamicLoaderPOSIXDYLD::CanLoadImage() { return Status(); } + +void DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + m_loaded_modules[module] = link_map_addr; + UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset); +} + +void DynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) { + m_loaded_modules.erase(module); + + UnloadSectionsCommon(module); +} + +void DynamicLoaderPOSIXDYLD::ProbeEntry() { + Log *log = GetLog(LLDBLog::DynamicLoader); + + // If we have a core file, we don't need any breakpoints. + if (IsCoreFile()) + return; + + const addr_t entry = GetEntryPoint(); + if (entry == LLDB_INVALID_ADDRESS) { + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " GetEntryPoint() returned no address, not setting entry breakpoint", + __FUNCTION__, m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + return; + } + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " GetEntryPoint() returned address 0x%" PRIx64 + ", setting entry breakpoint", + __FUNCTION__, + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID, entry); + + if (m_process) { + Breakpoint *const entry_break = + m_process->GetTarget().CreateBreakpoint(entry, true, false).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); + entry_break->SetBreakpointKind("shared-library-event"); + + // Shoudn't hit this more than once. + entry_break->SetOneShot(true); + } +} + +// The runtime linker has run and initialized the rendezvous structure once the +// process has hit its entry point. When we hit the corresponding breakpoint +// we interrogate the rendezvous structure to get the load addresses of all +// dependent modules for the process. Similarly, we can discover the runtime +// linker function and setup a breakpoint to notify us of any dynamically +// loaded modules (via dlopen). +bool DynamicLoaderPOSIXDYLD::EntryBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; + + Log *log = GetLog(LLDBLog::DynamicLoader); + DynamicLoaderPOSIXDYLD *const dyld_instance = + static_cast<DynamicLoaderPOSIXDYLD *>(baton); + LLDB_LOGF(log, "DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64, + __FUNCTION__, + dyld_instance->m_process ? dyld_instance->m_process->GetID() + : LLDB_INVALID_PROCESS_ID); + + // Disable the breakpoint --- if a stop happens right after this, which we've + // seen on occasion, we don't want the breakpoint stepping thread-plan logic + // to show a breakpoint instruction at the disassembled entry point to the + // program. Disabling it prevents it. (One-shot is not enough - one-shot + // removal logic only happens after the breakpoint goes public, which wasn't + // happening in our scenario). + if (dyld_instance->m_process) { + BreakpointSP breakpoint_sp = + dyld_instance->m_process->GetTarget().GetBreakpointByID(break_id); + if (breakpoint_sp) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " disabling breakpoint id %" PRIu64, + __FUNCTION__, dyld_instance->m_process->GetID(), break_id); + breakpoint_sp->SetEnabled(false); + } else { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " failed to find breakpoint for breakpoint id %" PRIu64, + __FUNCTION__, dyld_instance->m_process->GetID(), break_id); + } + } else { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s breakpoint id %" PRIu64 + " no Process instance! Cannot disable breakpoint", + __FUNCTION__, break_id); + } + + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +bool DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() { + Log *log = GetLog(LLDBLog::DynamicLoader); + + // If we have a core file, we don't need any breakpoints. + if (IsCoreFile()) + return false; + + if (m_dyld_bid != LLDB_INVALID_BREAK_ID) { + LLDB_LOG(log, + "Rendezvous breakpoint breakpoint id {0} for pid {1}" + "is already set.", + m_dyld_bid, + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + return true; + } + + addr_t break_addr; + Target &target = m_process->GetTarget(); + BreakpointSP dyld_break; + if (m_rendezvous.IsValid() && m_rendezvous.GetBreakAddress() != 0) { + break_addr = m_rendezvous.GetBreakAddress(); + LLDB_LOG(log, "Setting rendezvous break address for pid {0} at {1:x}", + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID, + break_addr); + dyld_break = target.CreateBreakpoint(break_addr, true, false); + } else { + LLDB_LOG(log, "Rendezvous structure is not set up yet. " + "Trying to locate rendezvous breakpoint in the interpreter " + "by symbol name."); + // Function names from different dynamic loaders that are known to be + // used as rendezvous between the loader and debuggers. + static std::vector<std::string> DebugStateCandidates{ + "_dl_debug_state", "rtld_db_dlactivity", "__dl_rtld_db_dlactivity", + "r_debug_state", "_r_debug_state", "_rtld_debug_state", + }; + + ModuleSP interpreter = LoadInterpreterModule(); + FileSpecList containingModules; + if (interpreter) + containingModules.Append(interpreter->GetFileSpec()); + else + containingModules.Append( + m_process->GetTarget().GetExecutableModulePointer()->GetFileSpec()); + + dyld_break = target.CreateBreakpoint( + &containingModules, /*containingSourceFiles=*/nullptr, + DebugStateCandidates, eFunctionNameTypeFull, eLanguageTypeC, + /*m_offset=*/0, + /*skip_prologue=*/eLazyBoolNo, + /*internal=*/true, + /*request_hardware=*/false); + } + + if (dyld_break->GetNumResolvedLocations() != 1) { + LLDB_LOG( + log, + "Rendezvous breakpoint has abnormal number of" + " resolved locations ({0}) in pid {1}. It's supposed to be exactly 1.", + dyld_break->GetNumResolvedLocations(), + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + + target.RemoveBreakpointByID(dyld_break->GetID()); + return false; + } + + BreakpointLocationSP location = dyld_break->GetLocationAtIndex(0); + LLDB_LOG(log, + "Successfully set rendezvous breakpoint at address {0:x} " + "for pid {1}", + location->GetLoadAddress(), + m_process ? m_process->GetID() : LLDB_INVALID_PROCESS_ID); + + dyld_break->SetCallback(RendezvousBreakpointHit, this, true); + dyld_break->SetBreakpointKind("shared-library-event"); + m_dyld_bid = dyld_break->GetID(); + return true; +} + +bool DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; + + Log *log = GetLog(LLDBLog::DynamicLoader); + DynamicLoaderPOSIXDYLD *const dyld_instance = + static_cast<DynamicLoaderPOSIXDYLD *>(baton); + LLDB_LOGF(log, "DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64, + __FUNCTION__, + dyld_instance->m_process ? dyld_instance->m_process->GetID() + : LLDB_INVALID_PROCESS_ID); + + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + const bool stop_when_images_change = dyld_instance->GetStopWhenImagesChange(); + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 + " stop_when_images_change=%s", + __FUNCTION__, + dyld_instance->m_process ? dyld_instance->m_process->GetID() + : LLDB_INVALID_PROCESS_ID, + stop_when_images_change ? "true" : "false"); + return stop_when_images_change; +} + +void DynamicLoaderPOSIXDYLD::RefreshModules() { + if (!m_rendezvous.Resolve()) + return; + + // The rendezvous class doesn't enumerate the main module, so track that + // ourselves here. + ModuleSP executable = GetTargetExecutable(); + m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); + + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + if (m_rendezvous.ModulesDidLoad() || !m_initial_modules_added) { + ModuleList new_modules; + + // If this is the first time rendezvous breakpoint fires, we need + // to take care of adding all the initial modules reported by + // the loader. This is necessary to list ld-linux.so on Linux, + // and all DT_NEEDED entries on *BSD. + if (m_initial_modules_added) { + I = m_rendezvous.loaded_begin(); + E = m_rendezvous.loaded_end(); + } else { + I = m_rendezvous.begin(); + E = m_rendezvous.end(); + m_initial_modules_added = true; + } + for (; I != E; ++I) { + // Don't load a duplicate copy of ld.so if we have already loaded it + // earlier in LoadInterpreterModule. If we instead loaded then unloaded it + // later, the section information for ld.so would be removed. That + // information is required for placing breakpoints on Arm/Thumb systems. + if ((m_interpreter_module.lock() != nullptr) && + (I->base_addr == m_interpreter_base)) + continue; + + ModuleSP module_sp = + LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true); + if (!module_sp.get()) + continue; + + if (module_sp->GetObjectFile()->GetBaseAddress().GetLoadAddress( + &m_process->GetTarget()) == m_interpreter_base) { + ModuleSP interpreter_sp = m_interpreter_module.lock(); + if (m_interpreter_module.lock() == nullptr) { + m_interpreter_module = module_sp; + } else if (module_sp == interpreter_sp) { + // Module already loaded. + continue; + } + } + + loaded_modules.AppendIfNeeded(module_sp); + new_modules.Append(module_sp); + } + m_process->GetTarget().ModulesDidLoad(new_modules); + } + + if (m_rendezvous.ModulesDidUnload()) { + ModuleList old_modules; + + E = m_rendezvous.unloaded_end(); + for (I = m_rendezvous.unloaded_begin(); I != E; ++I) { + ModuleSpec module_spec{I->file_spec}; + ModuleSP module_sp = loaded_modules.FindFirstModule(module_spec); + + if (module_sp.get()) { + old_modules.Append(module_sp); + UnloadSections(module_sp); + } + } + loaded_modules.Remove(old_modules); + m_process->GetTarget().ModulesDidUnload(old_modules, false); + } +} + +ThreadPlanSP +DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + ThreadPlanSP thread_plan_sp; + + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *sym = context.symbol; + + if (sym == nullptr || !sym->IsTrampoline()) + return thread_plan_sp; + + ConstString sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); + if (!sym_name) + return thread_plan_sp; + + SymbolContextList target_symbols; + Target &target = thread.GetProcess()->GetTarget(); + const ModuleList &images = target.GetImages(); + + llvm::StringRef target_name = sym_name.GetStringRef(); + // On AArch64, the trampoline name has a prefix (__AArch64ADRPThunk_ or + // __AArch64AbsLongThunk_) added to the function name. If we detect a + // trampoline with the prefix, we need to remove the prefix to find the + // function symbol. + if (target_name.consume_front("__AArch64ADRPThunk_") || + target_name.consume_front("__AArch64AbsLongThunk_")) { + // An empty target name can happen for trampolines generated for + // section-referencing relocations. + if (!target_name.empty()) { + sym_name = ConstString(target_name); + } + } + images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); + if (!target_symbols.GetSize()) + return thread_plan_sp; + + typedef std::vector<lldb::addr_t> AddressVector; + AddressVector addrs; + for (const SymbolContext &context : target_symbols) { + AddressRange range; + context.GetAddressRange(eSymbolContextEverything, 0, false, range); + lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); + if (addr != LLDB_INVALID_ADDRESS) + addrs.push_back(addr); + } + + if (addrs.size() > 0) { + AddressVector::iterator start = addrs.begin(); + AddressVector::iterator end = addrs.end(); + + llvm::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp = + std::make_shared<ThreadPlanRunToAddress>(thread, addrs, stop); + } + + return thread_plan_sp; +} + +void DynamicLoaderPOSIXDYLD::LoadVDSO() { + if (m_vdso_base == LLDB_INVALID_ADDRESS) + return; + + FileSpec file("[vdso]"); + + MemoryRegionInfo info; + Status status = m_process->GetMemoryRegionInfo(m_vdso_base, info); + if (status.Fail()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "Failed to get vdso region info: {0}", status); + return; + } + + if (ModuleSP module_sp = m_process->ReadModuleFromMemory( + file, m_vdso_base, info.GetRange().GetByteSize())) { + UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_vdso_base, false); + m_process->GetTarget().GetImages().AppendIfNeeded(module_sp); + } +} + +ModuleSP DynamicLoaderPOSIXDYLD::LoadInterpreterModule() { + if (m_interpreter_base == LLDB_INVALID_ADDRESS) + return nullptr; + + MemoryRegionInfo info; + Target &target = m_process->GetTarget(); + Status status = m_process->GetMemoryRegionInfo(m_interpreter_base, info); + if (status.Fail() || info.GetMapped() != MemoryRegionInfo::eYes || + info.GetName().IsEmpty()) { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOG(log, "Failed to get interpreter region info: {0}", status); + return nullptr; + } + + FileSpec file(info.GetName().GetCString()); + ModuleSpec module_spec(file, target.GetArchitecture()); + + // Don't notify that module is added here because its loading section + // addresses are not updated yet. We manually notify it below. + if (ModuleSP module_sp = + target.GetOrCreateModule(module_spec, /*notify=*/false)) { + UpdateLoadedSections(module_sp, LLDB_INVALID_ADDRESS, m_interpreter_base, + false); + // Manually notify that dynamic linker is loaded after updating load section + // addersses so that breakpoints can be resolved. + ModuleList module_list; + module_list.Append(module_sp); + target.ModulesDidLoad(module_list); + m_interpreter_module = module_sp; + return module_sp; + } + return nullptr; +} + +ModuleSP DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + file, link_map_addr, base_addr, base_addr_is_offset)) + return module_sp; + + // This works around an dynamic linker "bug" on android <= 23, where the + // dynamic linker would report the application name + // (e.g. com.example.myapplication) instead of the main process binary + // (/system/bin/app_process(32)). The logic is not sound in general (it + // assumes base_addr is the real address, even though it actually is a load + // bias), but it happens to work on android because app_process has a file + // address of zero. + // This should be removed after we drop support for android-23. + if (m_process->GetTarget().GetArchitecture().GetTriple().isAndroid()) { + MemoryRegionInfo memory_info; + Status error = m_process->GetMemoryRegionInfo(base_addr, memory_info); + if (error.Success() && memory_info.GetMapped() && + memory_info.GetRange().GetRangeBase() == base_addr && + !(memory_info.GetName().IsEmpty())) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + FileSpec(memory_info.GetName().GetStringRef()), link_map_addr, + base_addr, base_addr_is_offset)) + return module_sp; + } + } + + return nullptr; +} + +void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + ModuleList module_list; + Log *log = GetLog(LLDBLog::DynamicLoader); + + LoadVDSO(); + + if (!m_rendezvous.Resolve()) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s unable to resolve POSIX DYLD " + "rendezvous address", + __FUNCTION__); + return; + } + + // The rendezvous class doesn't enumerate the main module, so track that + // ourselves here. + ModuleSP executable = GetTargetExecutable(); + m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); + + std::vector<FileSpec> module_names; + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) + module_names.push_back(I->file_spec); + m_process->PrefetchModuleSpecs( + module_names, m_process->GetTarget().GetArchitecture().GetTriple()); + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + ModuleSP module_sp = + LoadModuleAtAddress(I->file_spec, I->link_addr, I->base_addr, true); + if (module_sp.get()) { + LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", + I->file_spec.GetFilename()); + module_list.Append(module_sp); + } else { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF( + log, + "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, + __FUNCTION__, I->file_spec.GetPath().c_str(), I->base_addr); + } + } + + m_process->GetTarget().ModulesDidLoad(module_list); + m_initial_modules_added = true; +} + +addr_t DynamicLoaderPOSIXDYLD::ComputeLoadOffset() { + addr_t virt_entry; + + if (m_load_offset != LLDB_INVALID_ADDRESS) + return m_load_offset; + + if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + ModuleSP module = m_process->GetTarget().GetExecutableModule(); + if (!module) + return LLDB_INVALID_ADDRESS; + + ObjectFile *exe = module->GetObjectFile(); + if (!exe) + return LLDB_INVALID_ADDRESS; + + Address file_entry = exe->GetEntryPointAddress(); + + if (!file_entry.IsValid()) + return LLDB_INVALID_ADDRESS; + + m_load_offset = virt_entry - file_entry.GetFileAddress(); + return m_load_offset; +} + +void DynamicLoaderPOSIXDYLD::EvalSpecialModulesStatus() { + if (std::optional<uint64_t> vdso_base = + m_auxv->GetAuxValue(AuxVector::AUXV_AT_SYSINFO_EHDR)) + m_vdso_base = *vdso_base; + + if (std::optional<uint64_t> interpreter_base = + m_auxv->GetAuxValue(AuxVector::AUXV_AT_BASE)) + m_interpreter_base = *interpreter_base; +} + +addr_t DynamicLoaderPOSIXDYLD::GetEntryPoint() { + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + + if (m_auxv == nullptr) + return LLDB_INVALID_ADDRESS; + + std::optional<uint64_t> entry_point = + m_auxv->GetAuxValue(AuxVector::AUXV_AT_ENTRY); + if (!entry_point) + return LLDB_INVALID_ADDRESS; + + m_entry_point = static_cast<addr_t>(*entry_point); + + const ArchSpec &arch = m_process->GetTarget().GetArchitecture(); + + // On ppc64, the entry point is actually a descriptor. Dereference it. + if (arch.GetMachine() == llvm::Triple::ppc64) + m_entry_point = ReadUnsignedIntWithSizeInBytes(m_entry_point, 8); + + return m_entry_point; +} + +lldb::addr_t +DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp, + const lldb::ThreadSP thread, + lldb::addr_t tls_file_addr) { + Log *log = GetLog(LLDBLog::DynamicLoader); + auto it = m_loaded_modules.find(module_sp); + if (it == m_loaded_modules.end()) { + LLDB_LOGF( + log, "GetThreadLocalData error: module(%s) not found in loaded modules", + module_sp->GetObjectName().AsCString()); + return LLDB_INVALID_ADDRESS; + } + + addr_t link_map = it->second; + if (link_map == LLDB_INVALID_ADDRESS || link_map == 0) { + LLDB_LOGF(log, + "GetThreadLocalData error: invalid link map address=0x%" PRIx64, + link_map); + return LLDB_INVALID_ADDRESS; + } + + const DYLDRendezvous::ThreadInfo &metadata = m_rendezvous.GetThreadInfo(); + if (!metadata.valid) { + LLDB_LOGF(log, + "GetThreadLocalData error: fail to read thread info metadata"); + return LLDB_INVALID_ADDRESS; + } + + LLDB_LOGF(log, + "GetThreadLocalData info: link_map=0x%" PRIx64 + ", thread info metadata: " + "modid_offset=0x%" PRIx32 ", dtv_offset=0x%" PRIx32 + ", tls_offset=0x%" PRIx32 ", dtv_slot_size=%" PRIx32 "\n", + link_map, metadata.modid_offset, metadata.dtv_offset, + metadata.tls_offset, metadata.dtv_slot_size); + + // Get the thread pointer. + addr_t tp = thread->GetThreadPointer(); + if (tp == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "GetThreadLocalData error: fail to read thread pointer"); + return LLDB_INVALID_ADDRESS; + } + + // Find the module's modid. + int modid_size = 4; // FIXME(spucci): This isn't right for big-endian 64-bit + int64_t modid = ReadUnsignedIntWithSizeInBytes( + link_map + metadata.modid_offset, modid_size); + if (modid == -1) { + LLDB_LOGF(log, "GetThreadLocalData error: fail to read modid"); + return LLDB_INVALID_ADDRESS; + } + + // Lookup the DTV structure for this thread. + addr_t dtv_ptr = tp + metadata.dtv_offset; + addr_t dtv = ReadPointer(dtv_ptr); + if (dtv == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "GetThreadLocalData error: fail to read dtv"); + return LLDB_INVALID_ADDRESS; + } + + // Find the TLS block for this module. + addr_t dtv_slot = dtv + metadata.dtv_slot_size * modid; + addr_t tls_block = ReadPointer(dtv_slot + metadata.tls_offset); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::Performed TLS lookup: " + "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64 + ", modid=%" PRId64 ", tls_block=0x%" PRIx64 "\n", + module_sp->GetObjectName().AsCString(""), link_map, tp, + (int64_t)modid, tls_block); + + if (tls_block == LLDB_INVALID_ADDRESS) { + LLDB_LOGF(log, "GetThreadLocalData error: fail to read tls_block"); + return LLDB_INVALID_ADDRESS; + } else + return tls_block + tls_file_addr; +} + +void DynamicLoaderPOSIXDYLD::ResolveExecutableModule( + lldb::ModuleSP &module_sp) { + Log *log = GetLog(LLDBLog::DynamicLoader); + + if (m_process == nullptr) + return; + + auto &target = m_process->GetTarget(); + const auto platform_sp = target.GetPlatform(); + + ProcessInstanceInfo process_info; + if (!m_process->GetProcessInfo(process_info)) { + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to get process info for " + "pid %" PRIu64, + __FUNCTION__, m_process->GetID()); + return; + } + + LLDB_LOGF( + log, "DynamicLoaderPOSIXDYLD::%s - got executable by pid %" PRIu64 ": %s", + __FUNCTION__, m_process->GetID(), + process_info.GetExecutableFile().GetPath().c_str()); + + ModuleSpec module_spec(process_info.GetExecutableFile(), + process_info.GetArchitecture()); + if (module_sp && module_sp->MatchesModuleSpec(module_spec)) + return; + + const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); + auto error = platform_sp->ResolveExecutable( + module_spec, module_sp, + !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + if (error.Fail()) { + StreamString stream; + module_spec.Dump(stream); + + LLDB_LOGF(log, + "DynamicLoaderPOSIXDYLD::%s - failed to resolve executable " + "with module spec \"%s\": %s", + __FUNCTION__, stream.GetData(), error.AsCString()); + return; + } + + target.SetExecutableModule(module_sp, eLoadDependentsNo); +} + +bool DynamicLoaderPOSIXDYLD::AlwaysRelyOnEHUnwindInfo( + lldb_private::SymbolContext &sym_ctx) { + ModuleSP module_sp; + if (sym_ctx.symbol) + module_sp = sym_ctx.symbol->GetAddressRef().GetModule(); + if (!module_sp && sym_ctx.function) + module_sp = + sym_ctx.function->GetAddressRange().GetBaseAddress().GetModule(); + if (!module_sp) + return false; + + return module_sp->GetFileSpec().GetPath() == "[vdso]"; +} + +bool DynamicLoaderPOSIXDYLD::IsCoreFile() const { + return !m_process->IsLiveDebugSession(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h new file mode 100644 index 000000000000..4c92335602cd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -0,0 +1,179 @@ +//===-- DynamicLoaderPOSIXDYLD.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_POSIX_DYLD_DYNAMICLOADERPOSIXDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYNAMICLOADERPOSIXDYLD_H + +#include <map> +#include <memory> + +#include "DYLDRendezvous.h" +#include "Plugins/Process/Utility/AuxVector.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Target/DynamicLoader.h" + +class AuxVector; + +class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader { +public: + DynamicLoaderPOSIXDYLD(lldb_private::Process *process); + + ~DynamicLoaderPOSIXDYLD() override; + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "posix-dyld"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + // DynamicLoader protocol + + void DidAttach() override; + + void DidLaunch() override; + + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Status CanLoadImage() override; + + lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module, + const lldb::ThreadSP thread, + lldb::addr_t tls_file_addr) override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + lldb::ModuleSP LoadModuleAtAddress(const lldb_private::FileSpec &file, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + +protected: + /// Runtime linker rendezvous structure. + DYLDRendezvous m_rendezvous; + + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + + /// Auxiliary vector of the inferior process. + std::unique_ptr<AuxVector> m_auxv; + + /// Rendezvous breakpoint. + lldb::break_id_t m_dyld_bid; + + /// Contains AT_SYSINFO_EHDR, which means a vDSO has been + /// mapped to the address space + lldb::addr_t m_vdso_base; + + /// Contains AT_BASE, which means a dynamic loader has been + /// mapped to the address space + lldb::addr_t m_interpreter_base; + + /// Contains the pointer to the interpret module, if loaded. + std::weak_ptr<lldb_private::Module> m_interpreter_module; + + /// Loaded module list. (link map for each module) + std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>> + m_loaded_modules; + + /// Returns true if the process is for a core file. + bool IsCoreFile() const; + + /// If possible sets a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + bool SetRendezvousBreakpoint(); + + /// Callback routine which updates the current list of loaded modules based + /// on the information supplied by the runtime linker. + static bool RendezvousBreakpointHit( + void *baton, lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + /// Indicates whether the initial set of modules was reported added. + bool m_initial_modules_added; + + /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set + /// of loaded modules. + void RefreshModules(); + + /// Updates the load address of every allocatable section in \p module. + /// + /// \param module The module to traverse. + /// + /// \param link_map_addr The virtual address of the link map for the @p + /// module. + /// + /// \param base_addr The virtual base address \p module is loaded at. + void UpdateLoadedSections(lldb::ModuleSP module, lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + + /// Removes the loaded sections from the target in \p module. + /// + /// \param module The module to traverse. + void UnloadSections(const lldb::ModuleSP module) override; + + /// Resolves the entry point for the current inferior process and sets a + /// breakpoint at that address. + void ProbeEntry(); + + /// Callback routine invoked when we hit the breakpoint on process entry. + /// + /// This routine is responsible for resolving the load addresses of all + /// dependent modules required by the inferior and setting up the rendezvous + /// breakpoint. + static bool + EntryBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + /// Helper for the entry breakpoint callback. Resolves the load addresses + /// of all dependent modules. + virtual void LoadAllCurrentModules(); + + void LoadVDSO(); + + // Loading an interpreter module (if present) assuming m_interpreter_base + // already points to its base address. + lldb::ModuleSP LoadInterpreterModule(); + + /// Computes a value for m_load_offset returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t ComputeLoadOffset(); + + /// Computes a value for m_entry_point returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t GetEntryPoint(); + + /// Evaluate if Aux vectors contain vDSO and LD information + /// in case they do, read and assign the address to m_vdso_base + /// and m_interpreter_base. + void EvalSpecialModulesStatus(); + + /// Loads Module from inferior process. + void ResolveExecutableModule(lldb::ModuleSP &module_sp); + + bool AlwaysRelyOnEHUnwindInfo(lldb_private::SymbolContext &sym_ctx) override; + +private: + DynamicLoaderPOSIXDYLD(const DynamicLoaderPOSIXDYLD &) = delete; + const DynamicLoaderPOSIXDYLD & + operator=(const DynamicLoaderPOSIXDYLD &) = delete; +}; + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYNAMICLOADERPOSIXDYLD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp new file mode 100644 index 000000000000..545998123dda --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp @@ -0,0 +1,150 @@ +//===-- DynamicLoaderStatic.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/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" + +#include "DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderStatic) + +// Create an instance of this class. This function is filled into the plugin +// info class that gets handed out by the plugin factory and allows the lldb to +// instantiate an instance of this class. +DynamicLoader *DynamicLoaderStatic::CreateInstance(Process *process, + bool force) { + bool create = force; + if (!create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + const llvm::Triple::OSType os_type = triple_ref.getOS(); + const llvm::Triple::ArchType arch_type = triple_ref.getArch(); + if (os_type == llvm::Triple::UnknownOS) { + // The WASM and Hexagon plugin check the ArchType rather than the OSType, + // so explicitly reject those here. + switch(arch_type) { + case llvm::Triple::hexagon: + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + break; + default: + create = true; + } + } + } + + if (!create) { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) { + create = (object_file->GetStrata() == ObjectFile::eStrataRawImage); + } + } + } + + if (create) + return new DynamicLoaderStatic(process); + return nullptr; +} + +// Constructor +DynamicLoaderStatic::DynamicLoaderStatic(Process *process) + : DynamicLoader(process) {} + +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +void DynamicLoaderStatic::DidAttach() { LoadAllImagesAtFileAddresses(); } + +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +void DynamicLoaderStatic::DidLaunch() { LoadAllImagesAtFileAddresses(); } + +void DynamicLoaderStatic::LoadAllImagesAtFileAddresses() { + const ModuleList &module_list = m_process->GetTarget().GetImages(); + + ModuleList loaded_module_list; + + // Disable JIT for static dynamic loader targets + m_process->SetCanJIT(false); + + Target &target = m_process->GetTarget(); + for (ModuleSP module_sp : module_list.Modules()) { + if (module_sp) { + bool changed = false; + bool no_load_addresses = true; + // If this module has a section with a load address set in + // the target, assume all necessary work is already done. There + // may be sections without a load address set intentionally + // and we don't want to mutate that. + // For a module with no load addresses set, set the load addresses + // to slide == 0, the same as the file addresses, in the target. + ObjectFile *image_object_file = module_sp->GetObjectFile(); + if (image_object_file) { + SectionList *section_list = image_object_file->GetSectionList(); + if (section_list) { + const size_t num_sections = section_list->GetSize(); + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); + if (section_sp) { + if (target.GetSectionLoadList().GetSectionLoadAddress( + section_sp) != LLDB_INVALID_ADDRESS) { + no_load_addresses = false; + break; + } + } + } + } + } + if (no_load_addresses) + module_sp->SetLoadAddress(target, 0, true /*value_is_offset*/, changed); + + if (changed) + loaded_module_list.AppendIfNeeded(module_sp); + } + } + + target.ModulesDidLoad(loaded_module_list); +} + +ThreadPlanSP +DynamicLoaderStatic::GetStepThroughTrampolinePlan(Thread &thread, + bool stop_others) { + return ThreadPlanSP(); +} + +Status DynamicLoaderStatic::CanLoadImage() { + Status error; + error.SetErrorString("can't load images on with a static debug session"); + return error; +} + +void DynamicLoaderStatic::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderStatic::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef DynamicLoaderStatic::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that will load any images at the static " + "addresses contained in each image."; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h new file mode 100644 index 000000000000..dac19dcd38d7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h @@ -0,0 +1,53 @@ +//===-- DynamicLoaderStatic.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_STATIC_DYNAMICLOADERSTATIC_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_STATIC_DYNAMICLOADERSTATIC_H + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/UUID.h" + +class DynamicLoaderStatic : public lldb_private::DynamicLoader { +public: + DynamicLoaderStatic(lldb_private::Process *process); + + // Static Functions + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "static"; } + + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + void DidAttach() override; + + void DidLaunch() override; + + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others) override; + + lldb_private::Status CanLoadImage() override; + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +private: + void LoadAllImagesAtFileAddresses(); +}; + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_STATIC_DYNAMICLOADERSTATIC_H diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp new file mode 100644 index 000000000000..f044aa786806 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp @@ -0,0 +1,216 @@ +//===-- DynamicLoaderWindowsDYLD.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 "DynamicLoaderWindowsDYLD.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +#include "llvm/TargetParser/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(DynamicLoaderWindowsDYLD) + +DynamicLoaderWindowsDYLD::DynamicLoaderWindowsDYLD(Process *process) + : DynamicLoader(process) {} + +DynamicLoaderWindowsDYLD::~DynamicLoaderWindowsDYLD() = default; + +void DynamicLoaderWindowsDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void DynamicLoaderWindowsDYLD::Terminate() {} + +llvm::StringRef DynamicLoaderWindowsDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in Windows processes."; +} + +DynamicLoader *DynamicLoaderWindowsDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + const llvm::Triple &triple_ref = + process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Win32) + should_create = true; + } + + if (should_create) + return new DynamicLoaderWindowsDYLD(process); + + return nullptr; +} + +void DynamicLoaderWindowsDYLD::OnLoadModule(lldb::ModuleSP module_sp, + const ModuleSpec module_spec, + lldb::addr_t module_addr) { + + // Resolve the module unless we already have one. + if (!module_sp) { + Status error; + module_sp = m_process->GetTarget().GetOrCreateModule(module_spec, + true /* notify */, &error); + if (error.Fail()) + return; + } + + m_loaded_modules[module_sp] = module_addr; + UpdateLoadedSectionsCommon(module_sp, module_addr, false); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidLoad(module_list); +} + +void DynamicLoaderWindowsDYLD::OnUnloadModule(lldb::addr_t module_addr) { + Address resolved_addr; + if (!m_process->GetTarget().ResolveLoadAddress(module_addr, resolved_addr)) + return; + + ModuleSP module_sp = resolved_addr.GetModule(); + if (module_sp) { + m_loaded_modules.erase(module_sp); + UnloadSectionsCommon(module_sp); + ModuleList module_list; + module_list.Append(module_sp); + m_process->GetTarget().ModulesDidUnload(module_list, false); + } +} + +lldb::addr_t DynamicLoaderWindowsDYLD::GetLoadAddress(ModuleSP executable) { + // First, see if the load address is already cached. + auto it = m_loaded_modules.find(executable); + if (it != m_loaded_modules.end() && it->second != LLDB_INVALID_ADDRESS) + return it->second; + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + + // Second, try to get it through the process plugins. For a remote process, + // the remote platform will be responsible for providing it. + FileSpec file_spec(executable->GetPlatformFileSpec()); + bool is_loaded = false; + Status status = + m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr); + // Servers other than lldb server could respond with a bogus address. + if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) { + m_loaded_modules[executable] = load_addr; + return load_addr; + } + + return LLDB_INVALID_ADDRESS; +} + +void DynamicLoaderWindowsDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + + if (!executable.get()) + return; + + // Try to fetch the load address of the file from the process, since there + // could be randomization of the load address. + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr == LLDB_INVALID_ADDRESS) + return; + + // Request the process base address. + lldb::addr_t image_base = m_process->GetImageInfoAddress(); + if (image_base == load_addr) + return; + + // Rebase the process's modules if there is a mismatch. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); +} + +void DynamicLoaderWindowsDYLD::DidLaunch() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()", __FUNCTION__); + + ModuleSP executable = GetTargetExecutable(); + if (!executable.get()) + return; + + lldb::addr_t load_addr = GetLoadAddress(executable); + if (load_addr != LLDB_INVALID_ADDRESS) { + // Update the loaded sections so that the breakpoints can be resolved. + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false); + + ModuleList module_list; + module_list.Append(executable); + m_process->GetTarget().ModulesDidLoad(module_list); + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}"); + } +} + +Status DynamicLoaderWindowsDYLD::CanLoadImage() { return Status(); } + +ThreadPlanSP +DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + auto arch = m_process->GetTarget().GetArchitecture(); + if (arch.GetMachine() != llvm::Triple::x86) { + return ThreadPlanSP(); + } + + uint64_t pc = thread.GetRegisterContext()->GetPC(); + // Max size of an instruction in x86 is 15 bytes. + AddressRange range(pc, 2 * 15); + + DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( + arch, nullptr, nullptr, m_process->GetTarget(), range); + if (!disassembler_sp) { + return ThreadPlanSP(); + } + + InstructionList *insn_list = &disassembler_sp->GetInstructionList(); + if (insn_list == nullptr) { + return ThreadPlanSP(); + } + + // First instruction in a x86 Windows trampoline is going to be an indirect + // jump through the IAT and the next one will be a nop (usually there for + // alignment purposes). e.g.: + // 0x70ff4cfc <+956>: jmpl *0x7100c2a8 + // 0x70ff4d02 <+962>: nop + + auto first_insn = insn_list->GetInstructionAtIndex(0); + auto second_insn = insn_list->GetInstructionAtIndex(1); + + ExecutionContext exe_ctx(m_process->GetTarget()); + if (first_insn == nullptr || second_insn == nullptr || + strcmp(first_insn->GetMnemonic(&exe_ctx), "jmpl") != 0 || + strcmp(second_insn->GetMnemonic(&exe_ctx), "nop") != 0) { + return ThreadPlanSP(); + } + + assert(first_insn->DoesBranch() && !second_insn->DoesBranch()); + + return ThreadPlanSP(new ThreadPlanStepInstruction( + thread, false, false, eVoteNoOpinion, eVoteNoOpinion)); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h new file mode 100644 index 000000000000..42ea5aacecb4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h @@ -0,0 +1,53 @@ +//===-- DynamicLoaderWindowsDYLD.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_WINDOWS_DYLD_DYNAMICLOADERWINDOWSDYLD_H +#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_WINDOWS_DYLD_DYNAMICLOADERWINDOWSDYLD_H + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/lldb-forward.h" + +#include <map> + +namespace lldb_private { + +class DynamicLoaderWindowsDYLD : public DynamicLoader { +public: + DynamicLoaderWindowsDYLD(Process *process); + + ~DynamicLoaderWindowsDYLD() override; + + static void Initialize(); + static void Terminate(); + static llvm::StringRef GetPluginNameStatic() { return "windows-dyld"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + void OnLoadModule(lldb::ModuleSP module_sp, const ModuleSpec module_spec, + lldb::addr_t module_addr); + void OnUnloadModule(lldb::addr_t module_addr); + + void DidAttach() override; + void DidLaunch() override; + Status CanLoadImage() override; + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + +protected: + lldb::addr_t GetLoadAddress(lldb::ModuleSP executable); + +private: + std::map<lldb::ModuleSP, lldb::addr_t> m_loaded_modules; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_DYNAMICLOADER_WINDOWS_DYLD_DYNAMICLOADERWINDOWSDYLD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp new file mode 100644 index 000000000000..d019415cb67a --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp @@ -0,0 +1,82 @@ +//===-- DynamicLoaderWasmDYLD.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 "DynamicLoaderWasmDYLD.h" + +#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::wasm; + +LLDB_PLUGIN_DEFINE(DynamicLoaderWasmDYLD) + +DynamicLoaderWasmDYLD::DynamicLoaderWasmDYLD(Process *process) + : DynamicLoader(process) {} + +void DynamicLoaderWasmDYLD::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +llvm::StringRef DynamicLoaderWasmDYLD::GetPluginDescriptionStatic() { + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in WebAssembly engines."; +} + +DynamicLoader *DynamicLoaderWasmDYLD::CreateInstance(Process *process, + bool force) { + bool should_create = force; + if (!should_create) { + should_create = + (process->GetTarget().GetArchitecture().GetTriple().getArch() == + llvm::Triple::wasm32); + } + + if (should_create) + return new DynamicLoaderWasmDYLD(process); + + return nullptr; +} + +void DynamicLoaderWasmDYLD::DidAttach() { + Log *log = GetLog(LLDBLog::DynamicLoader); + LLDB_LOGF(log, "DynamicLoaderWasmDYLD::%s()", __FUNCTION__); + + // Ask the process for the list of loaded WebAssembly modules. + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "Couldn't load modules: {0}"); +} + +ThreadPlanSP DynamicLoaderWasmDYLD::GetStepThroughTrampolinePlan(Thread &thread, + bool stop) { + return ThreadPlanSP(); +} + +lldb::ModuleSP DynamicLoaderWasmDYLD::LoadModuleAtAddress( + const lldb_private::FileSpec &file, lldb::addr_t link_map_addr, + lldb::addr_t base_addr, bool base_addr_is_offset) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + file, link_map_addr, base_addr, base_addr_is_offset)) + return module_sp; + + if (ModuleSP module_sp = m_process->ReadModuleFromMemory(file, base_addr)) { + UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); + m_process->GetTarget().GetImages().AppendIfNeeded(module_sp); + return module_sp; + } + + return nullptr; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h new file mode 100644 index 000000000000..5ed855395cca --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h @@ -0,0 +1,52 @@ +//===-- DynamicLoaderWasmDYLD.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 liblldb_Plugins_DynamicLoaderWasmDYLD_h_ +#define liblldb_Plugins_DynamicLoaderWasmDYLD_h_ + +#include "lldb/Target/DynamicLoader.h" + +namespace lldb_private { +namespace wasm { + +class DynamicLoaderWasmDYLD : public DynamicLoader { +public: + DynamicLoaderWasmDYLD(Process *process); + + static void Initialize(); + static void Terminate() {} + + static llvm::StringRef GetPluginNameStatic() { return "wasm-dyld"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static DynamicLoader *CreateInstance(Process *process, bool force); + + /// DynamicLoader + /// \{ + void DidAttach() override; + void DidLaunch() override {} + Status CanLoadImage() override { return Status(); } + lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, + bool stop) override; + lldb::ModuleSP LoadModuleAtAddress(const lldb_private::FileSpec &file, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + + /// \} + + /// PluginInterface protocol. + /// \{ + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + /// \} +}; + +} // namespace wasm +} // namespace lldb_private + +#endif // liblldb_Plugins_DynamicLoaderWasmDYLD_h_ |