From 14c8e270bb47c4e10ee3392661d4c62ab5c2f89d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Oct 2024 00:41:58 -0700 Subject: [PATCH] link: fix false positive crtbegin/crtend detection Embrace the Path abstraction, doing more operations based on directory handles rather than absolute file paths. Most of the diff noise here comes from this one. Fix sorting of crtbegin/crtend atoms. Previously it would look at all path components for those strings. Make the C runtime path detection partially a pure function, and move some logic to glibc.zig where it belongs. --- lib/std/Build/Cache.zig | 7 + lib/std/Build/Cache/Path.zig | 22 +- lib/std/zig/LibCInstallation.zig | 328 +++++++++++++++++++++++ src/Compilation.zig | 155 ++++++----- src/glibc.zig | 7 +- src/link.zig | 27 +- src/link/Coff/lld.zig | 47 ++-- src/link/Elf.zig | 443 ++++++++----------------------- src/link/Elf/Archive.zig | 20 +- src/link/Elf/LdScript.zig | 26 +- src/link/Elf/Object.zig | 27 +- src/link/Elf/SharedObject.zig | 15 +- src/link/Elf/ZigObject.zig | 8 +- src/link/Elf/file.zig | 38 +-- src/link/Elf/relocatable.zig | 26 +- src/link/MachO.zig | 104 ++++---- src/link/MachO/Archive.zig | 16 +- src/link/MachO/Dylib.zig | 11 +- src/link/MachO/Object.zig | 63 +++-- src/link/MachO/ZigObject.zig | 4 +- src/link/MachO/file.zig | 11 +- src/link/MachO/load_commands.zig | 3 +- src/link/MachO/relocatable.zig | 40 +-- src/link/StringTable.zig | 2 +- src/link/Wasm.zig | 59 ++-- src/main.zig | 47 ++-- src/mingw.zig | 42 +-- src/musl.zig | 4 +- src/wasi_libc.zig | 14 +- 29 files changed, 937 insertions(+), 679 deletions(-) diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 93908807eb..53f1dcff29 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -398,12 +398,19 @@ pub const Manifest = struct { return gop.index; } + /// Deprecated, use `addOptionalFilePath`. pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { self.hash.add(optional_file_path != null); const file_path = optional_file_path orelse return; _ = try self.addFile(file_path, null); } + pub fn addOptionalFilePath(self: *Manifest, optional_file_path: ?Path) !void { + self.hash.add(optional_file_path != null); + const file_path = optional_file_path orelse return; + _ = try self.addFilePath(file_path, null); + } + pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void { self.hash.add(list_of_files.len); for (list_of_files) |file_path| { diff --git a/lib/std/Build/Cache/Path.zig b/lib/std/Build/Cache/Path.zig index ee0666b70a..edd306d06d 100644 --- a/lib/std/Build/Cache/Path.zig +++ b/lib/std/Build/Cache/Path.zig @@ -11,7 +11,11 @@ pub fn clone(p: Path, arena: Allocator) Allocator.Error!Path { } pub fn cwd() Path { - return .{ .root_dir = Cache.Directory.cwd() }; + return initCwd(""); +} + +pub fn initCwd(sub_path: []const u8) Path { + return .{ .root_dir = Cache.Directory.cwd(), .sub_path = sub_path }; } pub fn join(p: Path, arena: Allocator, sub_path: []const u8) Allocator.Error!Path { @@ -126,6 +130,14 @@ pub fn makePath(p: Path, sub_path: []const u8) !void { return p.root_dir.handle.makePath(joined_path); } +pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 { + return std.fmt.allocPrint(allocator, "{}", .{p}); +} + +pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 { + return std.fmt.allocPrintZ(allocator, "{}", .{p}); +} + pub fn format( self: Path, comptime fmt_string: []const u8, @@ -182,6 +194,14 @@ pub fn subPathOrDot(self: Path) []const u8 { return if (self.sub_path.len == 0) "." else self.sub_path; } +pub fn stem(p: Path) []const u8 { + return fs.path.stem(p.sub_path); +} + +pub fn basename(p: Path) []const u8 { + return fs.path.basename(p.sub_path); +} + /// Useful to make `Path` a key in `std.ArrayHashMap`. pub const TableAdapter = struct { pub const Hash = std.hash.Wyhash; diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 44cd029f60..3d163532d1 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -690,12 +690,340 @@ fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void { } } +/// These are basenames. This data is produced with a pure function. See also +/// `CsuPaths`. +pub const CrtBasenames = struct { + crt0: ?[]const u8 = null, + crti: ?[]const u8 = null, + crtbegin: ?[]const u8 = null, + crtend: ?[]const u8 = null, + crtn: ?[]const u8 = null, + + pub const GetArgs = struct { + target: std.Target, + link_libc: bool, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + pie: bool, + }; + + /// Determine file system path names of C runtime startup objects for supported + /// link modes. + pub fn get(args: GetArgs) CrtBasenames { + // crt objects are only required for libc. + if (!args.link_libc) return .{}; + + // Flatten crt cases. + const mode: enum { + dynamic_lib, + dynamic_exe, + dynamic_pie, + static_exe, + static_pie, + } = switch (args.output_mode) { + .Obj => return .{}, + .Lib => switch (args.link_mode) { + .dynamic => .dynamic_lib, + .static => return .{}, + }, + .Exe => switch (args.link_mode) { + .dynamic => if (args.pie) .dynamic_pie else .dynamic_exe, + .static => if (args.pie) .static_pie else .static_exe, + }, + }; + + const target = args.target; + + if (target.isAndroid()) return switch (mode) { + .dynamic_lib => .{ + .crtbegin = "crtbegin_so.o", + .crtend = "crtend_so.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crtbegin = "crtbegin_dynamic.o", + .crtend = "crtend_android.o", + }, + .static_exe, .static_pie => .{ + .crtbegin = "crtbegin_static.o", + .crtend = "crtend_android.o", + }, + }; + + return switch (target.os.tag) { + .linux => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "rcrt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + }, + .dragonfly => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .freebsd => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "Scrt1.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .netbsd => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "crt0.o", + .crti = "crti.o", + .crtbegin = "crtbeginT.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .openbsd => switch (mode) { + .dynamic_lib => .{ + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crt0 = "crt0.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + }, + .static_exe, .static_pie => .{ + .crt0 = "rcrt0.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + }, + }, + .haiku => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .dynamic_exe => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .dynamic_pie => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + .static_exe => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbegin.o", + .crtend = "crtend.o", + .crtn = "crtn.o", + }, + .static_pie => .{ + .crt0 = "start_dyn.o", + .crti = "crti.o", + .crtbegin = "crtbeginS.o", + .crtend = "crtendS.o", + .crtn = "crtn.o", + }, + }, + .solaris, .illumos => switch (mode) { + .dynamic_lib => .{ + .crti = "crti.o", + .crtn = "crtn.o", + }, + .dynamic_exe, .dynamic_pie => .{ + .crt0 = "crt1.o", + .crti = "crti.o", + .crtn = "crtn.o", + }, + .static_exe, .static_pie => .{}, + }, + else => .{}, + }; + } +}; + +pub const CrtPaths = struct { + crt0: ?Path = null, + crti: ?Path = null, + crtbegin: ?Path = null, + crtend: ?Path = null, + crtn: ?Path = null, +}; + +pub fn resolveCrtPaths( + lci: LibCInstallation, + arena: Allocator, + crt_basenames: CrtBasenames, + target: std.Target, +) error{ OutOfMemory, LibCInstallationMissingCrtDir }!CrtPaths { + const crt_dir_path: Path = .{ + .root_dir = std.Build.Cache.Directory.cwd(), + .sub_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir, + }; + switch (target.os.tag) { + .dragonfly => { + const gccv: []const u8 = if (target.os.version_range.semver.isAtLeast(.{ + .major = 5, + .minor = 4, + .patch = 0, + }) orelse true) "gcc80" else "gcc54"; + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| .{ + .root_dir = crt_dir_path.root_dir, + .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), + } else null, + .crtend = if (crt_basenames.crtend) |basename| .{ + .root_dir = crt_dir_path.root_dir, + .sub_path = try fs.path.join(arena, &.{ crt_dir_path.sub_path, gccv, basename }), + } else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + .haiku => { + const gcc_dir_path: Path = .{ + .root_dir = std.Build.Cache.Directory.cwd(), + .sub_path = lci.gcc_dir orelse return error.LibCInstallationMissingCrtDir, + }; + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| try gcc_dir_path.join(arena, basename) else null, + .crtend = if (crt_basenames.crtend) |basename| try gcc_dir_path.join(arena, basename) else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + else => { + return .{ + .crt0 = if (crt_basenames.crt0) |basename| try crt_dir_path.join(arena, basename) else null, + .crti = if (crt_basenames.crti) |basename| try crt_dir_path.join(arena, basename) else null, + .crtbegin = if (crt_basenames.crtbegin) |basename| try crt_dir_path.join(arena, basename) else null, + .crtend = if (crt_basenames.crtend) |basename| try crt_dir_path.join(arena, basename) else null, + .crtn = if (crt_basenames.crtn) |basename| try crt_dir_path.join(arena, basename) else null, + }; + }, + } +} + const LibCInstallation = @This(); const std = @import("std"); const builtin = @import("builtin"); const Target = std.Target; const fs = std.fs; const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; const is_darwin = builtin.target.isDarwin(); const is_windows = builtin.target.os.tag == .windows; diff --git a/src/Compilation.zig b/src/Compilation.zig index 589c69fa97..6ba09d5c6d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -217,37 +217,37 @@ thread_pool: *ThreadPool, /// Populated when we build the libc++ static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxx_static_lib: ?CRTFile = null, +libcxx_static_lib: ?CrtFile = null, /// Populated when we build the libc++abi static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxxabi_static_lib: ?CRTFile = null, +libcxxabi_static_lib: ?CrtFile = null, /// Populated when we build the libunwind static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libunwind_static_lib: ?CRTFile = null, +libunwind_static_lib: ?CrtFile = null, /// Populated when we build the TSAN library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -tsan_lib: ?CRTFile = null, +tsan_lib: ?CrtFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libc_static_lib: ?CRTFile = null, +libc_static_lib: ?CrtFile = null, /// Populated when we build the libcompiler_rt static library. A Job to build this is indicated /// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush(). -compiler_rt_lib: ?CRTFile = null, +compiler_rt_lib: ?CrtFile = null, /// Populated when we build the compiler_rt_obj object. A Job to build this is indicated /// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush(). -compiler_rt_obj: ?CRTFile = null, +compiler_rt_obj: ?CrtFile = null, /// Populated when we build the libfuzzer static library. A Job to build this /// is indicated by setting `job_queued_fuzzer_lib` and resolved before /// calling linker.flush(). -fuzzer_lib: ?CRTFile = null, +fuzzer_lib: ?CrtFile = null, glibc_so_files: ?glibc.BuiltSharedObjects = null, -wasi_emulated_libs: []const wasi_libc.CRTFile, +wasi_emulated_libs: []const wasi_libc.CrtFile, /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. /// The key is the basename, and the value is the absolute path to the completed build artifact. -crt_files: std.StringHashMapUnmanaged(CRTFile) = .empty, +crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty, /// How many lines of reference trace should be included per compile error. /// Null means only show snippet on first error. @@ -276,20 +276,20 @@ digest: ?[Cache.bin_digest_len]u8 = null, pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Zcu.SemaError; -pub const CRTFile = struct { +pub const CrtFile = struct { lock: Cache.Lock, - full_object_path: []const u8, + full_object_path: Path, - pub fn isObject(cf: CRTFile) bool { - return switch (classifyFileExt(cf.full_object_path)) { + pub fn isObject(cf: CrtFile) bool { + return switch (classifyFileExt(cf.full_object_path.sub_path)) { .object => true, else => false, }; } - pub fn deinit(self: *CRTFile, gpa: Allocator) void { + pub fn deinit(self: *CrtFile, gpa: Allocator) void { self.lock.release(); - gpa.free(self.full_object_path); + gpa.free(self.full_object_path.sub_path); self.* = undefined; } }; @@ -369,13 +369,13 @@ const Job = union(enum) { resolve_type_fully: InternPool.Index, /// one of the glibc static objects - glibc_crt_file: glibc.CRTFile, + glibc_crt_file: glibc.CrtFile, /// all of the glibc shared objects glibc_shared_objects, /// one of the musl static objects - musl_crt_file: musl.CRTFile, + musl_crt_file: musl.CrtFile, /// one of the mingw-w64 static objects - mingw_crt_file: mingw.CRTFile, + mingw_crt_file: mingw.CrtFile, /// libunwind.a, usually needed when linking libc libunwind: void, libcxx: void, @@ -385,7 +385,7 @@ const Job = union(enum) { /// calls to, for example, memcpy and memset. zig_libc: void, /// one of WASI libc static objects - wasi_libc_crt_file: wasi_libc.CRTFile, + wasi_libc_crt_file: wasi_libc.CrtFile, /// The value is the index into `system_libs`. windows_import_lib: usize, @@ -422,8 +422,8 @@ pub const CObject = struct { status: union(enum) { new, success: struct { - /// The outputted result. Owned by gpa. - object_path: []u8, + /// The outputted result. `sub_path` owned by gpa. + object_path: Path, /// This is a file system lock on the cache hash manifest representing this /// object. It prevents other invocations of the Zig compiler from interfering /// with this object until released. @@ -719,7 +719,7 @@ pub const CObject = struct { return true; }, .success => |*success| { - gpa.free(success.object_path); + gpa.free(success.object_path.sub_path); success.lock.release(); self.status = .new; return false; @@ -1018,7 +1018,7 @@ const CacheUse = union(CacheMode) { }; pub const LinkObject = struct { - path: []const u8, + path: Path, must_link: bool = false, // When the library is passed via a positional argument, it will be // added as a full path. If it's `-l`, then just the basename. @@ -1027,7 +1027,7 @@ pub const LinkObject = struct { loption: bool = false, pub fn isObject(lo: LinkObject) bool { - return switch (classifyFileExt(lo.path)) { + return switch (classifyFileExt(lo.path.sub_path)) { .object => true, else => false, }; @@ -1095,7 +1095,7 @@ pub const CreateOptions = struct { /// * getpid /// * mman /// * signal - wasi_emulated_libs: []const wasi_libc.CRTFile = &.{}, + wasi_emulated_libs: []const wasi_libc.CrtFile = &.{}, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. @@ -2578,7 +2578,7 @@ fn addNonIncrementalStuffToCacheManifest( } for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } @@ -2703,9 +2703,8 @@ fn emitOthers(comp: *Compilation) void { return; } const obj_path = comp.c_object_table.keys()[0].status.success.object_path; - const cwd = std.fs.cwd(); - const ext = std.fs.path.extension(obj_path); - const basename = obj_path[0 .. obj_path.len - ext.len]; + const ext = std.fs.path.extension(obj_path.sub_path); + const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len]; // This obj path always ends with the object file extension, but if we change the // extension to .ll, .bc, or .s, then it will be the path to those things. const outs = [_]struct { @@ -2720,13 +2719,13 @@ fn emitOthers(comp: *Compilation) void { if (out.emit) |loc| { if (loc.directory) |directory| { const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ - basename, out.ext, + dirname, out.ext, }) catch |err| { - log.err("unable to copy {s}{s}: {s}", .{ basename, out.ext, @errorName(err) }); + log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) }); continue; }; defer comp.gpa.free(src_path); - cwd.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { + obj_path.root_dir.handle.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| { log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) }); }; } @@ -3774,7 +3773,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("glibc_crt_file"); defer named_frame.end(); - glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| { + glibc.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ @errorName(err), @@ -3798,7 +3797,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("musl_crt_file"); defer named_frame.end(); - musl.buildCRTFile(comp, crt_file, prog_node) catch |err| { + musl.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .musl_crt_file, @@ -3811,7 +3810,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("mingw_crt_file"); defer named_frame.end(); - mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| { + mingw.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .mingw_crt_file, @@ -3894,7 +3893,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre const named_frame = tracy.namedFrame("wasi_libc_crt_file"); defer named_frame.end(); - wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| { + wasi_libc.buildCrtFile(comp, crt_file, prog_node) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( .wasi_libc_crt_file, @@ -4602,7 +4601,7 @@ fn buildRt( root_source_name: []const u8, misc_task: MiscTask, output_mode: std.builtin.OutputMode, - out: *?CRTFile, + out: *?CrtFile, prog_node: std.Progress.Node, ) void { comp.buildOutputFromZig( @@ -4703,7 +4702,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr log.debug("updating C object: {s}", .{c_object.src.src_path}); - if (c_object.clearStatus(comp.gpa)) { + const gpa = comp.gpa; + + if (c_object.clearStatus(gpa)) { // There was previous failure. comp.mutex.lock(); defer comp.mutex.unlock(); @@ -4722,7 +4723,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try cache_helpers.hashCSource(&man, c_object.src); - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -4744,7 +4745,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr const target = comp.getTarget(); const o_ext = target.ofmt.fileExt(target.cpu.arch); const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { - var argv = std.ArrayList([]const u8).init(comp.gpa); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // In case we are doing passthrough mode, we need to detect -S and -emit-llvm. @@ -4908,7 +4909,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr switch (term) { .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { - const bundle = CObject.Diag.Bundle.parse(comp.gpa, diag_file_path) catch |err| { + const bundle = CObject.Diag.Bundle.parse(gpa, diag_file_path) catch |err| { log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); return comp.failCObj(c_object, "clang exited with code {d}", .{code}); }; @@ -4982,9 +4983,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr c_object.status = .{ .success = .{ - .object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, o_basename, - }), + .object_path = .{ + .root_dir = comp.local_cache_directory, + .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, o_basename }), + }, .lock = man.toOwnedLock(), }, }; @@ -6092,18 +6094,23 @@ test "classifyFileExt" { try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); } -pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { - if (comp.wantBuildGLibCFromSource() or - comp.wantBuildMuslFromSource() or - comp.wantBuildMinGWFromSource() or - comp.wantBuildWasiLibcFromSource()) - { - return comp.crt_files.get(basename).?.full_object_path; - } - const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; - const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; - const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); - return full_path; +pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path { + return (try crtFilePath(comp, basename)) orelse { + const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; + const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir; + const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); + return Path.initCwd(full_path); + }; +} + +pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { + const path = try get_libc_crt_file(comp, arena, basename); + return path.toString(arena); +} + +pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path { + const crt_file = comp.crt_files.get(basename) orelse return null; + return crt_file.full_object_path; } fn wantBuildLibCFromSource(comp: Compilation) bool { @@ -6314,7 +6321,7 @@ fn buildOutputFromZig( comp: *Compilation, src_basename: []const u8, output_mode: std.builtin.OutputMode, - out: *?CRTFile, + out: *?CrtFile, misc_task_tag: MiscTask, prog_node: std.Progress.Node, ) !void { @@ -6542,15 +6549,39 @@ pub fn build_crt_file( comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); } -pub fn toCrtFile(comp: *Compilation) Allocator.Error!CRTFile { +pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile { return .{ - .full_object_path = try comp.local_cache_directory.join(comp.gpa, &.{ - comp.cache_use.whole.bin_sub_path.?, - }), + .full_object_path = .{ + .root_dir = comp.local_cache_directory, + .sub_path = try comp.gpa.dupe(u8, comp.cache_use.whole.bin_sub_path.?), + }, .lock = comp.cache_use.whole.moveLock(), }; } +pub fn getCrtPaths( + comp: *Compilation, + arena: Allocator, +) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths { + const target = comp.root_mod.resolved_target.result; + const basenames = LibCInstallation.CrtBasenames.get(.{ + .target = target, + .link_libc = comp.config.link_libc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pie = comp.config.pie, + }); + if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target); + + return .{ + .crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null, + .crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null, + .crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null, + .crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null, + .crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null, + }; +} + pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { // Avoid deadlocking on building import libs such as kernel32.lib // This can happen when the user uses `build-exe foo.obj -lkernel32` and diff --git a/src/glibc.zig b/src/glibc.zig index 7afa4f7432..f01e867843 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -169,14 +169,14 @@ fn useElfInitFini(target: std.Target) bool { }; } -pub const CRTFile = enum { +pub const CrtFile = enum { crti_o, crtn_o, scrt1_o, libc_nonshared_a, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -292,7 +292,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre .owner = undefined, }; var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o }; - return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files); + const basename = if (comp.config.output_mode == .Exe and !comp.config.pie) "crt1" else "Scrt1"; + return comp.build_crt_file(basename, .Obj, .@"glibc Scrt1.o", prog_node, &files); }, .libc_nonshared_a => { const s = path.sep_str; diff --git a/src/link.zig b/src/link.zig index 879fca9a84..64115ab3ee 100644 --- a/src/link.zig +++ b/src/link.zig @@ -11,7 +11,7 @@ const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; -const Path = Cache.Path; +const Path = std.Build.Cache.Path; const Compilation = @import("Compilation.zig"); const LibCInstallation = std.zig.LibCInstallation; const Liveness = @import("Liveness.zig"); @@ -34,7 +34,7 @@ pub const SystemLib = struct { /// 1. Windows DLLs that zig ships such as advapi32. /// 2. extern "foo" fn declarations where we find out about libraries too late /// TODO: make this non-optional and resolve those two cases somehow. - path: ?[]const u8, + path: ?Path, }; pub fn hashAddSystemLibs( @@ -46,7 +46,7 @@ pub fn hashAddSystemLibs( for (hm.values()) |value| { man.hash.add(value.needed); man.hash.add(value.weak); - if (value.path) |p| _ = try man.addFile(p, null); + if (value.path) |p| _ = try man.addFilePath(p, null); } } @@ -551,7 +551,7 @@ pub const File = struct { LLDCrashed, LLDReportedFailure, LLD_LinkingIsTODO_ForSpirV, - LibCInstallationMissingCRTDir, + LibCInstallationMissingCrtDir, LibCInstallationNotAvailable, LinkingWithoutZigSourceUnimplemented, MalformedArchive, @@ -606,18 +606,15 @@ pub const File = struct { const comp = base.comp; if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { dev.check(.clang_command); - const gpa = comp.gpa; const emit = base.emit; // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) // Until then, we do `lld -r -o output.o input.o` even though the output is the same // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file // to the final location. See also the corresponding TODO in Coff linking. - const full_out_path = try emit.root_dir.join(gpa, &[_][]const u8{emit.sub_path}); - defer gpa.free(full_out_path); assert(comp.c_object_table.count() == 1); const the_key = comp.c_object_table.keys()[0]; const cached_pp_file_path = the_key.status.success.object_path; - try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{}); + try cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}); return; } @@ -781,7 +778,7 @@ pub const File = struct { log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); - const compiler_rt_path: ?[]const u8 = if (comp.include_compiler_rt) + const compiler_rt_path: ?Path = if (comp.include_compiler_rt) comp.compiler_rt_obj.?.full_object_path else null; @@ -806,18 +803,18 @@ pub const File = struct { base.releaseLock(); for (objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } for (comp.win32_resource_table.keys()) |key| { _ = try man.addFile(key.status.success.res_path, null); } try man.addOptionalFile(zcu_obj_path); - try man.addOptionalFile(compiler_rt_path); + try man.addOptionalFilePath(compiler_rt_path); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -851,10 +848,10 @@ pub const File = struct { defer object_files.deinit(); for (objects) |obj| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path)); + object_files.appendAssumeCapacity(try obj.path.toStringZ(arena)); } for (comp.c_object_table.keys()) |key| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path)); + object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); } for (comp.win32_resource_table.keys()) |key| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); @@ -863,7 +860,7 @@ pub const File = struct { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); } if (compiler_rt_path) |p| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + object_files.appendAssumeCapacity(try p.toStringZ(arena)); } if (comp.verbose_link) { diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 651c76c7f1..cf09174e88 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -7,6 +7,7 @@ const fs = std.fs; const log = std.log.scoped(.link); const mem = std.mem; const Cache = std.Build.Cache; +const Path = std.Build.Cache.Path; const mingw = @import("../../mingw.zig"); const link = @import("../../link.zig"); @@ -74,11 +75,11 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no comptime assert(Compilation.link_hash_implementation_version == 14); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } for (comp.win32_resource_table.keys()) |key| { _ = try man.addFile(key.status.success.res_path, null); @@ -154,17 +155,19 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + self.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -270,14 +273,14 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no try argv.ensureUnusedCapacity(comp.objects.len); for (comp.objects) |obj| { if (obj.must_link) { - argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); + argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)})); } else { - argv.appendAssumeCapacity(obj.path); + argv.appendAssumeCapacity(try obj.path.toString(arena)); } } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } for (comp.win32_resource_table.keys()) |key| { @@ -401,17 +404,17 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } if (is_dyn_lib) { - try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj")); + try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj")); if (target.cpu.arch == .x86) { try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); } else { try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); } } else { - try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj")); + try argv.append(try comp.crtFileAsString(arena, "crt2.obj")); } - try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); + try argv.append(try comp.crtFileAsString(arena, "mingw32.lib")); } else { const lib_str = switch (comp.config.link_mode) { .dynamic => "", @@ -456,36 +459,36 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { if (!comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); - if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); + if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); + if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); } try argv.ensureUnusedCapacity(comp.system_libs.count()); for (comp.system_libs.keys()) |key| { const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); if (comp.crt_files.get(lib_basename)) |crt_file| { - argv.appendAssumeCapacity(crt_file.full_object_path); + argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena)); continue; } if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 62bf6be63a..5f6a073842 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -383,9 +383,9 @@ pub fn createEmpty( const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( - zcu.main_mod.root_src_path, - )}), + .basename = try std.fmt.allocPrint(arena, "{s}.o", .{ + fs.path.stem(zcu.main_mod.root_src_path), + }), } }); self.zig_object_index = index; try self.zigObjectPtr().?.init(self, .{ @@ -742,13 +742,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const target = self.getTarget(); const link_mode = comp.config.link_mode; const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. - const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } + const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ + .root_dir = directory, + .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| + try fs.path.join(arena, &.{ dirname, path }) + else + path, } else null; // --verbose-link @@ -758,7 +757,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path); if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path); - const csu = try CsuObjects.init(arena, comp); + const csu = try comp.getCrtPaths(arena); // csu prelude if (csu.crt0) |path| try parseObjectReportingFailure(self, path); @@ -790,23 +789,22 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib); } - var system_libs = std.ArrayList(SystemLib).init(arena); - - try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); for (comp.system_libs.values()) |lib_info| { - system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? }); + try self.parseLibraryReportingFailure(.{ + .needed = lib_info.needed, + .path = lib_info.path.?, + }, false); } // 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 }); + try self.parseLibraryReportingFailure(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }, false); + try self.parseLibraryReportingFailure(.{ .path = comp.libcxx_static_lib.?.full_object_path }, false); } // libunwind dep if (comp.config.link_libunwind) { - try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path }); + try self.parseLibraryReportingFailure(.{ .path = comp.libunwind_static_lib.?.full_object_path }, false); } // libc dep @@ -814,7 +812,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (comp.config.link_libc) { if (comp.libc_installation) |lc| { const flags = target_util.libcFullLinkFlags(target); - try system_libs.ensureUnusedCapacity(flags.len); var test_path = std.ArrayList(u8).init(arena); var checked_paths = std.ArrayList([]const u8).init(arena); @@ -840,39 +837,34 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod continue; } - const resolved_path = try arena.dupe(u8, test_path.items); - system_libs.appendAssumeCapacity(.{ .path = resolved_path }); + const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items)); + try self.parseLibraryReportingFailure(.{ .path = resolved_path }, false); } } else if (target.isGnuLibC()) { - try system_libs.ensureUnusedCapacity(glibc.libs.len + 1); for (glibc.libs) |lib| { if (lib.removed_in) |rem_in| { if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; } - const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ + const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - system_libs.appendAssumeCapacity(.{ .path = lib_path }); + })); + try self.parseLibraryReportingFailure(.{ .path = lib_path }, false); } - system_libs.appendAssumeCapacity(.{ + try self.parseLibraryReportingFailure(.{ .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"), - }); + }, false); } else if (target.isMusl()) { const path = try comp.get_libc_crt_file(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", }); - try system_libs.append(.{ .path = path }); + try self.parseLibraryReportingFailure(.{ .path = path }, false); } else { comp.link_error_flags.missing_libc = true; } } - for (system_libs.items) |lib| { - try self.parseLibraryReportingFailure(lib, false); - } - // Finally, as the last input objects we add compiler_rt and CSU postlude (if any). // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs @@ -1066,10 +1058,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } } else null; - const csu = try CsuObjects.init(arena, comp); + const csu = try comp.getCrtPaths(arena); const compiler_rt_path: ?[]const u8 = blk: { - if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; - if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; + if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena); + if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena); break :blk null; }; @@ -1092,11 +1084,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { if (self.base.isRelocatable()) { for (comp.objects) |obj| { - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -1178,9 +1170,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // csu prelude - if (csu.crt0) |v| try argv.append(v); - if (csu.crti) |v| try argv.append(v); - if (csu.crtbegin) |v| try argv.append(v); + if (csu.crt0) |path| try argv.append(try path.toString(arena)); + if (csu.crti) |path| try argv.append(try path.toString(arena)); + if (csu.crtbegin) |path| try argv.append(try path.toString(arena)); for (self.lib_dirs) |lib_dir| { try argv.append("-L"); @@ -1205,10 +1197,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (obj.loption) { - assert(obj.path[0] == ':'); try argv.append("-l"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -1216,7 +1207,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -1224,17 +1215,17 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - try argv.append(comp.tsan_lib.?.full_object_path); + try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena)); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } // libc if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } @@ -1258,7 +1249,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { as_needed = true; }, } - argv.appendAssumeCapacity(lib_info.path.?); + argv.appendAssumeCapacity(try lib_info.path.?.toString(arena)); } if (!as_needed) { @@ -1268,13 +1259,13 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } // libc dep @@ -1295,9 +1286,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { }); try argv.append(lib_path); } - try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { + try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", })); @@ -1310,8 +1301,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // crt postlude - if (csu.crtend) |v| try argv.append(v); - if (csu.crtn) |v| try argv.append(v); + if (csu.crtend) |path| try argv.append(try path.toString(arena)); + if (csu.crtn) |path| try argv.append(try path.toString(arena)); } Compilation.dump_argv(argv.items); @@ -1331,7 +1322,7 @@ pub const ParseError = error{ UnknownFileType, } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; -fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void { +fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) error{OutOfMemory}!void { if (crt_file.isObject()) { try parseObjectReportingFailure(self, crt_file.full_object_path); } else { @@ -1339,7 +1330,7 @@ fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error } } -pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void { +pub fn parseObjectReportingFailure(self: *Elf, path: Path) error{OutOfMemory}!void { self.parseObject(path) catch |err| switch (err) { error.LinkFailure => return, // already reported error.OutOfMemory => return error.OutOfMemory, @@ -1367,17 +1358,20 @@ fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void { } } -fn parseObject(self: *Elf, path: []const u8) ParseError!void { +fn parseObject(self: *Elf, path: Path) ParseError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(handle); - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = fh, .index = index, } }); @@ -1387,15 +1381,15 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void { try object.parse(self); } -fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { +fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(handle); - var archive = Archive{}; + var archive: Archive = .{}; defer archive.deinit(gpa); try archive.parse(self, path, fh); @@ -1403,7 +1397,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void { defer gpa.free(objects); for (objects) |extracted| { - const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); + const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .object = extracted }); const object = &self.files.items(.data)[index].object; object.index = index; @@ -1418,12 +1412,15 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const handle = try fs.cwd().openFile(lib.path, .{}); + const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); defer handle.close(); const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .shared_object = .{ - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .alive = lib.needed, @@ -1439,12 +1436,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { defer tracy.end(); const gpa = self.base.comp.gpa; - const in_file = try fs.cwd().openFile(lib.path, .{}); + const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); defer in_file.close(); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); defer gpa.free(data); - var script = LdScript{ .path = lib.path }; + var script: LdScript = .{ .path = lib.path }; defer script.deinit(gpa); try script.parse(data, self); @@ -1455,12 +1452,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { var test_path = std.ArrayList(u8).init(arena); var checked_paths = std.ArrayList([]const u8).init(arena); - for (script.args.items) |scr_obj| { + for (script.args.items) |script_arg| { checked_paths.clearRetainingCapacity(); success: { - if (mem.startsWith(u8, scr_obj.path, "-l")) { - const lib_name = scr_obj.path["-l".len..]; + if (mem.startsWith(u8, script_arg.path, "-l")) { + const lib_name = script_arg.path["-l".len..]; // TODO I think technically we should re-use the mechanism used by the frontend here. // Maybe we should hoist search-strategy all the way here? @@ -1474,33 +1471,30 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } } else { var buffer: [fs.max_path_bytes]u8 = undefined; - if (fs.realpath(scr_obj.path, &buffer)) |path| { + if (fs.realpath(script_arg.path, &buffer)) |path| { test_path.clearRetainingCapacity(); try test_path.writer().writeAll(path); break :success; } else |_| {} - try checked_paths.append(try arena.dupe(u8, scr_obj.path)); + try checked_paths.append(try arena.dupe(u8, script_arg.path)); for (self.lib_dirs) |lib_dir| { - if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, scr_obj.path, null)) + if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, script_arg.path, null)) break :success; } } try self.reportMissingLibraryError( checked_paths.items, - "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found", - .{ - lib.path, - scr_obj.path, - }, + "missing library dependency: GNU ld script '{}' requires '{s}', but file not found", + .{ @as(Path, lib.path), script_arg.path }, ); continue; } - const full_path = test_path.items; + const full_path = Path.initCwd(test_path.items); self.parseLibrary(.{ - .needed = scr_obj.needed, + .needed = script_arg.needed, .path = full_path, }, false) catch |err| switch (err) { error.LinkFailure => continue, // already reported @@ -1841,7 +1835,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s const have_dynamic_linker = comp.config.link_libc and link_mode == .dynamic and is_exe_or_dyn_lib; const target = self.getTarget(); - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; @@ -1875,17 +1869,17 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s man.hash.add(self.allow_undefined_version); man.hash.addOptional(self.enable_new_dtags); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } try man.addOptionalFile(module_obj_path); - try man.addOptionalFile(compiler_rt_path); - try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null); - try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null); + try man.addOptionalFilePath(compiler_rt_path); + try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null); + try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null); // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. @@ -1982,17 +1976,19 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + self.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -2177,10 +2173,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s try argv.append(full_out_path); // csu prelude - const csu = try CsuObjects.init(arena, comp); - if (csu.crt0) |v| try argv.append(v); - if (csu.crti) |v| try argv.append(v); - if (csu.crtbegin) |v| try argv.append(v); + const csu = try comp.getCrtPaths(arena); + if (csu.crt0) |p| try argv.append(try p.toString(arena)); + if (csu.crti) |p| try argv.append(try p.toString(arena)); + if (csu.crtbegin) |p| try argv.append(try p.toString(arena)); for (self.rpath_table.keys()) |rpath| { try argv.appendSlice(&.{ "-rpath", rpath }); @@ -2244,10 +2240,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } if (obj.loption) { - assert(obj.path[0] == ':'); + assert(obj.path.sub_path[0] == ':'); try argv.append("-l"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -2255,7 +2251,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -2264,12 +2260,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s if (comp.tsan_lib) |lib| { assert(comp.config.any_sanitize_thread); - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } if (comp.fuzzer_lib) |lib| { assert(comp.config.any_fuzz); - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } // libc @@ -2278,7 +2274,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { - try argv.append(lib.full_object_path); + try argv.append(try lib.full_object_path.toString(arena)); } } @@ -2311,7 +2307,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // libraries and not static libraries (the check for that needs to be earlier), // but they could be full paths to .so files, in which case we // want to avoid prepending "-l". - argv.appendAssumeCapacity(lib_info.path.?); + argv.appendAssumeCapacity(try lib_info.path.?.toString(arena)); } if (!as_needed) { @@ -2321,13 +2317,13 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // libc++ dep if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); } // libunwind dep if (comp.config.link_libunwind) { - try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); } // libc dep @@ -2349,9 +2345,9 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s }); try argv.append(lib_path); } - try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { + try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { .static => "libc.a", .dynamic => "libc.so", })); @@ -2365,12 +2361,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s // to be after the shared libraries, so they are picked up from the shared // libraries, not libcompiler_rt. if (compiler_rt_path) |p| { - try argv.append(p); + try argv.append(try p.toString(arena)); } // crt postlude - if (csu.crtend) |v| try argv.append(v); - if (csu.crtn) |v| try argv.append(v); + if (csu.crtend) |p| try argv.append(try p.toString(arena)); + if (csu.crtn) |p| try argv.append(try p.toString(arena)); if (self.base.allow_shlib_undefined) { try argv.append("--allow-shlib-undefined"); @@ -3183,8 +3179,9 @@ fn sortInitFini(self: *Elf) !void { const object = atom_ptr.file(self).?.object; const priority = blk: { if (is_ctor_dtor) { - if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32); - if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32); + const basename = object.path.basename(); + if (mem.eql(u8, basename, "crtbegin.o")) break :blk std.math.minInt(i32); + if (mem.eql(u8, basename, "crtend.o")) break :blk std.math.maxInt(i32); } const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32); const name = atom_ptr.name(self); @@ -4472,210 +4469,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return actual_size +| (actual_size / ideal_factor); } -// Provide a blueprint of csu (c-runtime startup) objects for supported -// link modes. -// -// This is for cross-mode targets only. For host-mode targets the system -// compiler can be probed to produce a robust blueprint. -// -// Targets requiring a libc for which zig does not bundle a libc are -// host-mode targets. Unfortunately, host-mode probes are not yet -// implemented. For now the data is hard-coded here. Such targets are -// { freebsd, netbsd, openbsd, dragonfly }. -const CsuObjects = struct { - crt0: ?[]const u8 = null, - crti: ?[]const u8 = null, - crtbegin: ?[]const u8 = null, - crtend: ?[]const u8 = null, - crtn: ?[]const u8 = null, - - const InitArgs = struct {}; - - fn init(arena: Allocator, comp: *const Compilation) !CsuObjects { - // crt objects are only required for libc. - if (!comp.config.link_libc) return .{}; - - var result: CsuObjects = .{}; - - // Flatten crt cases. - const mode: enum { - dynamic_lib, - dynamic_exe, - dynamic_pie, - static_exe, - static_pie, - } = switch (comp.config.output_mode) { - .Obj => return CsuObjects{}, - .Lib => switch (comp.config.link_mode) { - .dynamic => .dynamic_lib, - .static => return CsuObjects{}, - }, - .Exe => switch (comp.config.link_mode) { - .dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe, - .static => if (comp.config.pie) .static_pie else .static_exe, - }, - }; - - const target = comp.root_mod.resolved_target.result; - - if (target.isAndroid()) { - switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ), - .dynamic_exe, - .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ), - .static_exe, - .static_pie => result.set( null, null, "crtbegin_static.o", "crtend_android.o", null ), - // zig fmt: on - } - } else { - switch (target.os.tag) { - .linux => { - switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - } - if (comp.libc_installation) |_| { - // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs - // and they are not known at comptime. For now null-out crtbegin/end objects; - // there is no feature loss, zig has never linked those objects in before. - result.crtbegin = null; - result.crtend = null; - } else { - // Bundled glibc only has Scrt1.o . - if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o"; - } - }, - .dragonfly => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .freebsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .netbsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .openbsd => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, null, "crtbeginS.o", "crtendS.o", null ), - .dynamic_exe, - .dynamic_pie => result.set( "crt0.o", null, "crtbegin.o", "crtend.o", null ), - .static_exe, - .static_pie => result.set( "rcrt0.o", null, "crtbegin.o", "crtend.o", null ), - // zig fmt: on - }, - .haiku => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - .static_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ), - .static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), - // zig fmt: on - }, - .solaris, .illumos => switch (mode) { - // zig fmt: off - .dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ), - .dynamic_exe, - .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ), - .static_exe, - .static_pie => result.set( null, null, null, null, null ), - // zig fmt: on - }, - else => {}, - } - } - - // Convert each object to a full pathname. - if (comp.libc_installation) |lci| { - const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; - switch (target.os.tag) { - .dragonfly => { - if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - - var gccv: []const u8 = undefined; - if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) { - gccv = "gcc80"; - } else { - gccv = "gcc54"; - } - - if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); - if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* }); - }, - .haiku => { - const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir; - if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - - if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); - if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* }); - }, - else => { - inline for (std.meta.fields(@TypeOf(result))) |f| { - if (@field(result, f.name)) |*obj| { - obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); - } - } - }, - } - } else { - inline for (std.meta.fields(@TypeOf(result))) |f| { - if (@field(result, f.name)) |*obj| { - if (comp.crt_files.get(obj.*)) |crtf| { - obj.* = crtf.full_object_path; - } else { - @field(result, f.name) = null; - } - } - } - } - - return result; - } - - fn set( - self: *CsuObjects, - crt0: ?[]const u8, - crti: ?[]const u8, - crtbegin: ?[]const u8, - crtend: ?[]const u8, - crtn: ?[]const u8, - ) void { - self.crt0 = crt0; - self.crti = crti; - self.crtbegin = crtbegin; - self.crtend = crtend; - self.crtn = crtn; - } -}; - /// If a target compiles other output modes as dynamic libraries, /// this function returns true for those too. pub fn isEffectivelyDynLib(self: Elf) bool { @@ -5089,13 +4882,13 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void { pub fn addParseError( self: *Elf, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { var err = try self.base.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {s}", .{path}); + try err.addNote("while parsing {}", .{path}); } pub fn addFileError( @@ -5121,7 +4914,7 @@ pub fn failFile( pub fn failParse( self: *Elf, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{ OutOfMemory, LinkFailure } { @@ -5274,7 +5067,7 @@ fn fmtDumpState( _ = options; if (self.zigObjectPtr()) |zig_object| { - try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path }); + try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename }); try writer.print("{}{}", .{ zig_object.fmtAtoms(self), zig_object.fmtSymtab(self), @@ -5299,7 +5092,7 @@ fn fmtDumpState( for (self.shared_objects.items) |index| { const shared_object = self.file(index).?.shared_object; try writer.print("shared_object({d}) : ", .{index}); - try writer.print("{s}", .{shared_object.path}); + try writer.print("{}", .{shared_object.path}); try writer.print(" : needed({})", .{shared_object.needed}); if (!shared_object.alive) try writer.writeAll(" : [*]"); try writer.writeByte('\n'); @@ -5482,7 +5275,7 @@ pub const null_shdr = elf.Elf64_Shdr{ pub const SystemLib = struct { needed: bool = false, - path: []const u8, + path: Path, }; pub const Ref = struct { diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 1314328d5b..7cc454f10c 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -1,8 +1,8 @@ objects: std.ArrayListUnmanaged(Object) = .empty, strtab: std.ArrayListUnmanaged(u8) = .empty, -pub fn isArchive(path: []const u8) !bool { - const file = try std.fs.cwd().openFile(path, .{}); +pub fn isArchive(path: Path) !bool { + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); defer file.close(); const reader = file.reader(); const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false; @@ -15,7 +15,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { self.strtab.deinit(allocator); } -pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void { +pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; const handle = elf_file.fileHandle(handle_index); @@ -59,19 +59,24 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil else unreachable; - const object = Object{ + const object: Object = .{ .archive = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .offset = pos, .size = obj_size, }, - .path = try gpa.dupe(u8, name), + .path = Path.initCwd(try gpa.dupe(u8, name)), .file_handle = handle_index, .index = undefined, .alive = false, }; - log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); + log.debug("extracting object '{}' from archive '{}'", .{ + @as(Path, object.path), @as(Path, path), + }); try self.objects.append(gpa, object); } @@ -292,6 +297,7 @@ const elf = std.elf; const fs = std.fs; const log = std.log.scoped(.link); const mem = std.mem; +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Archive = @This(); diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 6735430a42..7bb1f62104 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -1,6 +1,11 @@ -path: []const u8, +path: Path, cpu_arch: ?std.Target.Cpu.Arch = null, -args: std.ArrayListUnmanaged(Elf.SystemLib) = .empty, +args: std.ArrayListUnmanaged(Arg) = .empty, + +pub const Arg = struct { + needed: bool = false, + path: []const u8, +}; pub fn deinit(scr: *LdScript, allocator: Allocator) void { scr.args.deinit(allocator); @@ -47,7 +52,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { var it = TokenIterator{ .tokens = tokens.items }; var parser = Parser{ .source = data, .it = &it }; - var args = std.ArrayList(Elf.SystemLib).init(gpa); + var args = std.ArrayList(Arg).init(gpa); scr.doParse(.{ .parser = &parser, .args = &args, @@ -70,7 +75,7 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { fn doParse(scr: *LdScript, ctx: struct { parser: *Parser, - args: *std.ArrayList(Elf.SystemLib), + args: *std.ArrayList(Arg), }) !void { while (true) { ctx.parser.skipAny(&.{ .comment, .new_line }); @@ -142,7 +147,7 @@ const Parser = struct { return error.UnknownCpuArch; } - fn group(p: *Parser, args: *std.ArrayList(Elf.SystemLib)) !void { + fn group(p: *Parser, args: *std.ArrayList(Arg)) !void { if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (true) { @@ -162,7 +167,7 @@ const Parser = struct { _ = try p.require(.rparen); } - fn asNeeded(p: *Parser, args: *std.ArrayList(Elf.SystemLib)) !void { + fn asNeeded(p: *Parser, args: *std.ArrayList(Arg)) !void { if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (p.maybe(.literal)) |tok_id| { @@ -239,7 +244,7 @@ const Token = struct { const Index = usize; - inline fn get(tok: Token, source: []const u8) []const u8 { + fn get(tok: Token, source: []const u8) []const u8 { return source[tok.start..tok.end]; } }; @@ -399,11 +404,11 @@ const TokenIterator = struct { return it.tokens[it.pos]; } - inline fn reset(it: *TokenIterator) void { + fn reset(it: *TokenIterator) void { it.pos = 0; } - inline fn seekTo(it: *TokenIterator, pos: Token.Index) void { + fn seekTo(it: *TokenIterator, pos: Token.Index) void { it.pos = pos; } @@ -416,7 +421,7 @@ const TokenIterator = struct { } } - inline fn get(it: *TokenIterator, pos: Token.Index) Token { + fn get(it: *TokenIterator, pos: Token.Index) Token { assert(pos < it.tokens.len); return it.tokens[pos]; } @@ -426,6 +431,7 @@ const LdScript = @This(); const std = @import("std"); const assert = std.debug.assert; +const Path = std.Build.Cache.Path; const Allocator = std.mem.Allocator; const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 96178ec6c5..23216020a6 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -1,5 +1,7 @@ archive: ?InArchive = null, -path: []const u8, +/// Archive files cannot contain subdirectories, so only the basename is needed +/// for output. However, the full path is kept for error reporting. +path: Path, file_handle: File.HandleIndex, index: File.Index, @@ -36,8 +38,8 @@ output_symtab_ctx: Elf.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.archive) |*ar| allocator.free(ar.path); - allocator.free(self.path); + if (self.archive) |*ar| allocator.free(ar.path.sub_path); + allocator.free(self.path.sub_path); self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); @@ -474,8 +476,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { if (sym.type(elf_file) != elf.STT_FUNC) // TODO convert into an error log.debug("{s}: {s}: CIE referencing external data reference", .{ - self.fmtPath(), - sym.name(elf_file), + self.fmtPath(), sym.name(elf_file), }); sym.flags.needs_plt = true; } @@ -996,7 +997,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void { pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void { const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; const offset: u64 = if (self.archive) |ar| ar.offset else 0; - const name = self.path; + const name = std.fs.path.basename(self.path.sub_path); const hdr = Archive.setArHdr(.{ .name = if (name.len <= Archive.max_member_name_len) .{ .name = name } @@ -1489,15 +1490,14 @@ fn formatPath( _ = unused_fmt_string; _ = options; if (object.archive) |ar| { - try writer.writeAll(ar.path); - try writer.writeByte('('); - try writer.writeAll(object.path); - try writer.writeByte(')'); - } else try writer.writeAll(object.path); + try writer.print("{}({})", .{ ar.path, object.path }); + } else { + try writer.print("{}", .{object.path}); + } } const InArchive = struct { - path: []const u8, + path: Path, offset: u64, size: u32, }; @@ -1512,8 +1512,9 @@ const fs = std.fs; const log = std.log.scoped(.link); const math = std.math; const mem = std.mem; - +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; + const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const AtomList = @import("AtomList.zig"); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 581fba9560..7e6aabea38 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -1,4 +1,4 @@ -path: []const u8, +path: Path, index: File.Index, header: ?elf.Elf64_Ehdr = null, @@ -22,8 +22,8 @@ alive: bool, output_symtab_ctx: Elf.SymtabCtx = .{}, -pub fn isSharedObject(path: []const u8) !bool { - const file = try std.fs.cwd().openFile(path, .{}); +pub fn isSharedObject(path: Path) !bool { + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); defer file.close(); const reader = file.reader(); const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; @@ -34,7 +34,7 @@ pub fn isSharedObject(path: []const u8) !bool { } pub fn deinit(self: *SharedObject, allocator: Allocator) void { - allocator.free(self.path); + allocator.free(self.path.sub_path); self.shdrs.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); @@ -319,7 +319,7 @@ pub fn asFile(self: *SharedObject) File { fn verdefNum(self: *SharedObject) u32 { for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), + elf.DT_VERDEFNUM => return @intCast(entry.d_val), else => {}, }; return 0; @@ -327,10 +327,10 @@ fn verdefNum(self: *SharedObject) u32 { pub fn soname(self: *SharedObject) []const u8 { for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), + elf.DT_SONAME => return self.getString(@intCast(entry.d_val)), else => {}, }; - return std.fs.path.basename(self.path); + return std.fs.path.basename(self.path.sub_path); } pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { @@ -508,6 +508,7 @@ const assert = std.debug.assert; const elf = std.elf; const log = std.log.scoped(.elf); const mem = std.mem; +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Elf = @import("../Elf.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 846bcac15c..54d15297d5 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -5,7 +5,7 @@ data: std.ArrayListUnmanaged(u8) = .empty, /// Externally owned memory. -path: []const u8, +basename: []const u8, index: File.Index, symtab: std.MultiArrayList(ElfSym) = .{}, @@ -88,7 +88,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void { try self.strtab.buffer.append(gpa, 0); { - const name_off = try self.strtab.insert(gpa, self.path); + const name_off = try self.strtab.insert(gpa, self.basename); const symbol_index = try self.newLocalSymbol(gpa, name_off); const sym = self.symbol(symbol_index); const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; @@ -774,7 +774,7 @@ pub fn updateArSize(self: *ZigObject) void { } pub fn writeAr(self: ZigObject, writer: anytype) !void { - const name = self.path; + const name = self.basename; const hdr = Archive.setArHdr(.{ .name = if (name.len <= Archive.max_member_name_len) .{ .name = name } @@ -2384,9 +2384,9 @@ const relocation = @import("relocation.zig"); const target_util = @import("../../target.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); +const Allocator = std.mem.Allocator; const Air = @import("../../Air.zig"); -const Allocator = std.mem.Allocator; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Dwarf = @import("../Dwarf.zig"); diff --git a/src/link/Elf/file.zig b/src/link/Elf/file.zig index 740987feb2..6eb4c2201f 100644 --- a/src/link/Elf/file.zig +++ b/src/link/Elf/file.zig @@ -23,10 +23,10 @@ pub const File = union(enum) { _ = unused_fmt_string; _ = options; switch (file) { - .zig_object => |x| try writer.print("{s}", .{x.path}), + .zig_object => |zo| try writer.writeAll(zo.basename), .linker_defined => try writer.writeAll("(linker defined)"), .object => |x| try writer.print("{}", .{x.fmtPath()}), - .shared_object => |x| try writer.writeAll(x.path), + .shared_object => |x| try writer.print("{}", .{@as(Path, x.path)}), } } @@ -240,30 +240,31 @@ pub const File = union(enum) { return switch (file) { .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), .object => |x| x.updateArSymtab(ar_symtab, elf_file), - inline else => unreachable, + else => unreachable, }; } pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void { - const path = switch (file) { - .zig_object => |x| x.path, - .object => |x| x.path, - inline else => unreachable, - }; - const state = switch (file) { - .zig_object => |x| &x.output_ar_state, - .object => |x| &x.output_ar_state, - inline else => unreachable, - }; - if (path.len <= Archive.max_member_name_len) return; - state.name_off = try ar_strtab.insert(allocator, path); + switch (file) { + .zig_object => |zo| { + const basename = zo.basename; + if (basename.len <= Archive.max_member_name_len) return; + zo.output_ar_state.name_off = try ar_strtab.insert(allocator, basename); + }, + .object => |o| { + const basename = std.fs.path.basename(o.path.sub_path); + if (basename.len <= Archive.max_member_name_len) return; + o.output_ar_state.name_off = try ar_strtab.insert(allocator, basename); + }, + else => unreachable, + } } pub fn updateArSize(file: File, elf_file: *Elf) !void { return switch (file) { .zig_object => |x| x.updateArSize(), .object => |x| x.updateArSize(elf_file), - inline else => unreachable, + else => unreachable, }; } @@ -271,7 +272,7 @@ pub const File = union(enum) { return switch (file) { .zig_object => |x| x.writeAr(writer), .object => |x| x.writeAr(elf_file, writer), - inline else => unreachable, + else => unreachable, }; } @@ -292,8 +293,9 @@ pub const File = union(enum) { const std = @import("std"); const elf = std.elf; const log = std.log.scoped(.link); - +const Path = std.Build.Cache.Path; const Allocator = std.mem.Allocator; + const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const Cie = @import("eh_frame.zig").Cie; diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 8768c1d754..bdf0d378ff 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -1,8 +1,8 @@ -pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = comp.gpa; for (comp.objects) |obj| { - switch (Compilation.classifyFileExt(obj.path)) { + switch (Compilation.classifyFileExt(obj.path.sub_path)) { .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path), .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path), else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}), @@ -140,7 +140,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co if (elf_file.base.hasErrors()) return error.FlushFailure; } -pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { for (comp.objects) |obj| { if (obj.isObject()) { try elf_file.parseObjectReportingFailure(obj.path); @@ -198,7 +198,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const if (elf_file.base.hasErrors()) return error.FlushFailure; } -fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void { +fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { parseObjectStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, error.OutOfMemory => return error.OutOfMemory, @@ -206,7 +206,7 @@ fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{ }; } -fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void { +fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void { parseArchiveStaticLib(elf_file, path) catch |err| switch (err) { error.LinkFailure => return, error.OutOfMemory => return error.OutOfMemory, @@ -214,14 +214,17 @@ fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error }; } -fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { +fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void { const gpa = elf_file.base.comp.gpa; - const handle = try std.fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try elf_file.addFileHandle(handle); - const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa))); + const index: File.Index = @intCast(try elf_file.files.addOne(gpa)); elf_file.files.set(index, .{ .object = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = fh, .index = index, } }); @@ -231,9 +234,9 @@ fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { try object.parseAr(elf_file); } -fn parseArchiveStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void { +fn parseArchiveStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void { const gpa = elf_file.base.comp.gpa; - const handle = try std.fs.cwd().openFile(path, .{}); + const handle = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try elf_file.addFileHandle(handle); var archive = Archive{}; @@ -531,6 +534,7 @@ const log = std.log.scoped(.link); const math = std.math; const mem = std.mem; const state_log = std.log.scoped(.link_state); +const Path = std.Build.Cache.Path; const std = @import("std"); const Archive = @import("Archive.zig"); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ec766a69ac..38684e4d6a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -144,14 +144,14 @@ hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{}, pub const Framework = struct { needed: bool = false, weak: bool = false, - path: []const u8, + path: Path, }; pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { for (hm) |value| { man.hash.add(value.needed); man.hash.add(value.weak); - _ = try man.addFile(value.path, null); + _ = try man.addFilePath(value.path, null); } } @@ -239,9 +239,9 @@ pub fn createEmpty( const index: File.Index = @intCast(try self.files.addOne(gpa)); self.files.set(index, .{ .zig_object = .{ .index = index, - .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( - zcu.main_mod.root_src_path, - )}), + .basename = try std.fmt.allocPrint(arena, "{s}.o", .{ + fs.path.stem(zcu.main_mod.root_src_path), + }), } }); self.zig_object = index; const zo = self.getZigObject().?; @@ -356,13 +356,12 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n defer sub_prog_node.end(); const directory = self.base.emit.root_dir; - const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); - const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { - if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, path }); - } else { - break :blk path; - } + const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{ + .root_dir = directory, + .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname| + try fs.path.join(arena, &.{ dirname, path }) + else + path, } else null; // --verbose-link @@ -455,7 +454,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n } // Finally, link against compiler_rt. - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; @@ -567,7 +566,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n // The most important here is to have the correct vm and filesize of the __LINKEDIT segment // where the code signature goes into. var codesig = CodeSignature.init(self.getPageSize()); - codesig.code_directory.ident = fs.path.basename(full_out_path); + codesig.code_directory.ident = fs.path.basename(self.base.emit.sub_path); if (self.entitlements) |path| try codesig.addEntitlements(gpa, path); try self.writeCodeSignaturePadding(&codesig); break :blk codesig; @@ -625,11 +624,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { if (self.base.isRelocatable()) { for (comp.objects) |obj| { - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -711,11 +710,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { if (obj.must_link) { try argv.append("-force_load"); } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { @@ -723,13 +722,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; - try argv.append(path); - try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." }); + const path = try comp.tsan_lib.?.full_object_path.toString(arena); + try argv.appendSlice(&.{ path, "-rpath", std.fs.path.dirname(path) orelse "." }); } if (comp.config.any_fuzz) { - try argv.append(comp.fuzzer_lib.?.full_object_path); + try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena)); } for (self.lib_dirs) |lib_dir| { @@ -754,7 +752,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } for (self.frameworks) |framework| { - const name = fs.path.stem(framework.path); + const name = framework.path.stem(); const arg = if (framework.needed) try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) else if (framework.weak) @@ -765,14 +763,16 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { } if (comp.config.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); - try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.appendSlice(&.{ + try comp.libcxxabi_static_lib.?.full_object_path.toString(arena), + try comp.libcxx_static_lib.?.full_object_path.toString(arena), + }); } try argv.append("-lSystem"); - if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); - if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); + if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena)); + if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena)); } Compilation.dump_argv(argv.items); @@ -807,20 +807,20 @@ pub fn resolveLibSystem( return error.MissingLibSystem; } - const libsystem_path = try arena.dupe(u8, test_path.items); + 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, path: []const u8, lib: SystemLib, must_link: bool) !void { +pub fn classifyInputFile(self: *MachO, path: Path, lib: SystemLib, must_link: bool) !void { const tracy = trace(@src()); defer tracy.end(); - log.debug("classifying input file {s}", .{path}); + log.debug("classifying input file {}", .{path}); - const file = try std.fs.cwd().openFile(path, .{}); + const file = try path.root_dir.handle.openFile(path.sub_path, .{}); const fh = try self.addFileHandle(file); var buffer: [Archive.SARMAG]u8 = undefined; @@ -844,7 +844,7 @@ pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_li _ = try self.addTbd(lib, true, fh); } -fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch { +fn parseFatFile(self: *MachO, file: std.fs.File, path: Path) !?fat.Arch { const fat_h = fat.readFatHeader(file) catch return null; if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null; var fat_archs_buffer: [2]fat.Arch = undefined; @@ -873,7 +873,7 @@ pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8 return buffer[0..Archive.SARMAG]; } -fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u64) !void { +fn addObject(self: *MachO, path: Path, handle: File.HandleIndex, offset: u64) !void { const tracy = trace(@src()); defer tracy.end(); @@ -886,7 +886,10 @@ fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .object = .{ .offset = offset, - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .file_handle = handle, .mtime = mtime, .index = index, @@ -937,7 +940,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle const gpa = self.base.comp.gpa; - var archive = Archive{}; + var archive: Archive = .{}; defer archive.deinit(gpa); try archive.unpack(self, lib.path, handle, fat_arch); @@ -963,7 +966,10 @@ fn addDylib(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleInd .offset = offset, .file_handle = handle, .tag = .dylib, - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .weak = lib.weak, @@ -986,7 +992,10 @@ fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex .offset = 0, .file_handle = handle, .tag = .tbd, - .path = try gpa.dupe(u8, lib.path), + .path = .{ + .root_dir = lib.path.root_dir, + .sub_path = try gpa.dupe(u8, lib.path.sub_path), + }, .index = index, .needed = lib.needed, .weak = lib.weak, @@ -1175,11 +1184,11 @@ fn parseDependentDylibs(self: *MachO) !void { continue; } }; - const lib = SystemLib{ - .path = full_path, + const lib: SystemLib = .{ + .path = Path.initCwd(full_path), .weak = is_weak, }; - const file = try std.fs.cwd().openFile(lib.path, .{}); + const file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{}); const fh = try self.addFileHandle(file); const fat_arch = try self.parseFatFile(file, lib.path); const offset = if (fat_arch) |fa| fa.offset else 0; @@ -2865,7 +2874,8 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } { ncmds += 1; } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; + const path = try comp.tsan_lib.?.full_object_path.toString(gpa); + defer gpa.free(path); const rpath = std.fs.path.dirname(path) orelse "."; try load_commands.writeRpathLC(rpath, writer); ncmds += 1; @@ -3758,13 +3768,13 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 { pub fn reportParseError( self: *MachO, - path: []const u8, + path: Path, comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { var err = try self.base.addErrorWithNotes(1); try err.addMsg(format, args); - try err.addNote("while parsing {s}", .{path}); + try err.addNote("while parsing {}", .{path}); } pub fn reportParseError2( @@ -3913,7 +3923,7 @@ fn fmtDumpState( _ = options; _ = unused_fmt_string; if (self.getZigObject()) |zo| { - try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.path }); + try writer.print("zig_object({d}) : {s}\n", .{ zo.index, zo.basename }); try writer.print("{}{}\n", .{ zo.fmtAtoms(self), zo.fmtSymtab(self), @@ -3938,9 +3948,9 @@ fn fmtDumpState( } for (self.dylibs.items) |index| { const dylib = self.getFile(index).?.dylib; - try writer.print("dylib({d}) : {s} : needed({}) : weak({})", .{ + try writer.print("dylib({d}) : {} : needed({}) : weak({})", .{ index, - dylib.path, + @as(Path, dylib.path), dylib.needed, dylib.weak, }); @@ -4442,7 +4452,7 @@ pub const default_pagezero_size: u64 = 0x100000000; pub const default_headerpad_size: u32 = 0x1000; const SystemLib = struct { - path: []const u8, + path: Path, needed: bool = false, weak: bool = false, hidden: bool = false, diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index bb589dfb66..ba3c66689b 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -4,7 +4,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { self.objects.deinit(allocator); } -pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void { +pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void { const gpa = macho_file.base.comp.gpa; var arena = std.heap.ArenaAllocator.init(gpa); @@ -55,20 +55,23 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index mem.eql(u8, name, SYMDEF_SORTED) or mem.eql(u8, name, SYMDEF64_SORTED)) continue; - const object = Object{ + const object: Object = .{ .offset = pos, .in_archive = .{ - .path = try gpa.dupe(u8, path), + .path = .{ + .root_dir = path.root_dir, + .sub_path = try gpa.dupe(u8, path.sub_path), + }, .size = hdr_size, }, - .path = try gpa.dupe(u8, name), + .path = Path.initCwd(try gpa.dupe(u8, name)), .file_handle = handle_index, .index = undefined, .alive = false, .mtime = hdr.date() catch 0, }; - log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); + log.debug("extracting object '{}' from archive '{}'", .{ object.path, path }); try self.objects.append(gpa, object); } @@ -301,8 +304,9 @@ const log = std.log.scoped(.link); const macho = std.macho; const mem = std.mem; const std = @import("std"); - const Allocator = mem.Allocator; +const Path = std.Build.Cache.Path; + const Archive = @This(); const File = @import("file.zig").File; const MachO = @import("../MachO.zig"); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 9852cfb234..01dd25fa8c 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -1,6 +1,6 @@ /// Non-zero for fat dylibs offset: u64, -path: []const u8, +path: Path, index: File.Index, file_handle: File.HandleIndex, tag: enum { dylib, tbd }, @@ -28,7 +28,7 @@ referenced: bool = false, output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn deinit(self: *Dylib, allocator: Allocator) void { - allocator.free(self.path); + allocator.free(self.path.sub_path); self.exports.deinit(allocator); self.strtab.deinit(allocator); if (self.id) |*id| id.deinit(allocator); @@ -61,7 +61,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void { const file = macho_file.getFileHandle(self.file_handle); const offset = self.offset; - log.debug("parsing dylib from binary: {s}", .{self.path}); + log.debug("parsing dylib from binary: {}", .{@as(Path, self.path)}); var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; { @@ -267,7 +267,7 @@ fn parseTbd(self: *Dylib, macho_file: *MachO) !void { const gpa = macho_file.base.comp.gpa; - log.debug("parsing dylib from stub: {s}", .{self.path}); + log.debug("parsing dylib from stub: {}", .{self.path}); const file = macho_file.getFileHandle(self.file_handle); var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| { @@ -959,8 +959,9 @@ const mem = std.mem; const tapi = @import("../tapi.zig"); const trace = @import("../../tracy.zig").trace; const std = @import("std"); - const Allocator = mem.Allocator; +const Path = std.Build.Cache.Path; + const Dylib = @This(); const File = @import("file.zig").File; const LibStub = tapi.LibStub; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 81f28de65a..9181112f2a 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1,6 +1,8 @@ /// Non-zero for fat object files or archives offset: u64, -path: []const u8, +/// Archive files cannot contain subdirectories, so only the basename is needed +/// for output. However, the full path is kept for error reporting. +path: Path, file_handle: File.HandleIndex, mtime: u64, index: File.Index, @@ -39,8 +41,8 @@ output_symtab_ctx: MachO.SymtabCtx = .{}, output_ar_state: Archive.ArState = .{}, pub fn deinit(self: *Object, allocator: Allocator) void { - if (self.in_archive) |*ar| allocator.free(ar.path); - allocator.free(self.path); + if (self.in_archive) |*ar| allocator.free(ar.path.sub_path); + allocator.free(self.path.sub_path); for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| { relocs.deinit(allocator); sub.deinit(allocator); @@ -1723,7 +1725,8 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void { pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void { // Header const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; - try Archive.writeHeader(self.path, size, ar_format, writer); + const basename = std.fs.path.basename(self.path.sub_path); + try Archive.writeHeader(basename, size, ar_format, writer); // Data const file = macho_file.getFileHandle(self.file_handle); // TODO try using copyRangeAll @@ -1774,6 +1777,11 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { self.calcStabsSize(macho_file); } +fn pathLen(path: Path) usize { + // +1 for the path separator + return (if (path.root_dir.path) |p| p.len + @intFromBool(path.sub_path.len != 0) else 0) + path.sub_path.len; +} + pub fn calcStabsSize(self: *Object, macho_file: *MachO) void { if (self.compile_unit) |cu| { const comp_dir = cu.getCompDir(self.*); @@ -1784,9 +1792,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) void { self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name if (self.in_archive) |ar| { - self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1)); + self.output_symtab_ctx.strsize += @intCast(pathLen(ar.path) + 1 + self.path.basename().len + 1 + 1); } else { - self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1)); + self.output_symtab_ctx.strsize += @intCast(pathLen(self.path) + 1); } for (self.symbols.items, 0..) |sym, i| { @@ -2118,19 +2126,36 @@ pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) v }; index += 1; if (self.in_archive) |ar| { - @memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path); - n_strx += @intCast(ar.path.len); + if (ar.path.root_dir.path) |p| { + @memcpy(ctx.strtab.items[n_strx..][0..p.len], p); + n_strx += @intCast(p.len); + if (ar.path.sub_path.len != 0) { + ctx.strtab.items[n_strx] = '/'; + n_strx += 1; + } + } + @memcpy(ctx.strtab.items[n_strx..][0..ar.path.sub_path.len], ar.path.sub_path); + n_strx += @intCast(ar.path.sub_path.len); ctx.strtab.items[n_strx] = '('; n_strx += 1; - @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); - n_strx += @intCast(self.path.len); + const basename = self.path.basename(); + @memcpy(ctx.strtab.items[n_strx..][0..basename.len], basename); + n_strx += @intCast(basename.len); ctx.strtab.items[n_strx] = ')'; n_strx += 1; ctx.strtab.items[n_strx] = 0; n_strx += 1; } else { - @memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); - n_strx += @intCast(self.path.len); + if (self.path.root_dir.path) |p| { + @memcpy(ctx.strtab.items[n_strx..][0..p.len], p); + n_strx += @intCast(p.len); + if (self.path.sub_path.len != 0) { + ctx.strtab.items[n_strx] = '/'; + n_strx += 1; + } + } + @memcpy(ctx.strtab.items[n_strx..][0..self.path.sub_path.len], self.path.sub_path); + n_strx += @intCast(self.path.sub_path.len); ctx.strtab.items[n_strx] = 0; n_strx += 1; } @@ -2666,11 +2691,12 @@ fn formatPath( _ = unused_fmt_string; _ = options; if (object.in_archive) |ar| { - try writer.writeAll(ar.path); - try writer.writeByte('('); - try writer.writeAll(object.path); - try writer.writeByte(')'); - } else try writer.writeAll(object.path); + try writer.print("{}({s})", .{ + @as(Path, ar.path), object.path.basename(), + }); + } else { + try writer.print("{}", .{@as(Path, object.path)}); + } } const Section = struct { @@ -2777,7 +2803,7 @@ const CompileUnit = struct { }; const InArchive = struct { - path: []const u8, + path: Path, size: u32, }; @@ -3170,6 +3196,7 @@ const math = std.math; const mem = std.mem; const trace = @import("../../tracy.zig").trace; const std = @import("std"); +const Path = std.Build.Cache.Path; const Allocator = mem.Allocator; const Archive = @import("Archive.zig"); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index a2d578845c..2a89e5da56 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -1,6 +1,6 @@ data: std.ArrayListUnmanaged(u8) = .empty, /// Externally owned memory. -path: []const u8, +basename: []const u8, index: File.Index, symtab: std.MultiArrayList(Nlist) = .{}, @@ -317,7 +317,7 @@ pub fn updateArSize(self: *ZigObject) void { pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void { // Header const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; - try Archive.writeHeader(self.path, size, ar_format, writer); + try Archive.writeHeader(self.basename, size, ar_format, writer); // Data try writer.writeAll(self.data.items); } diff --git a/src/link/MachO/file.zig b/src/link/MachO/file.zig index 26b5957058..554a8b1966 100644 --- a/src/link/MachO/file.zig +++ b/src/link/MachO/file.zig @@ -23,10 +23,10 @@ pub const File = union(enum) { _ = unused_fmt_string; _ = options; switch (file) { - .zig_object => |x| try writer.writeAll(x.path), + .zig_object => |zo| try writer.writeAll(zo.basename), .internal => try writer.writeAll("internal"), .object => |x| try writer.print("{}", .{x.fmtPath()}), - .dylib => |x| try writer.writeAll(x.path), + .dylib => |dl| try writer.print("{}", .{@as(Path, dl.path)}), } } @@ -373,13 +373,14 @@ pub const File = union(enum) { pub const HandleIndex = Index; }; +const std = @import("std"); const assert = std.debug.assert; const log = std.log.scoped(.link); const macho = std.macho; -const std = @import("std"); -const trace = @import("../../tracy.zig").trace; - const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; + +const trace = @import("../../tracy.zig").trace; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); const InternalObject = @import("InternalObject.zig"); diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index b90b090864..08ab11e3f9 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -72,7 +72,8 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 } if (comp.config.any_sanitize_thread) { - const path = comp.tsan_lib.?.full_object_path; + const path = try comp.tsan_lib.?.full_object_path.toString(gpa); + defer gpa.free(path); const rpath = std.fs.path.dirname(path) orelse "."; sizeofcmds += calcInstallNameLen( @sizeOf(macho.rpath_command), diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index e231360928..91bb295ed6 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -1,6 +1,7 @@ -pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = macho_file.base.comp.gpa; + // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); try positionals.ensureUnusedCapacity(comp.objects.len); @@ -19,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c // TODO: in the future, when we implement `dsymutil` alternative directly in the Zig // compiler, investigate if we can get rid of this `if` prong here. const path = positionals.items[0].path; - const in_file = try std.fs.cwd().openFile(path, .{}); + const in_file = try path.root_dir.handle.openFile(path.sub_path, .{}); const stat = try in_file.stat(); const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size); if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error @@ -72,7 +73,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c try writeHeader(macho_file, ncmds, sizeofcmds); } -pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { +pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void { const gpa = comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); @@ -173,21 +174,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ? for (files.items) |index| { const file = macho_file.getFile(index).?; - const state = switch (file) { - .zig_object => |x| &x.output_ar_state, - .object => |x| &x.output_ar_state, + switch (file) { + .zig_object => |zo| { + const state = &zo.output_ar_state; + pos = mem.alignForward(usize, pos, 2); + state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width); + pos += math.cast(usize, state.size) orelse return error.Overflow; + }, + .object => |o| { + const state = &o.output_ar_state; + pos = mem.alignForward(usize, pos, 2); + state.file_off = pos; + pos += @sizeOf(Archive.ar_hdr); + pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width); + pos += math.cast(usize, state.size) orelse return error.Overflow; + }, else => unreachable, - }; - const path = switch (file) { - .zig_object => |x| x.path, - .object => |x| x.path, - else => unreachable, - }; - pos = mem.alignForward(usize, pos, 2); - state.file_off = pos; - pos += @sizeOf(Archive.ar_hdr); - pos += mem.alignForward(usize, path.len + 1, ptr_width); - pos += math.cast(usize, state.size) orelse return error.Overflow; + } } break :blk pos; @@ -777,6 +782,7 @@ const mem = std.mem; const state_log = std.log.scoped(.link_state); const std = @import("std"); const trace = @import("../../tracy.zig").trace; +const Path = std.Build.Cache.Path; const Archive = @import("Archive.zig"); const Atom = @import("Atom.zig"); diff --git a/src/link/StringTable.zig b/src/link/StringTable.zig index b03e025ff0..f8a917b249 100644 --- a/src/link/StringTable.zig +++ b/src/link/StringTable.zig @@ -15,7 +15,7 @@ pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 { if (gop.found_existing) return gop.key_ptr.*; try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); - const new_off = @as(u32, @intCast(self.buffer.items.len)); + const new_off: u32 = @intCast(self.buffer.items.len); self.buffer.appendSliceAssumeCapacity(string); self.buffer.appendAssumeCapacity(0); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index fed44b7331..6a27909b31 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2507,6 +2507,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } else null; // Positional arguments to the linker such as object files and static archives. + // TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list. var positionals = std.ArrayList([]const u8).init(arena); try positionals.ensureUnusedCapacity(comp.objects.len); @@ -2527,23 +2528,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no (output_mode == .Lib and link_mode == .dynamic); if (is_exe_or_dyn_lib) { for (comp.wasi_emulated_libs) |crt_file| { - try positionals.append(try comp.get_libc_crt_file( + try positionals.append(try comp.crtFileAsString( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } if (link_libc) { - try positionals.append(try comp.get_libc_crt_file( + try positionals.append(try comp.crtFileAsString( arena, wasi_libc.execModelCrtFileFullName(wasi_exec_model), )); - try positionals.append(try comp.get_libc_crt_file(arena, "libc.a")); + try positionals.append(try comp.crtFileAsString(arena, "libc.a")); } if (link_libcpp) { - try positionals.append(comp.libcxx_static_lib.?.full_object_path); - try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); + try positionals.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try positionals.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); } } } @@ -2553,15 +2554,15 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no } for (comp.objects) |object| { - try positionals.append(object.path); + try positionals.append(try object.path.toString(arena)); } for (comp.c_object_table.keys()) |c_object| { - try positionals.append(c_object.status.success.object_path); + try positionals.append(try c_object.status.success.object_path.toString(arena)); } - if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path); - if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path); + if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena)); + if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena)); try wasm.parseInputFiles(positionals.items); @@ -3365,7 +3366,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: defer sub_prog_node.end(); const is_obj = comp.config.output_mode == .Obj; - const compiler_rt_path: ?[]const u8 = blk: { + const compiler_rt_path: ?Path = blk: { if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; break :blk null; @@ -3387,14 +3388,14 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: comptime assert(Compilation.link_hash_implementation_version == 14); for (comp.objects) |obj| { - _ = try man.addFile(obj.path, null); + _ = try man.addFilePath(obj.path, null); man.hash.add(obj.must_link); } for (comp.c_object_table.keys()) |key| { - _ = try man.addFile(key.status.success.object_path, null); + _ = try man.addFilePath(key.status.success.object_path, null); } try man.addOptionalFile(module_obj_path); - try man.addOptionalFile(compiler_rt_path); + try man.addOptionalFilePath(compiler_rt_path); man.hash.addOptionalBytes(wasm.entry_name); man.hash.add(wasm.base.stack_size); man.hash.add(wasm.base.build_id); @@ -3450,17 +3451,19 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: break :blk comp.c_object_table.keys()[0].status.success.object_path; if (module_obj_path) |p| - break :blk p; + break :blk Path.initCwd(p); // TODO I think this is unreachable. Audit this situation when solving the above TODO // regarding eliding redundant object -> object transformations. return error.NoObjectsToLink; }; - // This can happen when using --enable-cache and using the stage1 backend. In this case - // we can skip the file copy. - if (!mem.eql(u8, the_object_path, full_out_path)) { - try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); - } + try std.fs.Dir.copyFile( + the_object_path.root_dir.handle, + the_object_path.sub_path, + directory.handle, + wasm.base.emit.sub_path, + .{}, + ); } else { // Create an LLD command line and invoke it. var argv = std.ArrayList([]const u8).init(gpa); @@ -3581,23 +3584,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); if (is_exe_or_dyn_lib) { for (comp.wasi_emulated_libs) |crt_file| { - try argv.append(try comp.get_libc_crt_file( + try argv.append(try comp.crtFileAsString( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } if (comp.config.link_libc) { - try argv.append(try comp.get_libc_crt_file( + try argv.append(try comp.crtFileAsString( arena, wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), )); - try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); + try argv.append(try comp.crtFileAsString(arena, "libc.a")); } if (comp.config.link_libcpp) { - try argv.append(comp.libcxx_static_lib.?.full_object_path); - try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena)); + try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena)); } } } @@ -3612,7 +3615,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try argv.append("-no-whole-archive"); whole_archive = false; } - try argv.append(obj.path); + try argv.append(try obj.path.toString(arena)); } if (whole_archive) { try argv.append("-no-whole-archive"); @@ -3620,7 +3623,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: } for (comp.c_object_table.keys()) |key| { - try argv.append(key.status.success.object_path); + try argv.append(try key.status.success.object_path.toString(arena)); } if (module_obj_path) |p| { try argv.append(p); @@ -3630,11 +3633,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: !comp.skip_linker_dependencies and !comp.config.link_libc) { - try argv.append(comp.libc_static_lib.?.full_object_path); + try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena)); } if (compiler_rt_path) |p| { - try argv.append(p); + try argv.append(try p.toString(arena)); } if (comp.verbose_link) { diff --git a/src/main.zig b/src/main.zig index 940e88e2cf..4e29611d9c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,6 +13,12 @@ const warn = std.log.warn; const ThreadPool = std.Thread.Pool; const cleanExit = std.process.cleanExit; const native_os = builtin.os.tag; +const Cache = std.Build.Cache; +const Path = std.Build.Cache.Path; +const EnvVar = std.zig.EnvVar; +const LibCInstallation = std.zig.LibCInstallation; +const AstGen = std.zig.AstGen; +const Server = std.zig.Server; const tracy = @import("tracy.zig"); const Compilation = @import("Compilation.zig"); @@ -20,16 +26,11 @@ const link = @import("link.zig"); const Package = @import("Package.zig"); const build_options = @import("build_options"); const introspect = @import("introspect.zig"); -const EnvVar = std.zig.EnvVar; -const LibCInstallation = std.zig.LibCInstallation; const wasi_libc = @import("wasi_libc.zig"); -const Cache = std.Build.Cache; const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); const Zcu = @import("Zcu.zig"); -const AstGen = std.zig.AstGen; const mingw = @import("mingw.zig"); -const Server = std.zig.Server; const dev = @import("dev.zig"); pub const std_options = .{ @@ -1724,14 +1725,14 @@ fn buildOutputType( } } else switch (file_ext orelse Compilation.classifyFileExt(arg)) { .shared_library => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); create_module.opts.any_dyn_libs = true; }, .object, .static_library => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); }, .res => { - try create_module.link_objects.append(arena, .{ .path = arg }); + try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) }); contains_res_file = true; }, .manifest => { @@ -1845,20 +1846,20 @@ fn buildOutputType( }, .shared_library => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); create_module.opts.any_dyn_libs = true; }, .unknown, .object, .static_library => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); }, .res => { try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, }); contains_res_file = true; @@ -1894,7 +1895,7 @@ fn buildOutputType( // binary: no extra rpaths and DSO filename exactly // as provided. Hello, Go. try create_module.link_objects.append(arena, .{ - .path = it.only_arg, + .path = Path.initCwd(it.only_arg), .must_link = must_link, .loption = true, }); @@ -2532,7 +2533,7 @@ fn buildOutputType( install_name = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-force_load")) { try create_module.link_objects.append(arena, .{ - .path = linker_args_it.nextOrFatal(), + .path = Path.initCwd(linker_args_it.nextOrFatal()), .must_link = true, }); } else if (mem.eql(u8, arg, "-hash-style") or @@ -2707,7 +2708,7 @@ fn buildOutputType( break :b create_module.c_source_files.items[0].src_path; if (create_module.link_objects.items.len >= 1) - break :b create_module.link_objects.items[0].path; + break :b create_module.link_objects.items[0].path.sub_path; if (emit_bin == .yes) break :b emit_bin.yes; @@ -2963,7 +2964,7 @@ fn buildOutputType( framework_dir_path, framework_name, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); try resolved_frameworks.append(.{ .needed = info.needed, .weak = info.weak, @@ -3635,7 +3636,7 @@ const CreateModule = struct { name: []const u8, lib: Compilation.SystemLib, }), - wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CRTFile), + wasi_emulated_libs: std.ArrayListUnmanaged(wasi_libc.CrtFile), c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile), rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), @@ -3808,7 +3809,7 @@ fn createModule( } if (target.os.tag == .wasi) { - if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { + if (wasi_libc.getEmulatedLibCrtFile(lib_name)) |crt_file| { try create_module.wasi_emulated_libs.append(arena, crt_file); continue; } @@ -3929,7 +3930,7 @@ fn createModule( target, info.preferred_mode, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.preferred_mode) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -3963,7 +3964,7 @@ fn createModule( target, info.fallbackMode(), )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.fallbackMode()) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -3997,7 +3998,7 @@ fn createModule( target, info.preferred_mode, )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.preferred_mode) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -4021,7 +4022,7 @@ fn createModule( target, info.fallbackMode(), )) { - const path = try arena.dupe(u8, test_path.items); + const path = Path.initCwd(try arena.dupe(u8, test_path.items)); switch (info.fallbackMode()) { .static => try create_module.link_objects.append(arena, .{ .path = path }), .dynamic => try create_module.resolved_system_libs.append(arena, .{ @@ -6163,7 +6164,7 @@ fn cmdAstCheck( } file.mod = try Package.Module.createLimited(arena, .{ - .root = Cache.Path.cwd(), + .root = Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", }); @@ -6523,7 +6524,7 @@ fn cmdChangelist( }; file.mod = try Package.Module.createLimited(arena, .{ - .root = Cache.Path.cwd(), + .root = Path.cwd(), .root_src_path = file.sub_file_path, .fully_qualified_name = "root", }); diff --git a/src/mingw.zig b/src/mingw.zig index ab5f4f26db..d26358450c 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -11,13 +11,13 @@ const build_options = @import("build_options"); const Cache = std.Build.Cache; const dev = @import("dev.zig"); -pub const CRTFile = enum { +pub const CrtFile = enum { crt2_o, dllcrt2_o, mingw32_lib, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } @@ -160,7 +160,9 @@ fn add_cc_args( pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { dev.check(.build_import_lib); - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + const gpa = comp.gpa; + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -178,7 +180,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { // Use the global cache directory. var cache: Cache = .{ - .gpa = comp.gpa, + .gpa = gpa, .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), }; cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); @@ -195,17 +197,18 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { _ = try man.addFile(def_file_path, null); - const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); - errdefer comp.gpa.free(final_lib_basename); + const final_lib_basename = try std.fmt.allocPrint(gpa, "{s}.lib", .{lib_name}); + errdefer gpa.free(final_lib_basename); if (try man.hit()) { const digest = man.final(); - try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); + try comp.crt_files.ensureUnusedCapacity(gpa, 1); comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ - .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, final_lib_basename, - }), + .full_object_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }), + }, .lock = man.toOwnedLock(), }); return; @@ -230,7 +233,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }; const aro = @import("aro"); - var aro_comp = aro.Compilation.init(comp.gpa, std.fs.cwd()); + var aro_comp = aro.Compilation.init(gpa, std.fs.cwd()); defer aro_comp.deinit(); const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }); @@ -244,7 +247,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { nosuspend stderr.print("output path: {s}\n", .{def_final_path}) catch break :print; } - try aro_comp.include_dirs.append(comp.gpa, include_dir); + try aro_comp.include_dirs.append(gpa, include_dir); const builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines); const user_macros = try aro_comp.addSourceFromBuffer("", target_defines); @@ -271,17 +274,15 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { try pp.prettyPrintTokens(def_final_file.writer(), .result_only); } - const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, final_lib_basename, - }); - errdefer comp.gpa.free(lib_final_path); + const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }); + errdefer gpa.free(lib_final_path); if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; const llvm_bindings = @import("codegen/llvm/bindings.zig"); const llvm = @import("codegen/llvm.zig"); const arch_tag = llvm.targetArch(target.cpu.arch); const def_final_path_z = try arena.dupeZ(u8, def_final_path); - const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); + const lib_final_path_z = try comp.global_cache_directory.joinZ(arena, &.{lib_final_path}); if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) { // TODO surface a proper error here log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); @@ -292,8 +293,11 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) }); }; - try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{ - .full_object_path = lib_final_path, + try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{ + .full_object_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = lib_final_path, + }, .lock = man.toOwnedLock(), }); } diff --git a/src/musl.zig b/src/musl.zig index 5ddbcb6652..48717dc5a1 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -9,7 +9,7 @@ const archName = std.zig.target.muslArchName; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); -pub const CRTFile = enum { +pub const CrtFile = enum { crti_o, crtn_o, crt1_o, @@ -19,7 +19,7 @@ pub const CRTFile = enum { libc_so, }; -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 57d93b6f56..ce94110b69 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); -pub const CRTFile = enum { +pub const CrtFile = enum { crt1_reactor_o, crt1_command_o, libc_a, @@ -16,7 +16,7 @@ pub const CRTFile = enum { libwasi_emulated_signal_a, }; -pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile { +pub fn getEmulatedLibCrtFile(lib_name: []const u8) ?CrtFile { if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) { return .libwasi_emulated_process_clocks_a; } @@ -32,7 +32,7 @@ pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile { return null; } -pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 { +pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 { return switch (crt_file) { .libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a", .libwasi_emulated_getpid_a => "libwasi-emulated-getpid.a", @@ -42,10 +42,10 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 { }; } -pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile { +pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile { return switch (wasi_exec_model) { - .reactor => CRTFile.crt1_reactor_o, - .command => CRTFile.crt1_command_o, + .reactor => CrtFile.crt1_reactor_o, + .command => CrtFile.crt1_command_o, }; } @@ -57,7 +57,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co }; } -pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void { +pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; }