diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-10-10 22:30:04 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-10 22:30:04 -0700 |
| commit | 1340565e22e154db41e90a8d4f0b552176711ba0 (patch) | |
| tree | 0fcb0dde8105082065f46d1f477c97903f69b7e5 /src/Compilation.zig | |
| parent | 92ae5818d26925af7816fabcaec85236133b9e46 (diff) | |
| parent | 2857a3bcb61ad8f119f46acf3a76fd70195f9bd5 (diff) | |
| download | zig-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.zig | 165 |
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 |
