Commit b94787afd7

Jakub Konka <kubkon@jakubkonka.com>
2021-06-26 10:11:37
zld: link against system libSystem.tbd
when native OS _and_ `libSystem.tbd` can be found. Otherwise, fallback to linking against Zig-provided `lib/libc/darwin/libSystem.B.tbd`.
1 parent 1534cd2
Changed files (2)
src
link
src/link/MachO/Zld.zig
@@ -32,7 +32,6 @@ out_path: ?[]const u8 = null,
 
 // TODO these args will become obselete once Zld is coalesced with incremental
 // linker.
-syslibroot: ?[]const u8 = null,
 stack_size: u64 = 0,
 
 objects: std.ArrayListUnmanaged(*Object) = .{},
@@ -197,6 +196,7 @@ pub fn closeFiles(self: Zld) void {
 }
 
 const LinkArgs = struct {
+    syslibroot: ?[]const u8,
     libs: []const []const u8,
     rpaths: []const []const u8,
     libc_stub_path: []const u8,
@@ -238,9 +238,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
     });
 
     try self.populateMetadata();
-    try self.parseInputFiles(files);
-    try self.parseLibs(args.libs);
-    try self.parseLibSystem(args.libc_stub_path);
+    try self.parseInputFiles(files, args.syslibroot);
+    try self.parseLibs(args.libs, args.syslibroot);
+    try self.parseLibSystem(args.libc_stub_path, args.syslibroot);
     try self.resolveSymbols();
     try self.resolveStubsAndGotEntries();
     try self.updateMetadata();
@@ -258,7 +258,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
     try self.flush();
 }
 
-fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
+fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
     for (files) |file_name| {
         const full_path = full_path: {
             var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
@@ -280,7 +280,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
             self.allocator,
             self.arch.?,
             full_path,
-            self.syslibroot,
+            syslibroot,
         )) |dylibs| {
             defer self.allocator.free(dylibs);
             try self.dylibs.appendSlice(self.allocator, dylibs);
@@ -291,13 +291,13 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
     }
 }
 
