Remove Debugger::GetOutputStream and Debugger::GetErrorStream in preparation for replacing both with a new variant that needs to be locked and hence can't be handed out like we do right now. The patch replaces most uses with GetAsyncOutputStream and GetAsyncErrorStream respectively. There methods return new StreamSP objects that automatically get flushed on destruction. See #126630 for more details.
380 lines
13 KiB
C++
380 lines
13 KiB
C++
//===-- 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().GetAsyncErrorStream() << 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) {
|
|
lldb::StreamSP s = target.GetDebugger().GetAsyncErrorStream();
|
|
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);
|
|
}
|