aboutsummaryrefslogtreecommitdiff
path: root/src/Compilation.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-10-10 22:30:04 -0700
committerGitHub <noreply@github.com>2024-10-10 22:30:04 -0700
commit1340565e22e154db41e90a8d4f0b552176711ba0 (patch)
tree0fcb0dde8105082065f46d1f477c97903f69b7e5 /src/Compilation.zig
parent92ae5818d26925af7816fabcaec85236133b9e46 (diff)
parent2857a3bcb61ad8f119f46acf3a76fd70195f9bd5 (diff)
downloadzig-1340565e22e154db41e90a8d4f0b552176711ba0.tar.gz
zig-1340565e22e154db41e90a8d4f0b552176711ba0.zip
Merge pull request #21654 from ziglang/embrace-path-abstraction
link: fix false positive crtbegin/crtend detection
Diffstat (limited to 'src/Compilation.zig')
-rw-r--r--src/Compilation.zig165
1 files changed, 102 insertions, 63 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 589c69fa97..8d276980ea 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.
@@ -2302,7 +2302,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
try pt.processExports();
}
- if (try comp.totalErrorCount() != 0) {
+ if (anyErrors(comp)) {
// Skip flushing and keep source files loaded for error reporting.
comp.link_error_flags = .{};
return;
@@ -2392,6 +2392,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
.sub_path = o_sub_path,
}, .main, main_progress_node);
+ // Calling `flush` may have produced errors, in which case the
+ // cache manifest must not be written.
+ if (anyErrors(comp)) return;
+
// Failure here only means an unnecessary cache miss.
man.writeManifest() catch |err| {
log.warn("failed to write cache manifest: {s}", .{@errorName(err)});
@@ -2578,7 +2582,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 +2707,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 +2723,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) });
};
}
@@ -3292,6 +3295,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
return bundle.toOwnedBundle(compile_log_text);
}
+fn anyErrors(comp: *Compilation) bool {
+ return (totalErrorCount(comp) catch return true) != 0;
+}
+
fn totalErrorCount(comp: *Compilation) !u32 {
var errors = try comp.getAllErrorsAlloc();
defer errors.deinit(comp.gpa);
@@ -3774,7 +3781,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 +3805,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 +3818,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 +3901,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 +4609,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 +4710,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 +4731,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 +4753,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 +4917,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 +4991,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 +6102,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 +6329,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 +6557,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