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.
This commit is contained in:
Andrew Kelley 2024-10-10 00:41:58 -07:00
parent 58349b2c8e
commit 14c8e270bb
29 changed files with 937 additions and 679 deletions

View file

@ -398,12 +398,19 @@ pub const Manifest = struct {
return gop.index; return gop.index;
} }
/// Deprecated, use `addOptionalFilePath`.
pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void {
self.hash.add(optional_file_path != null); self.hash.add(optional_file_path != null);
const file_path = optional_file_path orelse return; const file_path = optional_file_path orelse return;
_ = try self.addFile(file_path, null); _ = 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 { pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void {
self.hash.add(list_of_files.len); self.hash.add(list_of_files.len);
for (list_of_files) |file_path| { for (list_of_files) |file_path| {

View file

@ -11,7 +11,11 @@ pub fn clone(p: Path, arena: Allocator) Allocator.Error!Path {
} }
pub fn cwd() 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 { 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); 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( pub fn format(
self: Path, self: Path,
comptime fmt_string: []const u8, 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; 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`. /// Useful to make `Path` a key in `std.ArrayHashMap`.
pub const TableAdapter = struct { pub const TableAdapter = struct {
pub const Hash = std.hash.Wyhash; pub const Hash = std.hash.Wyhash;

View file

@ -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 LibCInstallation = @This();
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const Target = std.Target; const Target = std.Target;
const fs = std.fs; const fs = std.fs;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path;
const is_darwin = builtin.target.isDarwin(); const is_darwin = builtin.target.isDarwin();
const is_windows = builtin.target.os.tag == .windows; const is_windows = builtin.target.os.tag == .windows;

View file

@ -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 /// Populated when we build the libc++ static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush(). /// 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 /// 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(). /// 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 /// Populated when we build the libunwind static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush(). /// 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 /// Populated when we build the TSAN library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush(). /// 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 /// Populated when we build the libc static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush(). /// 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 /// 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(). /// 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 /// 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(). /// 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 /// Populated when we build the libfuzzer static library. A Job to build this
/// is indicated by setting `job_queued_fuzzer_lib` and resolved before /// is indicated by setting `job_queued_fuzzer_lib` and resolved before
/// calling linker.flush(). /// calling linker.flush().
fuzzer_lib: ?CRTFile = null, fuzzer_lib: ?CrtFile = null,
glibc_so_files: ?glibc.BuiltSharedObjects = 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, /// 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 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. /// 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. /// How many lines of reference trace should be included per compile error.
/// Null means only show snippet on first 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 default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
pub const SemaError = Zcu.SemaError; pub const SemaError = Zcu.SemaError;
pub const CRTFile = struct { pub const CrtFile = struct {
lock: Cache.Lock, lock: Cache.Lock,
full_object_path: []const u8, full_object_path: Path,
pub fn isObject(cf: CRTFile) bool { pub fn isObject(cf: CrtFile) bool {
return switch (classifyFileExt(cf.full_object_path)) { return switch (classifyFileExt(cf.full_object_path.sub_path)) {
.object => true, .object => true,
else => false, else => false,
}; };
} }
pub fn deinit(self: *CRTFile, gpa: Allocator) void { pub fn deinit(self: *CrtFile, gpa: Allocator) void {
self.lock.release(); self.lock.release();
gpa.free(self.full_object_path); gpa.free(self.full_object_path.sub_path);
self.* = undefined; self.* = undefined;
} }
}; };
@ -369,13 +369,13 @@ const Job = union(enum) {
resolve_type_fully: InternPool.Index, resolve_type_fully: InternPool.Index,
/// one of the glibc static objects /// one of the glibc static objects
glibc_crt_file: glibc.CRTFile, glibc_crt_file: glibc.CrtFile,
/// all of the glibc shared objects /// all of the glibc shared objects
glibc_shared_objects, glibc_shared_objects,
/// one of the musl static objects /// one of the musl static objects
musl_crt_file: musl.CRTFile, musl_crt_file: musl.CrtFile,
/// one of the mingw-w64 static objects /// 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.a, usually needed when linking libc
libunwind: void, libunwind: void,
libcxx: void, libcxx: void,
@ -385,7 +385,7 @@ const Job = union(enum) {
/// calls to, for example, memcpy and memset. /// calls to, for example, memcpy and memset.
zig_libc: void, zig_libc: void,
/// one of WASI libc static objects /// 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`. /// The value is the index into `system_libs`.
windows_import_lib: usize, windows_import_lib: usize,
@ -422,8 +422,8 @@ pub const CObject = struct {
status: union(enum) { status: union(enum) {
new, new,
success: struct { success: struct {
/// The outputted result. Owned by gpa. /// The outputted result. `sub_path` owned by gpa.
object_path: []u8, object_path: Path,
/// This is a file system lock on the cache hash manifest representing this /// This is a file system lock on the cache hash manifest representing this
/// object. It prevents other invocations of the Zig compiler from interfering /// object. It prevents other invocations of the Zig compiler from interfering
/// with this object until released. /// with this object until released.
@ -719,7 +719,7 @@ pub const CObject = struct {
return true; return true;
}, },
.success => |*success| { .success => |*success| {
gpa.free(success.object_path); gpa.free(success.object_path.sub_path);
success.lock.release(); success.lock.release();
self.status = .new; self.status = .new;
return false; return false;
@ -1018,7 +1018,7 @@ const CacheUse = union(CacheMode) {
}; };
pub const LinkObject = struct { pub const LinkObject = struct {
path: []const u8, path: Path,
must_link: bool = false, must_link: bool = false,
// When the library is passed via a positional argument, it will be // When the library is passed via a positional argument, it will be
// added as a full path. If it's `-l<lib>`, then just the basename. // added as a full path. If it's `-l<lib>`, then just the basename.
@ -1027,7 +1027,7 @@ pub const LinkObject = struct {
loption: bool = false, loption: bool = false,
pub fn isObject(lo: LinkObject) bool { pub fn isObject(lo: LinkObject) bool {
return switch (classifyFileExt(lo.path)) { return switch (classifyFileExt(lo.path.sub_path)) {
.object => true, .object => true,
else => false, else => false,
}; };
@ -1095,7 +1095,7 @@ pub const CreateOptions = struct {
/// * getpid /// * getpid
/// * mman /// * mman
/// * signal /// * 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 /// This means that if the output mode is an executable it will be a
/// Position Independent Executable. If the output mode is not an /// Position Independent Executable. If the output mode is not an
/// executable this field is ignored. /// executable this field is ignored.
@ -2578,7 +2578,7 @@ fn addNonIncrementalStuffToCacheManifest(
} }
for (comp.objects) |obj| { 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.must_link);
man.hash.add(obj.loption); man.hash.add(obj.loption);
} }
@ -2703,9 +2703,8 @@ fn emitOthers(comp: *Compilation) void {
return; return;
} }
const obj_path = comp.c_object_table.keys()[0].status.success.object_path; 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.sub_path);
const ext = std.fs.path.extension(obj_path); const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len];
const basename = obj_path[0 .. obj_path.len - ext.len];
// This obj path always ends with the object file extension, but if we change the // 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. // extension to .ll, .bc, or .s, then it will be the path to those things.
const outs = [_]struct { const outs = [_]struct {
@ -2720,13 +2719,13 @@ fn emitOthers(comp: *Compilation) void {
if (out.emit) |loc| { if (out.emit) |loc| {
if (loc.directory) |directory| { if (loc.directory) |directory| {
const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{ const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{
basename, out.ext, dirname, out.ext,
}) catch |err| { }) 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; continue;
}; };
defer comp.gpa.free(src_path); 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) }); 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"); const named_frame = tracy.namedFrame("glibc_crt_file");
defer named_frame.end(); 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. // TODO Surface more error details.
comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
@errorName(err), @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"); const named_frame = tracy.namedFrame("musl_crt_file");
defer named_frame.end(); 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. // TODO Surface more error details.
comp.lockAndSetMiscFailure( comp.lockAndSetMiscFailure(
.musl_crt_file, .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"); const named_frame = tracy.namedFrame("mingw_crt_file");
defer named_frame.end(); 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. // TODO Surface more error details.
comp.lockAndSetMiscFailure( comp.lockAndSetMiscFailure(
.mingw_crt_file, .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"); const named_frame = tracy.namedFrame("wasi_libc_crt_file");
defer named_frame.end(); 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. // TODO Surface more error details.
comp.lockAndSetMiscFailure( comp.lockAndSetMiscFailure(
.wasi_libc_crt_file, .wasi_libc_crt_file,
@ -4602,7 +4601,7 @@ fn buildRt(
root_source_name: []const u8, root_source_name: []const u8,
misc_task: MiscTask, misc_task: MiscTask,
output_mode: std.builtin.OutputMode, output_mode: std.builtin.OutputMode,
out: *?CRTFile, out: *?CrtFile,
prog_node: std.Progress.Node, prog_node: std.Progress.Node,
) void { ) void {
comp.buildOutputFromZig( 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}); 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. // There was previous failure.
comp.mutex.lock(); comp.mutex.lock();
defer comp.mutex.unlock(); 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); 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(); defer arena_allocator.deinit();
const arena = arena_allocator.allocator(); 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 target = comp.getTarget();
const o_ext = target.ofmt.fileExt(target.cpu.arch); const o_ext = target.ofmt.fileExt(target.cpu.arch);
const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: { 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(); defer argv.deinit();
// In case we are doing passthrough mode, we need to detect -S and -emit-llvm. // 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) { switch (term) {
.Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| { .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 }); log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
return comp.failCObj(c_object, "clang exited with code {d}", .{code}); 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 = .{ c_object.status = .{
.success = .{ .success = .{
.object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ .object_path = .{
"o", &digest, o_basename, .root_dir = comp.local_cache_directory,
}), .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, o_basename }),
},
.lock = man.toOwnedLock(), .lock = man.toOwnedLock(),
}, },
}; };
@ -6092,18 +6094,23 @@ test "classifyFileExt" {
try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
} }
pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 { pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
if (comp.wantBuildGLibCFromSource() or return (try crtFilePath(comp, basename)) orelse {
comp.wantBuildMuslFromSource() or const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
comp.wantBuildMinGWFromSource() or const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
comp.wantBuildWasiLibcFromSource()) const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
{ return Path.initCwd(full_path);
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; pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); const path = try get_libc_crt_file(comp, arena, basename);
return full_path; 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 { fn wantBuildLibCFromSource(comp: Compilation) bool {
@ -6314,7 +6321,7 @@ fn buildOutputFromZig(
comp: *Compilation, comp: *Compilation,
src_basename: []const u8, src_basename: []const u8,
output_mode: std.builtin.OutputMode, output_mode: std.builtin.OutputMode,
out: *?CRTFile, out: *?CrtFile,
misc_task_tag: MiscTask, misc_task_tag: MiscTask,
prog_node: std.Progress.Node, prog_node: std.Progress.Node,
) !void { ) !void {
@ -6542,15 +6549,39 @@ pub fn build_crt_file(
comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); 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 .{ return .{
.full_object_path = try comp.local_cache_directory.join(comp.gpa, &.{ .full_object_path = .{
comp.cache_use.whole.bin_sub_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(), .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 { pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// Avoid deadlocking on building import libs such as kernel32.lib // Avoid deadlocking on building import libs such as kernel32.lib
// This can happen when the user uses `build-exe foo.obj -lkernel32` and // This can happen when the user uses `build-exe foo.obj -lkernel32` and

View file

@ -169,14 +169,14 @@ fn useElfInitFini(target: std.Target) bool {
}; };
} }
pub const CRTFile = enum { pub const CrtFile = enum {
crti_o, crti_o,
crtn_o, crtn_o,
scrt1_o, scrt1_o,
libc_nonshared_a, 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) { if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions; return error.ZigCompilerNotBuiltWithLLVMExtensions;
} }
@ -292,7 +292,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre
.owner = undefined, .owner = undefined,
}; };
var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o }; 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 => { .libc_nonshared_a => {
const s = path.sep_str; const s = path.sep_str;

View file

@ -11,7 +11,7 @@ const wasi_libc = @import("wasi_libc.zig");
const Air = @import("Air.zig"); const Air = @import("Air.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache; const Cache = std.Build.Cache;
const Path = Cache.Path; const Path = std.Build.Cache.Path;
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
const LibCInstallation = std.zig.LibCInstallation; const LibCInstallation = std.zig.LibCInstallation;
const Liveness = @import("Liveness.zig"); const Liveness = @import("Liveness.zig");
@ -34,7 +34,7 @@ pub const SystemLib = struct {
/// 1. Windows DLLs that zig ships such as advapi32. /// 1. Windows DLLs that zig ships such as advapi32.
/// 2. extern "foo" fn declarations where we find out about libraries too late /// 2. extern "foo" fn declarations where we find out about libraries too late
/// TODO: make this non-optional and resolve those two cases somehow. /// TODO: make this non-optional and resolve those two cases somehow.
path: ?[]const u8, path: ?Path,
}; };
pub fn hashAddSystemLibs( pub fn hashAddSystemLibs(
@ -46,7 +46,7 @@ pub fn hashAddSystemLibs(
for (hm.values()) |value| { for (hm.values()) |value| {
man.hash.add(value.needed); man.hash.add(value.needed);
man.hash.add(value.weak); 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, LLDCrashed,
LLDReportedFailure, LLDReportedFailure,
LLD_LinkingIsTODO_ForSpirV, LLD_LinkingIsTODO_ForSpirV,
LibCInstallationMissingCRTDir, LibCInstallationMissingCrtDir,
LibCInstallationNotAvailable, LibCInstallationNotAvailable,
LinkingWithoutZigSourceUnimplemented, LinkingWithoutZigSourceUnimplemented,
MalformedArchive, MalformedArchive,
@ -606,18 +606,15 @@ pub const File = struct {
const comp = base.comp; const comp = base.comp;
if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
dev.check(.clang_command); dev.check(.clang_command);
const gpa = comp.gpa;
const emit = base.emit; const emit = base.emit;
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) // 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 // 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 // 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. // 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); assert(comp.c_object_table.count() == 1);
const the_key = comp.c_object_table.keys()[0]; const the_key = comp.c_object_table.keys()[0];
const cached_pp_file_path = the_key.status.success.object_path; 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; return;
} }
@ -781,7 +778,7 @@ pub const File = struct {
log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); 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 comp.compiler_rt_obj.?.full_object_path
else else
null; null;
@ -806,18 +803,18 @@ pub const File = struct {
base.releaseLock(); base.releaseLock();
for (objects) |obj| { 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.must_link);
man.hash.add(obj.loption); man.hash.add(obj.loption);
} }
for (comp.c_object_table.keys()) |key| { 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| { for (comp.win32_resource_table.keys()) |key| {
_ = try man.addFile(key.status.success.res_path, null); _ = try man.addFile(key.status.success.res_path, null);
} }
try man.addOptionalFile(zcu_obj_path); 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. // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
_ = try man.hit(); _ = try man.hit();
@ -851,10 +848,10 @@ pub const File = struct {
defer object_files.deinit(); defer object_files.deinit();
for (objects) |obj| { 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| { 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| { for (comp.win32_resource_table.keys()) |key| {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); 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)); object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
} }
if (compiler_rt_path) |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) { if (comp.verbose_link) {

View file

@ -7,6 +7,7 @@ const fs = std.fs;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const mem = std.mem; const mem = std.mem;
const Cache = std.Build.Cache; const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
const mingw = @import("../../mingw.zig"); const mingw = @import("../../mingw.zig");
const link = @import("../../link.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); comptime assert(Compilation.link_hash_implementation_version == 14);
for (comp.objects) |obj| { 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.must_link);
} }
for (comp.c_object_table.keys()) |key| { 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| { for (comp.win32_resource_table.keys()) |key| {
_ = try man.addFile(key.status.success.res_path, null); _ = 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; break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p| 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 // TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations. // regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink; return error.NoObjectsToLink;
}; };
// This can happen when using --enable-cache and using the stage1 backend. In this case try std.fs.Dir.copyFile(
// we can skip the file copy. the_object_path.root_dir.handle,
if (!mem.eql(u8, the_object_path, full_out_path)) { the_object_path.sub_path,
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); directory.handle,
} self.base.emit.sub_path,
.{},
);
} else { } else {
// Create an LLD command line and invoke it. // Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa); 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); try argv.ensureUnusedCapacity(comp.objects.len);
for (comp.objects) |obj| { for (comp.objects) |obj| {
if (obj.must_link) { if (obj.must_link) {
argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
} else { } else {
argv.appendAssumeCapacity(obj.path); argv.appendAssumeCapacity(try obj.path.toString(arena));
} }
} }
for (comp.c_object_table.keys()) |key| { 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| { 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) { 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) { if (target.cpu.arch == .x86) {
try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
} else { } else {
try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
} }
} else { } 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 { } else {
const lib_str = switch (comp.config.link_mode) { const lib_str = switch (comp.config.link_mode) {
.dynamic => "", .dynamic => "",
@ -456,36 +459,36 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
// libc++ dep // libc++ dep
if (comp.config.link_libcpp) { if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
} }
// libunwind dep // libunwind dep
if (comp.config.link_libunwind) { 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) { 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 (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
if (!comp.config.link_libc) { if (!comp.config.link_libc) {
if (comp.libc_static_lib) |lib| { 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 // 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. // 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_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
} }
try argv.ensureUnusedCapacity(comp.system_libs.count()); try argv.ensureUnusedCapacity(comp.system_libs.count());
for (comp.system_libs.keys()) |key| { for (comp.system_libs.keys()) |key| {
const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
if (comp.crt_files.get(lib_basename)) |crt_file| { 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; continue;
} }
if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| { if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {

View file

@ -383,9 +383,9 @@ pub fn createEmpty(
const index: File.Index = @intCast(try self.files.addOne(gpa)); const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .zig_object = .{ self.files.set(index, .{ .zig_object = .{
.index = index, .index = index,
.path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( .basename = try std.fmt.allocPrint(arena, "{s}.o", .{
zcu.main_mod.root_src_path, fs.path.stem(zcu.main_mod.root_src_path),
)}), }),
} }); } });
self.zig_object_index = index; self.zig_object_index = index;
try self.zigObjectPtr().?.init(self, .{ 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 target = self.getTarget();
const link_mode = comp.config.link_mode; const link_mode = comp.config.link_mode;
const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. 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 module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); .root_dir = directory,
if (fs.path.dirname(full_out_path)) |dirname| { .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
break :blk try fs.path.join(arena, &.{ dirname, path }); try fs.path.join(arena, &.{ dirname, path })
} else { else
break :blk path; path,
}
} else null; } else null;
// --verbose-link // --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.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
if (self.base.isObject()) return relocatable.flushObject(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 // csu prelude
if (csu.crt0) |path| try parseObjectReportingFailure(self, path); 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); 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| { 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 // libc++ dep
if (comp.config.link_libcpp) { if (comp.config.link_libcpp) {
try system_libs.ensureUnusedCapacity(2); try self.parseLibraryReportingFailure(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }, false);
system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); try self.parseLibraryReportingFailure(.{ .path = comp.libcxx_static_lib.?.full_object_path }, false);
system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
} }
// libunwind dep // libunwind dep
if (comp.config.link_libunwind) { 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 // 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.config.link_libc) {
if (comp.libc_installation) |lc| { if (comp.libc_installation) |lc| {
const flags = target_util.libcFullLinkFlags(target); const flags = target_util.libcFullLinkFlags(target);
try system_libs.ensureUnusedCapacity(flags.len);
var test_path = std.ArrayList(u8).init(arena); var test_path = std.ArrayList(u8).init(arena);
var checked_paths = std.ArrayList([]const 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; continue;
} }
const resolved_path = try arena.dupe(u8, test_path.items); const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items));
system_libs.appendAssumeCapacity(.{ .path = resolved_path }); try self.parseLibraryReportingFailure(.{ .path = resolved_path }, false);
} }
} else if (target.isGnuLibC()) { } else if (target.isGnuLibC()) {
try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
for (glibc.libs) |lib| { for (glibc.libs) |lib| {
if (lib.removed_in) |rem_in| { if (lib.removed_in) |rem_in| {
if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue; 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, 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"), .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
}); }, false);
} else if (target.isMusl()) { } else if (target.isMusl()) {
const path = try comp.get_libc_crt_file(arena, switch (link_mode) { const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
.static => "libc.a", .static => "libc.a",
.dynamic => "libc.so", .dynamic => "libc.so",
}); });
try system_libs.append(.{ .path = path }); try self.parseLibraryReportingFailure(.{ .path = path }, false);
} else { } else {
comp.link_error_flags.missing_libc = true; 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). // 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 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
@ -1066,10 +1058,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
} }
} else null; } else null;
const csu = try CsuObjects.init(arena, comp); const csu = try comp.getCrtPaths(arena);
const compiler_rt_path: ?[]const u8 = blk: { const compiler_rt_path: ?[]const u8 = blk: {
if (comp.compiler_rt_lib) |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 x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena);
break :blk null; break :blk null;
}; };
@ -1092,11 +1084,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
if (self.base.isRelocatable()) { if (self.base.isRelocatable()) {
for (comp.objects) |obj| { for (comp.objects) |obj| {
try argv.append(obj.path); try argv.append(try obj.path.toString(arena));
} }
for (comp.c_object_table.keys()) |key| { 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| { if (module_obj_path) |p| {
@ -1178,9 +1170,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
} }
// csu prelude // csu prelude
if (csu.crt0) |v| try argv.append(v); if (csu.crt0) |path| try argv.append(try path.toString(arena));
if (csu.crti) |v| try argv.append(v); if (csu.crti) |path| try argv.append(try path.toString(arena));
if (csu.crtbegin) |v| try argv.append(v); if (csu.crtbegin) |path| try argv.append(try path.toString(arena));
for (self.lib_dirs) |lib_dir| { for (self.lib_dirs) |lib_dir| {
try argv.append("-L"); try argv.append("-L");
@ -1205,10 +1197,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
} }
if (obj.loption) { if (obj.loption) {
assert(obj.path[0] == ':');
try argv.append("-l"); try argv.append("-l");
} }
try argv.append(obj.path); try argv.append(try obj.path.toString(arena));
} }
if (whole_archive) { if (whole_archive) {
try argv.append("-no-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| { 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| { if (module_obj_path) |p| {
@ -1224,17 +1215,17 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
} }
if (comp.config.any_sanitize_thread) { 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) { 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 // libc
if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
if (comp.libc_static_lib) |lib| { 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; as_needed = true;
}, },
} }
argv.appendAssumeCapacity(lib_info.path.?); argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
} }
if (!as_needed) { if (!as_needed) {
@ -1268,13 +1259,13 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
// libc++ dep // libc++ dep
if (comp.config.link_libcpp) { if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
} }
// libunwind dep // libunwind dep
if (comp.config.link_libunwind) { 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 // libc dep
@ -1295,9 +1286,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}); });
try argv.append(lib_path); 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()) { } 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", .static => "libc.a",
.dynamic => "libc.so", .dynamic => "libc.so",
})); }));
@ -1310,8 +1301,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
} }
// crt postlude // crt postlude
if (csu.crtend) |v| try argv.append(v); if (csu.crtend) |path| try argv.append(try path.toString(arena));
if (csu.crtn) |v| try argv.append(v); if (csu.crtn) |path| try argv.append(try path.toString(arena));
} }
Compilation.dump_argv(argv.items); Compilation.dump_argv(argv.items);
@ -1331,7 +1322,7 @@ pub const ParseError = error{
UnknownFileType, UnknownFileType,
} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError; } || 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()) { if (crt_file.isObject()) {
try parseObjectReportingFailure(self, crt_file.full_object_path); try parseObjectReportingFailure(self, crt_file.full_object_path);
} else { } 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) { self.parseObject(path) catch |err| switch (err) {
error.LinkFailure => return, // already reported error.LinkFailure => return, // already reported
error.OutOfMemory => return error.OutOfMemory, 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()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; 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 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 = .{ 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, .file_handle = fh,
.index = index, .index = index,
} }); } });
@ -1387,15 +1381,15 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
try object.parse(self); 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()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; 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 fh = try self.addFileHandle(handle);
var archive = Archive{}; var archive: Archive = .{};
defer archive.deinit(gpa); defer archive.deinit(gpa);
try archive.parse(self, path, fh); 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); defer gpa.free(objects);
for (objects) |extracted| { 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 }); self.files.set(index, .{ .object = extracted });
const object = &self.files.items(.data)[index].object; const object = &self.files.items(.data)[index].object;
object.index = index; object.index = index;
@ -1418,12 +1412,15 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; 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(); defer handle.close();
const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .shared_object = .{ 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, .index = index,
.needed = lib.needed, .needed = lib.needed,
.alive = lib.needed, .alive = lib.needed,
@ -1439,12 +1436,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
defer tracy.end(); defer tracy.end();
const gpa = self.base.comp.gpa; 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(); defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
defer gpa.free(data); defer gpa.free(data);
var script = LdScript{ .path = lib.path }; var script: LdScript = .{ .path = lib.path };
defer script.deinit(gpa); defer script.deinit(gpa);
try script.parse(data, self); 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 test_path = std.ArrayList(u8).init(arena);
var checked_paths = std.ArrayList([]const 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(); checked_paths.clearRetainingCapacity();
success: { success: {
if (mem.startsWith(u8, scr_obj.path, "-l")) { if (mem.startsWith(u8, script_arg.path, "-l")) {
const lib_name = scr_obj.path["-l".len..]; const lib_name = script_arg.path["-l".len..];
// TODO I think technically we should re-use the mechanism used by the frontend here. // 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? // Maybe we should hoist search-strategy all the way here?
@ -1474,33 +1471,30 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
} }
} else { } else {
var buffer: [fs.max_path_bytes]u8 = undefined; 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(); test_path.clearRetainingCapacity();
try test_path.writer().writeAll(path); try test_path.writer().writeAll(path);
break :success; break :success;
} else |_| {} } 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| { 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; break :success;
} }
} }
try self.reportMissingLibraryError( try self.reportMissingLibraryError(
checked_paths.items, checked_paths.items,
"missing library dependency: GNU ld script '{s}' requires '{s}', but file not found", "missing library dependency: GNU ld script '{}' requires '{s}', but file not found",
.{ .{ @as(Path, lib.path), script_arg.path },
lib.path,
scr_obj.path,
},
); );
continue; continue;
} }
const full_path = test_path.items; const full_path = Path.initCwd(test_path.items);
self.parseLibrary(.{ self.parseLibrary(.{
.needed = scr_obj.needed, .needed = script_arg.needed,
.path = full_path, .path = full_path,
}, false) catch |err| switch (err) { }, false) catch |err| switch (err) {
error.LinkFailure => continue, // already reported 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 const have_dynamic_linker = comp.config.link_libc and
link_mode == .dynamic and is_exe_or_dyn_lib; link_mode == .dynamic and is_exe_or_dyn_lib;
const target = self.getTarget(); 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_lib) |x| break :blk x.full_object_path;
if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
break :blk null; 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.add(self.allow_undefined_version);
man.hash.addOptional(self.enable_new_dtags); man.hash.addOptional(self.enable_new_dtags);
for (comp.objects) |obj| { 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.must_link);
man.hash.add(obj.loption); man.hash.add(obj.loption);
} }
for (comp.c_object_table.keys()) |key| { 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(module_obj_path);
try man.addOptionalFile(compiler_rt_path); try man.addOptionalFilePath(compiler_rt_path);
try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null); try man.addOptionalFilePath(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(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 // 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. // 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; break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p| 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 // TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations. // regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink; return error.NoObjectsToLink;
}; };
// This can happen when using --enable-cache and using the stage1 backend. In this case try std.fs.Dir.copyFile(
// we can skip the file copy. the_object_path.root_dir.handle,
if (!mem.eql(u8, the_object_path, full_out_path)) { the_object_path.sub_path,
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); directory.handle,
} self.base.emit.sub_path,
.{},
);
} else { } else {
// Create an LLD command line and invoke it. // Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa); 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); try argv.append(full_out_path);
// csu prelude // csu prelude
const csu = try CsuObjects.init(arena, comp); const csu = try comp.getCrtPaths(arena);
if (csu.crt0) |v| try argv.append(v); if (csu.crt0) |p| try argv.append(try p.toString(arena));
if (csu.crti) |v| try argv.append(v); if (csu.crti) |p| try argv.append(try p.toString(arena));
if (csu.crtbegin) |v| try argv.append(v); if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
for (self.rpath_table.keys()) |rpath| { for (self.rpath_table.keys()) |rpath| {
try argv.appendSlice(&.{ "-rpath", 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) { if (obj.loption) {
assert(obj.path[0] == ':'); assert(obj.path.sub_path[0] == ':');
try argv.append("-l"); try argv.append("-l");
} }
try argv.append(obj.path); try argv.append(try obj.path.toString(arena));
} }
if (whole_archive) { if (whole_archive) {
try argv.append("-no-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| { 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| { 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| { if (comp.tsan_lib) |lib| {
assert(comp.config.any_sanitize_thread); 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| { if (comp.fuzzer_lib) |lib| {
assert(comp.config.any_fuzz); assert(comp.config.any_fuzz);
try argv.append(lib.full_object_path); try argv.append(try lib.full_object_path.toString(arena));
} }
// libc // libc
@ -2278,7 +2274,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
!comp.config.link_libc) !comp.config.link_libc)
{ {
if (comp.libc_static_lib) |lib| { 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), // 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 // but they could be full paths to .so files, in which case we
// want to avoid prepending "-l". // want to avoid prepending "-l".
argv.appendAssumeCapacity(lib_info.path.?); argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
} }
if (!as_needed) { if (!as_needed) {
@ -2321,13 +2317,13 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
// libc++ dep // libc++ dep
if (comp.config.link_libcpp) { if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
} }
// libunwind dep // libunwind dep
if (comp.config.link_libunwind) { 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 // 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(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()) { } 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", .static => "libc.a",
.dynamic => "libc.so", .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 // to be after the shared libraries, so they are picked up from the shared
// libraries, not libcompiler_rt. // libraries, not libcompiler_rt.
if (compiler_rt_path) |p| { if (compiler_rt_path) |p| {
try argv.append(p); try argv.append(try p.toString(arena));
} }
// crt postlude // crt postlude
if (csu.crtend) |v| try argv.append(v); if (csu.crtend) |p| try argv.append(try p.toString(arena));
if (csu.crtn) |v| try argv.append(v); if (csu.crtn) |p| try argv.append(try p.toString(arena));
if (self.base.allow_shlib_undefined) { if (self.base.allow_shlib_undefined) {
try argv.append("--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 object = atom_ptr.file(self).?.object;
const priority = blk: { const priority = blk: {
if (is_ctor_dtor) { if (is_ctor_dtor) {
if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32); const basename = object.path.basename();
if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32); 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 default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32);
const name = atom_ptr.name(self); 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); 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, /// If a target compiles other output modes as dynamic libraries,
/// this function returns true for those too. /// this function returns true for those too.
pub fn isEffectivelyDynLib(self: Elf) bool { pub fn isEffectivelyDynLib(self: Elf) bool {
@ -5089,13 +4882,13 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
pub fn addParseError( pub fn addParseError(
self: *Elf, self: *Elf,
path: []const u8, path: Path,
comptime format: []const u8, comptime format: []const u8,
args: anytype, args: anytype,
) error{OutOfMemory}!void { ) error{OutOfMemory}!void {
var err = try self.base.addErrorWithNotes(1); var err = try self.base.addErrorWithNotes(1);
try err.addMsg(format, args); try err.addMsg(format, args);
try err.addNote("while parsing {s}", .{path}); try err.addNote("while parsing {}", .{path});
} }
pub fn addFileError( pub fn addFileError(
@ -5121,7 +4914,7 @@ pub fn failFile(
pub fn failParse( pub fn failParse(
self: *Elf, self: *Elf,
path: []const u8, path: Path,
comptime format: []const u8, comptime format: []const u8,
args: anytype, args: anytype,
) error{ OutOfMemory, LinkFailure } { ) error{ OutOfMemory, LinkFailure } {
@ -5274,7 +5067,7 @@ fn fmtDumpState(
_ = options; _ = options;
if (self.zigObjectPtr()) |zig_object| { 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("{}{}", .{ try writer.print("{}{}", .{
zig_object.fmtAtoms(self), zig_object.fmtAtoms(self),
zig_object.fmtSymtab(self), zig_object.fmtSymtab(self),
@ -5299,7 +5092,7 @@ fn fmtDumpState(
for (self.shared_objects.items) |index| { for (self.shared_objects.items) |index| {
const shared_object = self.file(index).?.shared_object; const shared_object = self.file(index).?.shared_object;
try writer.print("shared_object({d}) : ", .{index}); 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}); try writer.print(" : needed({})", .{shared_object.needed});
if (!shared_object.alive) try writer.writeAll(" : [*]"); if (!shared_object.alive) try writer.writeAll(" : [*]");
try writer.writeByte('\n'); try writer.writeByte('\n');
@ -5482,7 +5275,7 @@ pub const null_shdr = elf.Elf64_Shdr{
pub const SystemLib = struct { pub const SystemLib = struct {
needed: bool = false, needed: bool = false,
path: []const u8, path: Path,
}; };
pub const Ref = struct { pub const Ref = struct {

View file

@ -1,8 +1,8 @@
objects: std.ArrayListUnmanaged(Object) = .empty, objects: std.ArrayListUnmanaged(Object) = .empty,
strtab: std.ArrayListUnmanaged(u8) = .empty, strtab: std.ArrayListUnmanaged(u8) = .empty,
pub fn isArchive(path: []const u8) !bool { pub fn isArchive(path: Path) !bool {
const file = try std.fs.cwd().openFile(path, .{}); const file = try path.root_dir.handle.openFile(path.sub_path, .{});
defer file.close(); defer file.close();
const reader = file.reader(); const reader = file.reader();
const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false; 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); 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 comp = elf_file.base.comp;
const gpa = comp.gpa; const gpa = comp.gpa;
const handle = elf_file.fileHandle(handle_index); 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 else
unreachable; unreachable;
const object = Object{ const object: Object = .{
.archive = .{ .archive = .{
.path = try gpa.dupe(u8, path), .path = .{
.root_dir = path.root_dir,
.sub_path = try gpa.dupe(u8, path.sub_path),
},
.offset = pos, .offset = pos,
.size = obj_size, .size = obj_size,
}, },
.path = try gpa.dupe(u8, name), .path = Path.initCwd(try gpa.dupe(u8, name)),
.file_handle = handle_index, .file_handle = handle_index,
.index = undefined, .index = undefined,
.alive = false, .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); try self.objects.append(gpa, object);
} }
@ -292,6 +297,7 @@ const elf = std.elf;
const fs = std.fs; const fs = std.fs;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const mem = std.mem; const mem = std.mem;
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Archive = @This(); const Archive = @This();

View file

@ -1,6 +1,11 @@
path: []const u8, path: Path,
cpu_arch: ?std.Target.Cpu.Arch = null, 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 { pub fn deinit(scr: *LdScript, allocator: Allocator) void {
scr.args.deinit(allocator); 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 it = TokenIterator{ .tokens = tokens.items };
var parser = Parser{ .source = data, .it = &it }; var parser = Parser{ .source = data, .it = &it };
var args = std.ArrayList(Elf.SystemLib).init(gpa); var args = std.ArrayList(Arg).init(gpa);
scr.doParse(.{ scr.doParse(.{
.parser = &parser, .parser = &parser,
.args = &args, .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 { fn doParse(scr: *LdScript, ctx: struct {
parser: *Parser, parser: *Parser,
args: *std.ArrayList(Elf.SystemLib), args: *std.ArrayList(Arg),
}) !void { }) !void {
while (true) { while (true) {
ctx.parser.skipAny(&.{ .comment, .new_line }); ctx.parser.skipAny(&.{ .comment, .new_line });
@ -142,7 +147,7 @@ const Parser = struct {
return error.UnknownCpuArch; 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; if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
while (true) { while (true) {
@ -162,7 +167,7 @@ const Parser = struct {
_ = try p.require(.rparen); _ = 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; if (!p.skip(&.{.lparen})) return error.UnexpectedToken;
while (p.maybe(.literal)) |tok_id| { while (p.maybe(.literal)) |tok_id| {
@ -239,7 +244,7 @@ const Token = struct {
const Index = usize; 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]; return source[tok.start..tok.end];
} }
}; };
@ -399,11 +404,11 @@ const TokenIterator = struct {
return it.tokens[it.pos]; return it.tokens[it.pos];
} }
inline fn reset(it: *TokenIterator) void { fn reset(it: *TokenIterator) void {
it.pos = 0; it.pos = 0;
} }
inline fn seekTo(it: *TokenIterator, pos: Token.Index) void { fn seekTo(it: *TokenIterator, pos: Token.Index) void {
it.pos = pos; 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); assert(pos < it.tokens.len);
return it.tokens[pos]; return it.tokens[pos];
} }
@ -426,6 +431,7 @@ const LdScript = @This();
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const Path = std.Build.Cache.Path;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Elf = @import("../Elf.zig"); const Elf = @import("../Elf.zig");

View file

@ -1,5 +1,7 @@
archive: ?InArchive = null, 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, file_handle: File.HandleIndex,
index: File.Index, index: File.Index,
@ -36,8 +38,8 @@ output_symtab_ctx: Elf.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{}, output_ar_state: Archive.ArState = .{},
pub fn deinit(self: *Object, allocator: Allocator) void { pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.archive) |*ar| allocator.free(ar.path); if (self.archive) |*ar| allocator.free(ar.path.sub_path);
allocator.free(self.path); allocator.free(self.path.sub_path);
self.shdrs.deinit(allocator); self.shdrs.deinit(allocator);
self.symtab.deinit(allocator); self.symtab.deinit(allocator);
self.strtab.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) if (sym.type(elf_file) != elf.STT_FUNC)
// TODO convert into an error // TODO convert into an error
log.debug("{s}: {s}: CIE referencing external data reference", .{ log.debug("{s}: {s}: CIE referencing external data reference", .{
self.fmtPath(), self.fmtPath(), sym.name(elf_file),
sym.name(elf_file),
}); });
sym.flags.needs_plt = true; 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 { 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 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 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(.{ const hdr = Archive.setArHdr(.{
.name = if (name.len <= Archive.max_member_name_len) .name = if (name.len <= Archive.max_member_name_len)
.{ .name = name } .{ .name = name }
@ -1489,15 +1490,14 @@ fn formatPath(
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
if (object.archive) |ar| { if (object.archive) |ar| {
try writer.writeAll(ar.path); try writer.print("{}({})", .{ ar.path, object.path });
try writer.writeByte('('); } else {
try writer.writeAll(object.path); try writer.print("{}", .{object.path});
try writer.writeByte(')'); }
} else try writer.writeAll(object.path);
} }
const InArchive = struct { const InArchive = struct {
path: []const u8, path: Path,
offset: u64, offset: u64,
size: u32, size: u32,
}; };
@ -1512,8 +1512,9 @@ const fs = std.fs;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const AtomList = @import("AtomList.zig"); const AtomList = @import("AtomList.zig");

View file

@ -1,4 +1,4 @@
path: []const u8, path: Path,
index: File.Index, index: File.Index,
header: ?elf.Elf64_Ehdr = null, header: ?elf.Elf64_Ehdr = null,
@ -22,8 +22,8 @@ alive: bool,
output_symtab_ctx: Elf.SymtabCtx = .{}, output_symtab_ctx: Elf.SymtabCtx = .{},
pub fn isSharedObject(path: []const u8) !bool { pub fn isSharedObject(path: Path) !bool {
const file = try std.fs.cwd().openFile(path, .{}); const file = try path.root_dir.handle.openFile(path.sub_path, .{});
defer file.close(); defer file.close();
const reader = file.reader(); const reader = file.reader();
const header = reader.readStruct(elf.Elf64_Ehdr) catch return false; 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 { pub fn deinit(self: *SharedObject, allocator: Allocator) void {
allocator.free(self.path); allocator.free(self.path.sub_path);
self.shdrs.deinit(allocator); self.shdrs.deinit(allocator);
self.symtab.deinit(allocator); self.symtab.deinit(allocator);
self.strtab.deinit(allocator); self.strtab.deinit(allocator);
@ -319,7 +319,7 @@ pub fn asFile(self: *SharedObject) File {
fn verdefNum(self: *SharedObject) u32 { fn verdefNum(self: *SharedObject) u32 {
for (self.dynamic_table.items) |entry| switch (entry.d_tag) { 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 => {}, else => {},
}; };
return 0; return 0;
@ -327,10 +327,10 @@ fn verdefNum(self: *SharedObject) u32 {
pub fn soname(self: *SharedObject) []const u8 { pub fn soname(self: *SharedObject) []const u8 {
for (self.dynamic_table.items) |entry| switch (entry.d_tag) { 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 => {}, 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 { pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
@ -508,6 +508,7 @@ const assert = std.debug.assert;
const elf = std.elf; const elf = std.elf;
const log = std.log.scoped(.elf); const log = std.log.scoped(.elf);
const mem = std.mem; const mem = std.mem;
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Elf = @import("../Elf.zig"); const Elf = @import("../Elf.zig");

View file

@ -5,7 +5,7 @@
data: std.ArrayListUnmanaged(u8) = .empty, data: std.ArrayListUnmanaged(u8) = .empty,
/// Externally owned memory. /// Externally owned memory.
path: []const u8, basename: []const u8,
index: File.Index, index: File.Index,
symtab: std.MultiArrayList(ElfSym) = .{}, 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); 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 symbol_index = try self.newLocalSymbol(gpa, name_off);
const sym = self.symbol(symbol_index); const sym = self.symbol(symbol_index);
const esym = &self.symtab.items(.elf_sym)[sym.esym_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 { pub fn writeAr(self: ZigObject, writer: anytype) !void {
const name = self.path; const name = self.basename;
const hdr = Archive.setArHdr(.{ const hdr = Archive.setArHdr(.{
.name = if (name.len <= Archive.max_member_name_len) .name = if (name.len <= Archive.max_member_name_len)
.{ .name = name } .{ .name = name }
@ -2384,9 +2384,9 @@ const relocation = @import("relocation.zig");
const target_util = @import("../../target.zig"); const target_util = @import("../../target.zig");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const Air = @import("../../Air.zig"); const Air = @import("../../Air.zig");
const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const Dwarf = @import("../Dwarf.zig"); const Dwarf = @import("../Dwarf.zig");

View file

@ -23,10 +23,10 @@ pub const File = union(enum) {
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
switch (file) { 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)"), .linker_defined => try writer.writeAll("(linker defined)"),
.object => |x| try writer.print("{}", .{x.fmtPath()}), .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) { return switch (file) {
.zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file),
.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 { pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void {
const path = switch (file) { switch (file) {
.zig_object => |x| x.path, .zig_object => |zo| {
.object => |x| x.path, const basename = zo.basename;
inline else => unreachable, if (basename.len <= Archive.max_member_name_len) return;
}; zo.output_ar_state.name_off = try ar_strtab.insert(allocator, basename);
const state = switch (file) { },
.zig_object => |x| &x.output_ar_state, .object => |o| {
.object => |x| &x.output_ar_state, const basename = std.fs.path.basename(o.path.sub_path);
inline else => unreachable, if (basename.len <= Archive.max_member_name_len) return;
}; o.output_ar_state.name_off = try ar_strtab.insert(allocator, basename);
if (path.len <= Archive.max_member_name_len) return; },
state.name_off = try ar_strtab.insert(allocator, path); else => unreachable,
}
} }
pub fn updateArSize(file: File, elf_file: *Elf) !void { pub fn updateArSize(file: File, elf_file: *Elf) !void {
return switch (file) { return switch (file) {
.zig_object => |x| x.updateArSize(), .zig_object => |x| x.updateArSize(),
.object => |x| x.updateArSize(elf_file), .object => |x| x.updateArSize(elf_file),
inline else => unreachable, else => unreachable,
}; };
} }
@ -271,7 +272,7 @@ pub const File = union(enum) {
return switch (file) { return switch (file) {
.zig_object => |x| x.writeAr(writer), .zig_object => |x| x.writeAr(writer),
.object => |x| x.writeAr(elf_file, 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 std = @import("std");
const elf = std.elf; const elf = std.elf;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const Path = std.Build.Cache.Path;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const Cie = @import("eh_frame.zig").Cie; const Cie = @import("eh_frame.zig").Cie;

View file

@ -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; const gpa = comp.gpa;
for (comp.objects) |obj| { for (comp.objects) |obj| {
switch (Compilation.classifyFileExt(obj.path)) { switch (Compilation.classifyFileExt(obj.path.sub_path)) {
.object => try parseObjectStaticLibReportingFailure(elf_file, obj.path), .object => try parseObjectStaticLibReportingFailure(elf_file, obj.path),
.static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path), .static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path),
else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}), 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; 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| { for (comp.objects) |obj| {
if (obj.isObject()) { if (obj.isObject()) {
try elf_file.parseObjectReportingFailure(obj.path); 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; 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) { parseObjectStaticLib(elf_file, path) catch |err| switch (err) {
error.LinkFailure => return, error.LinkFailure => return,
error.OutOfMemory => return error.OutOfMemory, 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) { parseArchiveStaticLib(elf_file, path) catch |err| switch (err) {
error.LinkFailure => return, error.LinkFailure => return,
error.OutOfMemory => return error.OutOfMemory, 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 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 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 = .{ 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, .file_handle = fh,
.index = index, .index = index,
} }); } });
@ -231,9 +234,9 @@ fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
try object.parseAr(elf_file); 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 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 fh = try elf_file.addFileHandle(handle);
var archive = Archive{}; var archive = Archive{};
@ -531,6 +534,7 @@ const log = std.log.scoped(.link);
const math = std.math; const math = std.math;
const mem = std.mem; const mem = std.mem;
const state_log = std.log.scoped(.link_state); const state_log = std.log.scoped(.link_state);
const Path = std.Build.Cache.Path;
const std = @import("std"); const std = @import("std");
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");

View file

@ -144,14 +144,14 @@ hot_state: if (is_hot_update_compatible) HotUpdateState else struct {} = .{},
pub const Framework = struct { pub const Framework = struct {
needed: bool = false, needed: bool = false,
weak: bool = false, weak: bool = false,
path: []const u8, path: Path,
}; };
pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void {
for (hm) |value| { for (hm) |value| {
man.hash.add(value.needed); man.hash.add(value.needed);
man.hash.add(value.weak); 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)); const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .zig_object = .{ self.files.set(index, .{ .zig_object = .{
.index = index, .index = index,
.path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem( .basename = try std.fmt.allocPrint(arena, "{s}.o", .{
zcu.main_mod.root_src_path, fs.path.stem(zcu.main_mod.root_src_path),
)}), }),
} }); } });
self.zig_object = index; self.zig_object = index;
const zo = self.getZigObject().?; 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(); defer sub_prog_node.end();
const directory = self.base.emit.root_dir; 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: ?Path = if (self.base.zcu_object_sub_path) |path| .{
const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { .root_dir = directory,
if (fs.path.dirname(full_out_path)) |dirname| { .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
break :blk try fs.path.join(arena, &.{ dirname, path }); try fs.path.join(arena, &.{ dirname, path })
} else { else
break :blk path; path,
}
} else null; } else null;
// --verbose-link // --verbose-link
@ -455,7 +454,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
} }
// Finally, link against compiler_rt. // 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_lib) |x| break :blk x.full_object_path;
if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
break :blk null; 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 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into. // where the code signature goes into.
var codesig = CodeSignature.init(self.getPageSize()); 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); if (self.entitlements) |path| try codesig.addEntitlements(gpa, path);
try self.writeCodeSignaturePadding(&codesig); try self.writeCodeSignaturePadding(&codesig);
break :blk codesig; break :blk codesig;
@ -625,11 +624,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
if (self.base.isRelocatable()) { if (self.base.isRelocatable()) {
for (comp.objects) |obj| { for (comp.objects) |obj| {
try argv.append(obj.path); try argv.append(try obj.path.toString(arena));
} }
for (comp.c_object_table.keys()) |key| { 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| { if (module_obj_path) |p| {
@ -711,11 +710,11 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
if (obj.must_link) { if (obj.must_link) {
try argv.append("-force_load"); 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| { 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| { if (module_obj_path) |p| {
@ -723,13 +722,12 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
} }
if (comp.config.any_sanitize_thread) { if (comp.config.any_sanitize_thread) {
const path = comp.tsan_lib.?.full_object_path; const path = try comp.tsan_lib.?.full_object_path.toString(arena);
try argv.append(path); try argv.appendSlice(&.{ path, "-rpath", std.fs.path.dirname(path) orelse "." });
try argv.appendSlice(&.{ "-rpath", std.fs.path.dirname(path) orelse "." });
} }
if (comp.config.any_fuzz) { 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| { for (self.lib_dirs) |lib_dir| {
@ -754,7 +752,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
} }
for (self.frameworks) |framework| { for (self.frameworks) |framework| {
const name = fs.path.stem(framework.path); const name = framework.path.stem();
const arg = if (framework.needed) const arg = if (framework.needed)
try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
else if (framework.weak) else if (framework.weak)
@ -765,14 +763,16 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
} }
if (comp.config.link_libcpp) { if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.appendSlice(&.{
try argv.append(comp.libcxx_static_lib.?.full_object_path); try comp.libcxxabi_static_lib.?.full_object_path.toString(arena),
try comp.libcxx_static_lib.?.full_object_path.toString(arena),
});
} }
try argv.append("-lSystem"); try argv.append("-lSystem");
if (comp.compiler_rt_lib) |lib| try argv.append(lib.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(obj.full_object_path); if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
} }
Compilation.dump_argv(argv.items); Compilation.dump_argv(argv.items);
@ -807,20 +807,20 @@ pub fn resolveLibSystem(
return error.MissingLibSystem; 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(.{ try out_libs.append(.{
.needed = true, .needed = true,
.path = libsystem_path, .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()); const tracy = trace(@src());
defer tracy.end(); 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); const fh = try self.addFileHandle(file);
var buffer: [Archive.SARMAG]u8 = undefined; 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); _ = 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; 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; 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; 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]; 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()); const tracy = trace(@src());
defer tracy.end(); 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))); const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .object = .{ self.files.set(index, .{ .object = .{
.offset = offset, .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, .file_handle = handle,
.mtime = mtime, .mtime = mtime,
.index = index, .index = index,
@ -937,7 +940,7 @@ fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.Handle
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
var archive = Archive{}; var archive: Archive = .{};
defer archive.deinit(gpa); defer archive.deinit(gpa);
try archive.unpack(self, lib.path, handle, fat_arch); 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, .offset = offset,
.file_handle = handle, .file_handle = handle,
.tag = .dylib, .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, .index = index,
.needed = lib.needed, .needed = lib.needed,
.weak = lib.weak, .weak = lib.weak,
@ -986,7 +992,10 @@ fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex
.offset = 0, .offset = 0,
.file_handle = handle, .file_handle = handle,
.tag = .tbd, .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, .index = index,
.needed = lib.needed, .needed = lib.needed,
.weak = lib.weak, .weak = lib.weak,
@ -1175,11 +1184,11 @@ fn parseDependentDylibs(self: *MachO) !void {
continue; continue;
} }
}; };
const lib = SystemLib{ const lib: SystemLib = .{
.path = full_path, .path = Path.initCwd(full_path),
.weak = is_weak, .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 fh = try self.addFileHandle(file);
const fat_arch = try self.parseFatFile(file, lib.path); const fat_arch = try self.parseFatFile(file, lib.path);
const offset = if (fat_arch) |fa| fa.offset else 0; const offset = if (fat_arch) |fa| fa.offset else 0;
@ -2865,7 +2874,8 @@ fn writeLoadCommands(self: *MachO) !struct { usize, usize, u64 } {
ncmds += 1; ncmds += 1;
} }
if (comp.config.any_sanitize_thread) { 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 "."; const rpath = std.fs.path.dirname(path) orelse ".";
try load_commands.writeRpathLC(rpath, writer); try load_commands.writeRpathLC(rpath, writer);
ncmds += 1; ncmds += 1;
@ -3758,13 +3768,13 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
pub fn reportParseError( pub fn reportParseError(
self: *MachO, self: *MachO,
path: []const u8, path: Path,
comptime format: []const u8, comptime format: []const u8,
args: anytype, args: anytype,
) error{OutOfMemory}!void { ) error{OutOfMemory}!void {
var err = try self.base.addErrorWithNotes(1); var err = try self.base.addErrorWithNotes(1);
try err.addMsg(format, args); try err.addMsg(format, args);
try err.addNote("while parsing {s}", .{path}); try err.addNote("while parsing {}", .{path});
} }
pub fn reportParseError2( pub fn reportParseError2(
@ -3913,7 +3923,7 @@ fn fmtDumpState(
_ = options; _ = options;
_ = unused_fmt_string; _ = unused_fmt_string;
if (self.getZigObject()) |zo| { 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", .{ try writer.print("{}{}\n", .{
zo.fmtAtoms(self), zo.fmtAtoms(self),
zo.fmtSymtab(self), zo.fmtSymtab(self),
@ -3938,9 +3948,9 @@ fn fmtDumpState(
} }
for (self.dylibs.items) |index| { for (self.dylibs.items) |index| {
const dylib = self.getFile(index).?.dylib; const dylib = self.getFile(index).?.dylib;
try writer.print("dylib({d}) : {s} : needed({}) : weak({})", .{ try writer.print("dylib({d}) : {} : needed({}) : weak({})", .{
index, index,
dylib.path, @as(Path, dylib.path),
dylib.needed, dylib.needed,
dylib.weak, dylib.weak,
}); });
@ -4442,7 +4452,7 @@ pub const default_pagezero_size: u64 = 0x100000000;
pub const default_headerpad_size: u32 = 0x1000; pub const default_headerpad_size: u32 = 0x1000;
const SystemLib = struct { const SystemLib = struct {
path: []const u8, path: Path,
needed: bool = false, needed: bool = false,
weak: bool = false, weak: bool = false,
hidden: bool = false, hidden: bool = false,

View file

@ -4,7 +4,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
self.objects.deinit(allocator); 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; const gpa = macho_file.base.comp.gpa;
var arena = std.heap.ArenaAllocator.init(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, SYMDEF_SORTED) or
mem.eql(u8, name, SYMDEF64_SORTED)) continue; mem.eql(u8, name, SYMDEF64_SORTED)) continue;
const object = Object{ const object: Object = .{
.offset = pos, .offset = pos,
.in_archive = .{ .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, .size = hdr_size,
}, },
.path = try gpa.dupe(u8, name), .path = Path.initCwd(try gpa.dupe(u8, name)),
.file_handle = handle_index, .file_handle = handle_index,
.index = undefined, .index = undefined,
.alive = false, .alive = false,
.mtime = hdr.date() catch 0, .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); try self.objects.append(gpa, object);
} }
@ -301,8 +304,9 @@ const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const mem = std.mem; const mem = std.mem;
const std = @import("std"); const std = @import("std");
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Path = std.Build.Cache.Path;
const Archive = @This(); const Archive = @This();
const File = @import("file.zig").File; const File = @import("file.zig").File;
const MachO = @import("../MachO.zig"); const MachO = @import("../MachO.zig");

View file

@ -1,6 +1,6 @@
/// Non-zero for fat dylibs /// Non-zero for fat dylibs
offset: u64, offset: u64,
path: []const u8, path: Path,
index: File.Index, index: File.Index,
file_handle: File.HandleIndex, file_handle: File.HandleIndex,
tag: enum { dylib, tbd }, tag: enum { dylib, tbd },
@ -28,7 +28,7 @@ referenced: bool = false,
output_symtab_ctx: MachO.SymtabCtx = .{}, output_symtab_ctx: MachO.SymtabCtx = .{},
pub fn deinit(self: *Dylib, allocator: Allocator) void { pub fn deinit(self: *Dylib, allocator: Allocator) void {
allocator.free(self.path); allocator.free(self.path.sub_path);
self.exports.deinit(allocator); self.exports.deinit(allocator);
self.strtab.deinit(allocator); self.strtab.deinit(allocator);
if (self.id) |*id| id.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 file = macho_file.getFileHandle(self.file_handle);
const offset = self.offset; 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; 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; 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); const file = macho_file.getFileHandle(self.file_handle);
var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| { var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| {
@ -959,8 +959,9 @@ const mem = std.mem;
const tapi = @import("../tapi.zig"); const tapi = @import("../tapi.zig");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const std = @import("std"); const std = @import("std");
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Path = std.Build.Cache.Path;
const Dylib = @This(); const Dylib = @This();
const File = @import("file.zig").File; const File = @import("file.zig").File;
const LibStub = tapi.LibStub; const LibStub = tapi.LibStub;

View file

@ -1,6 +1,8 @@
/// Non-zero for fat object files or archives /// Non-zero for fat object files or archives
offset: u64, 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, file_handle: File.HandleIndex,
mtime: u64, mtime: u64,
index: File.Index, index: File.Index,
@ -39,8 +41,8 @@ output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{}, output_ar_state: Archive.ArState = .{},
pub fn deinit(self: *Object, allocator: Allocator) void { pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.in_archive) |*ar| allocator.free(ar.path); if (self.in_archive) |*ar| allocator.free(ar.path.sub_path);
allocator.free(self.path); allocator.free(self.path.sub_path);
for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| { for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
relocs.deinit(allocator); relocs.deinit(allocator);
sub.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 { pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
// Header // Header
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; 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 // Data
const file = macho_file.getFileHandle(self.file_handle); const file = macho_file.getFileHandle(self.file_handle);
// TODO try using copyRangeAll // TODO try using copyRangeAll
@ -1774,6 +1777,11 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
self.calcStabsSize(macho_file); 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 { pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
if (self.compile_unit) |cu| { if (self.compile_unit) |cu| {
const comp_dir = cu.getCompDir(self.*); 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 self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
if (self.in_archive) |ar| { 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 { } 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| { 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; index += 1;
if (self.in_archive) |ar| { if (self.in_archive) |ar| {
@memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path); if (ar.path.root_dir.path) |p| {
n_strx += @intCast(ar.path.len); @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] = '('; ctx.strtab.items[n_strx] = '(';
n_strx += 1; n_strx += 1;
@memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); const basename = self.path.basename();
n_strx += @intCast(self.path.len); @memcpy(ctx.strtab.items[n_strx..][0..basename.len], basename);
n_strx += @intCast(basename.len);
ctx.strtab.items[n_strx] = ')'; ctx.strtab.items[n_strx] = ')';
n_strx += 1; n_strx += 1;
ctx.strtab.items[n_strx] = 0; ctx.strtab.items[n_strx] = 0;
n_strx += 1; n_strx += 1;
} else { } else {
@memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path); if (self.path.root_dir.path) |p| {
n_strx += @intCast(self.path.len); @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; ctx.strtab.items[n_strx] = 0;
n_strx += 1; n_strx += 1;
} }
@ -2666,11 +2691,12 @@ fn formatPath(
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
if (object.in_archive) |ar| { if (object.in_archive) |ar| {
try writer.writeAll(ar.path); try writer.print("{}({s})", .{
try writer.writeByte('('); @as(Path, ar.path), object.path.basename(),
try writer.writeAll(object.path); });
try writer.writeByte(')'); } else {
} else try writer.writeAll(object.path); try writer.print("{}", .{@as(Path, object.path)});
}
} }
const Section = struct { const Section = struct {
@ -2777,7 +2803,7 @@ const CompileUnit = struct {
}; };
const InArchive = struct { const InArchive = struct {
path: []const u8, path: Path,
size: u32, size: u32,
}; };
@ -3170,6 +3196,7 @@ const math = std.math;
const mem = std.mem; const mem = std.mem;
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const std = @import("std"); const std = @import("std");
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");

View file

@ -1,6 +1,6 @@
data: std.ArrayListUnmanaged(u8) = .empty, data: std.ArrayListUnmanaged(u8) = .empty,
/// Externally owned memory. /// Externally owned memory.
path: []const u8, basename: []const u8,
index: File.Index, index: File.Index,
symtab: std.MultiArrayList(Nlist) = .{}, 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 { pub fn writeAr(self: ZigObject, ar_format: Archive.Format, writer: anytype) !void {
// Header // Header
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow; 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 // Data
try writer.writeAll(self.data.items); try writer.writeAll(self.data.items);
} }

View file

@ -23,10 +23,10 @@ pub const File = union(enum) {
_ = unused_fmt_string; _ = unused_fmt_string;
_ = options; _ = options;
switch (file) { switch (file) {
.zig_object => |x| try writer.writeAll(x.path), .zig_object => |zo| try writer.writeAll(zo.basename),
.internal => try writer.writeAll("internal"), .internal => try writer.writeAll("internal"),
.object => |x| try writer.print("{}", .{x.fmtPath()}), .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; pub const HandleIndex = Index;
}; };
const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const macho = std.macho; const macho = std.macho;
const std = @import("std");
const trace = @import("../../tracy.zig").trace;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path;
const trace = @import("../../tracy.zig").trace;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");
const InternalObject = @import("InternalObject.zig"); const InternalObject = @import("InternalObject.zig");

View file

@ -72,7 +72,8 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32
} }
if (comp.config.any_sanitize_thread) { 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 "."; const rpath = std.fs.path.dirname(path) orelse ".";
sizeofcmds += calcInstallNameLen( sizeofcmds += calcInstallNameLen(
@sizeOf(macho.rpath_command), @sizeOf(macho.rpath_command),

View file

@ -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; 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); var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
defer positionals.deinit(); defer positionals.deinit();
try positionals.ensureUnusedCapacity(comp.objects.len); 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 // 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. // compiler, investigate if we can get rid of this `if` prong here.
const path = positionals.items[0].path; 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 stat = try in_file.stat();
const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size); 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 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); 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; const gpa = comp.gpa;
var positionals = std.ArrayList(Compilation.LinkObject).init(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| { for (files.items) |index| {
const file = macho_file.getFile(index).?; const file = macho_file.getFile(index).?;
const state = switch (file) { switch (file) {
.zig_object => |x| &x.output_ar_state, .zig_object => |zo| {
.object => |x| &x.output_ar_state, 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, 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; break :blk pos;
@ -777,6 +782,7 @@ const mem = std.mem;
const state_log = std.log.scoped(.link_state); const state_log = std.log.scoped(.link_state);
const std = @import("std"); const std = @import("std");
const trace = @import("../../tracy.zig").trace; const trace = @import("../../tracy.zig").trace;
const Path = std.Build.Cache.Path;
const Archive = @import("Archive.zig"); const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig"); const Atom = @import("Atom.zig");

View file

@ -15,7 +15,7 @@ pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
if (gop.found_existing) return gop.key_ptr.*; if (gop.found_existing) return gop.key_ptr.*;
try self.buffer.ensureUnusedCapacity(gpa, string.len + 1); 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.appendSliceAssumeCapacity(string);
self.buffer.appendAssumeCapacity(0); self.buffer.appendAssumeCapacity(0);

View file

@ -2507,6 +2507,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
} else null; } else null;
// Positional arguments to the linker such as object files and static archives. // 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); var positionals = std.ArrayList([]const u8).init(arena);
try positionals.ensureUnusedCapacity(comp.objects.len); 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); (output_mode == .Lib and link_mode == .dynamic);
if (is_exe_or_dyn_lib) { if (is_exe_or_dyn_lib) {
for (comp.wasi_emulated_libs) |crt_file| { for (comp.wasi_emulated_libs) |crt_file| {
try positionals.append(try comp.get_libc_crt_file( try positionals.append(try comp.crtFileAsString(
arena, arena,
wasi_libc.emulatedLibCRFileLibName(crt_file), wasi_libc.emulatedLibCRFileLibName(crt_file),
)); ));
} }
if (link_libc) { if (link_libc) {
try positionals.append(try comp.get_libc_crt_file( try positionals.append(try comp.crtFileAsString(
arena, arena,
wasi_libc.execModelCrtFileFullName(wasi_exec_model), 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) { if (link_libcpp) {
try positionals.append(comp.libcxx_static_lib.?.full_object_path); try positionals.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); 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| { 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| { 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_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));
if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path); if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));
try wasm.parseInputFiles(positionals.items); 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(); defer sub_prog_node.end();
const is_obj = comp.config.output_mode == .Obj; 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_lib) |lib| break :blk lib.full_object_path;
if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
break :blk null; 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); comptime assert(Compilation.link_hash_implementation_version == 14);
for (comp.objects) |obj| { 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.must_link);
} }
for (comp.c_object_table.keys()) |key| { 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(module_obj_path);
try man.addOptionalFile(compiler_rt_path); try man.addOptionalFilePath(compiler_rt_path);
man.hash.addOptionalBytes(wasm.entry_name); man.hash.addOptionalBytes(wasm.entry_name);
man.hash.add(wasm.base.stack_size); man.hash.add(wasm.base.stack_size);
man.hash.add(wasm.base.build_id); 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; break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p| 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 // TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations. // regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink; return error.NoObjectsToLink;
}; };
// This can happen when using --enable-cache and using the stage1 backend. In this case try std.fs.Dir.copyFile(
// we can skip the file copy. the_object_path.root_dir.handle,
if (!mem.eql(u8, the_object_path, full_out_path)) { the_object_path.sub_path,
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); directory.handle,
} wasm.base.emit.sub_path,
.{},
);
} else { } else {
// Create an LLD command line and invoke it. // Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa); 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); (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
if (is_exe_or_dyn_lib) { if (is_exe_or_dyn_lib) {
for (comp.wasi_emulated_libs) |crt_file| { for (comp.wasi_emulated_libs) |crt_file| {
try argv.append(try comp.get_libc_crt_file( try argv.append(try comp.crtFileAsString(
arena, arena,
wasi_libc.emulatedLibCRFileLibName(crt_file), wasi_libc.emulatedLibCRFileLibName(crt_file),
)); ));
} }
if (comp.config.link_libc) { if (comp.config.link_libc) {
try argv.append(try comp.get_libc_crt_file( try argv.append(try comp.crtFileAsString(
arena, arena,
wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), 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) { if (comp.config.link_libcpp) {
try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
try argv.append(comp.libcxxabi_static_lib.?.full_object_path); 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"); try argv.append("-no-whole-archive");
whole_archive = false; whole_archive = false;
} }
try argv.append(obj.path); try argv.append(try obj.path.toString(arena));
} }
if (whole_archive) { if (whole_archive) {
try argv.append("-no-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| { 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| { if (module_obj_path) |p| {
try argv.append(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.skip_linker_dependencies and
!comp.config.link_libc) !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| { if (compiler_rt_path) |p| {
try argv.append(p); try argv.append(try p.toString(arena));
} }
if (comp.verbose_link) { if (comp.verbose_link) {

View file

@ -13,6 +13,12 @@ const warn = std.log.warn;
const ThreadPool = std.Thread.Pool; const ThreadPool = std.Thread.Pool;
const cleanExit = std.process.cleanExit; const cleanExit = std.process.cleanExit;
const native_os = builtin.os.tag; 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 tracy = @import("tracy.zig");
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
@ -20,16 +26,11 @@ const link = @import("link.zig");
const Package = @import("Package.zig"); const Package = @import("Package.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
const introspect = @import("introspect.zig"); const introspect = @import("introspect.zig");
const EnvVar = std.zig.EnvVar;
const LibCInstallation = std.zig.LibCInstallation;
const wasi_libc = @import("wasi_libc.zig"); const wasi_libc = @import("wasi_libc.zig");
const Cache = std.Build.Cache;
const target_util = @import("target.zig"); const target_util = @import("target.zig");
const crash_report = @import("crash_report.zig"); const crash_report = @import("crash_report.zig");
const Zcu = @import("Zcu.zig"); const Zcu = @import("Zcu.zig");
const AstGen = std.zig.AstGen;
const mingw = @import("mingw.zig"); const mingw = @import("mingw.zig");
const Server = std.zig.Server;
const dev = @import("dev.zig"); const dev = @import("dev.zig");
pub const std_options = .{ pub const std_options = .{
@ -1724,14 +1725,14 @@ fn buildOutputType(
} }
} else switch (file_ext orelse Compilation.classifyFileExt(arg)) { } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
.shared_library => { .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; create_module.opts.any_dyn_libs = true;
}, },
.object, .static_library => { .object, .static_library => {
try create_module.link_objects.append(arena, .{ .path = arg }); try create_module.link_objects.append(arena, .{ .path = Path.initCwd(arg) });
}, },
.res => { .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; contains_res_file = true;
}, },
.manifest => { .manifest => {
@ -1845,20 +1846,20 @@ fn buildOutputType(
}, },
.shared_library => { .shared_library => {
try create_module.link_objects.append(arena, .{ try create_module.link_objects.append(arena, .{
.path = it.only_arg, .path = Path.initCwd(it.only_arg),
.must_link = must_link, .must_link = must_link,
}); });
create_module.opts.any_dyn_libs = true; create_module.opts.any_dyn_libs = true;
}, },
.unknown, .object, .static_library => { .unknown, .object, .static_library => {
try create_module.link_objects.append(arena, .{ try create_module.link_objects.append(arena, .{
.path = it.only_arg, .path = Path.initCwd(it.only_arg),
.must_link = must_link, .must_link = must_link,
}); });
}, },
.res => { .res => {
try create_module.link_objects.append(arena, .{ try create_module.link_objects.append(arena, .{
.path = it.only_arg, .path = Path.initCwd(it.only_arg),
.must_link = must_link, .must_link = must_link,
}); });
contains_res_file = true; contains_res_file = true;
@ -1894,7 +1895,7 @@ fn buildOutputType(
// binary: no extra rpaths and DSO filename exactly // binary: no extra rpaths and DSO filename exactly
// as provided. Hello, Go. // as provided. Hello, Go.
try create_module.link_objects.append(arena, .{ try create_module.link_objects.append(arena, .{
.path = it.only_arg, .path = Path.initCwd(it.only_arg),
.must_link = must_link, .must_link = must_link,
.loption = true, .loption = true,
}); });
@ -2532,7 +2533,7 @@ fn buildOutputType(
install_name = linker_args_it.nextOrFatal(); install_name = linker_args_it.nextOrFatal();
} else if (mem.eql(u8, arg, "-force_load")) { } else if (mem.eql(u8, arg, "-force_load")) {
try create_module.link_objects.append(arena, .{ try create_module.link_objects.append(arena, .{
.path = linker_args_it.nextOrFatal(), .path = Path.initCwd(linker_args_it.nextOrFatal()),
.must_link = true, .must_link = true,
}); });
} else if (mem.eql(u8, arg, "-hash-style") or } 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; break :b create_module.c_source_files.items[0].src_path;
if (create_module.link_objects.items.len >= 1) 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) if (emit_bin == .yes)
break :b emit_bin.yes; break :b emit_bin.yes;
@ -2963,7 +2964,7 @@ fn buildOutputType(
framework_dir_path, framework_dir_path,
framework_name, 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(.{ try resolved_frameworks.append(.{
.needed = info.needed, .needed = info.needed,
.weak = info.weak, .weak = info.weak,
@ -3635,7 +3636,7 @@ const CreateModule = struct {
name: []const u8, name: []const u8,
lib: Compilation.SystemLib, 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), c_source_files: std.ArrayListUnmanaged(Compilation.CSourceFile),
rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile), rc_source_files: std.ArrayListUnmanaged(Compilation.RcSourceFile),
@ -3808,7 +3809,7 @@ fn createModule(
} }
if (target.os.tag == .wasi) { 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); try create_module.wasi_emulated_libs.append(arena, crt_file);
continue; continue;
} }
@ -3929,7 +3930,7 @@ fn createModule(
target, target,
info.preferred_mode, 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) { switch (info.preferred_mode) {
.static => try create_module.link_objects.append(arena, .{ .path = path }), .static => try create_module.link_objects.append(arena, .{ .path = path }),
.dynamic => try create_module.resolved_system_libs.append(arena, .{ .dynamic => try create_module.resolved_system_libs.append(arena, .{
@ -3963,7 +3964,7 @@ fn createModule(
target, target,
info.fallbackMode(), 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()) { switch (info.fallbackMode()) {
.static => try create_module.link_objects.append(arena, .{ .path = path }), .static => try create_module.link_objects.append(arena, .{ .path = path }),
.dynamic => try create_module.resolved_system_libs.append(arena, .{ .dynamic => try create_module.resolved_system_libs.append(arena, .{
@ -3997,7 +3998,7 @@ fn createModule(
target, target,
info.preferred_mode, 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) { switch (info.preferred_mode) {
.static => try create_module.link_objects.append(arena, .{ .path = path }), .static => try create_module.link_objects.append(arena, .{ .path = path }),
.dynamic => try create_module.resolved_system_libs.append(arena, .{ .dynamic => try create_module.resolved_system_libs.append(arena, .{
@ -4021,7 +4022,7 @@ fn createModule(
target, target,
info.fallbackMode(), 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()) { switch (info.fallbackMode()) {
.static => try create_module.link_objects.append(arena, .{ .path = path }), .static => try create_module.link_objects.append(arena, .{ .path = path }),
.dynamic => try create_module.resolved_system_libs.append(arena, .{ .dynamic => try create_module.resolved_system_libs.append(arena, .{
@ -6163,7 +6164,7 @@ fn cmdAstCheck(
} }
file.mod = try Package.Module.createLimited(arena, .{ file.mod = try Package.Module.createLimited(arena, .{
.root = Cache.Path.cwd(), .root = Path.cwd(),
.root_src_path = file.sub_file_path, .root_src_path = file.sub_file_path,
.fully_qualified_name = "root", .fully_qualified_name = "root",
}); });
@ -6523,7 +6524,7 @@ fn cmdChangelist(
}; };
file.mod = try Package.Module.createLimited(arena, .{ file.mod = try Package.Module.createLimited(arena, .{
.root = Cache.Path.cwd(), .root = Path.cwd(),
.root_src_path = file.sub_file_path, .root_src_path = file.sub_file_path,
.fully_qualified_name = "root", .fully_qualified_name = "root",
}); });

View file

@ -11,13 +11,13 @@ const build_options = @import("build_options");
const Cache = std.Build.Cache; const Cache = std.Build.Cache;
const dev = @import("dev.zig"); const dev = @import("dev.zig");
pub const CRTFile = enum { pub const CrtFile = enum {
crt2_o, crt2_o,
dllcrt2_o, dllcrt2_o,
mingw32_lib, 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) { if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions; return error.ZigCompilerNotBuiltWithLLVMExtensions;
} }
@ -160,7 +160,9 @@ fn add_cc_args(
pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
dev.check(.build_import_lib); 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(); defer arena_allocator.deinit();
const arena = arena_allocator.allocator(); const arena = arena_allocator.allocator();
@ -178,7 +180,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
// Use the global cache directory. // Use the global cache directory.
var cache: Cache = .{ var cache: Cache = .{
.gpa = comp.gpa, .gpa = gpa,
.manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
}; };
cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); 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); _ = try man.addFile(def_file_path, null);
const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); const final_lib_basename = try std.fmt.allocPrint(gpa, "{s}.lib", .{lib_name});
errdefer comp.gpa.free(final_lib_basename); errdefer gpa.free(final_lib_basename);
if (try man.hit()) { if (try man.hit()) {
const digest = man.final(); 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, .{ comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
.full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ .full_object_path = .{
"o", &digest, final_lib_basename, .root_dir = comp.global_cache_directory,
}), .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }),
},
.lock = man.toOwnedLock(), .lock = man.toOwnedLock(),
}); });
return; return;
@ -230,7 +233,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
}; };
const aro = @import("aro"); 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(); defer aro_comp.deinit();
const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }); 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; 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 builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines);
const user_macros = try aro_comp.addSourceFromBuffer("<command line>", target_defines); const user_macros = try aro_comp.addSourceFromBuffer("<command line>", 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); try pp.prettyPrintTokens(def_final_file.writer(), .result_only);
} }
const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename });
"o", &digest, final_lib_basename, errdefer gpa.free(lib_final_path);
});
errdefer comp.gpa.free(lib_final_path);
if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
const llvm_bindings = @import("codegen/llvm/bindings.zig"); const llvm_bindings = @import("codegen/llvm/bindings.zig");
const llvm = @import("codegen/llvm.zig"); const llvm = @import("codegen/llvm.zig");
const arch_tag = llvm.targetArch(target.cpu.arch); const arch_tag = llvm.targetArch(target.cpu.arch);
const def_final_path_z = try arena.dupeZ(u8, def_final_path); 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)) { if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) {
// TODO surface a proper error here // TODO surface a proper error here
log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); 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) }); 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, .{ try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{
.full_object_path = lib_final_path, .full_object_path = .{
.root_dir = comp.global_cache_directory,
.sub_path = lib_final_path,
},
.lock = man.toOwnedLock(), .lock = man.toOwnedLock(),
}); });
} }

View file

@ -9,7 +9,7 @@ const archName = std.zig.target.muslArchName;
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
pub const CRTFile = enum { pub const CrtFile = enum {
crti_o, crti_o,
crtn_o, crtn_o,
crt1_o, crt1_o,
@ -19,7 +19,7 @@ pub const CRTFile = enum {
libc_so, 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) { if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions; return error.ZigCompilerNotBuiltWithLLVMExtensions;
} }

View file

@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator;
const Compilation = @import("Compilation.zig"); const Compilation = @import("Compilation.zig");
const build_options = @import("build_options"); const build_options = @import("build_options");
pub const CRTFile = enum { pub const CrtFile = enum {
crt1_reactor_o, crt1_reactor_o,
crt1_command_o, crt1_command_o,
libc_a, libc_a,
@ -16,7 +16,7 @@ pub const CRTFile = enum {
libwasi_emulated_signal_a, 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")) { if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) {
return .libwasi_emulated_process_clocks_a; return .libwasi_emulated_process_clocks_a;
} }
@ -32,7 +32,7 @@ pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile {
return null; return null;
} }
pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 { pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 {
return switch (crt_file) { return switch (crt_file) {
.libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a", .libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a",
.libwasi_emulated_getpid_a => "libwasi-emulated-getpid.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) { return switch (wasi_exec_model) {
.reactor => CRTFile.crt1_reactor_o, .reactor => CrtFile.crt1_reactor_o,
.command => CRTFile.crt1_command_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) { if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions; return error.ZigCompilerNotBuiltWithLLVMExtensions;
} }