diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index fe44e3a307ccaa56671b2dbfe50b25afc4f4f935..a96bbaea26e8cdabad6aba221584916ee4c7d9cf 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -96,6 +96,10 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const log.err("sys_include_dir may not be empty", .{}); return error.ParseError; } + if (self.crt_dir == null) { + log.err("crt_dir may not be empty", .{}); + return error.ParseError; + } const os_tag = target.os.tag; @@ -103,12 +107,6 @@ pub fn parse(allocator: Allocator, io: Io, libc_file: []const u8, target: *const log.err("cc_dir may not be empty for {s}", .{@tagName(os_tag)}); return error.ParseError; } - - if (self.crt_dir == null and !target.os.tag.isDarwin()) { - log.err("crt_dir may not be empty for {s}", .{@tagName(os_tag)}); - return error.ParseError; - } - if (self.msvc_lib_dir == null and os_tag == .windows and (target.abi == .msvc or target.abi == .itanium)) { log.err("msvc_lib_dir may not be empty for {s}-{s}", .{ @tagName(os_tag), @@ -153,7 +151,6 @@ pub fn render(self: LibCInstallation, out: *std.Io.Writer) !void { \\ \\# The directory that contains `crt1.o` or `crt2.o`. \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. - \\# Not needed when targeting macOS. \\crt_dir={s} \\ \\# The directory that contains `vcruntime.lib`. @@ -193,12 +190,9 @@ pub fn findNative(gpa: Allocator, io: Io, args: FindNativeOptions) FindError!Lib return error.DarwinSdkNotFound; defer gpa.free(sdk); - self.include_dir = try fs.path.join(gpa, &.{ - sdk, "usr/include", - }); - self.sys_include_dir = try fs.path.join(gpa, &.{ - sdk, "usr/include", - }); + self.include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" }); + self.sys_include_dir = try fs.path.join(gpa, &.{ sdk, "usr/include" }); + self.crt_dir = try fs.path.join(gpa, &.{ sdk, "usr/lib" }); return self; } else if (is_windows) { const sdk = std.zig.WindowsSdk.find(gpa, io, args.target.cpu.arch, args.environ_map) catch |err| switch (err) { diff --git a/src/Compilation.zig b/src/Compilation.zig index a2e962fde90f0b6873335c12a2e566f8b9980bdb..8e2f545811a1a77237ca13560b0553cd48541b4f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2429,11 +2429,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, // If linking against host libc installation, instead queue up jobs // for loading those files in the linker. if (comp.config.link_libc and is_exe_or_dyn_lib) { - // If the "is darwin" check is moved below the libc_installation check below, - // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths(). - if (target.isDarwinLibC()) { - // TODO delete logic from MachO flush() and queue up tasks here instead. - } else if (comp.libc_installation) |lci| { + if (comp.libc_installation) |lci| { const basenames = LibCInstallation.CrtBasenames.get(.{ .target = target, .link_libc = comp.config.link_libc, @@ -2456,6 +2452,12 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, } // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`. comp.oneshot_prelink_tasks.appendAssumeCapacity(.load_host_libc); + } else if (target.isDarwinLibC()) { + try comp.oneshot_prelink_tasks.append(gpa, .{ .load_tbd = .{ + .root_dir = comp.dirs.zig_lib, + .sub_path = "libc" ++ fs.path.sep_str ++ "darwin" ++ fs.path.sep_str ++ "libSystem.tbd", + } }); + // TODO delete logic from MachO flush() and queue up tasks here instead. } else if (target.isMuslLibC()) { if (!std.zig.target.canBuildLibC(target)) return diag.fail(.cross_libc_unavailable); diff --git a/src/link.zig b/src/link.zig index c6d077c7772a51836a9d331f0120299745f8b542..247ca5d4b9250e2dd94408d34d2e12786483376a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1236,15 +1236,15 @@ pub const File = struct { } pub fn loadInput(base: *File, input: Input) anyerror!void { - if (base.tag == .lld) return; - assert(!base.post_prelink); - + if (base.tag != .lld) { + assert(!base.post_prelink); + } switch (base.tag) { - inline .coff, .elf, .elf2, .macho2, .wasm, .spirv => |tag| { + inline else => |tag| { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); }, - else => {}, + .c, .spork8, .plan9, .lld => {}, } } @@ -1450,6 +1450,7 @@ pub const PrelinkTask = union(enum) { /// Tells the linker to load a shared library, possibly one that is a /// GNU ld script. load_dso: Path, + load_tbd: Path, }; pub const ZcuTask = union(enum) { /// Sent once per update, as the very first `ZcuTask` in the update. Indicates that all per-file @@ -1504,6 +1505,7 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { error.AlreadyReported => return, // error reported via diags else => |e| switch (input) { .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {t}", .{e}), + .tbd => |tbd| diags.addParseError(tbd.path, "failed to parse tbd: {t}", .{e}), .object => |obj| diags.addParseError(obj.path, "failed to parse object: {t}", .{e}), .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {t}", .{e}), .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {t}", .{e}), @@ -1525,34 +1527,72 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { assert(mem.startsWith(u8, flag, "-l")); const lib_name = flag["-l".len..]; switch (comp.config.link_mode) { - .dynamic => { - const dso_path = Path.initCwd( - std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ - crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(), - }) catch return diags.setAllocFailure(), - ); - base.openLoadDso(dso_path, .{ + .dynamic => loaded: { + if (target.os.tag.isDarwin()) { + // Prefer .tbd over .dylib. + const tbd_path: Path = .initCwd(std.fmt.allocPrint( + comp.arena, + "{s}" ++ sep ++ "{s}{s}.tbd", + .{ crt_dir, target.libPrefix(), lib_name }, + ) catch return diags.setAllocFailure()); + if (tbd_path.root_dir.handle.openFile(io, tbd_path.sub_path, .{})) |file| { + errdefer file.close(io); + base.loadInput(.{ .tbd = .{ + .path = tbd_path, + .file = file, + .needed = false, + .weak = false, + .reexport = false, + } }) catch |err| switch (err) { + error.AlreadyReported => return, + error.Canceled => io.recancel(), + else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}), + }; + break :loaded; + } else |err| switch (err) { + error.FileNotFound => {}, + error.Canceled => io.recancel(), + else => |e| diags.addParseError(tbd_path, "failed to parse tbd: {t}", .{e}), + } + } + + // Try dynamic library. + const dso_path: Path = .initCwd(std.fmt.allocPrint( + comp.arena, + "{s}" ++ sep ++ "{s}{s}{s}", + .{ crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix() }, + ) catch return diags.setAllocFailure()); + if (base.openLoadDso(dso_path, .{ .preferred_mode = .dynamic, .search_strategy = .paths_first, - }) catch |err| switch (err) { - error.FileNotFound => { - // Also try static. - const archive_path = Path.initCwd( - std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ - crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), - }) catch return diags.setAllocFailure(), - ); - base.openLoadArchiveQuery(archive_path, .{ - .preferred_mode = .dynamic, - .search_strategy = .paths_first, - }) catch |archive_err| switch (archive_err) { - error.AlreadyReported => return, // error reported via diags - else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }), - }; - }, - error.AlreadyReported => return, // error reported via diags + })) { + break :loaded; + } else |err| switch (err) { + error.FileNotFound => {}, + error.AlreadyReported => return, + error.Canceled => io.recancel(), else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), - }; + } + + // Also try static. + const archive_path = Path.initCwd( + std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ + crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), + }) catch return diags.setAllocFailure(), + ); + if (base.openLoadArchiveQuery(archive_path, .{ + .preferred_mode = .dynamic, + .search_strategy = .paths_first, + })) { + break :loaded; + } else |archive_err| switch (archive_err) { + error.FileNotFound => {}, + error.AlreadyReported => return, + error.Canceled => io.recancel(), + else => |e| diags.addParseError(archive_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }), + } + + diags.addError("failed to find library '{s}'", .{lib_name}); }, .static => { const path = Path.initCwd( @@ -1647,6 +1687,28 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void { else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), }; }, + .load_tbd => |path| { + const prog_node = comp.link_prog_node.start("Parse Shared Library Stub", 0); + defer prog_node.end(); + if (path.root_dir.handle.openFile(io, path.sub_path, .{})) |file| { + errdefer file.close(io); + base.loadInput(.{ .tbd = .{ + .path = path, + .file = file, + .needed = false, + .weak = false, + .reexport = false, + } }) catch |err| switch (err) { + error.AlreadyReported => return, + error.Canceled => io.recancel(), + else => |e| diags.addParseError(path, "failed to parse tbd: {t}", .{e}), + }; + } else |err| switch (err) { + error.FileNotFound => {}, + error.Canceled => io.recancel(), + else => |e| diags.addParseError(path, "failed to parse tbd: {t}", .{e}), + } + }, } } pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void { @@ -1873,6 +1935,8 @@ pub const Input = union(enum) { /// May not be a GNU ld script. Those are resolved when converting from /// `UnresolvedInput` to `Input` values. dso: Dso, + /// Only possible when targeting Darwin. + tbd: Tbd, pub const Object = struct { path: Path, @@ -1897,24 +1961,32 @@ pub const Input = union(enum) { pub const FallbackSoname = enum { basename, full_path }; }; + pub const Tbd = struct { + path: Path, + file: Io.File, + needed: bool, + weak: bool, + reexport: bool, + }; + pub fn path(input: Input) Path { return switch (input) { .object, .archive => |obj| obj.path, - inline .res, .dso => |x| x.path, + inline .res, .dso, .tbd => |x| x.path, }; } pub fn pathAndFile(input: Input) struct { Path, Io.File } { return switch (input) { .object, .archive => |obj| .{ obj.path, obj.file }, - inline .res, .dso => |x| .{ x.path, x.file }, + inline .res, .dso, .tbd => |x| .{ x.path, x.file }, }; } pub fn taskName(input: Input) []const u8 { return switch (input) { .object, .archive => |obj| obj.path.basename(), - inline .res, .dso => |x| x.path.basename(), + inline .res, .dso, .tbd => |x| x.path.basename(), }; } }; @@ -1947,6 +2019,15 @@ pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void { man.hash.add(dso.reexport); man.hash.add(dso.fallback_soname); }, + .tbd => |tbd| { + _ = try man.addInputPath(tbd.path, .{ + .handle = .{ .file = tbd.file }, + .request_handle = true, + }); + man.hash.add(tbd.needed); + man.hash.add(tbd.weak); + man.hash.add(tbd.reexport); + }, } } } @@ -2236,16 +2317,14 @@ fn resolveLibInput( else => |e| fatal("searching for tbd library {qf}: {t}", .{ test_path, e }), }; errdefer file.close(io); - return finishResolveLibInput( - io, - resolved_inputs, - archive_dedup, - test_path, - file, - link_mode, - name_query.query, - .basename, - ); + resolved_inputs.appendAssumeCapacity(.{ .tbd = .{ + .path = test_path, + .file = file, + .needed = name_query.query.needed, + .weak = name_query.query.weak, + .reexport = name_query.query.reexport, + } }); + return .ok; } { @@ -2700,7 +2779,7 @@ pub fn anyObjectInputs(inputs: []const Input) bool { pub fn countObjectInputs(inputs: []const Input) usize { var count: usize = 0; for (inputs) |input| switch (input) { - .dso => continue, + .dso, .tbd => continue, .res, .object, .archive => count += 1, }; return count; @@ -2710,7 +2789,7 @@ pub fn countObjectInputs(inputs: []const Input) usize { pub fn firstObjectInput(inputs: []const Input) ?Input.Object { for (inputs) |input| switch (input) { .object, .archive => |obj| return obj, - .res, .dso => continue, + .res, .dso, .tbd => continue, }; return null; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index dbdea4966de0b5ba91b7e2b026a7b80c419d5854..754c3ad9a830078489427c9794455642527b7f20 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -3912,6 +3912,7 @@ pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void { coff.loadDll(dso.path, &fr) catch |err| return coff.failLoadInput(err, &fr, dso.path); }, + .tbd => unreachable, } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3cbcc53361033b74e8ea1c69189f6909d3be7ad6..5a3aa2648a58c61e59be964f0058525efa650fd9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -722,6 +722,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { const argv = &self.dump_argv_list; switch (input) { .res => unreachable, + .tbd => unreachable, .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)), .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)), } @@ -729,6 +730,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void { switch (input) { .res => unreachable, + .tbd => unreachable, .object => |obj| try parseObject(self, obj), .archive => |obj| if (self.base.isStaticLib()) { // Ignore static library inputs when generating a static library. diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index e47107140d51ffbbe200b64c1fda1e3f62bde6cb..a8f75ea6d122bdf3491914fd07b4aff94c6b6bcd 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -7148,7 +7148,6 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void }, }; }, - .res => unreachable, .dso => |dso| { try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1); var fr = dso.file.reader(io, &buf); @@ -7165,6 +7164,8 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void }, }; }, + .res => unreachable, + .tbd => unreachable, } } fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { diff --git a/src/link/Lld.zig b/src/link/Lld.zig index c56557dfb02336a36ab88ec32170b705a64d5e53..64a9aeeebf2e94889cd258ba0edc877a4518c1f4 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -309,7 +309,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) link.Error!void { try object_files.ensureUnusedCapacity(arena, comp.link_inputs.len); for (comp.link_inputs) |input| switch (input) { - .dso, .archive => {}, // static archives should not contain shared libraries or other static archives + .dso, .tbd, .archive => {}, // static archives should not contain shared libraries or other static archives .res, .object => { const path = try input.path().toStringZ(arena); object_files.appendAssumeCapacity(path); @@ -561,6 +561,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void { argv.appendAssumeCapacity(try obj.path.toString(arena)); } }, + .tbd => unreachable, }; for (comp.c_objects.items) |c_object| { @@ -1102,6 +1103,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { for (base.comp.link_inputs) |link_input| switch (link_input) { .res => unreachable, // Windows-only + .tbd => unreachable, // Darwin-only .dso => continue, .object, .archive => |obj| { if (obj.must_link and !whole_archive) { @@ -1163,6 +1165,7 @@ fn elfLink(lld: *Lld, arena: Allocator) !void { for (base.comp.link_inputs) |link_input| switch (link_input) { .res => unreachable, // Windows-only + .tbd => unreachable, // Darwin-only .object, .archive => continue, .dso => |dso| { const lib_as_needed = !dso.needed; @@ -1596,6 +1599,7 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void { .dso => |dso| { try argv.append(try dso.path.toString(arena)); }, + .tbd => unreachable, .res => unreachable, }; if (whole_archive) { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index dbabc88a512ff37af5fd0caa9ece4562b8e06378..db7d1733859235990e5cc60fcd840b5fa8cd30dc 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -4,6 +4,8 @@ pub const Relocation = @import("MachO/Relocation.zig"); base: link.File, +all_inputs: std.ArrayList(link.Input), + rpath_list: []const []const u8, /// Debug symbols bundle (or dSym). @@ -190,6 +192,7 @@ pub fn createEmpty( .file = null, .build_id = options.build_id, }, + .all_inputs = .empty, .rpath_list = options.rpath_list, .pagezero_size = options.pagezero_size, .headerpad_size = options.headerpad_size, @@ -271,6 +274,8 @@ pub fn deinit(self: *MachO) void { const gpa = comp.gpa; const io = comp.io; + self.all_inputs.deinit(gpa); + if (self.d_sym) |*d_sym| { d_sym.deinit(); } @@ -334,6 +339,11 @@ pub fn deinit(self: *MachO) void { self.thunks.deinit(gpa); } +pub fn loadInput(self: *MachO, input: link.Input) link.Error!void { + const gpa = self.base.comp.gpa; + try self.all_inputs.append(gpa, input); +} + pub fn flush( self: *MachO, arena: Allocator, @@ -343,6 +353,8 @@ pub fn flush( const tracy = trace(@src()); defer tracy.end(); + _ = arena; + const comp = self.base.comp; const gpa = comp.gpa; const io = comp.io; @@ -351,148 +363,47 @@ pub fn flush( const sub_prog_node = prog_node.start("MachO Flush", 0); defer sub_prog_node.end(); - const zcu_obj_path: ?Path = p: { - const zcu = comp.zcu orelse break :p null; - const llvm_object = zcu.llvm_object orelse break :p null; - break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); - }; - // --verbose-link if (comp.verbose_link) try self.dumpArgv(comp); if (self.getZigObject()) |zo| try zo.flush(self, tid); - if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, zcu_obj_path); - if (self.base.isObject()) return relocatable.flushObject(self, comp, zcu_obj_path); + if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp); + if (self.base.isObject()) return relocatable.flushObject(self, comp); - var positionals = std.array_list.Managed(link.Input).init(gpa); - defer positionals.deinit(); - - try positionals.ensureUnusedCapacity(comp.link_inputs.len); - - for (comp.link_inputs) |link_input| switch (link_input) { - .dso => continue, // handled below - .object, .archive => positionals.appendAssumeCapacity(link_input), - .res => unreachable, - }; - - // This is a set of object files emitted by clang in a single `build-exe` invocation. - // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up - // in this set. - try positionals.ensureUnusedCapacity(comp.c_objects.items.len); - for (comp.c_objects.items) |c_object| { - positionals.appendAssumeCapacity(try link.openObjectInput(io, diags, c_object.status.success.object_path)); + for (self.all_inputs.items) |input| { + self.classifyInputFile(input) catch |err| + diags.addParseError(input.path(), "failed to read input file: {t}", .{err}); } - if (zcu_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); - - if (comp.config.any_sanitize_thread) { - try positionals.append(try link.openObjectInput(io, diags, comp.tsan_lib.?.full_object_path)); - } - - if (comp.config.any_fuzz) { - try positionals.append(try link.openArchiveInput(io, diags, comp.fuzzer_lib.?.full_object_path, false, false)); - } - - if (comp.ubsan_rt_lib) |crt_file| { - const path = crt_file.full_object_path; - self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| - diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); - } else if (comp.ubsan_rt_obj) |crt_file| { - const path = crt_file.full_object_path; - self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err| - diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); - } - - for (positionals.items) |link_input| { - self.classifyInputFile(link_input) catch |err| - diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); - } - - var system_libs = std.array_list.Managed(SystemLib).init(gpa); - defer system_libs.deinit(); - // frameworks - try system_libs.ensureUnusedCapacity(self.frameworks.len); - for (self.frameworks) |info| { - system_libs.appendAssumeCapacity(.{ - .needed = info.needed, - .weak = info.weak, - .path = info.path, - }); - } - - // libc++ dep - if (comp.config.link_libcpp) { - try system_libs.ensureUnusedCapacity(2); - system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); - system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); - } - - const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or - (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); - - if (comp.config.link_libc and is_exe_or_dyn_lib) { - if (comp.zigc_static_lib) |zigc| { - const path = zigc.full_object_path; - self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| - diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); - } - } - - // libc/libSystem dep - self.resolveLibSystem(arena, comp, &system_libs) catch |err| switch (err) { - error.MissingLibSystem => {}, // already reported - else => |e| return diags.fail("failed to resolve libSystem: {s}", .{@errorName(e)}), - }; - - for (comp.link_inputs) |link_input| switch (link_input) { - .object, .archive => continue, - .res => unreachable, - .dso => { - self.classifyInputFile(link_input) catch |err| - diags.addParseError(link_input.path(), "failed to parse input file: {t}", .{err}); - }, - }; - - for (system_libs.items) |lib| { - switch (Compilation.classifyFileExt(lib.path.sub_path)) { + for (self.frameworks) |framework| { + switch (Compilation.classifyFileExt(framework.path.sub_path)) { .shared_library => { - const dso_input = try link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport); + const dso_input = try link.openDsoInput(io, diags, framework.path, framework.needed, framework.weak, false); self.classifyInputFile(dso_input) catch |err| - diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); + diags.addParseError(framework.path, "failed to parse input file: {s}", .{@errorName(err)}); }, .static_library => { - const archive_input = try link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden); + const archive_input = try link.openArchiveInput(io, diags, framework.path, false, false); self.classifyInputFile(archive_input) catch |err| - diags.addParseError(lib.path, "failed to parse input file: {s}", .{@errorName(err)}); + diags.addParseError(framework.path, "failed to parse input file: {s}", .{@errorName(err)}); }, else => { dso: { - const dso_input = link.openDsoInput(io, diags, lib.path, lib.needed, lib.weak, lib.reexport) catch break :dso; + const dso_input = link.openDsoInput(io, diags, framework.path, framework.needed, framework.weak, false) catch break :dso; self.classifyInputFile(dso_input) catch break :dso; continue; } ar: { - const archive_input = link.openArchiveInput(io, diags, lib.path, lib.must_link, lib.hidden) catch break :ar; + const archive_input = link.openArchiveInput(io, diags, framework.path, false, false) catch break :ar; self.classifyInputFile(archive_input) catch break :ar; continue; } - diags.addParseError(lib.path, "unknown file extension", .{}); + diags.addParseError(framework.path, "unknown file extension", .{}); }, } } - // Finally, link against compiler_rt. - if (comp.compiler_rt_lib) |crt_file| { - const path = crt_file.full_object_path; - self.classifyInputFile(try link.openArchiveInput(io, diags, path, false, false)) catch |err| - diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); - } else if (comp.compiler_rt_obj) |crt_file| { - const path = crt_file.full_object_path; - self.classifyInputFile(try link.openObjectInput(io, diags, path)) catch |err| - diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(err)}); - } - try self.parseInputFiles(); self.parseDependentDylibs() catch |err| { switch (err) { @@ -666,9 +577,8 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { for (comp.link_inputs) |link_input| switch (link_input) { .object, .archive => |obj| try argv.append(try obj.path.toString(arena)), .res => |res| try argv.append(try res.path.toString(arena)), - .dso => |dso| { - try argv.append(try dso.path.toString(arena)); - }, + .dso => |dso| try argv.append(try dso.path.toString(arena)), + .tbd => |tbd| try argv.append(try tbd.path.toString(arena)), }; for (comp.c_objects.items) |c_object| { @@ -754,7 +664,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } for (comp.link_inputs) |link_input| switch (link_input) { - .dso => continue, // handled below + .dso, .tbd => continue, // handled below .res => unreachable, // windows only .object, .archive => |obj| { if (obj.must_link) try argv.append("-force_load"); // TODO: verify this @@ -788,7 +698,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { for (comp.link_inputs) |link_input| switch (link_input) { .object, .archive => continue, // handled above .res => unreachable, // windows only - .dso => |dso| { + inline .dso, .tbd => |dso| { try argv.ensureUnusedCapacity(2); if (dso.needed) { argv.appendAssumeCapacity("-needed-l"); @@ -836,46 +746,6 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { try Compilation.dumpArgv(io, argv.items); } -/// TODO delete this, libsystem must be resolved when setting up the compilation pipeline -pub fn resolveLibSystem( - self: *MachO, - arena: Allocator, - comp: *Compilation, - out_libs: anytype, -) !void { - const io = comp.io; - const diags = &comp.link_diags; - - var test_path = std.array_list.Managed(u8).init(arena); - var checked_paths = std.array_list.Managed([]const u8).init(arena); - - success: { - if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { - .sdk => { - const dir = try fs.path.join(arena, &.{ comp.sysroot.?, "usr", "lib" }); - if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success; - }, - .vendored => { - const dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "darwin" }); - if (try accessLibPath(arena, io, &test_path, &checked_paths, dir, "System")) break :success; - }, - }; - - for (self.lib_directories) |directory| { - if (try accessLibPath(arena, io, &test_path, &checked_paths, directory.path orelse ".", "System")) break :success; - } - - diags.addMissingLibraryError(checked_paths.items, "unable to find libSystem system library", .{}); - return error.MissingLibSystem; - } - - const libsystem_path = Path.initCwd(try arena.dupe(u8, test_path.items)); - try out_libs.append(.{ - .needed = true, - .path = libsystem_path, - }); -} - pub fn classifyInputFile(self: *MachO, input: link.Input) !void { const tracy = trace(@src()); defer tracy.end(); @@ -4497,7 +4367,7 @@ const SystemLib = struct { .must_link = obj.must_link, .hidden = obj.hidden, }, - .dso => |dso| .{ + inline .dso, .tbd => |dso| .{ .path = dso.path, .needed = dso.needed, .weak = dso.weak, diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index 38420414382928237d93b16b5c8648e4c91fb6f5..1b679cc9058932f34ea8098032ef75af49295059 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -1,25 +1,25 @@ -pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { - const gpa = comp.gpa; +pub fn flushObject(macho_file: *MachO, comp: *Compilation) link.Error!void { const io = comp.io; const diags = &comp.link_diags; - // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. - var positionals = std.array_list.Managed(link.Input).init(gpa); - defer positionals.deinit(); - try positionals.ensureUnusedCapacity(comp.link_inputs.len); - positionals.appendSliceAssumeCapacity(comp.link_inputs); - - for (comp.c_objects.items) |c_object| { - try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path)); - } - - if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); - - if (macho_file.getZigObject() == null and positionals.items.len == 1) { + one_input: { + if (macho_file.getZigObject() != null) { + break :one_input; + } + var only_input: ?link.Input = null; + for (macho_file.all_inputs.items) |input| switch (input) { + .res => unreachable, + .dso, .tbd => {}, + .object, .archive => if (only_input == null) { + only_input = input; + } else { + break :one_input; + }, + }; // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all // debug info segments/sections (this is apparently by design by Apple), we copy // the *only* input file over. - const path = positionals.items[0].path(); + const path = (only_input orelse break :one_input).path(); const in_file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| return diags.fail("failed to open {f}: {s}", .{ path, @errorName(err) }); const stat = in_file.stat(io) catch |err| @@ -29,10 +29,12 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat return; } - for (positionals.items) |link_input| { - macho_file.classifyInputFile(link_input) catch |err| - diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); - } + for (macho_file.all_inputs.items) |input| switch (input) { + .res => unreachable, + .dso, .tbd => {}, + .object, .archive => macho_file.classifyInputFile(input) catch |err| + diags.addParseError(input.path(), "failed to read input file: {t}", .{err}), + }; if (diags.hasErrors()) return error.AlreadyReported; @@ -75,35 +77,16 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Pat try writeHeader(macho_file, ncmds, sizeofcmds); } -pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.Error!void { +pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void { const gpa = comp.gpa; - const io = comp.io; const diags = &macho_file.base.comp.link_diags; - var positionals = std.array_list.Managed(link.Input).init(gpa); - defer positionals.deinit(); - - try positionals.ensureUnusedCapacity(comp.link_inputs.len); - positionals.appendSliceAssumeCapacity(comp.link_inputs); - - for (comp.c_objects.items) |c_object| { - try positionals.append(try link.openObjectInput(io, diags, c_object.status.success.object_path)); - } - - if (module_obj_path) |path| try positionals.append(try link.openObjectInput(io, diags, path)); - - if (comp.compiler_rt_strat == .obj) { - try positionals.append(try link.openObjectInput(io, diags, comp.compiler_rt_obj.?.full_object_path)); - } - - if (comp.ubsan_rt_strat == .obj) { - try positionals.append(try link.openObjectInput(io, diags, comp.ubsan_rt_obj.?.full_object_path)); - } - - for (positionals.items) |link_input| { - macho_file.classifyInputFile(link_input) catch |err| - diags.addParseError(link_input.path(), "failed to read input file: {t}", .{err}); - } + for (macho_file.all_inputs.items) |input| switch (input) { + .res => unreachable, + .dso, .tbd => {}, + .object, .archive => macho_file.classifyInputFile(input) catch |err| + diags.addParseError(input.path(), "failed to read input file: {t}", .{err}), + }; if (diags.hasErrors()) return error.AlreadyReported; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 30868f14272601c048c01e495e239d9819c32a85..20b069960000d231f69ad2b80ad89a65764ab61f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -3789,6 +3789,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { switch (input) { .res => unreachable, .dso => unreachable, + .tbd => unreachable, .object, .archive => |obj| { try argv.append(gpa, try obj.path.toString(comp.arena)); }, @@ -3798,6 +3799,7 @@ pub fn loadInput(wasm: *Wasm, input: link.Input) !void { switch (input) { .res => unreachable, .dso => unreachable, + .tbd => unreachable, .object => |obj| try parseObject(wasm, obj), .archive => |obj| try parseArchive(wasm, obj), } diff --git a/src/target.zig b/src/target.zig index 5e36be5fd4f7c7ad799f619e775bcc611747003f..6234c11a146b18c45b6b36ecf12138edebb99a5c 100644 --- a/src/target.zig +++ b/src/target.zig @@ -510,7 +510,7 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 { }, // On SerenityOS libc includes libm, libpthread, libdl, and libssp. .serenity => &.{"-lc"}, - else => &.{}, + else => if (target.os.tag.isDarwin()) &.{"-lSystem"} else &.{}, }; return result; }