zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 2207c62bb5f6d61fc9f430e849d0aed13cf63d35 (tree)
parent 7cbe05cbd4d89fe88f17e4c82888823864c6cc8d
Author: Mathias Lafeldt <mathias.lafeldt@gmail.com>
Date:   Tue, 25 Nov 2025 10:22:35 +0100

MachO: fix dynamic lookup of undefined symbols at runtime

Ensures `MH_NOUNDEFS` is not set when dynamic lookup is enabled for
undefined symbols via `linker_allow_shlib_undefined`.

Diffstat:
Msrc/link/MachO.zig | 8+++++++-
Mtest/link/macho.zig | 24++++++++++++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/src/link/MachO.zig b/src/link/MachO.zig @@ -2959,7 +2959,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 @@ -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 })); @@ -2634,6 +2635,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);