https://github.com/GeorgeHuyubo updated https://github.com/llvm/llvm-project/pull/176266
>From 436fb32d40430dfbeffa1d8a6eb9f1c82f357daf Mon Sep 17 00:00:00 2001 From: George Hu <[email protected]> Date: Thu, 15 Jan 2026 15:01:33 -0800 Subject: [PATCH] [lldb] Enable locate module callback for main executable in launch mode --- lldb/include/lldb/Core/ModuleSpec.h | 15 ++- lldb/source/Core/ModuleList.cpp | 30 ++++-- lldb/source/Target/TargetList.cpp | 4 + .../Target/LocateModuleCallbackTest.cpp | 101 ++++++++++++++++++ 4 files changed, 138 insertions(+), 12 deletions(-) diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index acbc85b48f02c..1af5f144169a8 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -136,7 +136,16 @@ class ModuleSpec { /// settings, like the executable and debug info search paths, can be /// essential. The target's platform can also be used to locate or download /// the specified module. - void SetTarget(std::shared_ptr<Target> target) { m_target_wp = target; } + void SetTarget(lldb::TargetSP target) { m_target_wp = target; } + + lldb::PlatformSP GetPlatformSP() const { return m_platform_wp.lock(); } + + /// Set the platform to be used when resolving a module. + /// + /// This is useful when a Target is not yet available (e.g., during target + /// creation) but a Platform is. The platform can be used to invoke locate + /// module callbacks and other platform-specific module resolution logic. + void SetPlatform(lldb::PlatformSP platform) { m_platform_wp = platform; } void Clear() { m_file.Clear(); @@ -150,6 +159,7 @@ class ModuleSpec { m_source_mappings.Clear(false); m_object_mod_time = llvm::sys::TimePoint<>(); m_target_wp.reset(); + m_platform_wp.reset(); } explicit operator bool() const { @@ -283,6 +293,9 @@ class ModuleSpec { /// debug info search paths, can be essential. The target's platform can also /// be used to locate or download the specified module. std::weak_ptr<Target> m_target_wp; + /// The platform used when resolving a module. This is useful when a Target + /// is not yet available (e.g., during target creation) but a Platform is. + std::weak_ptr<Platform> m_platform_wp; uint64_t m_object_offset = 0; uint64_t m_object_size = 0; llvm::sys::TimePoint<> m_object_mod_time; diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index be6ff723e0ffa..e6d8192ea4a55 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -1103,18 +1103,26 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, if (module_sp) return error; - // Try target's platform locate module callback before second attempt. + // Try platform's locate module callback before second attempt. + // The platform can come from either the Target (if available) or directly + // from the ModuleSpec (useful when Target is not yet created, e.g., during + // target creation for launch mode). if (invoke_locate_callback) { - TargetSP target_sp = module_spec.GetTargetSP(); - if (target_sp && target_sp->IsValid()) { - if (PlatformSP platform_sp = target_sp->GetPlatform()) { - FileSpec symbol_file_spec; - platform_sp->CallLocateModuleCallbackIfSet( - module_spec, module_sp, symbol_file_spec, did_create_ptr); - if (module_sp) { - // The callback found a module. - return error; - } + PlatformSP platform_sp; + if (TargetSP target_sp = module_spec.GetTargetSP()) { + if (target_sp->IsValid()) + platform_sp = target_sp->GetPlatform(); + } + if (!platform_sp) + platform_sp = module_spec.GetPlatformSP(); + + if (platform_sp) { + FileSpec symbol_file_spec; + platform_sp->CallLocateModuleCallbackIfSet( + module_spec, module_sp, symbol_file_spec, did_create_ptr); + if (module_sp) { + // The callback found a module. + return error; } } } diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp index ce04e9c1209b8..623f8c4cf8324 100644 --- a/lldb/source/Target/TargetList.cpp +++ b/lldb/source/Target/TargetList.cpp @@ -306,6 +306,10 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, if (platform_sp) { ModuleSpec module_spec(file, arch); module_spec.SetTarget(target_sp); + // Set the platform so that GetSharedModule can use it for the locate + // module callback, even when Target is not yet available (during target + // creation for launch mode). + module_spec.SetPlatform(platform_sp); error = platform_sp->ResolveExecutable(module_spec, exe_module_sp); } diff --git a/lldb/unittests/Target/LocateModuleCallbackTest.cpp b/lldb/unittests/Target/LocateModuleCallbackTest.cpp index d727cea9f6eae..f5792e6ce4890 100644 --- a/lldb/unittests/Target/LocateModuleCallbackTest.cpp +++ b/lldb/unittests/Target/LocateModuleCallbackTest.cpp @@ -896,3 +896,104 @@ TEST_F(LocateModuleCallbackTest, CheckUnstrippedSymbol(m_module_sp); ModuleList::RemoveSharedModule(m_module_sp); } + +TEST_F(LocateModuleCallbackTest, + GetSharedModuleWithPlatformFallbackWhenTargetNotAvailable) { + // Test that when a Target is not available but Platform is set on ModuleSpec, + // the locate module callback is still invoked via the Platform fallback. + // This is the key functionality added for launch mode support where the + // Target is not yet created but we need to resolve the main executable. + BuildEmptyCacheDir(m_test_dir); + + int callback_call_count = 0; + m_platform_sp->SetLocateModuleCallback( + [this, &callback_call_count](const ModuleSpec &module_spec, + FileSpec &module_file_spec, + FileSpec &symbol_file_spec) { + CheckCallbackArgsWithUUID(module_spec, module_file_spec, + symbol_file_spec, ++callback_call_count); + module_file_spec.SetPath(GetInputFilePath(k_module_file)); + return Status(); + }); + + // Create a ModuleSpec without Target but with Platform set + ModuleSpec module_spec_with_platform = m_module_spec; + module_spec_with_platform.SetPlatform(m_platform_sp); + + // Call GetSharedModule directly (simulating the path taken during + // target creation in launch mode) + bool always_create = true; + Status error = ModuleList::GetSharedModule( + module_spec_with_platform, m_module_sp, nullptr, nullptr, always_create); + + ASSERT_TRUE(error.Success()); + ASSERT_EQ(callback_call_count, 1); + CheckModule(m_module_sp); + ASSERT_EQ(m_module_sp->GetFileSpec(), + FileSpec(GetInputFilePath(k_module_file))); + ModuleList::RemoveSharedModule(m_module_sp); +} + +TEST_F(LocateModuleCallbackTest, + GetSharedModulePreferTargetPlatformOverModuleSpecPlatform) { + // Test that when both Target and Platform are available on ModuleSpec, + // the Target's platform is preferred. This ensures backward compatibility + // and that the existing behavior is maintained for cases where Target + // is available. + FileSpec uuid_view = BuildCacheDir(m_test_dir); + + int callback_call_count = 0; + m_platform_sp->SetLocateModuleCallback( + [this, &callback_call_count](const ModuleSpec &module_spec, + FileSpec &module_file_spec, + FileSpec &symbol_file_spec) { + CheckCallbackArgsWithUUID(module_spec, module_file_spec, + symbol_file_spec, ++callback_call_count); + return Status(); + }); + + // Set both Target and Platform on ModuleSpec + ModuleSpec module_spec_with_both = m_module_spec; + module_spec_with_both.SetTarget(m_target_sp); + module_spec_with_both.SetPlatform(m_platform_sp); + + m_module_sp = + m_target_sp->GetOrCreateModule(module_spec_with_both, /*notify=*/false); + ASSERT_EQ(callback_call_count, 3); + CheckModule(m_module_sp); + ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); + ModuleList::RemoveSharedModule(m_module_sp); +} + +TEST_F(LocateModuleCallbackTest, + GetSharedModuleWithPlatformFallbackNoCallback) { + // Test that when Platform is set on ModuleSpec but no callback is + // registered, GetSharedModule still works and finds the module through + // normal resolution (using the file path in ModuleSpec). + // This verifies the no-callback case is handled gracefully and + // doesn't break normal module resolution. + BuildEmptyCacheDir(m_test_dir); + + CheckNoCallback(); + + // Create a ModuleSpec with an actual file path that exists, and Platform set + ModuleSpec module_spec_with_platform( + FileSpec(GetInputFilePath(k_module_file)), ArchSpec(k_arch)); + module_spec_with_platform.GetUUID().SetFromStringRef(k_module_uuid); + module_spec_with_platform.SetObjectSize(k_module_size); + module_spec_with_platform.SetPlatform(m_platform_sp); + + Status error = ModuleList::GetSharedModule( + module_spec_with_platform, m_module_sp, nullptr, nullptr, false); + + // Without a callback, the module should still be found via the file path + // in the ModuleSpec. The platform fallback code path is exercised (checking + // for callback) but since there's none, it falls through to normal + // resolution. + ASSERT_TRUE(error.Success()); + ASSERT_TRUE(m_module_sp); + ASSERT_EQ(m_module_sp->GetUUID().GetAsString(), k_module_uuid); + ASSERT_EQ(m_module_sp->GetFileSpec(), + FileSpec(GetInputFilePath(k_module_file))); + ModuleList::RemoveSharedModule(m_module_sp); +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
