| //===-- DynamicLoader.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/Target/DynamicLoader.h" |
| |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ModuleList.h" |
| #include "lldb/Core/ModuleSpec.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Progress.h" |
| #include "lldb/Core/Section.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Target/MemoryRegionInfo.h" |
| #include "lldb/Target/Platform.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/ConstString.h" |
| #include "lldb/Utility/LLDBLog.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/lldb-private-interfaces.h" |
| |
| #include "llvm/ADT/StringRef.h" |
| |
| #include <memory> |
| |
| #include <cassert> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| DynamicLoader *DynamicLoader::FindPlugin(Process *process, |
| llvm::StringRef plugin_name) { |
| DynamicLoaderCreateInstance create_callback = nullptr; |
| if (!plugin_name.empty()) { |
| create_callback = |
| PluginManager::GetDynamicLoaderCreateCallbackForPluginName(plugin_name); |
| if (create_callback) { |
| std::unique_ptr<DynamicLoader> instance_up( |
| create_callback(process, true)); |
| if (instance_up) |
| return instance_up.release(); |
| } |
| } else { |
| for (uint32_t idx = 0; |
| (create_callback = |
| PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != |
| nullptr; |
| ++idx) { |
| std::unique_ptr<DynamicLoader> instance_up( |
| create_callback(process, false)); |
| if (instance_up) |
| return instance_up.release(); |
| } |
| } |
| return nullptr; |
| } |
| |
| DynamicLoader::DynamicLoader(Process *process) : m_process(process) {} |
| |
| // Accessors to the global setting as to whether to stop at image (shared |
| // library) loading/unloading. |
| |
| bool DynamicLoader::GetStopWhenImagesChange() const { |
| return m_process->GetStopOnSharedLibraryEvents(); |
| } |
| |
| void DynamicLoader::SetStopWhenImagesChange(bool stop) { |
| m_process->SetStopOnSharedLibraryEvents(stop); |
| } |
| |
| ModuleSP DynamicLoader::GetTargetExecutable() { |
| Target &target = m_process->GetTarget(); |
| ModuleSP executable = target.GetExecutableModule(); |
| |
| if (executable) { |
| if (FileSystem::Instance().Exists(executable->GetFileSpec())) { |
| ModuleSpec module_spec(executable->GetFileSpec(), |
| executable->GetArchitecture()); |
| auto module_sp = std::make_shared<Module>(module_spec); |
| // If we're a coredump and we already have a main executable, we don't |
| // need to reload the module list that target already has |
| if (!m_process->IsLiveDebugSession()) { |
| return executable; |
| } |
| // Check if the executable has changed and set it to the target |
| // executable if they differ. |
| if (module_sp && module_sp->GetUUID().IsValid() && |
| executable->GetUUID().IsValid()) { |
| if (module_sp->GetUUID() != executable->GetUUID()) |
| executable.reset(); |
| } else if (executable->FileHasChanged()) { |
| executable.reset(); |
| } |
| |
| if (!executable) { |
| 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; |
| } |
| |
| void DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, |
| addr_t base_addr, |
| bool base_addr_is_offset) { |
| UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset); |
| } |
| |
| void DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, |
| addr_t base_addr, |
| bool base_addr_is_offset) { |
| bool changed; |
| module->SetLoadAddress(m_process->GetTarget(), base_addr, base_addr_is_offset, |
| changed); |
| } |
| |
| void DynamicLoader::UnloadSections(const ModuleSP module) { |
| UnloadSectionsCommon(module); |
| } |
| |
| void DynamicLoader::UnloadSectionsCommon(const ModuleSP module) { |
| Target &target = m_process->GetTarget(); |
| const SectionList *sections = GetSectionListFromModule(module); |
| |
| assert(sections && "SectionList missing from unloaded 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); |
| } |
| } |
| |
| const SectionList * |
| DynamicLoader::GetSectionListFromModule(const ModuleSP module) const { |
| SectionList *sections = nullptr; |
| if (module) { |
| ObjectFile *obj_file = module->GetObjectFile(); |
| if (obj_file != nullptr) { |
| sections = obj_file->GetSectionList(); |
| } |
| } |
| return sections; |
| } |
| |
| ModuleSP DynamicLoader::FindModuleViaTarget(const FileSpec &file) { |
| Target &target = m_process->GetTarget(); |
| ModuleSpec module_spec(file, target.GetArchitecture()); |
| if (UUID uuid = m_process->FindModuleUUID(file.GetPath())) { |
| // Process may be able to augment the module_spec with UUID, e.g. ELF core. |
| module_spec.GetUUID() = uuid; |
| } |
| |
| if (ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec)) |
| return module_sp; |
| |
| if (ModuleSP module_sp = target.GetOrCreateModule(module_spec, false)) |
| return module_sp; |
| |
| return nullptr; |
| } |
| |
| ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file, |
| addr_t link_map_addr, |
| addr_t base_addr, |
| bool base_addr_is_offset) { |
| if (ModuleSP module_sp = FindModuleViaTarget(file)) { |
| UpdateLoadedSections(module_sp, link_map_addr, base_addr, |
| base_addr_is_offset); |
| return module_sp; |
| } |
| |
| return nullptr; |
| } |
| |
| static ModuleSP ReadUnnamedMemoryModule(Process *process, addr_t addr, |
| llvm::StringRef name) { |
| char namebuf[80]; |
| if (name.empty()) { |
| snprintf(namebuf, sizeof(namebuf), "memory-image-0x%" PRIx64, addr); |
| name = namebuf; |
| } |
| return process->ReadModuleFromMemory(FileSpec(name), addr); |
| } |
| |
| ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress( |
| Process *process, llvm::StringRef name, UUID uuid, addr_t value, |
| bool value_is_offset, bool force_symbol_search, bool notify, |
| bool set_address_in_target, bool allow_memory_image_last_resort) { |
| ModuleSP memory_module_sp; |
| ModuleSP module_sp; |
| PlatformSP platform_sp = process->GetTarget().GetPlatform(); |
| Target &target = process->GetTarget(); |
| Status error; |
| |
| StreamString prog_str; |
| if (!name.empty()) { |
| prog_str << name.str() << " "; |
| } |
| if (uuid.IsValid()) |
| prog_str << uuid.GetAsString(); |
| if (value_is_offset == 0 && value != LLDB_INVALID_ADDRESS) { |
| prog_str << "at 0x"; |
| prog_str.PutHex64(value); |
| } |
| |
| if (!uuid.IsValid() && !value_is_offset) { |
| memory_module_sp = ReadUnnamedMemoryModule(process, value, name); |
| |
| if (memory_module_sp) { |
| uuid = memory_module_sp->GetUUID(); |
| if (uuid.IsValid()) { |
| prog_str << " "; |
| prog_str << uuid.GetAsString(); |
| } |
| } |
| } |
| ModuleSpec module_spec; |
| module_spec.GetUUID() = uuid; |
| FileSpec name_filespec(name); |
| |
| if (uuid.IsValid()) { |
| Progress progress("Locating binary", prog_str.GetString().str()); |
| |
| // Has lldb already seen a module with this UUID? |
| // Or have external lookup enabled in DebugSymbols on macOS. |
| if (!module_sp) |
| error = ModuleList::GetSharedModule(module_spec, module_sp, nullptr, |
| nullptr, nullptr); |
| |
| // Can lldb's symbol/executable location schemes |
| // find an executable and symbol file. |
| if (!module_sp) { |
| FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); |
| module_spec.GetSymbolFileSpec() = |
| PluginManager::LocateExecutableSymbolFile(module_spec, search_paths); |
| ModuleSpec objfile_module_spec = |
| PluginManager::LocateExecutableObjectFile(module_spec); |
| module_spec.GetFileSpec() = objfile_module_spec.GetFileSpec(); |
| if (FileSystem::Instance().Exists(module_spec.GetFileSpec()) && |
| FileSystem::Instance().Exists(module_spec.GetSymbolFileSpec())) { |
| module_sp = std::make_shared<Module>(module_spec); |
| } |
| } |
| |
| // If we haven't found a binary, or we don't have a SymbolFile, see |
| // if there is an external search tool that can find it. |
| if (!module_sp || !module_sp->GetSymbolFileFileSpec()) { |
| PluginManager::DownloadObjectAndSymbolFile(module_spec, error, |
| force_symbol_search); |
| if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { |
| module_sp = std::make_shared<Module>(module_spec); |
| } else if (force_symbol_search && error.AsCString("") && |
| error.AsCString("")[0] != '\0') { |
| target.GetDebugger().GetErrorStream() << error.AsCString(); |
| } |
| } |
| |
| // If we only found the executable, create a Module based on that. |
| if (!module_sp && FileSystem::Instance().Exists(module_spec.GetFileSpec())) |
| module_sp = std::make_shared<Module>(module_spec); |
| } |
| |
| // If we couldn't find the binary anywhere else, as a last resort, |
| // read it out of memory. |
| if (allow_memory_image_last_resort && !module_sp.get() && |
| value != LLDB_INVALID_ADDRESS && !value_is_offset) { |
| if (!memory_module_sp) |
| memory_module_sp = ReadUnnamedMemoryModule(process, value, name); |
| if (memory_module_sp) |
| module_sp = memory_module_sp; |
| } |
| |
| Log *log = GetLog(LLDBLog::DynamicLoader); |
| if (module_sp.get()) { |
| // Ensure the Target has an architecture set in case |
| // we need it while processing this binary/eh_frame/debug info. |
| if (!target.GetArchitecture().IsValid()) |
| target.SetArchitecture(module_sp->GetArchitecture()); |
| target.GetImages().AppendIfNeeded(module_sp, false); |
| |
| bool changed = false; |
| if (set_address_in_target) { |
| if (module_sp->GetObjectFile()) { |
| if (value != LLDB_INVALID_ADDRESS) { |
| LLDB_LOGF(log, |
| "DynamicLoader::LoadBinaryWithUUIDAndAddress Loading " |
| "binary %s UUID %s at %s 0x%" PRIx64, |
| name.str().c_str(), uuid.GetAsString().c_str(), |
| value_is_offset ? "offset" : "address", value); |
| module_sp->SetLoadAddress(target, value, value_is_offset, changed); |
| } else { |
| // No address/offset/slide, load the binary at file address, |
| // offset 0. |
| LLDB_LOGF(log, |
| "DynamicLoader::LoadBinaryWithUUIDAndAddress Loading " |
| "binary %s UUID %s at file address", |
| name.str().c_str(), uuid.GetAsString().c_str()); |
| module_sp->SetLoadAddress(target, 0, true /* value_is_slide */, |
| changed); |
| } |
| } else { |
| // In-memory image, load at its true address, offset 0. |
| LLDB_LOGF(log, |
| "DynamicLoader::LoadBinaryWithUUIDAndAddress Loading binary " |
| "%s UUID %s from memory at address 0x%" PRIx64, |
| name.str().c_str(), uuid.GetAsString().c_str(), value); |
| module_sp->SetLoadAddress(target, 0, true /* value_is_slide */, |
| changed); |
| } |
| } |
| |
| if (notify) { |
| ModuleList added_module; |
| added_module.Append(module_sp, false); |
| target.ModulesDidLoad(added_module); |
| } |
| } else { |
| if (force_symbol_search) { |
| Stream &s = target.GetDebugger().GetErrorStream(); |
| s.Printf("Unable to find file"); |
| if (!name.empty()) |
| s.Printf(" %s", name.str().c_str()); |
| if (uuid.IsValid()) |
| s.Printf(" with UUID %s", uuid.GetAsString().c_str()); |
| if (value != LLDB_INVALID_ADDRESS) { |
| if (value_is_offset) |
| s.Printf(" with slide 0x%" PRIx64, value); |
| else |
| s.Printf(" at address 0x%" PRIx64, value); |
| } |
| s.Printf("\n"); |
| } |
| LLDB_LOGF(log, |
| "Unable to find binary %s with UUID %s and load it at " |
| "%s 0x%" PRIx64, |
| name.str().c_str(), uuid.GetAsString().c_str(), |
| value_is_offset ? "offset" : "address", value); |
| } |
| |
| return module_sp; |
| } |
| |
| int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr, |
| int size_in_bytes) { |
| Status error; |
| uint64_t value = |
| m_process->ReadUnsignedIntegerFromMemory(addr, size_in_bytes, 0, error); |
| if (error.Fail()) |
| return -1; |
| else |
| return (int64_t)value; |
| } |
| |
| addr_t DynamicLoader::ReadPointer(addr_t addr) { |
| Status error; |
| addr_t value = m_process->ReadPointerFromMemory(addr, error); |
| if (error.Fail()) |
| return LLDB_INVALID_ADDRESS; |
| else |
| return value; |
| } |
| |
| void DynamicLoader::LoadOperatingSystemPlugin(bool flush) |
| { |
| if (m_process) |
| m_process->LoadOperatingSystemPlugin(flush); |
| } |