diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 4dd23bb1e4dbec..2979bf69bf762a 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5140,12 +5140,20 @@ uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) { case LC_LOADFVMLIB: case LC_LOAD_UPWARD_DYLIB: { uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + // For LC_LOAD_DYLIB there is an alternate encoding + // which adds a uint32_t `flags` field for `DYLD_USE_*` + // flags. This can be detected by a timestamp field with + // the `DYLIB_USE_MARKER` constant value. bool is_delayed_init = false; uint32_t use_command_marker = m_data.GetU32(&offset); if (use_command_marker == 0x1a741800 /* DYLIB_USE_MARKER */) { offset += 4; /* uint32_t current_version */ offset += 4; /* uint32_t compat_version */ uint32_t flags = m_data.GetU32(&offset); + // If this LC_LOAD_DYLIB is marked delay-init, + // don't report it as a dependent library -- it + // may be loaded in the process at some point, + // but will most likely not be load at launch. if (flags & 0x08 /* DYLIB_USE_DELAYED_INIT */) is_delayed_init = true; } diff --git a/lldb/test/API/macosx/delay-init-dependency/Makefile b/lldb/test/API/macosx/delay-init-dependency/Makefile new file mode 100644 index 00000000000000..246ea0f34e1a1c --- /dev/null +++ b/lldb/test/API/macosx/delay-init-dependency/Makefile @@ -0,0 +1,11 @@ +C_SOURCES := main.c +LD_EXTRAS := -L. -Wl,-delay_library,libfoo.dylib + +.PHONY: build-libfoo +all: build-libfoo a.out + +include Makefile.rules + +build-libfoo: foo.c + $(MAKE) -f $(MAKEFILE_RULES) \ + DYLIB_C_SOURCES=foo.c DYLIB_NAME=foo DYLIB_ONLY=YES diff --git a/lldb/test/API/macosx/delay-init-dependency/TestDelayInitDependency.py b/lldb/test/API/macosx/delay-init-dependency/TestDelayInitDependency.py new file mode 100644 index 00000000000000..44ed2b1d21f186 --- /dev/null +++ b/lldb/test/API/macosx/delay-init-dependency/TestDelayInitDependency.py @@ -0,0 +1,62 @@ +"""Test binaries with delay-init dependencies.""" + +import subprocess +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestDelayInitDependencies(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessDarwin + def test_delay_init_dependency(self): + TestBase.setUp(self) + out = subprocess.run( + ["xcrun", "ld", "-delay_library"], + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + if "delay_library missing" not in out.stderr: + self.skipTest( + "Skipped because the linker doesn't know about -delay_library" + ) + self.build() + main_source = "main.c" + exe = self.getBuildArtifact("a.out") + lib = self.getBuildArtifact("libfoo.dylib") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # libfoo.dylib should not be in the target pre-execution + for m in target.modules: + self.assertNotEqual(m.GetFileSpec().GetFilename(), "libfoo.dylib") + + # This run without arguments will not load libfoo.dylib + li = lldb.SBLaunchInfo([]) + li.SetWorkingDirectory(self.getBuildDir()) + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.c"), li + ) + for m in target.modules: + self.assertNotEqual(m.GetFileSpec().GetFilename(), "libfoo.dylib") + + process.Kill() + self.dbg.DeleteTarget(target) + + # This run with one argument will load libfoo.dylib + li = lldb.SBLaunchInfo([]) + li.SetWorkingDirectory(self.getBuildDir()) + li.SetArguments(["one-argument"], True) + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( + self, "// break here", lldb.SBFileSpec("main.c"), li + ) + + found_libfoo = False + for m in target.modules: + if m.GetFileSpec().GetFilename() == "libfoo.dylib": + found_libfoo = True + self.assertTrue(found_libfoo) diff --git a/lldb/test/API/macosx/delay-init-dependency/foo.c b/lldb/test/API/macosx/delay-init-dependency/foo.c new file mode 100644 index 00000000000000..de1cbc4c4648a1 --- /dev/null +++ b/lldb/test/API/macosx/delay-init-dependency/foo.c @@ -0,0 +1 @@ +int foo() { return 5; } diff --git a/lldb/test/API/macosx/delay-init-dependency/main.c b/lldb/test/API/macosx/delay-init-dependency/main.c new file mode 100644 index 00000000000000..57d251e6b2abe4 --- /dev/null +++ b/lldb/test/API/macosx/delay-init-dependency/main.c @@ -0,0 +1,9 @@ +int foo(); +int main(int argc, char **argv) { + int retval = 0; + // Only call foo() if one argument is passed + if (argc == 2) + retval = foo(); + + return retval; // break here +}