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

View File

@ -11,7 +11,11 @@ pub fn clone(p: Path, arena: Allocator) Allocator.Error!Path {
}
pub fn cwd() Path {
return .{ .root_dir = Cache.Directory.cwd() };
return initCwd("");
}
pub fn initCwd(sub_path: []const u8) Path {
return .{ .root_dir = Cache.Directory.cwd(), .sub_path = sub_path };
}
pub fn join(p: Path, arena: Allocator, sub_path: []const u8) Allocator.Error!Path {
@ -126,6 +130,14 @@ pub fn makePath(p: Path, sub_path: []const u8) !void {
return p.root_dir.handle.makePath(joined_path);
}
pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {
return std.fmt.allocPrint(allocator, "{}", .{p});
}
pub fn toStringZ(p: Path, allocator: Allocator) Allocator.Error![:0]u8 {
return std.fmt.allocPrintZ(allocator, "{}", .{p});
}
pub fn format(
self: Path,
comptime fmt_string: []const u8,
@ -182,6 +194,14 @@ pub fn subPathOrDot(self: Path) []const u8 {
return if (self.sub_path.len == 0) "." else self.sub_path;
}
pub fn stem(p: Path) []const u8 {
return fs.path.stem(p.sub_path);
}
pub fn basename(p: Path) []const u8 {
return fs.path.basename(p.sub_path);
}
/// Useful to make `Path` a key in `std.ArrayHashMap`.
pub const TableAdapter = struct {
pub const Hash = std.hash.Wyhash;

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

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
/// and resolved before calling linker.flush().
libcxx_static_lib: ?CRTFile = null,
libcxx_static_lib: ?CrtFile = null,
/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libcxxabi_static_lib: ?CRTFile = null,
libcxxabi_static_lib: ?CrtFile = null,
/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libunwind_static_lib: ?CRTFile = null,
libunwind_static_lib: ?CrtFile = null,
/// Populated when we build the TSAN library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
tsan_lib: ?CRTFile = null,
tsan_lib: ?CrtFile = null,
/// Populated when we build the libc static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
libc_static_lib: ?CRTFile = null,
libc_static_lib: ?CrtFile = null,
/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated
/// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush().
compiler_rt_lib: ?CRTFile = null,
compiler_rt_lib: ?CrtFile = null,
/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
compiler_rt_obj: ?CRTFile = null,
compiler_rt_obj: ?CrtFile = null,
/// Populated when we build the libfuzzer static library. A Job to build this
/// is indicated by setting `job_queued_fuzzer_lib` and resolved before
/// calling linker.flush().
fuzzer_lib: ?CRTFile = null,
fuzzer_lib: ?CrtFile = null,
glibc_so_files: ?glibc.BuiltSharedObjects = null,
wasi_emulated_libs: []const wasi_libc.CRTFile,
wasi_emulated_libs: []const wasi_libc.CrtFile,
/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
/// The key is the basename, and the value is the absolute path to the completed build artifact.
crt_files: std.StringHashMapUnmanaged(CRTFile) = .empty,
crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty,
/// How many lines of reference trace should be included per compile error.
/// Null means only show snippet on first error.
@ -276,20 +276,20 @@ digest: ?[Cache.bin_digest_len]u8 = null,
pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
pub const SemaError = Zcu.SemaError;
pub const CRTFile = struct {
pub const CrtFile = struct {
lock: Cache.Lock,
full_object_path: []const u8,
full_object_path: Path,
pub fn isObject(cf: CRTFile) bool {
return switch (classifyFileExt(cf.full_object_path)) {
pub fn isObject(cf: CrtFile) bool {
return switch (classifyFileExt(cf.full_object_path.sub_path)) {
.object => true,
else => false,
};
}
pub fn deinit(self: *CRTFile, gpa: Allocator) void {
pub fn deinit(self: *CrtFile, gpa: Allocator) void {
self.lock.release();
gpa.free(self.full_object_path);
gpa.free(self.full_object_path.sub_path);
self.* = undefined;
}
};
@ -369,13 +369,13 @@ const Job = union(enum) {
resolve_type_fully: InternPool.Index,
/// one of the glibc static objects
glibc_crt_file: glibc.CRTFile,
glibc_crt_file: glibc.CrtFile,
/// all of the glibc shared objects
glibc_shared_objects,
/// one of the musl static objects
musl_crt_file: musl.CRTFile,
musl_crt_file: musl.CrtFile,
/// one of the mingw-w64 static objects
mingw_crt_file: mingw.CRTFile,
mingw_crt_file: mingw.CrtFile,
/// libunwind.a, usually needed when linking libc
libunwind: void,
libcxx: void,
@ -385,7 +385,7 @@ const Job = union(enum) {
/// calls to, for example, memcpy and memset.
zig_libc: void,
/// one of WASI libc static objects
wasi_libc_crt_file: wasi_libc.CRTFile,
wasi_libc_crt_file: wasi_libc.CrtFile,
/// The value is the index into `system_libs`.
windows_import_lib: usize,
@ -422,8 +422,8 @@ pub const CObject = struct {
status: union(enum) {
new,
success: struct {
/// The outputted result. Owned by gpa.
object_path: []u8,
/// The outputted result. `sub_path` owned by gpa.
object_path: Path,
/// This is a file system lock on the cache hash manifest representing this
/// object. It prevents other invocations of the Zig compiler from interfering
/// with this object until released.
@ -719,7 +719,7 @@ pub const CObject = struct {
return true;
},
.success => |*success| {
gpa.free(success.object_path);
gpa.free(success.object_path.sub_path);
success.lock.release();
self.status = .new;
return false;
@ -1018,7 +1018,7 @@ const CacheUse = union(CacheMode) {
};
pub const LinkObject = struct {
path: []const u8,
path: Path,
must_link: bool = false,
// When the library is passed via a positional argument, it will be
// added as a full path. If it's `-l<lib>`, then just the basename.
@ -1027,7 +1027,7 @@ pub const LinkObject = struct {
loption: bool = false,
pub fn isObject(lo: LinkObject) bool {
return switch (classifyFileExt(lo.path)) {
return switch (classifyFileExt(lo.path.sub_path)) {
.object => true,
else => false,
};
@ -1095,7 +1095,7 @@ pub const CreateOptions = struct {
/// * getpid
/// * mman
/// * signal
wasi_emulated_libs: []const wasi_libc.CRTFile = &.{},
wasi_emulated_libs: []const wasi_libc.CrtFile = &.{},
/// This means that if the output mode is an executable it will be a
/// Position Independent Executable. If the output mode is not an
/// executable this field is ignored.
@ -2578,7 +2578,7 @@ fn addNonIncrementalStuffToCacheManifest(
}
for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
_ = try man.addFilePath(obj.path, null);
man.hash.add(obj.must_link);
man.hash.add(obj.loption);
}
@ -2703,9 +2703,8 @@ fn emitOthers(comp: *Compilation) void {
return;
}
const obj_path = comp.c_object_table.keys()[0].status.success.object_path;
const cwd = std.fs.cwd();
const ext = std.fs.path.extension(obj_path);
const basename = obj_path[0 .. obj_path.len - ext.len];
const ext = std.fs.path.extension(obj_path.sub_path);
const dirname = obj_path.sub_path[0 .. obj_path.sub_path.len - ext.len];
// This obj path always ends with the object file extension, but if we change the
// extension to .ll, .bc, or .s, then it will be the path to those things.
const outs = [_]struct {
@ -2720,13 +2719,13 @@ fn emitOthers(comp: *Compilation) void {
if (out.emit) |loc| {
if (loc.directory) |directory| {
const src_path = std.fmt.allocPrint(comp.gpa, "{s}{s}", .{
basename, out.ext,
dirname, out.ext,
}) catch |err| {
log.err("unable to copy {s}{s}: {s}", .{ basename, out.ext, @errorName(err) });
log.err("unable to copy {s}{s}: {s}", .{ dirname, out.ext, @errorName(err) });
continue;
};
defer comp.gpa.free(src_path);
cwd.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| {
obj_path.root_dir.handle.copyFile(src_path, directory.handle, loc.basename, .{}) catch |err| {
log.err("unable to copy {s}: {s}", .{ src_path, @errorName(err) });
};
}
@ -3774,7 +3773,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const named_frame = tracy.namedFrame("glibc_crt_file");
defer named_frame.end();
glibc.buildCRTFile(comp, crt_file, prog_node) catch |err| {
glibc.buildCrtFile(comp, crt_file, prog_node) catch |err| {
// TODO Surface more error details.
comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{
@errorName(err),
@ -3798,7 +3797,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const named_frame = tracy.namedFrame("musl_crt_file");
defer named_frame.end();
musl.buildCRTFile(comp, crt_file, prog_node) catch |err| {
musl.buildCrtFile(comp, crt_file, prog_node) catch |err| {
// TODO Surface more error details.
comp.lockAndSetMiscFailure(
.musl_crt_file,
@ -3811,7 +3810,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const named_frame = tracy.namedFrame("mingw_crt_file");
defer named_frame.end();
mingw.buildCRTFile(comp, crt_file, prog_node) catch |err| {
mingw.buildCrtFile(comp, crt_file, prog_node) catch |err| {
// TODO Surface more error details.
comp.lockAndSetMiscFailure(
.mingw_crt_file,
@ -3894,7 +3893,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const named_frame = tracy.namedFrame("wasi_libc_crt_file");
defer named_frame.end();
wasi_libc.buildCRTFile(comp, crt_file, prog_node) catch |err| {
wasi_libc.buildCrtFile(comp, crt_file, prog_node) catch |err| {
// TODO Surface more error details.
comp.lockAndSetMiscFailure(
.wasi_libc_crt_file,
@ -4602,7 +4601,7 @@ fn buildRt(
root_source_name: []const u8,
misc_task: MiscTask,
output_mode: std.builtin.OutputMode,
out: *?CRTFile,
out: *?CrtFile,
prog_node: std.Progress.Node,
) void {
comp.buildOutputFromZig(
@ -4703,7 +4702,9 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
log.debug("updating C object: {s}", .{c_object.src.src_path});
if (c_object.clearStatus(comp.gpa)) {
const gpa = comp.gpa;
if (c_object.clearStatus(gpa)) {
// There was previous failure.
comp.mutex.lock();
defer comp.mutex.unlock();
@ -4722,7 +4723,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
try cache_helpers.hashCSource(&man, c_object.src);
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
@ -4744,7 +4745,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
const target = comp.getTarget();
const o_ext = target.ofmt.fileExt(target.cpu.arch);
const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
var argv = std.ArrayList([]const u8).init(comp.gpa);
var argv = std.ArrayList([]const u8).init(gpa);
defer argv.deinit();
// In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
@ -4908,7 +4909,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
switch (term) {
.Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
const bundle = CObject.Diag.Bundle.parse(comp.gpa, diag_file_path) catch |err| {
const bundle = CObject.Diag.Bundle.parse(gpa, diag_file_path) catch |err| {
log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
return comp.failCObj(c_object, "clang exited with code {d}", .{code});
};
@ -4982,9 +4983,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr
c_object.status = .{
.success = .{
.object_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{
"o", &digest, o_basename,
}),
.object_path = .{
.root_dir = comp.local_cache_directory,
.sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, o_basename }),
},
.lock = man.toOwnedLock(),
},
};
@ -6092,18 +6094,23 @@ test "classifyFileExt" {
try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
}
pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
if (comp.wantBuildGLibCFromSource() or
comp.wantBuildMuslFromSource() or
comp.wantBuildMinGWFromSource() or
comp.wantBuildWasiLibcFromSource())
{
return comp.crt_files.get(basename).?.full_object_path;
}
const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
return full_path;
pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Path {
return (try crtFilePath(comp, basename)) orelse {
const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
return Path.initCwd(full_path);
};
}
pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
const path = try get_libc_crt_file(comp, arena, basename);
return path.toString(arena);
}
pub fn crtFilePath(comp: *Compilation, basename: []const u8) Allocator.Error!?Path {
const crt_file = comp.crt_files.get(basename) orelse return null;
return crt_file.full_object_path;
}
fn wantBuildLibCFromSource(comp: Compilation) bool {
@ -6314,7 +6321,7 @@ fn buildOutputFromZig(
comp: *Compilation,
src_basename: []const u8,
output_mode: std.builtin.OutputMode,
out: *?CRTFile,
out: *?CrtFile,
misc_task_tag: MiscTask,
prog_node: std.Progress.Node,
) !void {
@ -6542,15 +6549,39 @@ pub fn build_crt_file(
comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile());
}
pub fn toCrtFile(comp: *Compilation) Allocator.Error!CRTFile {
pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
return .{
.full_object_path = try comp.local_cache_directory.join(comp.gpa, &.{
comp.cache_use.whole.bin_sub_path.?,
}),
.full_object_path = .{
.root_dir = comp.local_cache_directory,
.sub_path = try comp.gpa.dupe(u8, comp.cache_use.whole.bin_sub_path.?),
},
.lock = comp.cache_use.whole.moveLock(),
};
}
pub fn getCrtPaths(
comp: *Compilation,
arena: Allocator,
) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
const target = comp.root_mod.resolved_target.result;
const basenames = LibCInstallation.CrtBasenames.get(.{
.target = target,
.link_libc = comp.config.link_libc,
.output_mode = comp.config.output_mode,
.link_mode = comp.config.link_mode,
.pie = comp.config.pie,
});
if (comp.libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
return .{
.crt0 = if (basenames.crt0) |basename| try comp.crtFilePath(basename) else null,
.crti = if (basenames.crti) |basename| try comp.crtFilePath(basename) else null,
.crtbegin = if (basenames.crtbegin) |basename| try comp.crtFilePath(basename) else null,
.crtend = if (basenames.crtend) |basename| try comp.crtFilePath(basename) else null,
.crtn = if (basenames.crtn) |basename| try comp.crtFilePath(basename) else null,
};
}
pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
// Avoid deadlocking on building import libs such as kernel32.lib
// This can happen when the user uses `build-exe foo.obj -lkernel32` and

View File

@ -169,14 +169,14 @@ fn useElfInitFini(target: std.Target) bool {
};
}
pub const CRTFile = enum {
pub const CrtFile = enum {
crti_o,
crtn_o,
scrt1_o,
libc_nonshared_a,
};
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void {
if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}
@ -292,7 +292,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre
.owner = undefined,
};
var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o };
return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files);
const basename = if (comp.config.output_mode == .Exe and !comp.config.pie) "crt1" else "Scrt1";
return comp.build_crt_file(basename, .Obj, .@"glibc Scrt1.o", prog_node, &files);
},
.libc_nonshared_a => {
const s = path.sep_str;

View File

@ -11,7 +11,7 @@ const wasi_libc = @import("wasi_libc.zig");
const Air = @import("Air.zig");
const Allocator = std.mem.Allocator;
const Cache = std.Build.Cache;
const Path = Cache.Path;
const Path = std.Build.Cache.Path;
const Compilation = @import("Compilation.zig");
const LibCInstallation = std.zig.LibCInstallation;
const Liveness = @import("Liveness.zig");
@ -34,7 +34,7 @@ pub const SystemLib = struct {
/// 1. Windows DLLs that zig ships such as advapi32.
/// 2. extern "foo" fn declarations where we find out about libraries too late
/// TODO: make this non-optional and resolve those two cases somehow.
path: ?[]const u8,
path: ?Path,
};
pub fn hashAddSystemLibs(
@ -46,7 +46,7 @@ pub fn hashAddSystemLibs(
for (hm.values()) |value| {
man.hash.add(value.needed);
man.hash.add(value.weak);
if (value.path) |p| _ = try man.addFile(p, null);
if (value.path) |p| _ = try man.addFilePath(p, null);
}
}
@ -551,7 +551,7 @@ pub const File = struct {
LLDCrashed,
LLDReportedFailure,
LLD_LinkingIsTODO_ForSpirV,
LibCInstallationMissingCRTDir,
LibCInstallationMissingCrtDir,
LibCInstallationNotAvailable,
LinkingWithoutZigSourceUnimplemented,
MalformedArchive,
@ -606,18 +606,15 @@ pub const File = struct {
const comp = base.comp;
if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
dev.check(.clang_command);
const gpa = comp.gpa;
const emit = base.emit;
// TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
// Until then, we do `lld -r -o output.o input.o` even though the output is the same
// as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
// to the final location. See also the corresponding TODO in Coff linking.
const full_out_path = try emit.root_dir.join(gpa, &[_][]const u8{emit.sub_path});
defer gpa.free(full_out_path);
assert(comp.c_object_table.count() == 1);
const the_key = comp.c_object_table.keys()[0];
const cached_pp_file_path = the_key.status.success.object_path;
try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{});
try cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{});
return;
}
@ -781,7 +778,7 @@ pub const File = struct {
log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"});
const compiler_rt_path: ?[]const u8 = if (comp.include_compiler_rt)
const compiler_rt_path: ?Path = if (comp.include_compiler_rt)
comp.compiler_rt_obj.?.full_object_path
else
null;
@ -806,18 +803,18 @@ pub const File = struct {
base.releaseLock();
for (objects) |obj| {
_ = try man.addFile(obj.path, null);
_ = try man.addFilePath(obj.path, null);
man.hash.add(obj.must_link);
man.hash.add(obj.loption);
}
for (comp.c_object_table.keys()) |key| {
_ = try man.addFile(key.status.success.object_path, null);
_ = try man.addFilePath(key.status.success.object_path, null);
}
for (comp.win32_resource_table.keys()) |key| {
_ = try man.addFile(key.status.success.res_path, null);
}
try man.addOptionalFile(zcu_obj_path);
try man.addOptionalFile(compiler_rt_path);
try man.addOptionalFilePath(compiler_rt_path);
// We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
_ = try man.hit();
@ -851,10 +848,10 @@ pub const File = struct {
defer object_files.deinit();
for (objects) |obj| {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path));
object_files.appendAssumeCapacity(try obj.path.toStringZ(arena));
}
for (comp.c_object_table.keys()) |key| {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path));
object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
}
for (comp.win32_resource_table.keys()) |key| {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
@ -863,7 +860,7 @@ pub const File = struct {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
}
if (compiler_rt_path) |p| {
object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
object_files.appendAssumeCapacity(try p.toStringZ(arena));
}
if (comp.verbose_link) {

View File

@ -7,6 +7,7 @@ const fs = std.fs;
const log = std.log.scoped(.link);
const mem = std.mem;
const Cache = std.Build.Cache;
const Path = std.Build.Cache.Path;
const mingw = @import("../../mingw.zig");
const link = @import("../../link.zig");
@ -74,11 +75,11 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
comptime assert(Compilation.link_hash_implementation_version == 14);
for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
_ = try man.addFilePath(obj.path, null);
man.hash.add(obj.must_link);
}
for (comp.c_object_table.keys()) |key| {
_ = try man.addFile(key.status.success.object_path, null);
_ = try man.addFilePath(key.status.success.object_path, null);
}
for (comp.win32_resource_table.keys()) |key| {
_ = try man.addFile(key.status.success.res_path, null);
@ -154,17 +155,19 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p|
break :blk p;
break :blk Path.initCwd(p);
// TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink;
};
// This can happen when using --enable-cache and using the stage1 backend. In this case
// we can skip the file copy.
if (!mem.eql(u8, the_object_path, full_out_path)) {
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
}
try std.fs.Dir.copyFile(
the_object_path.root_dir.handle,
the_object_path.sub_path,
directory.handle,
self.base.emit.sub_path,
.{},
);
} else {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa);
@ -270,14 +273,14 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
try argv.ensureUnusedCapacity(comp.objects.len);
for (comp.objects) |obj| {
if (obj.must_link) {
argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{}", .{@as(Path, obj.path)}));
} else {
argv.appendAssumeCapacity(obj.path);
argv.appendAssumeCapacity(try obj.path.toString(arena));
}
}
for (comp.c_object_table.keys()) |key| {
try argv.append(key.status.success.object_path);
try argv.append(try key.status.success.object_path.toString(arena));
}
for (comp.win32_resource_table.keys()) |key| {
@ -401,17 +404,17 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
}
if (is_dyn_lib) {
try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj"));
if (target.cpu.arch == .x86) {
try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
} else {
try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
}
} else {
try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
try argv.append(try comp.crtFileAsString(arena, "crt2.obj"));
}
try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
try argv.append(try comp.crtFileAsString(arena, "mingw32.lib"));
} else {
const lib_str = switch (comp.config.link_mode) {
.dynamic => "",
@ -456,36 +459,36 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
// libc++ dep
if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
try argv.append(comp.libcxx_static_lib.?.full_object_path);
try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
}
// libunwind dep
if (comp.config.link_libunwind) {
try argv.append(comp.libunwind_static_lib.?.full_object_path);
try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
}
if (comp.config.any_fuzz) {
try argv.append(comp.fuzzer_lib.?.full_object_path);
try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
}
if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
if (!comp.config.link_libc) {
if (comp.libc_static_lib) |lib| {
try argv.append(lib.full_object_path);
try argv.append(try lib.full_object_path.toString(arena));
}
}
// MSVC compiler_rt is missing some stuff, so we build it unconditionally but
// and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path);
if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path);
if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
}
try argv.ensureUnusedCapacity(comp.system_libs.count());
for (comp.system_libs.keys()) |key| {
const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
if (comp.crt_files.get(lib_basename)) |crt_file| {
argv.appendAssumeCapacity(crt_file.full_object_path);
argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
continue;
}
if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| {

View File

@ -383,9 +383,9 @@ pub fn createEmpty(
const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .zig_object = .{
.index = index,
.path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem(
zcu.main_mod.root_src_path,
)}),
.basename = try std.fmt.allocPrint(arena, "{s}.o", .{
fs.path.stem(zcu.main_mod.root_src_path),
}),
} });
self.zig_object_index = index;
try self.zigObjectPtr().?.init(self, .{
@ -742,13 +742,12 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
const target = self.getTarget();
const link_mode = comp.config.link_mode;
const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, path });
} else {
break :blk path;
}
const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
.root_dir = directory,
.sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
try fs.path.join(arena, &.{ dirname, path })
else
path,
} else null;
// --verbose-link
@ -758,7 +757,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
const csu = try CsuObjects.init(arena, comp);
const csu = try comp.getCrtPaths(arena);
// csu prelude
if (csu.crt0) |path| try parseObjectReportingFailure(self, path);
@ -790,23 +789,22 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib);
}
var system_libs = std.ArrayList(SystemLib).init(arena);
try system_libs.ensureUnusedCapacity(comp.system_libs.values().len);
for (comp.system_libs.values()) |lib_info| {
system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? });
try self.parseLibraryReportingFailure(.{
.needed = lib_info.needed,
.path = lib_info.path.?,
}, false);
}
// libc++ dep
if (comp.config.link_libcpp) {
try system_libs.ensureUnusedCapacity(2);
system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
try self.parseLibraryReportingFailure(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }, false);
try self.parseLibraryReportingFailure(.{ .path = comp.libcxx_static_lib.?.full_object_path }, false);
}
// libunwind dep
if (comp.config.link_libunwind) {
try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path });
try self.parseLibraryReportingFailure(.{ .path = comp.libunwind_static_lib.?.full_object_path }, false);
}
// libc dep
@ -814,7 +812,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
if (comp.config.link_libc) {
if (comp.libc_installation) |lc| {
const flags = target_util.libcFullLinkFlags(target);
try system_libs.ensureUnusedCapacity(flags.len);
var test_path = std.ArrayList(u8).init(arena);
var checked_paths = std.ArrayList([]const u8).init(arena);
@ -840,39 +837,34 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
continue;
}
const resolved_path = try arena.dupe(u8, test_path.items);
system_libs.appendAssumeCapacity(.{ .path = resolved_path });
const resolved_path = Path.initCwd(try arena.dupe(u8, test_path.items));
try self.parseLibraryReportingFailure(.{ .path = resolved_path }, false);
}
} else if (target.isGnuLibC()) {
try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
for (glibc.libs) |lib| {
if (lib.removed_in) |rem_in| {
if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
}
const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
const lib_path = Path.initCwd(try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
});
system_libs.appendAssumeCapacity(.{ .path = lib_path });
}));
try self.parseLibraryReportingFailure(.{ .path = lib_path }, false);
}
system_libs.appendAssumeCapacity(.{
try self.parseLibraryReportingFailure(.{
.path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
});
}, false);
} else if (target.isMusl()) {
const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
.static => "libc.a",
.dynamic => "libc.so",
});
try system_libs.append(.{ .path = path });
try self.parseLibraryReportingFailure(.{ .path = path }, false);
} else {
comp.link_error_flags.missing_libc = true;
}
}
for (system_libs.items) |lib| {
try self.parseLibraryReportingFailure(lib, false);
}
// Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
// compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
@ -1066,10 +1058,10 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
} else null;
const csu = try CsuObjects.init(arena, comp);
const csu = try comp.getCrtPaths(arena);
const compiler_rt_path: ?[]const u8 = blk: {
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
if (comp.compiler_rt_lib) |x| break :blk try x.full_object_path.toString(arena);
if (comp.compiler_rt_obj) |x| break :blk try x.full_object_path.toString(arena);
break :blk null;
};
@ -1092,11 +1084,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
if (self.base.isRelocatable()) {
for (comp.objects) |obj| {
try argv.append(obj.path);
try argv.append(try obj.path.toString(arena));
}
for (comp.c_object_table.keys()) |key| {
try argv.append(key.status.success.object_path);
try argv.append(try key.status.success.object_path.toString(arena));
}
if (module_obj_path) |p| {
@ -1178,9 +1170,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
// csu prelude
if (csu.crt0) |v| try argv.append(v);
if (csu.crti) |v| try argv.append(v);
if (csu.crtbegin) |v| try argv.append(v);
if (csu.crt0) |path| try argv.append(try path.toString(arena));
if (csu.crti) |path| try argv.append(try path.toString(arena));
if (csu.crtbegin) |path| try argv.append(try path.toString(arena));
for (self.lib_dirs) |lib_dir| {
try argv.append("-L");
@ -1205,10 +1197,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
if (obj.loption) {
assert(obj.path[0] == ':');
try argv.append("-l");
}
try argv.append(obj.path);
try argv.append(try obj.path.toString(arena));
}
if (whole_archive) {
try argv.append("-no-whole-archive");
@ -1216,7 +1207,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
for (comp.c_object_table.keys()) |key| {
try argv.append(key.status.success.object_path);
try argv.append(try key.status.success.object_path.toString(arena));
}
if (module_obj_path) |p| {
@ -1224,17 +1215,17 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
if (comp.config.any_sanitize_thread) {
try argv.append(comp.tsan_lib.?.full_object_path);
try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena));
}
if (comp.config.any_fuzz) {
try argv.append(comp.fuzzer_lib.?.full_object_path);
try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
}
// libc
if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
if (comp.libc_static_lib) |lib| {
try argv.append(lib.full_object_path);
try argv.append(try lib.full_object_path.toString(arena));
}
}
@ -1258,7 +1249,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
as_needed = true;
},
}
argv.appendAssumeCapacity(lib_info.path.?);
argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
}
if (!as_needed) {
@ -1268,13 +1259,13 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
// libc++ dep
if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
try argv.append(comp.libcxx_static_lib.?.full_object_path);
try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
}
// libunwind dep
if (comp.config.link_libunwind) {
try argv.append(comp.libunwind_static_lib.?.full_object_path);
try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
}
// libc dep
@ -1295,9 +1286,9 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
});
try argv.append(lib_path);
}
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
} else if (target.isMusl()) {
try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) {
try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
.static => "libc.a",
.dynamic => "libc.so",
}));
@ -1310,8 +1301,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
}
// crt postlude
if (csu.crtend) |v| try argv.append(v);
if (csu.crtn) |v| try argv.append(v);
if (csu.crtend) |path| try argv.append(try path.toString(arena));
if (csu.crtn) |path| try argv.append(try path.toString(arena));
}
Compilation.dump_argv(argv.items);
@ -1331,7 +1322,7 @@ pub const ParseError = error{
UnknownFileType,
} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void {
fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CrtFile) error{OutOfMemory}!void {
if (crt_file.isObject()) {
try parseObjectReportingFailure(self, crt_file.full_object_path);
} else {
@ -1339,7 +1330,7 @@ fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error
}
}
pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void {
pub fn parseObjectReportingFailure(self: *Elf, path: Path) error{OutOfMemory}!void {
self.parseObject(path) catch |err| switch (err) {
error.LinkFailure => return, // already reported
error.OutOfMemory => return error.OutOfMemory,
@ -1367,17 +1358,20 @@ fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
}
}
fn parseObject(self: *Elf, path: []const u8) ParseError!void {
fn parseObject(self: *Elf, path: Path) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
const handle = try fs.cwd().openFile(path, .{});
const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
const fh = try self.addFileHandle(handle);
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .object = .{
.path = try gpa.dupe(u8, path),
.path = .{
.root_dir = path.root_dir,
.sub_path = try gpa.dupe(u8, path.sub_path),
},
.file_handle = fh,
.index = index,
} });
@ -1387,15 +1381,15 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
try object.parse(self);
}
fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
fn parseArchive(self: *Elf, path: Path, must_link: bool) ParseError!void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
const handle = try fs.cwd().openFile(path, .{});
const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
const fh = try self.addFileHandle(handle);
var archive = Archive{};
var archive: Archive = .{};
defer archive.deinit(gpa);
try archive.parse(self, path, fh);
@ -1403,7 +1397,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
defer gpa.free(objects);
for (objects) |extracted| {
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .object = extracted });
const object = &self.files.items(.data)[index].object;
object.index = index;
@ -1418,12 +1412,15 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
defer tracy.end();
const gpa = self.base.comp.gpa;
const handle = try fs.cwd().openFile(lib.path, .{});
const handle = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
defer handle.close();
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
self.files.set(index, .{ .shared_object = .{
.path = try gpa.dupe(u8, lib.path),
.path = .{
.root_dir = lib.path.root_dir,
.sub_path = try gpa.dupe(u8, lib.path.sub_path),
},
.index = index,
.needed = lib.needed,
.alive = lib.needed,
@ -1439,12 +1436,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
defer tracy.end();
const gpa = self.base.comp.gpa;
const in_file = try fs.cwd().openFile(lib.path, .{});
const in_file = try lib.path.root_dir.handle.openFile(lib.path.sub_path, .{});
defer in_file.close();
const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
defer gpa.free(data);
var script = LdScript{ .path = lib.path };
var script: LdScript = .{ .path = lib.path };
defer script.deinit(gpa);
try script.parse(data, self);
@ -1455,12 +1452,12 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
var test_path = std.ArrayList(u8).init(arena);
var checked_paths = std.ArrayList([]const u8).init(arena);
for (script.args.items) |scr_obj| {
for (script.args.items) |script_arg| {
checked_paths.clearRetainingCapacity();
success: {
if (mem.startsWith(u8, scr_obj.path, "-l")) {
const lib_name = scr_obj.path["-l".len..];
if (mem.startsWith(u8, script_arg.path, "-l")) {
const lib_name = script_arg.path["-l".len..];
// TODO I think technically we should re-use the mechanism used by the frontend here.
// Maybe we should hoist search-strategy all the way here?
@ -1474,33 +1471,30 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
}
} else {
var buffer: [fs.max_path_bytes]u8 = undefined;
if (fs.realpath(scr_obj.path, &buffer)) |path| {
if (fs.realpath(script_arg.path, &buffer)) |path| {
test_path.clearRetainingCapacity();
try test_path.writer().writeAll(path);
break :success;
} else |_| {}
try checked_paths.append(try arena.dupe(u8, scr_obj.path));
try checked_paths.append(try arena.dupe(u8, script_arg.path));
for (self.lib_dirs) |lib_dir| {
if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, scr_obj.path, null))
if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, script_arg.path, null))
break :success;
}
}
try self.reportMissingLibraryError(
checked_paths.items,
"missing library dependency: GNU ld script '{s}' requires '{s}', but file not found",
.{
lib.path,
scr_obj.path,
},
"missing library dependency: GNU ld script '{}' requires '{s}', but file not found",
.{ @as(Path, lib.path), script_arg.path },
);
continue;
}
const full_path = test_path.items;
const full_path = Path.initCwd(test_path.items);
self.parseLibrary(.{
.needed = scr_obj.needed,
.needed = script_arg.needed,
.path = full_path,
}, false) catch |err| switch (err) {
error.LinkFailure => continue, // already reported
@ -1841,7 +1835,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
const have_dynamic_linker = comp.config.link_libc and
link_mode == .dynamic and is_exe_or_dyn_lib;
const target = self.getTarget();
const compiler_rt_path: ?[]const u8 = blk: {
const compiler_rt_path: ?Path = blk: {
if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
break :blk null;
@ -1875,17 +1869,17 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
man.hash.add(self.allow_undefined_version);
man.hash.addOptional(self.enable_new_dtags);
for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
_ = try man.addFilePath(obj.path, null);
man.hash.add(obj.must_link);
man.hash.add(obj.loption);
}
for (comp.c_object_table.keys()) |key| {
_ = try man.addFile(key.status.success.object_path, null);
_ = try man.addFilePath(key.status.success.object_path, null);
}
try man.addOptionalFile(module_obj_path);
try man.addOptionalFile(compiler_rt_path);
try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null);
try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null);
try man.addOptionalFilePath(compiler_rt_path);
try man.addOptionalFilePath(if (comp.tsan_lib) |l| l.full_object_path else null);
try man.addOptionalFilePath(if (comp.fuzzer_lib) |l| l.full_object_path else null);
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
// installation sources because they are always a product of the compiler version + target information.
@ -1982,17 +1976,19 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p|
break :blk p;
break :blk Path.initCwd(p);
// TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink;
};
// This can happen when using --enable-cache and using the stage1 backend. In this case
// we can skip the file copy.
if (!mem.eql(u8, the_object_path, full_out_path)) {
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
}
try std.fs.Dir.copyFile(
the_object_path.root_dir.handle,
the_object_path.sub_path,
directory.handle,
self.base.emit.sub_path,
.{},
);
} else {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa);
@ -2177,10 +2173,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
try argv.append(full_out_path);
// csu prelude
const csu = try CsuObjects.init(arena, comp);
if (csu.crt0) |v| try argv.append(v);
if (csu.crti) |v| try argv.append(v);
if (csu.crtbegin) |v| try argv.append(v);
const csu = try comp.getCrtPaths(arena);
if (csu.crt0) |p| try argv.append(try p.toString(arena));
if (csu.crti) |p| try argv.append(try p.toString(arena));
if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
for (self.rpath_table.keys()) |rpath| {
try argv.appendSlice(&.{ "-rpath", rpath });
@ -2244,10 +2240,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
}
if (obj.loption) {
assert(obj.path[0] == ':');
assert(obj.path.sub_path[0] == ':');
try argv.append("-l");
}
try argv.append(obj.path);
try argv.append(try obj.path.toString(arena));
}
if (whole_archive) {
try argv.append("-no-whole-archive");
@ -2255,7 +2251,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
}
for (comp.c_object_table.keys()) |key| {
try argv.append(key.status.success.object_path);
try argv.append(try key.status.success.object_path.toString(arena));
}
if (module_obj_path) |p| {
@ -2264,12 +2260,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
if (comp.tsan_lib) |lib| {
assert(comp.config.any_sanitize_thread);
try argv.append(lib.full_object_path);
try argv.append(try lib.full_object_path.toString(arena));
}
if (comp.fuzzer_lib) |lib| {
assert(comp.config.any_fuzz);
try argv.append(lib.full_object_path);
try argv.append(try lib.full_object_path.toString(arena));
}
// libc
@ -2278,7 +2274,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
!comp.config.link_libc)
{
if (comp.libc_static_lib) |lib| {
try argv.append(lib.full_object_path);
try argv.append(try lib.full_object_path.toString(arena));
}
}
@ -2311,7 +2307,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
// libraries and not static libraries (the check for that needs to be earlier),
// but they could be full paths to .so files, in which case we
// want to avoid prepending "-l".
argv.appendAssumeCapacity(lib_info.path.?);
argv.appendAssumeCapacity(try lib_info.path.?.toString(arena));
}
if (!as_needed) {
@ -2321,13 +2317,13 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
// libc++ dep
if (comp.config.link_libcpp) {
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
try argv.append(comp.libcxx_static_lib.?.full_object_path);
try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
}
// libunwind dep
if (comp.config.link_libunwind) {
try argv.append(comp.libunwind_static_lib.?.full_object_path);
try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
}
// libc dep
@ -2349,9 +2345,9 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
});
try argv.append(lib_path);
}
try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
} else if (target.isMusl()) {
try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) {
try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
.static => "libc.a",
.dynamic => "libc.so",
}));
@ -2365,12 +2361,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
// to be after the shared libraries, so they are picked up from the shared
// libraries, not libcompiler_rt.
if (compiler_rt_path) |p| {
try argv.append(p);
try argv.append(try p.toString(arena));
}
// crt postlude
if (csu.crtend) |v| try argv.append(v);
if (csu.crtn) |v| try argv.append(v);
if (csu.crtend) |p| try argv.append(try p.toString(arena));
if (csu.crtn) |p| try argv.append(try p.toString(arena));
if (self.base.allow_shlib_undefined) {
try argv.append("--allow-shlib-undefined");
@ -3183,8 +3179,9 @@ fn sortInitFini(self: *Elf) !void {
const object = atom_ptr.file(self).?.object;
const priority = blk: {
if (is_ctor_dtor) {
if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32);
if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32);
const basename = object.path.basename();
if (mem.eql(u8, basename, "crtbegin.o")) break :blk std.math.minInt(i32);
if (mem.eql(u8, basename, "crtend.o")) break :blk std.math.maxInt(i32);
}
const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32);
const name = atom_ptr.name(self);
@ -4472,210 +4469,6 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
return actual_size +| (actual_size / ideal_factor);
}
// Provide a blueprint of csu (c-runtime startup) objects for supported
// link modes.
//
// This is for cross-mode targets only. For host-mode targets the system
// compiler can be probed to produce a robust blueprint.
//
// Targets requiring a libc for which zig does not bundle a libc are
// host-mode targets. Unfortunately, host-mode probes are not yet
// implemented. For now the data is hard-coded here. Such targets are
// { freebsd, netbsd, openbsd, dragonfly }.
const CsuObjects = struct {
crt0: ?[]const u8 = null,
crti: ?[]const u8 = null,
crtbegin: ?[]const u8 = null,
crtend: ?[]const u8 = null,
crtn: ?[]const u8 = null,
const InitArgs = struct {};
fn init(arena: Allocator, comp: *const Compilation) !CsuObjects {
// crt objects are only required for libc.
if (!comp.config.link_libc) return .{};
var result: CsuObjects = .{};
// Flatten crt cases.
const mode: enum {
dynamic_lib,
dynamic_exe,
dynamic_pie,
static_exe,
static_pie,
} = switch (comp.config.output_mode) {
.Obj => return CsuObjects{},
.Lib => switch (comp.config.link_mode) {
.dynamic => .dynamic_lib,
.static => return CsuObjects{},
},
.Exe => switch (comp.config.link_mode) {
.dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe,
.static => if (comp.config.pie) .static_pie else .static_exe,
},
};
const target = comp.root_mod.resolved_target.result;
if (target.isAndroid()) {
switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ),
.dynamic_exe,
.dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ),
.static_exe,
.static_pie => result.set( null, null, "crtbegin_static.o", "crtend_android.o", null ),
// zig fmt: on
}
} else {
switch (target.os.tag) {
.linux => {
switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ),
.static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
// zig fmt: on
}
if (comp.libc_installation) |_| {
// hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs
// and they are not known at comptime. For now null-out crtbegin/end objects;
// there is no feature loss, zig has never linked those objects in before.
result.crtbegin = null;
result.crtend = null;
} else {
// Bundled glibc only has Scrt1.o .
if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o";
}
},
.dragonfly => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.static_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
// zig fmt: on
},
.freebsd => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.dynamic_exe => result.set( "crt1.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.static_exe => result.set( "crt1.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ),
.static_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
// zig fmt: on
},
.netbsd => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.static_exe => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o", "crtn.o" ),
.static_pie => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ),
// zig fmt: on
},
.openbsd => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, null, "crtbeginS.o", "crtendS.o", null ),
.dynamic_exe,
.dynamic_pie => result.set( "crt0.o", null, "crtbegin.o", "crtend.o", null ),
.static_exe,
.static_pie => result.set( "rcrt0.o", null, "crtbegin.o", "crtend.o", null ),
// zig fmt: on
},
.haiku => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
.static_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o", "crtend.o", "crtn.o" ),
.static_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
// zig fmt: on
},
.solaris, .illumos => switch (mode) {
// zig fmt: off
.dynamic_lib => result.set( null, "crti.o", null, null, "crtn.o" ),
.dynamic_exe,
.dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ),
.static_exe,
.static_pie => result.set( null, null, null, null, null ),
// zig fmt: on
},
else => {},
}
}
// Convert each object to a full pathname.
if (comp.libc_installation) |lci| {
const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
switch (target.os.tag) {
.dragonfly => {
if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
var gccv: []const u8 = undefined;
if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) {
gccv = "gcc80";
} else {
gccv = "gcc54";
}
if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
},
.haiku => {
const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir;
if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
},
else => {
inline for (std.meta.fields(@TypeOf(result))) |f| {
if (@field(result, f.name)) |*obj| {
obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
}
}
},
}
} else {
inline for (std.meta.fields(@TypeOf(result))) |f| {
if (@field(result, f.name)) |*obj| {
if (comp.crt_files.get(obj.*)) |crtf| {
obj.* = crtf.full_object_path;
} else {
@field(result, f.name) = null;
}
}
}
}
return result;
}
fn set(
self: *CsuObjects,
crt0: ?[]const u8,
crti: ?[]const u8,
crtbegin: ?[]const u8,
crtend: ?[]const u8,
crtn: ?[]const u8,
) void {
self.crt0 = crt0;
self.crti = crti;
self.crtbegin = crtbegin;
self.crtend = crtend;
self.crtn = crtn;
}
};
/// If a target compiles other output modes as dynamic libraries,
/// this function returns true for those too.
pub fn isEffectivelyDynLib(self: Elf) bool {
@ -5089,13 +4882,13 @@ fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
pub fn addParseError(
self: *Elf,
path: []const u8,
path: Path,
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
var err = try self.base.addErrorWithNotes(1);
try err.addMsg(format, args);
try err.addNote("while parsing {s}", .{path});
try err.addNote("while parsing {}", .{path});
}
pub fn addFileError(
@ -5121,7 +4914,7 @@ pub fn failFile(
pub fn failParse(
self: *Elf,
path: []const u8,
path: Path,
comptime format: []const u8,
args: anytype,
) error{ OutOfMemory, LinkFailure } {
@ -5274,7 +5067,7 @@ fn fmtDumpState(
_ = options;
if (self.zigObjectPtr()) |zig_object| {
try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path });
try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.basename });
try writer.print("{}{}", .{
zig_object.fmtAtoms(self),
zig_object.fmtSymtab(self),
@ -5299,7 +5092,7 @@ fn fmtDumpState(
for (self.shared_objects.items) |index| {
const shared_object = self.file(index).?.shared_object;
try writer.print("shared_object({d}) : ", .{index});
try writer.print("{s}", .{shared_object.path});
try writer.print("{}", .{shared_object.path});
try writer.print(" : needed({})", .{shared_object.needed});
if (!shared_object.alive) try writer.writeAll(" : [*]");
try writer.writeByte('\n');
@ -5482,7 +5275,7 @@ pub const null_shdr = elf.Elf64_Shdr{
pub const SystemLib = struct {
needed: bool = false,
path: []const u8,
path: Path,
};
pub const Ref = struct {

View File

@ -1,8 +1,8 @@
objects: std.ArrayListUnmanaged(Object) = .empty,
strtab: std.ArrayListUnmanaged(u8) = .empty,
pub fn isArchive(path: []const u8) !bool {
const file = try std.fs.cwd().openFile(path, .{});
pub fn isArchive(path: Path) !bool {
const file = try path.root_dir.handle.openFile(path.sub_path, .{});
defer file.close();
const reader = file.reader();
const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false;
@ -15,7 +15,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
self.strtab.deinit(allocator);
}
pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: File.HandleIndex) !void {
pub fn parse(self: *Archive, elf_file: *Elf, path: Path, handle_index: File.HandleIndex) !void {
const comp = elf_file.base.comp;
const gpa = comp.gpa;
const handle = elf_file.fileHandle(handle_index);
@ -59,19 +59,24 @@ pub fn parse(self: *Archive, elf_file: *Elf, path: []const u8, handle_index: Fil
else
unreachable;
const object = Object{
const object: Object = .{
.archive = .{
.path = try gpa.dupe(u8, path),
.path = .{
.root_dir = path.root_dir,
.sub_path = try gpa.dupe(u8, path.sub_path),
},
.offset = pos,
.size = obj_size,
},
.path = try gpa.dupe(u8, name),
.path = Path.initCwd(try gpa.dupe(u8, name)),
.file_handle = handle_index,
.index = undefined,
.alive = false,
};
log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path });
log.debug("extracting object '{}' from archive '{}'", .{
@as(Path, object.path), @as(Path, path),
});
try self.objects.append(gpa, object);
}
@ -292,6 +297,7 @@ const elf = std.elf;
const fs = std.fs;
const log = std.log.scoped(.link);
const mem = std.mem;
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator;
const Archive = @This();

View File

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

View File

@ -1,5 +1,7 @@
archive: ?InArchive = null,
path: []const u8,
/// Archive files cannot contain subdirectories, so only the basename is needed
/// for output. However, the full path is kept for error reporting.
path: Path,
file_handle: File.HandleIndex,
index: File.Index,
@ -36,8 +38,8 @@ output_symtab_ctx: Elf.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.archive) |*ar| allocator.free(ar.path);
allocator.free(self.path);
if (self.archive) |*ar| allocator.free(ar.path.sub_path);
allocator.free(self.path.sub_path);
self.shdrs.deinit(allocator);
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
@ -474,8 +476,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
if (sym.type(elf_file) != elf.STT_FUNC)
// TODO convert into an error
log.debug("{s}: {s}: CIE referencing external data reference", .{
self.fmtPath(),
sym.name(elf_file),
self.fmtPath(), sym.name(elf_file),
});
sym.flags.needs_plt = true;
}
@ -996,7 +997,7 @@ pub fn updateArSize(self: *Object, elf_file: *Elf) !void {
pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
const offset: u64 = if (self.archive) |ar| ar.offset else 0;
const name = self.path;
const name = std.fs.path.basename(self.path.sub_path);
const hdr = Archive.setArHdr(.{
.name = if (name.len <= Archive.max_member_name_len)
.{ .name = name }
@ -1489,15 +1490,14 @@ fn formatPath(
_ = unused_fmt_string;
_ = options;
if (object.archive) |ar| {
try writer.writeAll(ar.path);
try writer.writeByte('(');
try writer.writeAll(object.path);
try writer.writeByte(')');
} else try writer.writeAll(object.path);
try writer.print("{}({})", .{ ar.path, object.path });
} else {
try writer.print("{}", .{object.path});
}
}
const InArchive = struct {
path: []const u8,
path: Path,
offset: u64,
size: u32,
};
@ -1512,8 +1512,9 @@ const fs = std.fs;
const log = std.log.scoped(.link);
const math = std.math;
const mem = std.mem;
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const AtomList = @import("AtomList.zig");

View File

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

View File

@ -5,7 +5,7 @@
data: std.ArrayListUnmanaged(u8) = .empty,
/// Externally owned memory.
path: []const u8,
basename: []const u8,
index: File.Index,
symtab: std.MultiArrayList(ElfSym) = .{},
@ -88,7 +88,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf, options: InitOptions) !void {
try self.strtab.buffer.append(gpa, 0);
{
const name_off = try self.strtab.insert(gpa, self.path);
const name_off = try self.strtab.insert(gpa, self.basename);
const symbol_index = try self.newLocalSymbol(gpa, name_off);
const sym = self.symbol(symbol_index);
const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
@ -774,7 +774,7 @@ pub fn updateArSize(self: *ZigObject) void {
}
pub fn writeAr(self: ZigObject, writer: anytype) !void {
const name = self.path;
const name = self.basename;
const hdr = Archive.setArHdr(.{
.name = if (name.len <= Archive.max_member_name_len)
.{ .name = name }
@ -2384,9 +2384,9 @@ const relocation = @import("relocation.zig");
const target_util = @import("../../target.zig");
const trace = @import("../../tracy.zig").trace;
const std = @import("std");
const Allocator = std.mem.Allocator;
const Air = @import("../../Air.zig");
const Allocator = std.mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const Dwarf = @import("../Dwarf.zig");

View File

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

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;
for (comp.objects) |obj| {
switch (Compilation.classifyFileExt(obj.path)) {
switch (Compilation.classifyFileExt(obj.path.sub_path)) {
.object => try parseObjectStaticLibReportingFailure(elf_file, obj.path),
.static_library => try parseArchiveStaticLibReportingFailure(elf_file, obj.path),
else => try elf_file.addParseError(obj.path, "unrecognized file extension", .{}),
@ -140,7 +140,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
if (elf_file.base.hasErrors()) return error.FlushFailure;
}
pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
for (comp.objects) |obj| {
if (obj.isObject()) {
try elf_file.parseObjectReportingFailure(obj.path);
@ -198,7 +198,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
if (elf_file.base.hasErrors()) return error.FlushFailure;
}
fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void {
fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void {
parseObjectStaticLib(elf_file, path) catch |err| switch (err) {
error.LinkFailure => return,
error.OutOfMemory => return error.OutOfMemory,
@ -206,7 +206,7 @@ fn parseObjectStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{
};
}
fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error{OutOfMemory}!void {
fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: Path) error{OutOfMemory}!void {
parseArchiveStaticLib(elf_file, path) catch |err| switch (err) {
error.LinkFailure => return,
error.OutOfMemory => return error.OutOfMemory,
@ -214,14 +214,17 @@ fn parseArchiveStaticLibReportingFailure(elf_file: *Elf, path: []const u8) error
};
}
fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
fn parseObjectStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
const gpa = elf_file.base.comp.gpa;
const handle = try std.fs.cwd().openFile(path, .{});
const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
const fh = try elf_file.addFileHandle(handle);
const index = @as(File.Index, @intCast(try elf_file.files.addOne(gpa)));
const index: File.Index = @intCast(try elf_file.files.addOne(gpa));
elf_file.files.set(index, .{ .object = .{
.path = try gpa.dupe(u8, path),
.path = .{
.root_dir = path.root_dir,
.sub_path = try gpa.dupe(u8, path.sub_path),
},
.file_handle = fh,
.index = index,
} });
@ -231,9 +234,9 @@ fn parseObjectStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
try object.parseAr(elf_file);
}
fn parseArchiveStaticLib(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
fn parseArchiveStaticLib(elf_file: *Elf, path: Path) Elf.ParseError!void {
const gpa = elf_file.base.comp.gpa;
const handle = try std.fs.cwd().openFile(path, .{});
const handle = try path.root_dir.handle.openFile(path.sub_path, .{});
const fh = try elf_file.addFileHandle(handle);
var archive = Archive{};
@ -531,6 +534,7 @@ const log = std.log.scoped(.link);
const math = std.math;
const mem = std.mem;
const state_log = std.log.scoped(.link_state);
const Path = std.Build.Cache.Path;
const std = @import("std");
const Archive = @import("Archive.zig");

View File

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

View File

@ -4,7 +4,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
self.objects.deinit(allocator);
}
pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
pub fn unpack(self: *Archive, macho_file: *MachO, path: Path, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
const gpa = macho_file.base.comp.gpa;
var arena = std.heap.ArenaAllocator.init(gpa);
@ -55,20 +55,23 @@ pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index
mem.eql(u8, name, SYMDEF_SORTED) or
mem.eql(u8, name, SYMDEF64_SORTED)) continue;
const object = Object{
const object: Object = .{
.offset = pos,
.in_archive = .{
.path = try gpa.dupe(u8, path),
.path = .{
.root_dir = path.root_dir,
.sub_path = try gpa.dupe(u8, path.sub_path),
},
.size = hdr_size,
},
.path = try gpa.dupe(u8, name),
.path = Path.initCwd(try gpa.dupe(u8, name)),
.file_handle = handle_index,
.index = undefined,
.alive = false,
.mtime = hdr.date() catch 0,
};
log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path });
log.debug("extracting object '{}' from archive '{}'", .{ object.path, path });
try self.objects.append(gpa, object);
}
@ -301,8 +304,9 @@ const log = std.log.scoped(.link);
const macho = std.macho;
const mem = std.mem;
const std = @import("std");
const Allocator = mem.Allocator;
const Path = std.Build.Cache.Path;
const Archive = @This();
const File = @import("file.zig").File;
const MachO = @import("../MachO.zig");

View File

@ -1,6 +1,6 @@
/// Non-zero for fat dylibs
offset: u64,
path: []const u8,
path: Path,
index: File.Index,
file_handle: File.HandleIndex,
tag: enum { dylib, tbd },
@ -28,7 +28,7 @@ referenced: bool = false,
output_symtab_ctx: MachO.SymtabCtx = .{},
pub fn deinit(self: *Dylib, allocator: Allocator) void {
allocator.free(self.path);
allocator.free(self.path.sub_path);
self.exports.deinit(allocator);
self.strtab.deinit(allocator);
if (self.id) |*id| id.deinit(allocator);
@ -61,7 +61,7 @@ fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
const file = macho_file.getFileHandle(self.file_handle);
const offset = self.offset;
log.debug("parsing dylib from binary: {s}", .{self.path});
log.debug("parsing dylib from binary: {}", .{@as(Path, self.path)});
var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{
@ -267,7 +267,7 @@ fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
log.debug("parsing dylib from stub: {s}", .{self.path});
log.debug("parsing dylib from stub: {}", .{self.path});
const file = macho_file.getFileHandle(self.file_handle);
var lib_stub = LibStub.loadFromFile(gpa, file) catch |err| {
@ -959,8 +959,9 @@ const mem = std.mem;
const tapi = @import("../tapi.zig");
const trace = @import("../../tracy.zig").trace;
const std = @import("std");
const Allocator = mem.Allocator;
const Path = std.Build.Cache.Path;
const Dylib = @This();
const File = @import("file.zig").File;
const LibStub = tapi.LibStub;

View File

@ -1,6 +1,8 @@
/// Non-zero for fat object files or archives
offset: u64,
path: []const u8,
/// Archive files cannot contain subdirectories, so only the basename is needed
/// for output. However, the full path is kept for error reporting.
path: Path,
file_handle: File.HandleIndex,
mtime: u64,
index: File.Index,
@ -39,8 +41,8 @@ output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.in_archive) |*ar| allocator.free(ar.path);
allocator.free(self.path);
if (self.in_archive) |*ar| allocator.free(ar.path.sub_path);
allocator.free(self.path.sub_path);
for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| {
relocs.deinit(allocator);
sub.deinit(allocator);
@ -1723,7 +1725,8 @@ pub fn updateArSize(self: *Object, macho_file: *MachO) !void {
pub fn writeAr(self: Object, ar_format: Archive.Format, macho_file: *MachO, writer: anytype) !void {
// Header
const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
try Archive.writeHeader(self.path, size, ar_format, writer);
const basename = std.fs.path.basename(self.path.sub_path);
try Archive.writeHeader(basename, size, ar_format, writer);
// Data
const file = macho_file.getFileHandle(self.file_handle);
// TODO try using copyRangeAll
@ -1774,6 +1777,11 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void {
self.calcStabsSize(macho_file);
}
fn pathLen(path: Path) usize {
// +1 for the path separator
return (if (path.root_dir.path) |p| p.len + @intFromBool(path.sub_path.len != 0) else 0) + path.sub_path.len;
}
pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
if (self.compile_unit) |cu| {
const comp_dir = cu.getCompDir(self.*);
@ -1784,9 +1792,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) void {
self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name
if (self.in_archive) |ar| {
self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1));
self.output_symtab_ctx.strsize += @intCast(pathLen(ar.path) + 1 + self.path.basename().len + 1 + 1);
} else {
self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
self.output_symtab_ctx.strsize += @intCast(pathLen(self.path) + 1);
}
for (self.symbols.items, 0..) |sym, i| {
@ -2118,19 +2126,36 @@ pub fn writeStabs(self: Object, stroff: u32, macho_file: *MachO, ctx: anytype) v
};
index += 1;
if (self.in_archive) |ar| {
@memcpy(ctx.strtab.items[n_strx..][0..ar.path.len], ar.path);
n_strx += @intCast(ar.path.len);
if (ar.path.root_dir.path) |p| {
@memcpy(ctx.strtab.items[n_strx..][0..p.len], p);
n_strx += @intCast(p.len);
if (ar.path.sub_path.len != 0) {
ctx.strtab.items[n_strx] = '/';
n_strx += 1;
}
}
@memcpy(ctx.strtab.items[n_strx..][0..ar.path.sub_path.len], ar.path.sub_path);
n_strx += @intCast(ar.path.sub_path.len);
ctx.strtab.items[n_strx] = '(';
n_strx += 1;
@memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
n_strx += @intCast(self.path.len);
const basename = self.path.basename();
@memcpy(ctx.strtab.items[n_strx..][0..basename.len], basename);
n_strx += @intCast(basename.len);
ctx.strtab.items[n_strx] = ')';
n_strx += 1;
ctx.strtab.items[n_strx] = 0;
n_strx += 1;
} else {
@memcpy(ctx.strtab.items[n_strx..][0..self.path.len], self.path);
n_strx += @intCast(self.path.len);
if (self.path.root_dir.path) |p| {
@memcpy(ctx.strtab.items[n_strx..][0..p.len], p);
n_strx += @intCast(p.len);
if (self.path.sub_path.len != 0) {
ctx.strtab.items[n_strx] = '/';
n_strx += 1;
}
}
@memcpy(ctx.strtab.items[n_strx..][0..self.path.sub_path.len], self.path.sub_path);
n_strx += @intCast(self.path.sub_path.len);
ctx.strtab.items[n_strx] = 0;
n_strx += 1;
}
@ -2666,11 +2691,12 @@ fn formatPath(
_ = unused_fmt_string;
_ = options;
if (object.in_archive) |ar| {
try writer.writeAll(ar.path);
try writer.writeByte('(');
try writer.writeAll(object.path);
try writer.writeByte(')');
} else try writer.writeAll(object.path);
try writer.print("{}({s})", .{
@as(Path, ar.path), object.path.basename(),
});
} else {
try writer.print("{}", .{@as(Path, object.path)});
}
}
const Section = struct {
@ -2777,7 +2803,7 @@ const CompileUnit = struct {
};
const InArchive = struct {
path: []const u8,
path: Path,
size: u32,
};
@ -3170,6 +3196,7 @@ const math = std.math;
const mem = std.mem;
const trace = @import("../../tracy.zig").trace;
const std = @import("std");
const Path = std.Build.Cache.Path;
const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");

View File

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

View File

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

View File

@ -72,7 +72,8 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32
}
if (comp.config.any_sanitize_thread) {
const path = comp.tsan_lib.?.full_object_path;
const path = try comp.tsan_lib.?.full_object_path.toString(gpa);
defer gpa.free(path);
const rpath = std.fs.path.dirname(path) orelse ".";
sizeofcmds += calcInstallNameLen(
@sizeOf(macho.rpath_command),

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;
// TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
defer positionals.deinit();
try positionals.ensureUnusedCapacity(comp.objects.len);
@ -19,7 +20,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
// TODO: in the future, when we implement `dsymutil` alternative directly in the Zig
// compiler, investigate if we can get rid of this `if` prong here.
const path = positionals.items[0].path;
const in_file = try std.fs.cwd().openFile(path, .{});
const in_file = try path.root_dir.handle.openFile(path.sub_path, .{});
const stat = try in_file.stat();
const amt = try in_file.copyRangeAll(0, macho_file.base.file.?, 0, stat.size);
if (amt != stat.size) return error.InputOutput; // TODO: report an actual user error
@ -72,7 +73,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
try writeHeader(macho_file, ncmds, sizeofcmds);
}
pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?Path) link.File.FlushError!void {
const gpa = comp.gpa;
var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
@ -173,21 +174,25 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
for (files.items) |index| {
const file = macho_file.getFile(index).?;
const state = switch (file) {
.zig_object => |x| &x.output_ar_state,
.object => |x| &x.output_ar_state,
switch (file) {
.zig_object => |zo| {
const state = &zo.output_ar_state;
pos = mem.alignForward(usize, pos, 2);
state.file_off = pos;
pos += @sizeOf(Archive.ar_hdr);
pos += mem.alignForward(usize, zo.basename.len + 1, ptr_width);
pos += math.cast(usize, state.size) orelse return error.Overflow;
},
.object => |o| {
const state = &o.output_ar_state;
pos = mem.alignForward(usize, pos, 2);
state.file_off = pos;
pos += @sizeOf(Archive.ar_hdr);
pos += mem.alignForward(usize, o.path.basename().len + 1, ptr_width);
pos += math.cast(usize, state.size) orelse return error.Overflow;
},
else => unreachable,
};
const path = switch (file) {
.zig_object => |x| x.path,
.object => |x| x.path,
else => unreachable,
};
pos = mem.alignForward(usize, pos, 2);
state.file_off = pos;
pos += @sizeOf(Archive.ar_hdr);
pos += mem.alignForward(usize, path.len + 1, ptr_width);
pos += math.cast(usize, state.size) orelse return error.Overflow;
}
}
break :blk pos;
@ -777,6 +782,7 @@ const mem = std.mem;
const state_log = std.log.scoped(.link_state);
const std = @import("std");
const trace = @import("../../tracy.zig").trace;
const Path = std.Build.Cache.Path;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");

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.*;
try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
const new_off = @as(u32, @intCast(self.buffer.items.len));
const new_off: u32 = @intCast(self.buffer.items.len);
self.buffer.appendSliceAssumeCapacity(string);
self.buffer.appendAssumeCapacity(0);

View File

@ -2507,6 +2507,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
} else null;
// Positional arguments to the linker such as object files and static archives.
// TODO: "positional arguments" is a CLI concept, not a linker concept. Delete this unnecessary array list.
var positionals = std.ArrayList([]const u8).init(arena);
try positionals.ensureUnusedCapacity(comp.objects.len);
@ -2527,23 +2528,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
(output_mode == .Lib and link_mode == .dynamic);
if (is_exe_or_dyn_lib) {
for (comp.wasi_emulated_libs) |crt_file| {
try positionals.append(try comp.get_libc_crt_file(
try positionals.append(try comp.crtFileAsString(
arena,
wasi_libc.emulatedLibCRFileLibName(crt_file),
));
}
if (link_libc) {
try positionals.append(try comp.get_libc_crt_file(
try positionals.append(try comp.crtFileAsString(
arena,
wasi_libc.execModelCrtFileFullName(wasi_exec_model),
));
try positionals.append(try comp.get_libc_crt_file(arena, "libc.a"));
try positionals.append(try comp.crtFileAsString(arena, "libc.a"));
}
if (link_libcpp) {
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
try positionals.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
try positionals.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
}
}
}
@ -2553,15 +2554,15 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
}
for (comp.objects) |object| {
try positionals.append(object.path);
try positionals.append(try object.path.toString(arena));
}
for (comp.c_object_table.keys()) |c_object| {
try positionals.append(c_object.status.success.object_path);
try positionals.append(try c_object.status.success.object_path.toString(arena));
}
if (comp.compiler_rt_lib) |lib| try positionals.append(lib.full_object_path);
if (comp.compiler_rt_obj) |obj| try positionals.append(obj.full_object_path);
if (comp.compiler_rt_lib) |lib| try positionals.append(try lib.full_object_path.toString(arena));
if (comp.compiler_rt_obj) |obj| try positionals.append(try obj.full_object_path.toString(arena));
try wasm.parseInputFiles(positionals.items);
@ -3365,7 +3366,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
defer sub_prog_node.end();
const is_obj = comp.config.output_mode == .Obj;
const compiler_rt_path: ?[]const u8 = blk: {
const compiler_rt_path: ?Path = blk: {
if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
break :blk null;
@ -3387,14 +3388,14 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
comptime assert(Compilation.link_hash_implementation_version == 14);
for (comp.objects) |obj| {
_ = try man.addFile(obj.path, null);
_ = try man.addFilePath(obj.path, null);
man.hash.add(obj.must_link);
}
for (comp.c_object_table.keys()) |key| {
_ = try man.addFile(key.status.success.object_path, null);
_ = try man.addFilePath(key.status.success.object_path, null);
}
try man.addOptionalFile(module_obj_path);
try man.addOptionalFile(compiler_rt_path);
try man.addOptionalFilePath(compiler_rt_path);
man.hash.addOptionalBytes(wasm.entry_name);
man.hash.add(wasm.base.stack_size);
man.hash.add(wasm.base.build_id);
@ -3450,17 +3451,19 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
break :blk comp.c_object_table.keys()[0].status.success.object_path;
if (module_obj_path) |p|
break :blk p;
break :blk Path.initCwd(p);
// TODO I think this is unreachable. Audit this situation when solving the above TODO
// regarding eliding redundant object -> object transformations.
return error.NoObjectsToLink;
};
// This can happen when using --enable-cache and using the stage1 backend. In this case
// we can skip the file copy.
if (!mem.eql(u8, the_object_path, full_out_path)) {
try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
}
try std.fs.Dir.copyFile(
the_object_path.root_dir.handle,
the_object_path.sub_path,
directory.handle,
wasm.base.emit.sub_path,
.{},
);
} else {
// Create an LLD command line and invoke it.
var argv = std.ArrayList([]const u8).init(gpa);
@ -3581,23 +3584,23 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
(comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
if (is_exe_or_dyn_lib) {
for (comp.wasi_emulated_libs) |crt_file| {
try argv.append(try comp.get_libc_crt_file(
try argv.append(try comp.crtFileAsString(
arena,
wasi_libc.emulatedLibCRFileLibName(crt_file),
));
}
if (comp.config.link_libc) {
try argv.append(try comp.get_libc_crt_file(
try argv.append(try comp.crtFileAsString(
arena,
wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
));
try argv.append(try comp.get_libc_crt_file(arena, "libc.a"));
try argv.append(try comp.crtFileAsString(arena, "libc.a"));
}
if (comp.config.link_libcpp) {
try argv.append(comp.libcxx_static_lib.?.full_object_path);
try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
}
}
}
@ -3612,7 +3615,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
try argv.append("-no-whole-archive");
whole_archive = false;
}
try argv.append(obj.path);
try argv.append(try obj.path.toString(arena));
}
if (whole_archive) {
try argv.append("-no-whole-archive");
@ -3620,7 +3623,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
}
for (comp.c_object_table.keys()) |key| {
try argv.append(key.status.success.object_path);
try argv.append(try key.status.success.object_path.toString(arena));
}
if (module_obj_path) |p| {
try argv.append(p);
@ -3630,11 +3633,11 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
!comp.skip_linker_dependencies and
!comp.config.link_libc)
{
try argv.append(comp.libc_static_lib.?.full_object_path);
try argv.append(try comp.libc_static_lib.?.full_object_path.toString(arena));
}
if (compiler_rt_path) |p| {
try argv.append(p);
try argv.append(try p.toString(arena));
}
if (comp.verbose_link) {

View File

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

View File

@ -11,13 +11,13 @@ const build_options = @import("build_options");
const Cache = std.Build.Cache;
const dev = @import("dev.zig");
pub const CRTFile = enum {
pub const CrtFile = enum {
crt2_o,
dllcrt2_o,
mingw32_lib,
};
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void {
if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}
@ -160,7 +160,9 @@ fn add_cc_args(
pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
dev.check(.build_import_lib);
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
const gpa = comp.gpa;
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
@ -178,7 +180,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
// Use the global cache directory.
var cache: Cache = .{
.gpa = comp.gpa,
.gpa = gpa,
.manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
};
cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
@ -195,17 +197,18 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
_ = try man.addFile(def_file_path, null);
const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name});
errdefer comp.gpa.free(final_lib_basename);
const final_lib_basename = try std.fmt.allocPrint(gpa, "{s}.lib", .{lib_name});
errdefer gpa.free(final_lib_basename);
if (try man.hit()) {
const digest = man.final();
try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1);
try comp.crt_files.ensureUnusedCapacity(gpa, 1);
comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
.full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
"o", &digest, final_lib_basename,
}),
.full_object_path = .{
.root_dir = comp.global_cache_directory,
.sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename }),
},
.lock = man.toOwnedLock(),
});
return;
@ -230,7 +233,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
};
const aro = @import("aro");
var aro_comp = aro.Compilation.init(comp.gpa, std.fs.cwd());
var aro_comp = aro.Compilation.init(gpa, std.fs.cwd());
defer aro_comp.deinit();
const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" });
@ -244,7 +247,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
nosuspend stderr.print("output path: {s}\n", .{def_final_path}) catch break :print;
}
try aro_comp.include_dirs.append(comp.gpa, include_dir);
try aro_comp.include_dirs.append(gpa, include_dir);
const builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines);
const user_macros = try aro_comp.addSourceFromBuffer("<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);
}
const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
"o", &digest, final_lib_basename,
});
errdefer comp.gpa.free(lib_final_path);
const lib_final_path = try std.fs.path.join(gpa, &.{ "o", &digest, final_lib_basename });
errdefer gpa.free(lib_final_path);
if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
const llvm_bindings = @import("codegen/llvm/bindings.zig");
const llvm = @import("codegen/llvm.zig");
const arch_tag = llvm.targetArch(target.cpu.arch);
const def_final_path_z = try arena.dupeZ(u8, def_final_path);
const lib_final_path_z = try arena.dupeZ(u8, lib_final_path);
const lib_final_path_z = try comp.global_cache_directory.joinZ(arena, &.{lib_final_path});
if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) {
// TODO surface a proper error here
log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name });
@ -292,8 +293,11 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) });
};
try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{
.full_object_path = lib_final_path,
try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{
.full_object_path = .{
.root_dir = comp.global_cache_directory,
.sub_path = lib_final_path,
},
.lock = man.toOwnedLock(),
});
}

View File

@ -9,7 +9,7 @@ const archName = std.zig.target.muslArchName;
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
pub const CRTFile = enum {
pub const CrtFile = enum {
crti_o,
crtn_o,
crt1_o,
@ -19,7 +19,7 @@ pub const CRTFile = enum {
libc_so,
};
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void {
if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}

View File

@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator;
const Compilation = @import("Compilation.zig");
const build_options = @import("build_options");
pub const CRTFile = enum {
pub const CrtFile = enum {
crt1_reactor_o,
crt1_command_o,
libc_a,
@ -16,7 +16,7 @@ pub const CRTFile = enum {
libwasi_emulated_signal_a,
};
pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile {
pub fn getEmulatedLibCrtFile(lib_name: []const u8) ?CrtFile {
if (mem.eql(u8, lib_name, "wasi-emulated-process-clocks")) {
return .libwasi_emulated_process_clocks_a;
}
@ -32,7 +32,7 @@ pub fn getEmulatedLibCRTFile(lib_name: []const u8) ?CRTFile {
return null;
}
pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
pub fn emulatedLibCRFileLibName(crt_file: CrtFile) []const u8 {
return switch (crt_file) {
.libwasi_emulated_process_clocks_a => "libwasi-emulated-process-clocks.a",
.libwasi_emulated_getpid_a => "libwasi-emulated-getpid.a",
@ -42,10 +42,10 @@ pub fn emulatedLibCRFileLibName(crt_file: CRTFile) []const u8 {
};
}
pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CRTFile {
pub fn execModelCrtFile(wasi_exec_model: std.builtin.WasiExecModel) CrtFile {
return switch (wasi_exec_model) {
.reactor => CRTFile.crt1_reactor_o,
.command => CRTFile.crt1_command_o,
.reactor => CrtFile.crt1_reactor_o,
.command => CrtFile.crt1_command_o,
};
}
@ -57,7 +57,7 @@ pub fn execModelCrtFileFullName(wasi_exec_model: std.builtin.WasiExecModel) []co
};
}
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progress.Node) !void {
pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) !void {
if (!build_options.have_llvm) {
return error.ZigCompilerNotBuiltWithLLVMExtensions;
}