diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-10-10 00:41:58 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-10-10 14:21:52 -0700 |
| commit | 14c8e270bb47c4e10ee3392661d4c62ab5c2f89d (patch) | |
| tree | c74fe79e74d3fc6bff7dd61f372153906e29e040 /src/Compilation.zig | |
| parent | 58349b2c8ebc57718bbfbf939c30b586d5c85466 (diff) | |
| download | zig-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.zig | 155 |
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 |
