diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 0f16eada61..1c1842aaed 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -22,7 +22,14 @@ weak: bool = false, /// Parsed symbol table represented as hash map of symbols' /// names. We can and should defer creating *Symbols until /// a symbol is referenced by an object file. -symbols: std.StringArrayHashMapUnmanaged(void) = .{}, +/// +/// The value for each parsed symbol represents whether the +/// symbol is defined as a weak symbol or strong. +/// TODO when the referenced symbol is weak, ld64 marks it as +/// N_REF_TO_WEAK but need to investigate if there's more to it +/// such as weak binding entry or simply weak. For now, we generate +/// standard bind or lazy bind. +symbols: std.StringArrayHashMapUnmanaged(bool) = .{}, pub const Id = struct { name: []const u8, @@ -168,7 +175,7 @@ pub fn parseFromBinary( if (!add_to_symtab) continue; const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); - try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {}); + try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), false); } }, .ID_DYLIB => { @@ -202,25 +209,30 @@ fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) for (expanded) |sym| { if (self.symbols.contains(sym)) continue; - try self.symbols.putNoClobber(allocator, sym, {}); + try self.symbols.putNoClobber(allocator, sym, false); } } fn addObjCIVarSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { const expanded = try std.fmt.allocPrint(allocator, "_OBJC_IVAR_$_{s}", .{sym_name}); if (self.symbols.contains(expanded)) return; - try self.symbols.putNoClobber(allocator, expanded, {}); + try self.symbols.putNoClobber(allocator, expanded, false); } fn addObjCEhTypeSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { const expanded = try std.fmt.allocPrint(allocator, "_OBJC_EHTYPE_$_{s}", .{sym_name}); if (self.symbols.contains(expanded)) return; - try self.symbols.putNoClobber(allocator, expanded, {}); + try self.symbols.putNoClobber(allocator, expanded, false); } fn addSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { if (self.symbols.contains(sym_name)) return; - try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {}); + try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), false); +} + +fn addWeakSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { + if (self.symbols.contains(sym_name)) return; + try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), true); } const TargetMatcher = struct { @@ -359,6 +371,12 @@ pub fn parseFromStub( } } + if (exp.weak_symbols) |symbols| { + for (symbols) |sym_name| { + try self.addWeakSymbol(allocator, sym_name); + } + } + if (exp.objc_classes) |objc_classes| { for (objc_classes) |class_name| { try self.addObjCClassSymbol(allocator, class_name); @@ -402,6 +420,12 @@ pub fn parseFromStub( } } + if (exp.weak_symbols) |symbols| { + for (symbols) |sym_name| { + try self.addWeakSymbol(allocator, sym_name); + } + } + if (exp.objc_classes) |classes| { for (classes) |sym_name| { try self.addObjCClassSymbol(allocator, sym_name); @@ -432,6 +456,12 @@ pub fn parseFromStub( } } + if (reexp.weak_symbols) |symbols| { + for (symbols) |sym_name| { + try self.addWeakSymbol(allocator, sym_name); + } + } + if (reexp.objc_classes) |classes| { for (classes) |sym_name| { try self.addObjCClassSymbol(allocator, sym_name); diff --git a/src/link/tapi.zig b/src/link/tapi.zig index e31ca92ed9..20a3a2493e 100644 --- a/src/link/tapi.zig +++ b/src/link/tapi.zig @@ -26,6 +26,7 @@ pub const TbdV3 = struct { allowable_clients: ?[]const []const u8, re_exports: ?[]const []const u8, symbols: ?[]const []const u8, + weak_symbols: ?[]const []const u8, objc_classes: ?[]const []const u8, objc_ivars: ?[]const []const u8, objc_eh_types: ?[]const []const u8, @@ -53,6 +54,7 @@ pub const TbdV4 = struct { exports: ?[]const struct { targets: []const []const u8, symbols: ?[]const []const u8, + weak_symbols: ?[]const []const u8, objc_classes: ?[]const []const u8, objc_ivars: ?[]const []const u8, objc_eh_types: ?[]const []const u8, @@ -60,6 +62,7 @@ pub const TbdV4 = struct { reexports: ?[]const struct { targets: []const []const u8, symbols: ?[]const []const u8, + weak_symbols: ?[]const []const u8, objc_classes: ?[]const []const u8, objc_ivars: ?[]const []const u8, objc_eh_types: ?[]const []const u8, diff --git a/test/link.zig b/test/link.zig index 9ad695ba26..47e51859bc 100644 --- a/test/link.zig +++ b/test/link.zig @@ -79,6 +79,11 @@ fn addWasmCases(cases: *tests.StandaloneContext) void { } fn addMachOCases(cases: *tests.StandaloneContext) void { + cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{ .build_modes = true, }); diff --git a/test/link/macho/bugs/13056/build.zig b/test/link/macho/bugs/13056/build.zig new file mode 100644 index 0000000000..751a7c4db6 --- /dev/null +++ b/test/link/macho/bugs/13056/build.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; + const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse + @panic("macOS SDK is required to run the test"); + + const test_step = b.step("test", "Test the program"); + + const exe = b.addExecutable("test", null); + b.default_step.dependOn(&exe.step); + exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); + exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); + exe.addCSourceFile("test.cpp", &.{ + "-nostdlib++", + "-nostdinc++", + }); + exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable); + exe.setBuildMode(mode); + + const run_cmd = exe.run(); + run_cmd.expectStdErrEqual("x: 5\n"); + + test_step.dependOn(&run_cmd.step); +} diff --git a/test/link/macho/bugs/13056/test.cpp b/test/link/macho/bugs/13056/test.cpp new file mode 100644 index 0000000000..d042cb0a2a --- /dev/null +++ b/test/link/macho/bugs/13056/test.cpp @@ -0,0 +1,10 @@ +// test.cpp +#include +#include + +int main() { + int *x = new int; + *x = 5; + fprintf(stderr, "x: %d\n", *x); + delete x; +}