diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 95389c2d95..db0c6de63f 100644 --- a/src/link/MachO.zig +++ b/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 (resolved_dirs.items) |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(); - - try resolved_paths.append(full_path); - found = true; - break :ext; + 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 }); + }, } - } + }; - if (!found) { - switch (kind) { + 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(); + + return full_path; + } + } + + return null; +} + +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; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 6cabdfa73e..56462c1140 100644 --- a/src/link/MachO/Zld.zig +++ b/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);