aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-10-10 00:41:58 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-10-10 14:21:52 -0700
commit14c8e270bb47c4e10ee3392661d4c62ab5c2f89d (patch)
treec74fe79e74d3fc6bff7dd61f372153906e29e040 /src/Compilation.zig
parent58349b2c8ebc57718bbfbf939c30b586d5c85466 (diff)
downloadzig-14c8e270bb47c4e10ee3392661d4c62ab5c2f89d.tar.gz
zig-14c8e270bb47c4e10ee3392661d4c62ab5c2f89d.zip
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.
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig155
1 files changed, 93 insertions, 62 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 589c69fa97..6ba09d5c6d 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -217,37 +217,37 @@ thread_pool: *ThreadPool,
/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
-libcxx_static_lib: ?CRTFile = null,
+libcxx_static_lib: ?CrtFile = null,
/// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
-libcxxabi_static_lib: ?CRTFile = null,
+libcxxabi_static_lib: ?CrtFile = null,
/// Populated when we build the libunwind static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
-libunwind_static_lib: ?CRTFile = null,
+libunwind_static_lib: ?CrtFile = null,
/// Populated when we build the TSAN library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
-tsan_lib: ?CRTFile = null,
+tsan_lib: ?CrtFile = null,
/// Populated when we build the libc static library. A Job to build this is placed in the queue
/// and resolved before calling linker.flush().
-libc_static_lib: ?CRTFile = null,
+libc_static_lib: ?CrtFile = null,
/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated
/// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush().
-compiler_rt_lib: ?CRTFile = null,
+compiler_rt_lib: ?CrtFile = null,
/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush().
-compiler_rt_obj: ?CRTFile = null,
+compiler_rt_obj: ?CrtFile = null,
/// Populated when we build the libfuzzer static library. A Job to build this
/// is indicated by setting `job_queued_fuzzer_lib` and resolved before
/// calling linker.flush().
-fuzzer_lib: ?CRTFile = null,
+fuzzer_lib: ?CrtFile = null,
glibc_so_files: ?glibc.BuiltSharedObjects = null,
-wasi_emulated_libs: []const wasi_libc.CRTFile,
+wasi_emulated_libs: []const wasi_libc.CrtFile,
/// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
/// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
/// The key is the basename, and the value is the absolute path to the completed build artifact.
-crt_files: std.StringHashMapUnmanaged(CRTFile) = .empty,
+crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty,
/// How many lines of reference trace should be included per compile error.
/// Null means only show snippet on first error.
@@ -276,20 +276,20 @@ digest: ?[Cache.bin_digest_len]u8 = null,
pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
pub const SemaError = Zcu.SemaError;
-pub const CRTFile = struct {
+pub const CrtFile = struct {
lock: Cache.Lock,
- full_object_path: []const u8,
+ full_object_path: Path,
- pub fn isObject(cf: CRTFile) bool {
- return switch (classifyFileExt(cf.full_object_path)) {
+ pub fn isObject(cf: CrtFile) bool {
+ return switch (classifyFileExt(cf.full_object_path.sub_path)) {
.object => true,
else => false,
};
}
- pub fn deinit(self: *CRTFile, gpa: Allocator) void {
+ pub fn deinit(self: *CrtFile, gpa: Allocator) void {
self.lock.release();
- gpa.free(self.full_object_path);
+ gpa.free(self.full_object_path.sub_path);
self.* = undefined;
}
};
@@ -369,13 +369,13 @@ const Job = union(enum) {
resolve_type_fully: InternPool.Index,
/// one of the glibc static objects
- glibc_crt_file: glibc.CRTFile,
+ glibc_crt_file: glibc.CrtFile,
/// all of the glibc shared objects
glibc_shared_objects,
/// one of the musl static objects
- musl_crt_file: musl.CRTFile,
+ musl_crt_file: musl.CrtFile,
/// one of the mingw-w64 static objects
- mingw_crt_file: mingw.CRTFile,
+ mingw_crt_file: mingw.CrtFile,
/// libunwind.a, usually needed when linking libc
libunwind: void,
libcxx: void,
@@ -385,7 +385,7 @@ const Job = union(enum) {
/// calls to, for example, memcpy and memset.
zig_libc: void,
/// one of WASI libc static objects
- wasi_libc_crt_file: wasi_libc.CRTFile,
+ wasi_libc_crt_file: wasi_libc.CrtFile,
/// The value is the index into `system_libs`.
windows_import_lib: usize,
@@ -422,8 +422,8 @@ pub const CObject = struct {
status: union(enum) {
new,
success: struct {
- /// The outputted result. Owned by gpa.
- object_path: []u8,
+ /// The outputted result. `sub_path` owned by gpa.
+ object_path: Path,
/// This is a file system lock on the cache hash manifest representing this
/// object. It prevents other invocations of the Zig compiler from interfering
/// with this object until released.
@@ -719,7 +719,7 @@ pub const CObject = struct {
return true;
},
.success => |*success| {
- gpa.free(success.object_path);
+ gpa.free(success.object_path.sub_path);
success.lock.release();
self.status = .new;
return false;
@@ -1018,7 +1018,7 @@ const CacheUse = union(CacheMode) {
};
pub const LinkObject = struct {
- path: []const u8,
+ path: Path,
must_link: bool = false,
// When the library is passed via a positional argument, it will be
// added as a full path. If it's `-l<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