https://github.com/GeorgeHuyubo updated https://github.com/llvm/llvm-project/pull/176266
>From eee5126ce82961af950b4888c7e923ed2e4d0463 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, 139 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/Core/ModuleSpec.h b/lldb/include/lldb/Core/ModuleSpec.h index acbc85b48f02c..1ce857c9765cb 100644 --- a/lldb/include/lldb/Core/ModuleSpec.h +++ b/lldb/include/lldb/Core/ModuleSpec.h @@ -138,6 +138,17 @@ class ModuleSpec { /// the specified module. void SetTarget(std::shared_ptr<Target> 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(std::shared_ptr<Platform> platform) { + m_platform_wp = platform; + } + void Clear() { m_file.Clear(); m_platform_file.Clear(); @@ -150,6 +161,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 +295,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