-fn parseLibs(self: *Zld, libs: []const []const u8) !void {
+fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !void {
     for (libs) |lib| {
         if (try Dylib.createAndParseFromPath(
             self.allocator,
             self.arch.?,
             lib,
-            self.syslibroot,
+            syslibroot,
         )) |dylibs| {
             defer self.allocator.free(dylibs);
             try self.dylibs.appendSlice(self.allocator, dylibs);
@@ -313,12 +313,12 @@ fn parseLibs(self: *Zld, libs: []const []const u8) !void {
     }
 }
 
-fn parseLibSystem(self: *Zld, libc_stub_path: []const u8) !void {
+fn parseLibSystem(self: *Zld, libc_stub_path: []const u8, syslibroot: ?[]const u8) !void {
     const dylibs = (try Dylib.createAndParseFromPath(
         self.allocator,
         self.arch.?,
         libc_stub_path,
-        self.syslibroot,
+        syslibroot,
     )) orelse return error.FailedToParseLibSystem;
     defer self.allocator.free(dylibs);
 
src/link/MachO.zig
@@ -514,15 +514,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
     }
 }
 
-fn resolvePaths(
+const LibKind = enum {
+    lib,
+    framework,
+};
+
+fn resolveDirs(
     arena: *Allocator,
-    resolved_paths: *std.ArrayList([]const u8),
+    resolved_dirs: *std.ArrayList([]const u8),
     syslibroot: ?[]const u8,
     search_dirs: []const []const u8,
-    lib_names: []const []const u8,
-    kind: enum { lib, framework },
+    lib_kind: LibKind,
 ) !void {
-    var resolved_dirs = std.ArrayList([]const u8).init(arena);
     for (search_dirs) |dir| {
         if (fs.path.isAbsolute(dir)) {
             var candidates = std.ArrayList([]const u8).init(arena);
@@ -547,7 +550,7 @@ fn resolvePaths(
             }
 
             if (!found) {
-                switch (kind) {
+                switch (lib_kind) {
                     .lib => log.warn("directory not found for '-L{s}'", .{dir}),
                     .framework => log.warn("directory not found for '-F{s}'", .{dir}),
                 }
@@ -556,7 +559,7 @@ fn resolvePaths(
             // Verify that search path actually exists
             var tmp = fs.cwd().openDir(dir, .{}) catch |err| switch (err) {
                 error.FileNotFound => {
-                    switch (kind) {
+                    switch (lib_kind) {
                         .lib => log.warn("directory not found for '-L{s}'", .{dir}),
                         .framework => log.warn("directory not found for '-F{s}'", .{dir}),
                     }
@@ -569,48 +572,63 @@ fn resolvePaths(
             try resolved_dirs.append(dir);
         }
     }
+}
 
+fn resolveLib(
+    arena: *Allocator,
+    lib_dirs: []const []const u8,
+    lib_name: []const u8,
+    lib_kind: LibKind,
+) !?[]const u8 {
     // Assume ld64 default: -search_paths_first
     // Look in each directory for a dylib (next, tbd), and then for archive
     // TODO implement alternative: -search_dylibs_first
-    const exts = switch (kind) {
+    const exts = switch (lib_kind) {
         .lib => &[_][]const u8{ "dylib", "tbd", "a" },
         .framework => &[_][]const u8{ "dylib", "tbd" },
     };
 
-    for (lib_names) |lib_name| {
-        var found = false;
-
-        ext: for (exts) |ext| {
-            const lib_name_ext = blk: {
-                switch (kind) {
-                    .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }),
-                    .framework => {
-                        const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name});
-                        const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext });
-                        break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn });
-                    },
-                }
-            };
+    for (exts) |ext| {
+        const lib_name_ext = blk: {
+            switch (lib_kind) {
+                .lib => break :blk try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ lib_name, ext }),
+                .framework => {
+                    const prefix = try std.fmt.allocPrint(arena, "{s}.framework", .{lib_name});
+                    const nn = try std.fmt.allocPrint(arena, "{s}.{s}", .{ lib_name, ext });
+                    break :blk try fs.path.join(arena, &[_][]const u8{ prefix, nn });
+                },
+            }
+        };
 
-            for (resolved_dirs.items) |dir| {
-                const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
+        for (lib_dirs) |dir| {
+            const full_path = try fs.path.join(arena, &[_][]const u8{ dir, lib_name_ext });
 
-                // Check if the lib file exists.
-                const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
-                    error.FileNotFound => continue,
-                    else => |e| return e,
-                };
-                defer tmp.close();
+            // Check if the lib file exists.
+            const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
+                error.FileNotFound => continue,
+                else => |e| return e,
+            };
+            defer tmp.close();
 
-                try resolved_paths.append(full_path);
-                found = true;
-                break :ext;
-            }
+            return full_path;
         }
+    }
+
+    return null;
+}
 
-        if (!found) {
-            switch (kind) {
+fn resolveLibs(
+    arena: *Allocator,
+    resolved_libs: *std.ArrayList([]const u8),
+    lib_dirs: []const []const u8,
+    lib_names: []const []const u8,
+    lib_kind: LibKind,
+) !void {
+    for (lib_names) |lib_name| {
+        if (try resolveLib(arena, lib_dirs, lib_name, lib_kind)) |full_path| {
+            try resolved_libs.append(full_path);
+        } else {
+            switch (lib_kind) {
                 .lib => {
                     log.warn("library not found for '-l{s}'", .{lib_name});
                     log.warn("Library search paths:", .{});
@@ -620,7 +638,7 @@ fn resolvePaths(
                     log.warn("Framework search paths:", .{});
                 },
             }
-            for (resolved_dirs.items) |dir| {
+            for (lib_dirs) |dir| {
                 log.warn("  {s}", .{dir});
             }
         }
@@ -789,7 +807,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
                 zld.deinit();
             }
             zld.arch = target.cpu.arch;
-            zld.syslibroot = self.base.options.sysroot;
             zld.stack_size = stack_size;
 
             // Positional arguments to the linker such as object files and static archives.
@@ -829,16 +846,56 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
                 try search_lib_names.append(link_lib);
             }
 
-            var libs = std.ArrayList([]const u8).init(arena);
-            try resolvePaths(
+            var lib_dirs = std.ArrayList([]const u8).init(arena);
+            try resolveDirs(
                 arena,
-                &libs,
+                &lib_dirs,
                 self.base.options.sysroot,
                 self.base.options.lib_dirs,
+                .lib,
+            );
+
+            var libs = std.ArrayList([]const u8).init(arena);
+            try resolveLibs(
+                arena,
+                &libs,
+                lib_dirs.items,
                 search_lib_names.items,
                 .lib,
             );
 
+            // If we're compiling native and we can find libSystem.B.{dylib, tbd},
+            // we link against that instead of embedded libSystem.B.tbd file.
+            const libc_stub_path = blk: {
+                if (self.base.options.is_native_os) {
+                    if (try resolveLib(arena, lib_dirs.items, "System", .lib)) |full_path| {
+                        break :blk full_path;
+                    }
+                }
+
+                break :blk try comp.zig_lib_directory.join(arena, &[_][]const u8{
+                    "libc", "darwin", "libSystem.B.tbd",
+                });
+            };
+
+            // frameworks
+            var framework_dirs = std.ArrayList([]const u8).init(arena);
+            try resolveDirs(
+                arena,
+                &framework_dirs,
+                self.base.options.sysroot,
+                self.base.options.framework_dirs,
+                .framework,
+            );
+
+            try resolveLibs(
+                arena,
+                &libs,
+                framework_dirs.items,
+                self.base.options.frameworks,
+                .framework,
+            );
+
             // rpaths
             var rpath_table = std.StringArrayHashMap(void).init(arena);
             for (self.base.options.rpath_list) |rpath| {
@@ -852,16 +909,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
                 rpaths.appendAssumeCapacity(key.*);
             }
 
-            // frameworks
-            try resolvePaths(
-                arena,
-                &libs,
-                self.base.options.sysroot,
-                self.base.options.framework_dirs,
-                self.base.options.frameworks,
-                .framework,
-            );
-
             if (self.base.options.verbose_link) {
                 var argv = std.ArrayList([]const u8).init(arena);
 
@@ -895,11 +942,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
             }
 
             try zld.link(positionals.items, full_out_path, .{
+                .syslibroot = self.base.options.sysroot,
                 .libs = libs.items,
                 .rpaths = rpaths.items,
-                .libc_stub_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
-                    "libc", "darwin", "libSystem.B.tbd",
-                }),
+                .libc_stub_path = libc_stub_path,
             });
 
             break :outer;