Files
Greg Clayton 45afced2e7 Fix ProcessElfCore::FindModuleUUID() so it work with symlinks. (#206601)
Resumbitting https://github.com/llvm/llvm-project/pull/205235 with
disabled parallel module loading to fix buildbot deadlock.

ProcessElfCore was reading the NT_FILE list and using that to help
FindModuleUUID to provide UUID information when loading core files. The
NT_FILE list contains resolved paths only, while the
DynamicLoaderPOSIXDYLD plug-in was using paths found in the r_debug
structure which contains a linked list of all of the shared libraries in
a process. The issue was these paths could be symlinks which would cause
ProcessELFCore::FindModuleUUID(...) to fail because the paths wouldn't
match up. This led to the ProcessELFCore often not being able to provide
UUIDs for shared libraries and cause the incorrect binaries to be loaded
from the current machine even when the shared library UUIDs don't match.

The solution was to add the ability for a ModuleSpec to contain a load
address for the shared library. This allows ProcessELFCore to uniquely
identify a library regardless of the name used in NT_FILE. We can now
correctly supply the UUID from the .gnu-build-id to any binaries which
use symlinks when linking, but have differing resolved paths to the
libraries.

The process virtual function for finding a UUID was changed from:

virtual lldb_private::UUID FindModuleUUID(const llvm::StringRef path);
to:

    virtual bool FindModuleUUID(ModuleSpec &spec);
to allow Process::FindModuleUUID to rely on other data in the ModuleSpec
since the path isn't enough.

We will be able to use the ModuleSpec's load address for creating a
module from a ModuleSpec, but that isn't in this PR.
2026-06-30 10:44:00 -07:00

407 lines
14 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 (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;
}
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.SetTarget(target.shared_from_this());
module_spec.GetUUID() = uuid;
FileSpec name_filespec(name);
if (FileSystem::Instance().Exists(name_filespec))
module_spec.GetFileSpec() = name_filespec;
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);
// Can lldb's symbol/executable location schemes
// find an executable and symbol file.
if (!module_sp) {
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
StatisticsMap symbol_locator_map;
module_spec.GetSymbolFileSpec() =
PluginManager::LocateExecutableSymbolFile(module_spec, search_paths,
symbol_locator_map);
ModuleSpec objfile_module_spec =
PluginManager::LocateExecutableObjectFile(module_spec,
symbol_locator_map);
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 (module_sp) {
module_sp->GetSymbolLocatorStatistics().merge(symbol_locator_map);
}
}
// 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::StreamUP 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);
}