From c2898c436f0c858d10367b7631091f5a52cce76f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 18 Oct 2024 22:29:50 -0700 Subject: [PATCH] branch fixes --- lib/std/zig/target.zig | 4 +- src/Compilation.zig | 308 ++++++++++++++++++++-------------------- src/ThreadSafeQueue.zig | 8 ++ src/glibc.zig | 71 ++++++--- src/libcxx.zig | 4 +- src/libtsan.zig | 2 +- src/libunwind.zig | 2 +- src/link.zig | 32 +---- src/link/Elf.zig | 282 ++++++++---------------------------- src/musl.zig | 2 +- 10 files changed, 286 insertions(+), 429 deletions(-) diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index 6db17c1f71..41ccc6b811 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -25,7 +25,7 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .thumbeb, .os = .linux, .abi = .musleabihf }, .{ .arch = .aarch64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } }, .{ .arch = .aarch64, .os = .linux, .abi = .musl }, - .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } }, + .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 13, .minor = 0, .patch = 0 } }, .{ .arch = .aarch64, .os = .windows, .abi = .gnu }, .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } }, .{ .arch = .aarch64_be, .os = .linux, .abi = .musl }, @@ -74,7 +74,7 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .x86_64, .os = .linux, .abi = .gnu }, .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 }, .{ .arch = .x86_64, .os = .linux, .abi = .musl }, - .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } }, + .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 13, .minor = 0, .patch = 0 } }, .{ .arch = .x86_64, .os = .windows, .abi = .gnu }, }; diff --git a/src/Compilation.zig b/src/Compilation.zig index 387a6acd52..0b058b99a4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -49,6 +49,8 @@ pub const Config = @import("Compilation/Config.zig"); gpa: Allocator, /// Arena-allocated memory, mostly used during initialization. However, it can /// be used for other things requiring the same lifetime as the `Compilation`. +/// Not thread-safe - lock `mutex` if potentially accessing from multiple +/// threads at once. arena: Allocator, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. zcu: ?*Zcu, @@ -1760,172 +1762,166 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .incremental => comp.bin_file != null, }; - if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) { - if (target.isDarwin()) { - switch (target.abi) { - .none, - .simulator, - .macabi, - => {}, - else => return error.LibCUnavailable, - } - } - // If we need to build glibc for the target, add work items for it. - // We go through the work queue so that building can be done in parallel. - // If linking against host libc installation, instead queue up jobs - // for loading those files in the linker. - if (comp.config.link_libc and is_exe_or_dyn_lib and target.ofmt != .c) { - if (comp.libc_installation) |lci| { - 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, - }); - const paths = try lci.resolveCrtPaths(arena, basenames, target); + if (have_bin_emit and target.ofmt != .c) { + if (!comp.skip_linker_dependencies) { + // If we need to build libc for the target, add work items for it. + // We go through the work queue so that building can be done in parallel. + // If linking against host libc installation, instead queue up jobs + // for loading those files in the linker. + if (comp.config.link_libc and is_exe_or_dyn_lib) { + if (comp.libc_installation) |lci| { + 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, + }); + const paths = try lci.resolveCrtPaths(arena, basenames, target); - const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; - try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len); - inline for (fields) |field| { - if (@field(paths, field.name)) |path| { - comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); + const fields = @typeInfo(@TypeOf(paths)).@"struct".fields; + try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, fields.len); + inline for (fields) |field| { + if (@field(paths, field.name)) |path| { + comp.link_task_queue.shared.appendAssumeCapacity(.{ .load_object = path }); + } } - } - const flags = target_util.libcFullLinkFlags(target); - try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, flags.len); - for (flags) |flag| { - assert(mem.startsWith(u8, flag, "-l")); - const lib_name = flag["-l".len..]; - const suffix = switch (comp.config.link_mode) { - .static => target.staticLibSuffix(), - .dynamic => target.dynamicLibSuffix(), - }; - const sep = std.fs.path.sep_str; - const lib_path = try std.fmt.allocPrint(arena, "{s}" ++ sep ++ "lib{s}{s}", .{ - lci.crt_dir.?, lib_name, suffix, - }); - const resolved_path = Path.initCwd(lib_path); - comp.link_task_queue.shared.appendAssumeCapacity(switch (comp.config.link_mode) { - .static => .{ .load_archive = resolved_path }, - .dynamic => .{ .load_dso = resolved_path }, - }); - } - } else if (target.isMusl() and !target.isWasm()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + const flags = target_util.libcFullLinkFlags(target); + try comp.link_task_queue.shared.ensureUnusedCapacity(gpa, flags.len); + for (flags) |flag| { + assert(mem.startsWith(u8, flag, "-l")); + const lib_name = flag["-l".len..]; + const suffix = switch (comp.config.link_mode) { + .static => target.staticLibSuffix(), + .dynamic => target.dynamicLibSuffix(), + }; + const sep = std.fs.path.sep_str; + const lib_path = try std.fmt.allocPrint(arena, "{s}" ++ sep ++ "lib{s}{s}", .{ + lci.crt_dir.?, lib_name, suffix, + }); + const resolved_path = Path.initCwd(lib_path); + comp.link_task_queue.shared.appendAssumeCapacity(switch (comp.config.link_mode) { + .static => .{ .load_archive = resolved_path }, + .dynamic => .{ .load_dso = resolved_path }, + }); + } + } else if (target.isMusl() and !target.isWasm()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - if (musl.needsCrtiCrtn(target)) { + if (musl.needsCrtiCrtn(target)) { + try comp.queueJobs(&[_]Job{ + .{ .musl_crt_file = .crti_o }, + .{ .musl_crt_file = .crtn_o }, + }); + } try comp.queueJobs(&[_]Job{ - .{ .musl_crt_file = .crti_o }, - .{ .musl_crt_file = .crtn_o }, + .{ .musl_crt_file = .crt1_o }, + .{ .musl_crt_file = .scrt1_o }, + .{ .musl_crt_file = .rcrt1_o }, + switch (comp.config.link_mode) { + .static => .{ .musl_crt_file = .libc_a }, + .dynamic => .{ .musl_crt_file = .libc_so }, + }, }); - } - try comp.queueJobs(&[_]Job{ - .{ .musl_crt_file = .crt1_o }, - .{ .musl_crt_file = .scrt1_o }, - .{ .musl_crt_file = .rcrt1_o }, - switch (comp.config.link_mode) { - .static => .{ .musl_crt_file = .libc_a }, - .dynamic => .{ .musl_crt_file = .libc_so }, - }, - }); - } else if (target.isGnuLibC()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + } else if (target.isGnuLibC()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - if (glibc.needsCrtiCrtn(target)) { + if (glibc.needsCrtiCrtn(target)) { + try comp.queueJobs(&[_]Job{ + .{ .glibc_crt_file = .crti_o }, + .{ .glibc_crt_file = .crtn_o }, + }); + } try comp.queueJobs(&[_]Job{ - .{ .glibc_crt_file = .crti_o }, - .{ .glibc_crt_file = .crtn_o }, + .{ .glibc_crt_file = .scrt1_o }, + .{ .glibc_crt_file = .libc_nonshared_a }, + .{ .glibc_shared_objects = {} }, }); - } - try comp.queueJobs(&[_]Job{ - .{ .glibc_crt_file = .scrt1_o }, - .{ .glibc_crt_file = .libc_nonshared_a }, - .{ .glibc_shared_objects = {} }, - }); - } else if (target.isWasm() and target.os.tag == .wasi) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + } else if (target.isWasm() and target.os.tag == .wasi) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - for (comp.wasi_emulated_libs) |crt_file| { - try comp.queueJob(.{ - .wasi_libc_crt_file = crt_file, + for (comp.wasi_emulated_libs) |crt_file| { + try comp.queueJob(.{ + .wasi_libc_crt_file = crt_file, + }); + } + try comp.queueJobs(&[_]Job{ + .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, + .{ .wasi_libc_crt_file = .libc_a }, }); + } else if (target.isMinGW()) { + if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; + + const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; + try comp.queueJobs(&.{ + .{ .mingw_crt_file = .mingw32_lib }, + crt_job, + }); + + // When linking mingw-w64 there are some import libs we always need. + try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); + for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); + } else if (target.isDarwin()) { + switch (target.abi) { + .none, .simulator, .macabi => {}, + else => return error.LibCUnavailable, + } + } else if (target.os.tag == .freestanding and capable_of_building_zig_libc) { + try comp.queueJob(.{ .zig_libc = {} }); + } else { + return error.LibCUnavailable; } - try comp.queueJobs(&[_]Job{ - .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, - .{ .wasi_libc_crt_file = .libc_a }, - }); - } else if (target.isMinGW()) { - if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; - - const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o }; - try comp.queueJobs(&.{ - .{ .mingw_crt_file = .mingw32_lib }, - crt_job, - }); - - // When linking mingw-w64 there are some import libs we always need. - try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); - for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); - } else { - return error.LibCUnavailable; } - } - // Generate Windows import libs. - if (target.os.tag == .windows) { - const count = comp.windows_libs.count(); - for (0..count) |i| { - try comp.queueJob(.{ .windows_import_lib = i }); + // Generate Windows import libs. + if (target.os.tag == .windows) { + const count = comp.windows_libs.count(); + for (0..count) |i| { + try comp.queueJob(.{ .windows_import_lib = i }); + } } - } - if (comp.wantBuildLibUnwindFromSource()) { - try comp.queueJob(.{ .libunwind = {} }); - } - if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { - try comp.queueJob(.libcxx); - try comp.queueJob(.libcxxabi); - } - if (build_options.have_llvm and comp.config.any_sanitize_thread) { - try comp.queueJob(.libtsan); - } - - if (target.isMinGW() and comp.config.any_non_single_threaded) { - // LLD might drop some symbols as unused during LTO and GCing, therefore, - // we force mark them for resolution here. - - const tls_index_sym = switch (target.cpu.arch) { - .x86 => "__tls_index", - else => "_tls_index", - }; - - try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); - } - - if (comp.include_compiler_rt and capable_of_building_compiler_rt) { - if (is_exe_or_dyn_lib) { - log.debug("queuing a job to build compiler_rt_lib", .{}); - comp.job_queued_compiler_rt_lib = true; - } else if (output_mode != .Obj) { - log.debug("queuing a job to build compiler_rt_obj", .{}); - // In this case we are making a static library, so we ask - // for a compiler-rt object to put in it. - comp.job_queued_compiler_rt_obj = true; + if (comp.wantBuildLibUnwindFromSource()) { + try comp.queueJob(.{ .libunwind = {} }); } - } - - if (comp.config.any_fuzz and capable_of_building_compiler_rt) { - if (is_exe_or_dyn_lib) { - log.debug("queuing a job to build libfuzzer", .{}); - comp.job_queued_fuzzer_lib = true; + if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { + try comp.queueJob(.libcxx); + try comp.queueJob(.libcxxabi); + } + if (build_options.have_llvm and comp.config.any_sanitize_thread) { + try comp.queueJob(.libtsan); } - } - if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and - !comp.config.link_libc and capable_of_building_zig_libc) - { - try comp.queueJob(.{ .zig_libc = {} }); + if (target.isMinGW() and comp.config.any_non_single_threaded) { + // LLD might drop some symbols as unused during LTO and GCing, therefore, + // we force mark them for resolution here. + + const tls_index_sym = switch (target.cpu.arch) { + .x86 => "__tls_index", + else => "_tls_index", + }; + + try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); + } + + if (comp.include_compiler_rt and capable_of_building_compiler_rt) { + if (is_exe_or_dyn_lib) { + log.debug("queuing a job to build compiler_rt_lib", .{}); + comp.job_queued_compiler_rt_lib = true; + } else if (output_mode != .Obj) { + log.debug("queuing a job to build compiler_rt_obj", .{}); + // In this case we are making a static library, so we ask + // for a compiler-rt object to put in it. + comp.job_queued_compiler_rt_obj = true; + } + } + + if (comp.config.any_fuzz and capable_of_building_compiler_rt) { + if (is_exe_or_dyn_lib) { + log.debug("queuing a job to build libfuzzer", .{}); + comp.job_queued_fuzzer_lib = true; + } + } } try comp.link_task_queue.shared.append(gpa, .load_explicitly_provided); @@ -3521,7 +3517,7 @@ fn performAllTheWorkInner( defer work_queue_wait_group.wait(); if (comp.bin_file) |lf| { - if (try comp.link_task_queue.enqueue(comp.gpa, &.{.load_explicitly_provided})) { + if (comp.link_task_queue.start()) { comp.thread_pool.spawnWg(work_queue_wait_group, link.File.flushTaskQueue, .{ lf, main_progress_node }); } } @@ -4980,7 +4976,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr }, }; - comp.enqueueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); + comp.queueLinkTasks(&.{.{ .load_object = c_object.status.success.object_path }}); } fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void { @@ -6114,7 +6110,7 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c; } -fn setAllocFailure(comp: *Compilation) void { +pub fn setAllocFailure(comp: *Compilation) void { @branchHint(.cold); log.debug("memory allocation failure", .{}); comp.alloc_failure_occurred = true; @@ -6355,7 +6351,7 @@ fn buildOutputFromZig( assert(out.* == null); out.* = crt_file; - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); } pub fn build_crt_file( @@ -6463,7 +6459,7 @@ pub fn build_crt_file( try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); const crt_file = try sub_compilation.toCrtFile(); - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); { comp.mutex.lock(); @@ -6473,8 +6469,8 @@ pub fn build_crt_file( } } -pub fn enqueueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void { - comp.enqueueLinkTasks(switch (output_mode) { +pub fn queueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.builtin.OutputMode) void { + comp.queueLinkTasks(switch (output_mode) { .Exe => unreachable, .Obj => &.{.{ .load_object = path }}, .Lib => &.{.{ .load_archive = path }}, @@ -6483,7 +6479,7 @@ pub fn enqueueLinkTaskMode(comp: *Compilation, path: Path, output_mode: std.buil /// Only valid to call during `update`. Automatically handles queuing up a /// linker worker task if there is not already one. -fn enqueueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void { +pub fn queueLinkTasks(comp: *Compilation, tasks: []const link.File.Task) void { const use_lld = build_options.have_llvm and comp.config.use_lld; if (use_lld) return; const target = comp.root_mod.resolved_target.result; diff --git a/src/ThreadSafeQueue.zig b/src/ThreadSafeQueue.zig index b014017736..1cf6aaaa3a 100644 --- a/src/ThreadSafeQueue.zig +++ b/src/ThreadSafeQueue.zig @@ -59,5 +59,13 @@ pub fn ThreadSafeQueue(comptime T: type) type { self.state = .run; return was_waiting; } + + /// Safe only to call exactly once when initially starting the worker. + pub fn start(self: *Self) bool { + assert(self.state == .wait); + if (self.shared.items.len == 0) return false; + self.state = .run; + return true; + } }; } diff --git a/src/glibc.zig b/src/glibc.zig index 58684fec58..65d3cc3b01 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -6,12 +6,14 @@ const fs = std.fs; const path = fs.path; const assert = std.debug.assert; const Version = std.SemanticVersion; +const Path = std.Build.Cache.Path; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; const Cache = std.Build.Cache; const Module = @import("Package/Module.zig"); +const link = @import("link.zig"); pub const Lib = struct { name: []const u8, @@ -717,11 +719,11 @@ fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const pub const BuiltSharedObjects = struct { lock: Cache.Lock, - dir_path: []u8, + dir_path: Path, pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void { self.lock.release(); - gpa.free(self.dir_path); + gpa.free(self.dir_path.sub_path); self.* = undefined; } }; @@ -742,7 +744,9 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi return error.ZigCompilerNotBuiltWithLLVMExtensions; } - 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(); @@ -751,7 +755,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi // 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 = fs.cwd() }); @@ -772,12 +776,13 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi if (try man.hit()) { const digest = man.final(); - assert(comp.glibc_so_files == null); - comp.glibc_so_files = BuiltSharedObjects{ + return queueSharedObjects(comp, .{ .lock = man.toOwnedLock(), - .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }), - }; - return; + .dir_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + }, + }); } const digest = man.final(); @@ -790,8 +795,8 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi defer o_directory.handle.close(); const abilists_contents = man.files.keys()[abilists_index].contents.?; - const metadata = try loadMetaData(comp.gpa, abilists_contents); - defer metadata.destroy(comp.gpa); + const metadata = try loadMetaData(gpa, abilists_contents); + defer metadata.destroy(gpa); const target_targ_index = for (metadata.all_targets, 0..) |targ, i| { if (targ.arch == target.cpu.arch and @@ -835,7 +840,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi map_contents.deinit(); // The most recent allocation of an arena can be freed :) } - var stubs_asm = std.ArrayList(u8).init(comp.gpa); + var stubs_asm = std.ArrayList(u8).init(gpa); defer stubs_asm.deinit(); for (libs, 0..) |lib, lib_i| { @@ -1195,7 +1200,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items }); - try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib, prog_node); } @@ -1203,11 +1207,44 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)}); }; - assert(comp.glibc_so_files == null); - comp.glibc_so_files = .{ + return queueSharedObjects(comp, .{ .lock = man.toOwnedLock(), - .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }), - }; + .dir_path = .{ + .root_dir = comp.global_cache_directory, + .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest), + }, + }); +} + +fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { + const target_version = comp.getTarget().os.version_range.linux.glibc; + + assert(comp.glibc_so_files == null); + comp.glibc_so_files = so_files; + + var task_buffer: [libs.len]link.File.Task = undefined; + var task_buffer_i: usize = 0; + + { + comp.mutex.lock(); // protect comp.arena + defer comp.mutex.unlock(); + + for (libs) |lib| { + if (lib.removed_in) |rem_in| { + if (target_version.order(rem_in) != .lt) continue; + } + const so_path: Path = .{ + .root_dir = so_files.dir_path.root_dir, + .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{ + so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover, + }) catch return comp.setAllocFailure(), + }; + task_buffer[task_buffer_i] = .{ .load_dso = so_path }; + task_buffer_i += 1; + } + } + + comp.queueLinkTasks(task_buffer[0..task_buffer_i]); } fn buildSharedLib( diff --git a/src/libcxx.zig b/src/libcxx.zig index 17e5777376..a9f3030c42 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -357,7 +357,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: std.Progress.Node) BuildError! assert(comp.libcxx_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxx_static_lib = crt_file; - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); } pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { @@ -588,7 +588,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: std.Progress.Node) BuildErr assert(comp.libcxxabi_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxxabi_static_lib = crt_file; - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); } pub fn hardeningModeFlag(optimize_mode: std.builtin.OptimizeMode) []const u8 { diff --git a/src/libtsan.zig b/src/libtsan.zig index 534b8219b9..d078fa2a38 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -343,7 +343,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo }; const crt_file = try sub_compilation.toCrtFile(); - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); assert(comp.tsan_lib == null); comp.tsan_lib = crt_file; } diff --git a/src/libunwind.zig b/src/libunwind.zig index 12acea13fc..fba604e725 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -200,7 +200,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr }; const crt_file = try sub_compilation.toCrtFile(); - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); assert(comp.libunwind_static_lib == null); comp.libunwind_static_lib = crt_file; } diff --git a/src/link.zig b/src/link.zig index 8935ba0a90..bf4d182d35 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1349,12 +1349,14 @@ pub const File = struct { /// Does all the tasks in the queue. Runs in exactly one separate thread /// from the rest of compilation. All tasks performed here are /// single-threaded with respect to one another. - pub fn flushTaskQueue(base: *File, prog_node: std.Progress.Node) void { + pub fn flushTaskQueue(base: *File, parent_prog_node: std.Progress.Node) void { const comp = base.comp; base.task_queue_safety.lock(); defer base.task_queue_safety.unlock(); + const prog_node = parent_prog_node.start("Parse Linker Inputs", 0); + defer prog_node.end(); while (comp.link_task_queue.check()) |tasks| { - for (tasks) |task| doTask(base, prog_node, task); + for (tasks) |task| doTask(base, task); } } @@ -1374,16 +1376,11 @@ pub const File = struct { load_input: Input, }; - fn doTask(base: *File, parent_prog_node: std.Progress.Node, task: Task) void { + fn doTask(base: *File, task: Task) void { const comp = base.comp; switch (task) { .load_explicitly_provided => { - const prog_node = parent_prog_node.start("Linker Parse Input", comp.link_inputs.len); - defer prog_node.end(); - for (comp.link_inputs) |input| { - const sub_node = prog_node.start(input.taskName(), 0); - defer sub_node.end(); base.loadInput(input) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags else => |e| { @@ -1397,33 +1394,18 @@ pub const File = struct { } }, .load_object => |path| { - const prog_node = parent_prog_node.start("Linker Parse Object", 0); - defer prog_node.end(); - const sub_node = prog_node.start(path.basename(), 0); - defer sub_node.end(); - base.openLoadObject(path) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags else => |e| comp.link_diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}), }; }, .load_archive => |path| { - const prog_node = parent_prog_node.start("Linker Parse Archive", 0); - defer prog_node.end(); - const sub_node = prog_node.start(path.basename(), 0); - defer sub_node.end(); - base.openLoadArchive(path) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags else => |e| comp.link_diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), }; }, .load_dso => |path| { - const prog_node = parent_prog_node.start("Linker Parse Shared Library", 0); - defer prog_node.end(); - const sub_node = prog_node.start(path.basename(), 0); - defer sub_node.end(); - base.openLoadDso(path, .{ .preferred_mode = .dynamic, .search_strategy = .paths_first, @@ -1433,10 +1415,6 @@ pub const File = struct { }; }, .load_input => |input| { - const prog_node = parent_prog_node.start("Linker Parse Input", 0); - defer prog_node.end(); - const sub_node = prog_node.start(input.taskName(), 0); - defer sub_node.end(); base.loadInput(input) catch |err| switch (err) { error.LinkFailure => return, // error reported via link_diags else => |e| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 28a161da0f..f0dba06e41 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -114,6 +114,10 @@ comment_merge_section_index: ?Merge.Section.Index = null, first_eflags: ?elf.Word = null, +/// `--verbose-link` output. +/// Initialized on creation, appended to as inputs are added, printed during `flush`. +dump_argv_list: std.ArrayListUnmanaged([]const u8), + const SectionIndexes = struct { copy_rel: ?u32 = null, dynamic: ?u32 = null, @@ -338,6 +342,7 @@ pub fn createEmpty( .enable_new_dtags = options.enable_new_dtags, .print_icf_sections = options.print_icf_sections, .print_map = options.print_map, + .dump_argv_list = .empty, }; if (use_llvm and comp.config.have_zcu) { self.llvm_object = try LlvmObject.create(arena, comp); @@ -350,7 +355,7 @@ pub fn createEmpty( } // --verbose-link - if (comp.verbose_link) try self.dumpArgv(comp); + if (comp.verbose_link) try dumpArgvInit(self, arena); const is_obj = output_mode == .Obj; const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static); @@ -501,6 +506,7 @@ pub fn deinit(self: *Elf) void { self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); self.comdat_group_sections.deinit(gpa); + self.dump_argv_list.deinit(gpa); } pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 { @@ -753,12 +759,23 @@ pub fn allocateChunk(self: *Elf, args: struct { } pub fn loadInput(self: *Elf, input: link.Input) !void { - const gpa = self.base.comp.gpa; - const diags = &self.base.comp.link_diags; + const comp = self.base.comp; + const gpa = comp.gpa; + const diags = &comp.link_diags; const target = self.getTarget(); - const debug_fmt_strip = self.base.comp.config.debug_format == .strip; + const debug_fmt_strip = comp.config.debug_format == .strip; const default_sym_version = self.default_sym_version; + if (comp.verbose_link) { + const argv = &self.dump_argv_list; + switch (input) { + .res => unreachable, + .dso_exact => |dso_exact| try argv.appendSlice(gpa, &.{ "-l", dso_exact.name }), + .object, .archive => |obj| try argv.append(gpa, try obj.path.toString(comp.arena)), + .dso => |dso| try argv.append(gpa, try dso.path.toString(comp.arena)), + } + } + switch (input) { .res => unreachable, .dso_exact => @panic("TODO"), @@ -790,6 +807,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (use_lld) return; } + if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items); + const sub_prog_node = prog_node.start("ELF Flush", 0); defer sub_prog_node.end(); @@ -965,291 +984,110 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod if (diags.hasErrors()) return error.FlushFailure; } -/// --verbose-link output -fn dumpArgv(self: *Elf, comp: *Compilation) !void { - const gpa = self.base.comp.gpa; - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - +fn dumpArgvInit(self: *Elf, arena: Allocator) !void { + const comp = self.base.comp; + const gpa = comp.gpa; const target = self.getTarget(); - const link_mode = self.base.comp.config.link_mode; - const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. - 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; - } - } else null; + const full_out_path = try self.base.emit.root_dir.join(arena, &[_][]const u8{self.base.emit.sub_path}); - const crt_basenames = std.zig.LibCInstallation.CrtBasenames.get(.{ - .target = target, - .link_libc = comp.config.link_libc, - .output_mode = comp.config.output_mode, - .link_mode = link_mode, - .pie = comp.config.pie, - }); - const crt_paths: std.zig.LibCInstallation.CrtPaths = if (comp.libc_installation) |lci| - try lci.resolveCrtPaths(arena, crt_basenames, target) - else - .{}; - const compiler_rt_path: ?[]const u8 = blk: { - 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; - }; + const argv = &self.dump_argv_list; - var argv = std.ArrayList([]const u8).init(arena); - - try argv.append("zig"); + try argv.append(gpa, "zig"); if (self.base.isStaticLib()) { - try argv.append("ar"); + try argv.append(gpa, "ar"); } else { - try argv.append("ld"); + try argv.append(gpa, "ld"); } if (self.base.isObject()) { - try argv.append("-r"); + try argv.append(gpa, "-r"); } - try argv.append("-o"); - try argv.append(full_out_path); + try argv.append(gpa, "-o"); + try argv.append(gpa, full_out_path); - if (self.base.isRelocatable()) { - for (self.base.comp.link_inputs) |link_input| switch (link_input) { - .res => unreachable, - .dso => |dso| try argv.append(try dso.path.toString(arena)), - .object, .archive => |obj| try argv.append(try obj.path.toString(arena)), - .dso_exact => |dso_exact| { - assert(dso_exact.name[0] == ':'); - try argv.appendSlice(&.{ "-l", dso_exact.name }); - }, - }; - - for (comp.c_object_table.keys()) |key| { - try argv.append(try key.status.success.object_path.toString(arena)); - } - - if (module_obj_path) |p| { - try argv.append(p); - } - } else { + if (!self.base.isRelocatable()) { if (!self.base.isStatic()) { if (target.dynamic_linker.get()) |path| { - try argv.append("-dynamic-linker"); - try argv.append(path); + try argv.appendSlice(gpa, &.{ "-dynamic-linker", try arena.dupe(u8, path) }); } } if (self.base.isDynLib()) { if (self.soname) |name| { - try argv.append("-soname"); - try argv.append(name); + try argv.append(gpa, "-soname"); + try argv.append(gpa, name); } } if (self.entry_name) |name| { - try argv.appendSlice(&.{ "--entry", name }); + try argv.appendSlice(gpa, &.{ "--entry", name }); } for (self.rpath_table.keys()) |rpath| { - try argv.appendSlice(&.{ "-rpath", rpath }); + try argv.appendSlice(gpa, &.{ "-rpath", rpath }); } - try argv.appendSlice(&.{ + try argv.appendSlice(gpa, &.{ "-z", try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), }); - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); + try argv.append(gpa, try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); if (self.base.gc_sections) { - try argv.append("--gc-sections"); + try argv.append(gpa, "--gc-sections"); } if (self.base.print_gc_sections) { - try argv.append("--print-gc-sections"); + try argv.append(gpa, "--print-gc-sections"); } if (comp.link_eh_frame_hdr) { - try argv.append("--eh-frame-hdr"); + try argv.append(gpa, "--eh-frame-hdr"); } if (comp.config.rdynamic) { - try argv.append("--export-dynamic"); + try argv.append(gpa, "--export-dynamic"); } if (self.z_notext) { - try argv.append("-z"); - try argv.append("notext"); + try argv.append(gpa, "-z"); + try argv.append(gpa, "notext"); } if (self.z_nocopyreloc) { - try argv.append("-z"); - try argv.append("nocopyreloc"); + try argv.append(gpa, "-z"); + try argv.append(gpa, "nocopyreloc"); } if (self.z_now) { - try argv.append("-z"); - try argv.append("now"); + try argv.append(gpa, "-z"); + try argv.append(gpa, "now"); } if (self.base.isStatic()) { - try argv.append("-static"); + try argv.append(gpa, "-static"); } else if (self.isEffectivelyDynLib()) { - try argv.append("-shared"); + try argv.append(gpa, "-shared"); } if (comp.config.pie and self.base.isExe()) { - try argv.append("-pie"); + try argv.append(gpa, "-pie"); } if (comp.config.debug_format == .strip) { - try argv.append("-s"); + try argv.append(gpa, "-s"); } - if (crt_paths.crt0) |path| try argv.append(try path.toString(arena)); - if (crt_paths.crti) |path| try argv.append(try path.toString(arena)); - if (crt_paths.crtbegin) |path| try argv.append(try path.toString(arena)); - if (comp.config.link_libc) { - if (self.base.comp.libc_installation) |libc_installation| { - try argv.append("-L"); - try argv.append(libc_installation.crt_dir.?); + if (self.base.comp.libc_installation) |lci| { + try argv.append(gpa, "-L"); + try argv.append(gpa, lci.crt_dir.?); } } - - var whole_archive = false; - - for (self.base.comp.link_inputs) |link_input| switch (link_input) { - .res => unreachable, - .dso => continue, - .object, .archive => |obj| { - if (obj.must_link and !whole_archive) { - try argv.append("-whole-archive"); - whole_archive = true; - } else if (!obj.must_link and whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - try argv.append(try obj.path.toString(arena)); - }, - .dso_exact => |dso_exact| { - assert(dso_exact.name[0] == ':'); - try argv.appendSlice(&.{ "-l", dso_exact.name }); - }, - }; - - if (whole_archive) { - try argv.append("-no-whole-archive"); - whole_archive = false; - } - - for (comp.c_object_table.keys()) |key| { - try argv.append(try key.status.success.object_path.toString(arena)); - } - - if (module_obj_path) |p| { - try argv.append(p); - } - - if (comp.config.any_sanitize_thread) { - try argv.append(try comp.tsan_lib.?.full_object_path.toString(arena)); - } - - if (comp.config.any_fuzz) { - 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(try lib.full_object_path.toString(arena)); - } - } - - // Shared libraries. - // Worst-case, we need an --as-needed argument for every lib, as well - // as one before and one after. - argv.appendAssumeCapacity("--as-needed"); - var as_needed = true; - - for (self.base.comp.link_inputs) |link_input| switch (link_input) { - .object, .archive, .dso_exact => continue, - .dso => |dso| { - const lib_as_needed = !dso.needed; - switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { - 0b00, 0b11 => {}, - 0b01 => { - try argv.append("--no-as-needed"); - as_needed = false; - }, - 0b10 => { - try argv.append("--as-needed"); - as_needed = true; - }, - } - argv.appendAssumeCapacity(try dso.path.toString(arena)); - }, - .res => unreachable, - }; - - if (!as_needed) { - argv.appendAssumeCapacity("--as-needed"); - as_needed = true; - } - - // libc++ dep - if (comp.config.link_libcpp) { - 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(try comp.libunwind_static_lib.?.full_object_path.toString(arena)); - } - - // libc dep - if (comp.config.link_libc) { - if (self.base.comp.libc_installation != null) { - const needs_grouping = link_mode == .static; - if (needs_grouping) try argv.append("--start-group"); - try argv.appendSlice(target_util.libcFullLinkFlags(target)); - if (needs_grouping) try argv.append("--end-group"); - } else if (target.isGnuLibC()) { - 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}", .{ - comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, - }); - try argv.append(lib_path); - } - try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a")); - } else if (target.isMusl()) { - try argv.append(try comp.crtFileAsString(arena, switch (link_mode) { - .static => "libc.a", - .dynamic => "libc.so", - })); - } - } - - // compiler-rt - if (compiler_rt_path) |p| { - try argv.append(p); - } - - if (crt_paths.crtend) |path| try argv.append(try path.toString(arena)); - if (crt_paths.crtn) |path| try argv.append(try path.toString(arena)); } - - Compilation.dump_argv(argv.items); } pub const ParseError = error{ @@ -2177,7 +2015,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s 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 = try std.fmt.allocPrint(arena, "{}{c}lib{s}.so.{d}", .{ comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, }); try argv.append(lib_path); diff --git a/src/musl.zig b/src/musl.zig index 60ed3c040a..41a9c89143 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -281,7 +281,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro errdefer comp.gpa.free(basename); const crt_file = try sub_compilation.toCrtFile(); - comp.enqueueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); { comp.mutex.lock(); defer comp.mutex.unlock();