diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 035bd601558a..218497824577 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2937,7 +2937,13 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void { var header: macho.mach_header_64 = .{}; - header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK; + header.flags = macho.MH_DYLDLINK; + + // Only set MH_NOUNDEFS if we're not allowing undefined symbols via dynamic lookup. + // When dynamic_lookup is enabled, undefined symbols are resolved at runtime by dyld. + if (self.undefined_treatment != .dynamic_lookup) { + header.flags |= macho.MH_NOUNDEFS; + } // TODO: if (self.options.namespace == .two_level) { header.flags |= macho.MH_TWOLEVEL; diff --git a/test/link/macho.zig b/test/link/macho.zig index 4fc0cad0eedb..3f1f28adb430 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -68,6 +68,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target })); macho_step.dependOn(testTlsZig(b, .{ .target = default_target })); macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target })); + macho_step.dependOn(testUndefinedDynamicLookup(b, .{ .target = default_target })); macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target })); macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target })); macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target })); @@ -2632,6 +2633,29 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step { return test_step; } +fn testUndefinedDynamicLookup(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "undefined-dynamic-lookup", opts); + + // Create a dylib with an undefined external symbol reference + const dylib = addSharedLibrary(b, opts, .{ .name = "a" }); + addCSourceBytes(dylib, + \\extern int undefined_symbol(void); + \\int call_undefined(void) { + \\ return undefined_symbol(); + \\} + , &.{}); + dylib.linker_allow_shlib_undefined = true; + + // Verify the Mach-O header does NOT contain NOUNDEFS flag + const check = dylib.checkObject(); + check.checkInHeaders(); + check.checkExact("header"); + check.checkNotPresent("NOUNDEFS"); + test_step.dependOn(&check.step); + + return test_step; +} + fn testUnresolvedError(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "unresolved-error", opts);