Commit 8d488246da

Jakub Konka <kubkon@jakubkonka.com>
2022-11-06 22:44:58
Merge pull request #13463 from ziglang/fix-13056
macho: parse weak imports in tbd descriptors
1 parent 5f74bec
Changed files (5)
src
test
link
macho
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);
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,
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);
+}
test/link/macho/bugs/13056/test.cpp
@@ -0,0 +1,10 @@
+// test.cpp
+#include <new>
+#include <cstdio>
+
+int main() {
+    int *x = new int;
+    *x = 5;
+    fprintf(stderr, "x: %d\n", *x);
+    delete x;
+}
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,
     });