This patch improves LLDB launch time on Linux machines for **preload scenarios**, particularly for executables with a lot of shared library dependencies (or modules). Specifically: * Launching a binary with `target.preload-symbols = true` * Attaching to a process with `target.preload-symbols = true`. It's completely controlled by a new flag added in the first commit `plugin.dynamic-loader.posix-dyld.parallel-module-load`, which *defaults to false*. This was inspired by similar work on Darwin #110646. Some rough numbers to showcase perf improvement, run on a very beefy machine: * Executable with ~5600 modules: baseline 45s, improvement 15s * Executable with ~3800 modules: baseline 25s, improvement 10s * Executable with ~6650 modules: baseline 67s, improvement 20s * Executable with ~12500 modules: baseline 185s, improvement 85s * Executable with ~14700 modules: baseline 235s, improvement 120s A lot of targets we deal with have a *ton* of modules, and unfortunately we're unable to convince other folks to reduce the number of modules, so performance improvements like this can be very impactful for user experience. This patch achieves the performance improvement by parallelizing `DynamicLoaderPOSIXDYLD::RefreshModules` for the launch scenario, and `DynamicLoaderPOSIXDYLD::LoadAllCurrentModules` for the attach scenario. The commits have some context on their specific changes as well -- hopefully this helps the review. # More context on implementation We discovered the bottlenecks by via `perf record -g -p <lldb's pid>` on a Linux machine. With an executable known to have 1000s of shared library dependencies, I ran ``` (lldb) b main (lldb) r # taking a while ``` and showed the resulting perf trace (snippet shown) ``` Samples: 85K of event 'cycles:P', Event count (approx.): 54615855812 Children Self Command Shared Object Symbol - 93.54% 0.00% intern-state libc.so.6 [.] clone3 clone3 start_thread lldb_private::HostNativeThreadBase::ThreadCreateTrampoline(void*) r std::_Function_handler<void* (), lldb_private::Process::StartPrivateStateThread(bool)::$_0>::_M_invoke(std::_Any_data const&) lldb_private::Process::RunPrivateStateThread(bool) n - lldb_private::Process::HandlePrivateEvent(std::shared_ptr<lldb_private::Event>&) - 93.54% lldb_private::Process::ShouldBroadcastEvent(lldb_private::Event*) - 93.54% lldb_private::ThreadList::ShouldStop(lldb_private::Event*) - lldb_private::Thread::ShouldStop(lldb_private::Event*) * - 93.53% lldb_private::StopInfoBreakpoint::ShouldStopSynchronous(lldb_private::Event*) t - 93.52% lldb_private::BreakpointSite::ShouldStop(lldb_private::StoppointCallbackContext*) i lldb_private::BreakpointLocationCollection::ShouldStop(lldb_private::StoppointCallbackContext*) k lldb_private::BreakpointLocation::ShouldStop(lldb_private::StoppointCallbackContext*) b lldb_private::BreakpointOptions::InvokeCallback(lldb_private::StoppointCallbackContext*, unsigned long, unsigned long) i DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void*, lldb_private::StoppointCallbackContext*, unsigned long, unsigned lo - DynamicLoaderPOSIXDYLD::RefreshModules() O - 93.42% DynamicLoaderPOSIXDYLD::RefreshModules()::$_0::operator()(DYLDRendezvous::SOEntry const&) const u - 93.40% DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(lldb_private::FileSpec const&, unsigned long, unsigned long, bools - lldb_private::DynamicLoader::LoadModuleAtAddress(lldb_private::FileSpec const&, unsigned long, unsigned long, boos - 83.90% lldb_private::DynamicLoader::FindModuleViaTarget(lldb_private::FileSpec const&) o - 83.01% lldb_private::Target::GetOrCreateModule(lldb_private::ModuleSpec const&, bool, lldb_private::Status* - 77.89% lldb_private::Module::PreloadSymbols() - 44.06% lldb_private::Symtab::PreloadSymbols() - 43.66% lldb_private::Symtab::InitNameIndexes() ... ``` We saw that majority of time was spent in `RefreshModules`, with the main culprit within it `LoadModuleAtAddress` which eventually calls `PreloadSymbols`. At first, `DynamicLoaderPOSIXDYLD::LoadModuleAtAddress` appears fairly independent -- most of it deals with different files and then getting or creating Modules from these files. The portions that aren't independent seem to deal with ModuleLists, which appear concurrency safe. There were members of `DynamicLoaderPOSIXDYLD` I had to synchronize though: namely `m_loaded_modules` which `DynamicLoaderPOSIXDYLD` maintains to map its loaded modules to their link addresses. Without synchronizing this, I ran into SEGFAULTS and other issues when running `check-lldb`. I also locked the assignment and comparison of `m_interpreter_module`, which may be unnecessary. # Alternate implementations When creating this patch, another implementation I considered was directly background-ing the call to `Module::PreloadSymbol` in `Target::GetOrCreateModule`. It would have the added benefit of working across platforms generically, and appeared to be concurrency safe. It was done via `Debugger::GetThreadPool().async` directly. However, there were a ton of concurrency issues, so I abandoned that approach for now. # Testing With the feature active, I tested via `ninja check-lldb` on both Debug and Release builds several times (~5 or 6 altogether?), and didn't spot additional failing or flaky tests. I also tested manually on several different binaries, some with around 14000 modules, but just basic operations: launching, reaching main, setting breakpoint, stepping, showing some backtraces. I've also tested with the flag off just to make sure things behave properly synchronously.
1017 lines
36 KiB
C++
1017 lines
36 KiB
C++
//===-- 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/Debugger.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/RegisterContext.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 "llvm/Support/ThreadPool.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 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::SetLoadedModule(const ModuleSP &module_sp,
|
|
addr_t link_map_addr) {
|
|
llvm::sys::ScopedWriter lock(m_loaded_modules_rw_mutex);
|
|
m_loaded_modules[module_sp] = link_map_addr;
|
|
}
|
|
|
|
void DynamicLoaderPOSIXDYLD::UnloadModule(const ModuleSP &module_sp) {
|
|
llvm::sys::ScopedWriter lock(m_loaded_modules_rw_mutex);
|
|
m_loaded_modules.erase(module_sp);
|
|
}
|
|
|
|
std::optional<lldb::addr_t>
|
|
DynamicLoaderPOSIXDYLD::GetLoadedModuleLinkAddr(const ModuleSP &module_sp) {
|
|
llvm::sys::ScopedReader lock(m_loaded_modules_rw_mutex);
|
|
auto it = m_loaded_modules.find(module_sp);
|
|
if (it != m_loaded_modules.end())
|
|
return it->second;
|
|
return std::nullopt;
|
|
}
|
|
|
|
void DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module,
|
|
addr_t link_map_addr,
|
|
addr_t base_addr,
|
|
bool base_addr_is_offset) {
|
|
SetLoadedModule(module, link_map_addr);
|
|
|
|
UpdateLoadedSectionsCommon(module, base_addr, base_addr_is_offset);
|
|
}
|
|
|
|
void DynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) {
|
|
UnloadModule(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();
|
|
SetLoadedModule(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;
|
|
}
|
|
|
|
// Synchronize reading and writing of `m_interpreter_module`.
|
|
std::mutex interpreter_module_mutex;
|
|
// We should be able to take SOEntry as reference since the data
|
|
// exists for the duration of this call in `m_rendezvous`.
|
|
auto load_module_fn =
|
|
[this, &loaded_modules, &new_modules,
|
|
&interpreter_module_mutex](const DYLDRendezvous::SOEntry &so_entry) {
|
|
// 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.
|
|
{
|
|
// `m_interpreter_module` may be modified by another thread at the
|
|
// same time, so we guard the access here.
|
|
std::lock_guard<std::mutex> lock(interpreter_module_mutex);
|
|
if ((m_interpreter_module.lock() != nullptr) &&
|
|
(so_entry.base_addr == m_interpreter_base))
|
|
return;
|
|
}
|
|
|
|
ModuleSP module_sp = LoadModuleAtAddress(
|
|
so_entry.file_spec, so_entry.link_addr, so_entry.base_addr, true);
|
|
if (!module_sp.get())
|
|
return;
|
|
|
|
{
|
|
// `m_interpreter_module` may be modified by another thread at the
|
|
// same time, so we guard the access here.
|
|
std::lock_guard<std::mutex> lock(interpreter_module_mutex);
|
|
// Set the interpreter module, if this is the interpreter.
|
|
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.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: in a multi-threaded environment, these module lists may be
|
|
// appended to out-of-order. This is fine, since there's no
|
|
// expectation for `loaded_modules` or `new_modules` to be in any
|
|
// particular order, and appending to each module list is thread-safe.
|
|
// Also, `new_modules` is only used for the `ModulesDidLoad` call at
|
|
// the end of this function.
|
|
loaded_modules.AppendIfNeeded(module_sp);
|
|
new_modules.Append(module_sp);
|
|
};
|
|
|
|
if (m_process->GetTarget().GetParallelModuleLoad()) {
|
|
llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool());
|
|
for (; I != E; ++I)
|
|
task_group.async(load_module_fn, *I);
|
|
task_group.wait();
|
|
} else {
|
|
for (; I != E; ++I)
|
|
load_module_fn(*I);
|
|
}
|
|
|
|
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) {
|
|
addr_t addr = context.GetFunctionOrSymbolAddress().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();
|
|
SetLoadedModule(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());
|
|
|
|
auto load_module_fn = [this, &module_list,
|
|
&log](const DYLDRendezvous::SOEntry &so_entry) {
|
|
ModuleSP module_sp = LoadModuleAtAddress(
|
|
so_entry.file_spec, so_entry.link_addr, so_entry.base_addr, true);
|
|
if (module_sp.get()) {
|
|
LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}",
|
|
so_entry.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__, so_entry.file_spec.GetPath().c_str(),
|
|
so_entry.base_addr);
|
|
}
|
|
};
|
|
if (m_process->GetTarget().GetParallelModuleLoad()) {
|
|
llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool());
|
|
for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I)
|
|
task_group.async(load_module_fn, *I);
|
|
task_group.wait();
|
|
} else {
|
|
for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) {
|
|
load_module_fn(*I);
|
|
}
|
|
}
|
|
|
|
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);
|
|
std::optional<addr_t> link_map_addr_opt = GetLoadedModuleLinkAddr(module_sp);
|
|
if (!link_map_addr_opt.has_value()) {
|
|
LLDB_LOGF(
|
|
log, "GetThreadLocalData error: module(%s) not found in loaded modules",
|
|
module_sp->GetObjectName().AsCString());
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
addr_t link_map = link_map_addr_opt.value();
|
|
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->GetAddress().GetModule();
|
|
if (!module_sp)
|
|
return false;
|
|
|
|
return module_sp->GetFileSpec().GetPath() == "[vdso]";
|
|
}
|
|
|
|
bool DynamicLoaderPOSIXDYLD::IsCoreFile() const {
|
|
return !m_process->IsLiveDebugSession();
|
|
}
|
|
|
|
// For our ELF/POSIX builds save off the fs_base/gs_base regions
|
|
static void AddThreadLocalMemoryRegions(Process &process, ThreadSP &thread_sp,
|
|
std::vector<MemoryRegionInfo> &ranges) {
|
|
lldb::RegisterContextSP reg_ctx = thread_sp->GetRegisterContext();
|
|
if (!reg_ctx)
|
|
return;
|
|
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
|
|
lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_TP);
|
|
if (!reg_info)
|
|
return;
|
|
|
|
lldb_private::RegisterValue thread_local_register_value;
|
|
bool success = reg_ctx->ReadRegister(reg_info, thread_local_register_value);
|
|
if (!success)
|
|
return;
|
|
|
|
const uint64_t fail_value = UINT64_MAX;
|
|
bool readSuccess = false;
|
|
const lldb::addr_t reg_value_addr =
|
|
thread_local_register_value.GetAsUInt64(fail_value, &readSuccess);
|
|
if (!readSuccess || reg_value_addr == fail_value)
|
|
return;
|
|
|
|
MemoryRegionInfo thread_local_region;
|
|
Status err = process.GetMemoryRegionInfo(reg_value_addr, thread_local_region);
|
|
if (err.Fail())
|
|
return;
|
|
|
|
ranges.push_back(thread_local_region);
|
|
}
|
|
|
|
// Save off the link map for core files.
|
|
static void AddLinkMapSections(Process &process,
|
|
std::vector<MemoryRegionInfo> &ranges) {
|
|
ModuleList &module_list = process.GetTarget().GetImages();
|
|
Target *target = &process.GetTarget();
|
|
for (size_t idx = 0; idx < module_list.GetSize(); idx++) {
|
|
ModuleSP module_sp = module_list.GetModuleAtIndex(idx);
|
|
if (!module_sp)
|
|
continue;
|
|
|
|
ObjectFile *obj = module_sp->GetObjectFile();
|
|
if (!obj)
|
|
continue;
|
|
Address addr = obj->GetImageInfoAddress(target);
|
|
addr_t load_addr = addr.GetLoadAddress(target);
|
|
if (load_addr == LLDB_INVALID_ADDRESS)
|
|
continue;
|
|
|
|
MemoryRegionInfo link_map_section;
|
|
Status err = process.GetMemoryRegionInfo(load_addr, link_map_section);
|
|
if (err.Fail())
|
|
continue;
|
|
|
|
ranges.push_back(link_map_section);
|
|
}
|
|
}
|
|
|
|
void DynamicLoaderPOSIXDYLD::CalculateDynamicSaveCoreRanges(
|
|
lldb_private::Process &process,
|
|
std::vector<lldb_private::MemoryRegionInfo> &ranges,
|
|
llvm::function_ref<bool(const lldb_private::Thread &)>
|
|
save_thread_predicate) {
|
|
ThreadList &thread_list = process.GetThreadList();
|
|
for (size_t idx = 0; idx < thread_list.GetSize(); idx++) {
|
|
ThreadSP thread_sp = thread_list.GetThreadAtIndex(idx);
|
|
if (!thread_sp)
|
|
continue;
|
|
|
|
if (!save_thread_predicate(*thread_sp))
|
|
continue;
|
|
|
|
AddThreadLocalMemoryRegions(process, thread_sp, ranges);
|
|
}
|
|
|
|
AddLinkMapSections(process, ranges);
|
|
}
|