Skip to content

Commit 8710c5f

Browse files
committed
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`.
1 parent 53e615b commit 8710c5f

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

src/link/MachO.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2937,7 +2937,13 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
29372937

29382938
fn writeHeader(self: *MachO, ncmds: usize, sizeofcmds: usize) !void {
29392939
var header: macho.mach_header_64 = .{};
2940-
header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK;
2940+
header.flags = macho.MH_DYLDLINK;
2941+
2942+
// Only set MH_NOUNDEFS if we're not allowing undefined symbols via dynamic lookup.
2943+
// When dynamic_lookup is enabled, undefined symbols are resolved at runtime by dyld.
2944+
if (self.undefined_treatment != .dynamic_lookup) {
2945+
header.flags |= macho.MH_NOUNDEFS;
2946+
}
29412947

29422948
// TODO: if (self.options.namespace == .two_level) {
29432949
header.flags |= macho.MH_TWOLEVEL;

test/link/macho.zig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
6868
macho_step.dependOn(testTlsLargeTbss(b, .{ .target = default_target }));
6969
macho_step.dependOn(testTlsZig(b, .{ .target = default_target }));
7070
macho_step.dependOn(testUndefinedFlag(b, .{ .target = default_target }));
71+
macho_step.dependOn(testUndefinedDynamicLookup(b, .{ .target = default_target }));
7172
macho_step.dependOn(testDiscardLocalSymbols(b, .{ .target = default_target }));
7273
macho_step.dependOn(testUnresolvedError(b, .{ .target = default_target }));
7374
macho_step.dependOn(testUnresolvedError2(b, .{ .target = default_target }));
@@ -2632,6 +2633,29 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step {
26322633
return test_step;
26332634
}
26342635

2636+
fn testUndefinedDynamicLookup(b: *Build, opts: Options) *Step {
2637+
const test_step = addTestStep(b, "undefined-dynamic-lookup", opts);
2638+
2639+
// Create a dylib with an undefined external symbol reference
2640+
const dylib = addSharedLibrary(b, opts, .{ .name = "a" });
2641+
addCSourceBytes(dylib,
2642+
\\extern int undefined_symbol(void);
2643+
\\int call_undefined(void) {
2644+
\\ return undefined_symbol();
2645+
\\}
2646+
, &.{});
2647+
dylib.linker_allow_shlib_undefined = true;
2648+
2649+
// Verify the Mach-O header does NOT contain NOUNDEFS flag
2650+
const check = dylib.checkObject();
2651+
check.checkInHeaders();
2652+
check.checkExact("header");
2653+
check.checkNotPresent("NOUNDEFS");
2654+
test_step.dependOn(&check.step);
2655+
2656+
return test_step;
2657+
}
2658+
26352659
fn testUnresolvedError(b: *Build, opts: Options) *Step {
26362660
const test_step = addTestStep(b, "unresolved-error", opts);
26372661

0 commit comments

Comments
 (0)