diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -622,6 +622,10 @@ """Decorate the item to skip tests that should be skipped on any non Darwin platform.""" return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func) +def skipUnlessLinux(func): + """Decorate the item to skip tests that should be skipped on any non-Linux platform.""" + return skipUnlessPlatform(["linux"])(func) + def skipUnlessTargetAndroid(func): return unittest2.skipUnless(lldbplatformutil.target_is_android(), "requires target to be Android")(func) 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 @@ -441,6 +441,7 @@ if (module_sp.get()) { if (module_sp->GetObjectFile()->GetBaseAddress().GetLoadAddress( &m_process->GetTarget()) == m_interpreter_base && + m_interpreter_module.lock() && 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 diff --git a/lldb/test/API/functionalities/module_load_attach/Makefile b/lldb/test/API/functionalities/module_load_attach/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/module_load_attach/Makefile @@ -0,0 +1,10 @@ +C_SOURCES := main.c +LD_EXTRAS := -Wl,-rpath "-Wl,$(shell pwd)" +USE_LIBDL := 1 + +feature: + $(MAKE) -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_NAME=feature DYLIB_C_SOURCES=feature.c +all: feature + +include Makefile.rules diff --git a/lldb/test/API/functionalities/module_load_attach/TestModuleLoadAttach.py b/lldb/test/API/functionalities/module_load_attach/TestModuleLoadAttach.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/module_load_attach/TestModuleLoadAttach.py @@ -0,0 +1,49 @@ +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__) + + def build_launch_and_attach(self): + self.build() + # launch + exe = self.getBuildArtifact("a.out") + popen = self.spawnSubprocess(exe) + # attach + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + listener = lldb.SBListener("my.attach.listener") + error = lldb.SBError() + process = target.AttachToProcessWithID(listener, popen.pid, error) + self.assertTrue(error.Success() and process, PROCESS_IS_VALID) + return process + + def assertModuleIsLoaded(self, module_name): + feature_module = self.dbg.GetSelectedTarget().FindModule(lldb.SBFileSpec(module_name)) + self.assertTrue(feature_module.IsValid(), f"Module {module_name} should be loaded") + + def assertModuleIsNotLoaded(self, module_name): + feature_module = self.dbg.GetSelectedTarget().FindModule(lldb.SBFileSpec(module_name)) + self.assertFalse(feature_module.IsValid(), f"Module {module_name} should not be loaded") + + @skipIfRemote + @skipUnlessLinux + @no_debug_info_test + def test(self): + ''' + This test makes sure that after attach lldb still gets notifications + about new modules being loaded by the process + ''' + process = self.build_launch_and_attach() + thread = process.GetSelectedThread() + self.assertModuleIsNotLoaded("libfeature.so") + thread.GetSelectedFrame().EvaluateExpression("flip_to_1_to_continue = 1") + # Continue so that dlopen is called. + breakpoint = self.target().BreakpointCreateBySourceRegex( + "// break after dlopen", lldb.SBFileSpec("main.c")) + self.assertNotEqual(breakpoint.GetNumResolvedLocations(), 0) + stopped_threads = lldbutil.continue_to_breakpoint(self.process(), breakpoint) + self.assertModuleIsLoaded("libfeature.so") diff --git a/lldb/test/API/functionalities/module_load_attach/feature.c b/lldb/test/API/functionalities/module_load_attach/feature.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/module_load_attach/feature.c @@ -0,0 +1 @@ +extern void feature() {} diff --git a/lldb/test/API/functionalities/module_load_attach/main.c b/lldb/test/API/functionalities/module_load_attach/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/module_load_attach/main.c @@ -0,0 +1,15 @@ +#include +#include +#include + +volatile int flip_to_1_to_continue = 0; + +int main() { + lldb_enable_attach(); + while (! flip_to_1_to_continue) // Wait for debugger to attach + sleep(1); + // dlopen the feature + void *feature = dlopen("libfeature.so", RTLD_NOW); + assert(feature && "dlopen failed?"); + return 0; // break after dlopen +}