diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -442,14 +442,18 @@ if (module_sp->GetObjectFile()->GetBaseAddress().GetLoadAddress( &m_process->GetTarget()) == m_interpreter_base && module_sp != m_interpreter_module.lock()) { - // If this is a duplicate instance of ld.so, unload it. We may end up - // with it if we load it via a different path than before (symlink - // vs real path). - // TODO: remove this once we either fix library matching or avoid - // loading the interpreter when setting the rendezvous breakpoint. - UnloadSections(module_sp); - loaded_modules.Remove(module_sp); - continue; + if (m_interpreter_module.lock() == nullptr) { + m_interpreter_module = module_sp; + } else { + // If this is a duplicate instance of ld.so, unload it. We may end + // up with it if we load it via a different path than before + // (symlink vs real path). + // TODO: remove this once we either fix library matching or avoid + // loading the interpreter when setting the rendezvous breakpoint. + UnloadSections(module_sp); + loaded_modules.Remove(module_sp); + continue; + } } loaded_modules.AppendIfNeeded(module_sp); @@ -620,6 +624,7 @@ } m_process->GetTarget().ModulesDidLoad(module_list); + m_initial_modules_added = true; } addr_t DynamicLoaderPOSIXDYLD::ComputeLoadOffset() { diff --git a/lldb/test/API/functionalities/dlopen/Makefile b/lldb/test/API/functionalities/dlopen/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dlopen/Makefile @@ -0,0 +1,9 @@ +CXX_SOURCES := main.cpp +USE_LIBDL := 1 + +lib_b: + $(MAKE) -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=lib_b +all: lib_b + +include Makefile.rules diff --git a/lldb/test/API/functionalities/dlopen/TestDlopen.py b/lldb/test/API/functionalities/dlopen/TestDlopen.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dlopen/TestDlopen.py @@ -0,0 +1,60 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + @skipIfWindows + def test_dlopen_after_attach(self): + self.build() + exe = self.getBuildArtifact("a.out") + so = self.getBuildArtifact("liblib_b.so") + + # Spawn a new process. + # use realpath to workaround llvm.org/pr48376 + # Pass path to solib for dlopen to properly locate the library. + popen = self.spawnSubprocess(os.path.realpath(exe), args = [os.path.realpath(so)]) + pid = popen.pid + + # Attach to the spawned process. + self.runCmd("process attach -p " + str(pid)) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + # Continue until first breakpoint. + breakpoint1 = self.target().BreakpointCreateBySourceRegex( + "// break here", lldb.SBFileSpec("main.cpp")) + self.assertEqual(breakpoint1.GetNumResolvedLocations(), 1) + stopped_threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint1) + self.assertEqual(len(stopped_threads), 1) + + # Check that image list does not contain lib_b.so before dlopen. + self.match( + "image list", + patterns = ["liblib_b.so"], + matching = False, + msg = "liblib_b.so should not have been in image list") + + # Change a variable to escape the loop + self.runCmd("expression main_thread_continue = 1") + + # Continue so that dlopen is called. + breakpoint2 = self.target().BreakpointCreateBySourceRegex( + "// break after dlopen", lldb.SBFileSpec("main.cpp")) + self.assertEqual(breakpoint2.GetNumResolvedLocations(), 1) + process.Continue() + self.assertEqual(len(stopped_threads), 1) + + # Check that image list contains lib_b.so after dlopen. + self.match( + "image list", + patterns = ["liblib_b.so"], + matching = True, + msg = "Missing liblib_b.so in image list") + diff --git a/lldb/test/API/functionalities/dlopen/b.cpp b/lldb/test/API/functionalities/dlopen/b.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dlopen/b.cpp @@ -0,0 +1,4 @@ + + +int LLDB_DYLIB_EXPORT b_function() { return 500; } + diff --git a/lldb/test/API/functionalities/dlopen/main.cpp b/lldb/test/API/functionalities/dlopen/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/dlopen/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + const char* solib = "./liblib_b.so"; + if (argc == 2) { + solib = argv[1]; + } + printf("Using solib at: %s\n", solib); + + int main_thread_continue = 0; + // Wait until debugger is attached. + int i = 0; + int timeout = 10; + for (i = 0; i < timeout; i++) { + usleep(1000*1000); // break here + if (main_thread_continue) { + break; + } + } + assert(i != timeout && "timed out waiting for debugger"); + + // dlopen the 'liblib_b.so' shared library. + void* h = dlopen(solib, RTLD_LAZY); + assert(h && "dlopen failed?"); + return i; // break after dlopen +}