blob: 7eaf329766c2a61f7f80f0e3180b8de41f52e1e7 [file]
//===-- 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/Section.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolLocator.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/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <optional>
#include <string>
#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 (auto create_callback :
PluginManager::GetDynamicLoaderCreateCallbacks()) {
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 ModuleSpec &spec) {
ModuleSpec module_spec(spec);
Target &target = m_process->GetTarget();
// The process may be able to augment the module_spec with a UUID.
if (!module_spec.GetUUID().IsValid())
m_process->FindModuleUUID(module_spec);
if (ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec))
return module_sp;
if (ModuleSP module_sp =
target.GetOrCreateModule(module_spec, /*notify=*/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) {
Target &target = m_process->GetTarget();
ModuleSpec module_spec(file, target.GetArchitecture());
module_spec.SetLoadAddress(base_addr);
ModuleSP module_sp = FindModuleViaTarget(module_spec);
// We have a core file, try to load the image from memory if we didn't find
// the module.
if (!module_sp && !m_process->IsLiveDebugSession()) {
llvm::Expected<ModuleSP> memory_module_sp_or_err =
m_process->ReadModuleFromMemory(file, base_addr);
if (auto err = memory_module_sp_or_err.takeError())
LLDB_LOG_ERROR(GetLog(LLDBLog::DynamicLoader), std::move(err),
"Failed to read module from memory: {0}");
else {
module_sp = *memory_module_sp_or_err;
m_process->GetTarget().GetImages().AppendIfNeeded(module_sp, false);
}
}
if (module_sp)
UpdateLoadedSections(module_sp, link_map_addr, base_addr,
base_addr_is_offset);
return module_sp;
}
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;
}
llvm::Expected<ModuleSP> module_sp_or_err =
process->ReadModuleFromMemory(FileSpec(name), addr);
if (auto err = module_sp_or_err.takeError()) {
LLDB_LOG_ERROR(GetLog(LLDBLog::DynamicLoader), std::move(err),
"Failed to read module from memory: {0}");
return {};
}
return *module_sp_or_err;
}
static std::string
GetBinaryDescription(const DynamicLoader::BinarySpec &bin_spec) {
StreamString desc;
if (!bin_spec.name.empty())
desc << bin_spec.name << " ";
if (bin_spec.uuid.IsValid())
desc << bin_spec.uuid.GetAsString();
if (!bin_spec.value_is_offset && bin_spec.value != LLDB_INVALID_ADDRESS) {
desc << " at 0x";
desc.PutHex64(bin_spec.value);
}
return desc.GetString().str();
}
static std::string
GetBinaryNotFoundMessage(const DynamicLoader::BinarySpec &bin_spec) {
StreamString msg;
msg << "Unable to find file";
if (!bin_spec.name.empty())
msg << " " << bin_spec.name;
if (bin_spec.uuid.IsValid())
msg << " with UUID " << bin_spec.uuid.GetAsString();
if (bin_spec.value != LLDB_INVALID_ADDRESS) {
if (bin_spec.value_is_offset)
msg.Printf(" with slide 0x%" PRIx64, bin_spec.value);
else
msg.Printf(" at address 0x%" PRIx64, bin_spec.value);
}
return msg.GetString().str();
}
/// Reads the Target, so it has to be called for one binary at a time.
///
/// \return What to search for, or nothing when the binary is already in hand.
static std::optional<SymbolLocator::Request>
PrepareSearch(Target &target, DynamicLoader::BinarySpec &bin_spec) {
ModuleSpec module_spec;
module_spec.SetTarget(target.shared_from_this());
module_spec.GetUUID() = bin_spec.uuid;
FileSpec name_filespec(bin_spec.name);
if (FileSystem::Instance().Exists(name_filespec))
module_spec.GetFileSpec() = name_filespec;
// Has lldb already seen a module with this UUID? A module whose symbols are
// already in hand is the answer, and searching would only find them again.
// Without them the search still has something to add.
ModuleList::GetSharedModule(module_spec, bin_spec.module_sp, nullptr, nullptr,
/*invoke_locate_callback=*/true,
/*invoke_symbol_locators=*/false);
if (bin_spec.module_sp && bin_spec.module_sp->GetSymbolFileFileSpec())
return std::nullopt;
SymbolLocator::Request request;
request.module_spec = module_spec;
request.platform = target.GetPlatform();
request.external_lookup = bin_spec.force_symbol_search;
request.description = GetBinaryDescription(bin_spec);
return request;
}
/// The module is not registered with the Target until LoadBinaryInTarget.
static void FinishSearch(DynamicLoader::BinarySpec &bin_spec,
llvm::Expected<SymbolLocator::Result> &&located) {
if (!located) {
// Loading a binary that was never found already reports that, so a bare
// not-found error would only say it a second time. Any other error says
// something that report cannot.
llvm::Error error = located.takeError();
if (error.isA<SymbolLocator::NotFound>())
llvm::consumeError(std::move(error));
else
bin_spec.error = Status::FromError(std::move(error));
return;
}
if (located->symbol_error)
bin_spec.error = Status::FromError(std::move(*located->symbol_error));
ModuleSP located_module_sp;
ModuleList::GetSharedModule(located->module_spec, located_module_sp, nullptr,
nullptr, /*invoke_locate_callback=*/false,
/*invoke_symbol_locators=*/false);
// A located binary always yields a module, whatever ObjectFile makes of the
// file, because the caller has nowhere else to record what the search found.
if (!located_module_sp)
located_module_sp = std::make_shared<Module>(located->module_spec);
// Published only now, so that a search that came up empty leaves whatever the
// shared module list had in hand.
bin_spec.module_sp = std::move(located_module_sp);
bin_spec.module_sp->GetSymbolLocatorStatistics().merge(located->statistics);
}
static void FindBinaryUUIDInMemory(Process *process,
DynamicLoader::BinarySpec &bin_spec) {
bin_spec.memory_module_sp =
ReadUnnamedMemoryModule(process, bin_spec.value, bin_spec.name);
if (bin_spec.memory_module_sp)
bin_spec.uuid = bin_spec.memory_module_sp->GetUUID();
}
void DynamicLoader::LocateBinaries(
Process *process, llvm::MutableArrayRef<BinarySpec> bin_specs) {
Target &target = process->GetTarget();
const FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
// Reading a binary's UUID out of memory has to happen on this thread, and
// before any search, so that a binary whose UUID is not known yet still joins
// the batch.
llvm::SmallVector<BinarySpec *> to_search;
std::vector<SymbolLocator::Request> requests;
for (BinarySpec &bin_spec : bin_specs) {
if (!bin_spec.uuid.IsValid() && !bin_spec.value_is_offset)
FindBinaryUUIDInMemory(process, bin_spec);
if (!bin_spec.uuid.IsValid())
continue;
if (std::optional<SymbolLocator::Request> request =
PrepareSearch(target, bin_spec)) {
to_search.push_back(&bin_spec);
requests.push_back(std::move(*request));
}
}
std::vector<llvm::Expected<SymbolLocator::Result>> located =
SymbolLocator::Locate(requests, search_paths,
target.GetParallelModuleLoad());
for (auto [bin_spec, result] : llvm::zip_equal(to_search, located))
FinishSearch(*bin_spec, std::move(result));
}
llvm::Expected<ModuleSP>
DynamicLoader::LoadBinaryInTarget(Process *process, BinarySpec &bin_spec) {
Target &target = process->GetTarget();
// The error belongs to this function now: every path below either reports it
// or folds it into the failure.
llvm::Error search_error = bin_spec.error.takeError();
// If we couldn't find the binary anywhere else, as a last resort,
// read it out of memory.
if (bin_spec.allow_memory_image_last_resort && !bin_spec.module_sp &&
bin_spec.value != LLDB_INVALID_ADDRESS && !bin_spec.value_is_offset) {
if (!bin_spec.memory_module_sp)
bin_spec.memory_module_sp =
ReadUnnamedMemoryModule(process, bin_spec.value, bin_spec.name);
if (bin_spec.memory_module_sp)
bin_spec.module_sp = bin_spec.memory_module_sp;
}
Log *log = GetLog(LLDBLog::DynamicLoader);
if (!bin_spec.module_sp) {
std::string message = GetBinaryNotFoundMessage(bin_spec);
LLDB_LOG(log, "{0}", message);
llvm::Error error = llvm::createStringError(message);
if (search_error)
return llvm::joinErrors(std::move(search_error), std::move(error));
return std::move(error);
}
// A binary was found, but a symbol server may still have had something to say
// about its symbols. Name the binary: a symbol locator's error is not
// required to identify what it was asked to look for.
if (search_error)
*target.GetDebugger().GetAsyncErrorStream()
<< GetBinaryDescription(bin_spec) << ": "
<< llvm::toString(std::move(search_error)) << "\n";
// 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(bin_spec.module_sp->GetArchitecture());
target.GetImages().AppendIfNeeded(bin_spec.module_sp, false);
bool changed = false;
if (bin_spec.set_address_in_target) {
if (bin_spec.module_sp->GetObjectFile()) {
if (bin_spec.value != LLDB_INVALID_ADDRESS) {
LLDB_LOGF(log,
"DynamicLoader::LoadBinaryInTarget Loading "
"binary %s UUID %s at %s 0x%" PRIx64,
bin_spec.name.c_str(), bin_spec.uuid.GetAsString().c_str(),
bin_spec.value_is_offset ? "offset" : "address",
bin_spec.value);
bin_spec.module_sp->SetLoadAddress(target, bin_spec.value,
bin_spec.value_is_offset, changed);
} else {
// No address/offset/slide, load the binary at file address,
// offset 0.
LLDB_LOGF(log,
"DynamicLoader::LoadBinaryInTarget Loading "
"binary %s UUID %s at file address",
bin_spec.name.c_str(), bin_spec.uuid.GetAsString().c_str());
bin_spec.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::LoadBinaryInTarget Loading binary "
"%s UUID %s from memory at address 0x%" PRIx64,
bin_spec.name.c_str(), bin_spec.uuid.GetAsString().c_str(),
bin_spec.value);
bin_spec.module_sp->SetLoadAddress(target, 0, true /* value_is_slide */,
changed);
}
}
if (bin_spec.notify) {
ModuleList added_module;
added_module.Append(bin_spec.module_sp, false);
target.ModulesDidLoad(added_module);
}
return bin_spec.module_sp;
}
llvm::Expected<ModuleSP>
DynamicLoader::LocateAndLoadBinary(Process *process, BinarySpec &bin_spec) {
LocateBinaries(process, bin_spec);
return LoadBinaryInTarget(process, bin_spec);
}
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);
}