Commit c2898c436f
Changed files (10)
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 },
};
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);
-
- 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));
- }
+ try argv.append(gpa, "-o");
+ try argv.append(gpa, full_out_path);
- 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);
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 },
+ 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)) {
+ try comp.queueJobs(&[_]Job{
+ .{ .musl_crt_file = .crti_o },
+ .{ .musl_crt_file = .crtn_o },
+ });
+ }
+ 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.isMusl() and !target.isWasm()) {
- if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
-
- if (musl.needsCrtiCrtn(target)) {
+ } else if (target.isGnuLibC()) {
+ if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
+
+ if (glibc.needsCrtiCrtn(target)) {
+ try comp.queueJobs(&[_]Job{
+ .{ .glibc_crt_file = .crti_o },
+ .{ .glibc_crt_file = .crtn_o },
+ });
+ }
try comp.queueJobs(&[_]Job{
- .{ .musl_crt_file = .crti_o },
- .{ .musl_crt_file = .crtn_o },
+ .{ .glibc_crt_file = .scrt1_o },
+ .{ .glibc_crt_file = .libc_nonshared_a },
+ .{ .glibc_shared_objects = {} },
});
- }
- 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.isWasm() and target.os.tag == .wasi) {
+ if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
- if (glibc.needsCrtiCrtn(target)) {
+ for (comp.wasi_emulated_libs) |crt_file| {
+ try comp.queueJob(.{
+ .wasi_libc_crt_file = crt_file,
+ });
+ }
try comp.queueJobs(&[_]Job{
- .{ .glibc_crt_file = .crti_o },
- .{ .glibc_crt_file = .crtn_o },
+ .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) },
+ .{ .wasi_libc_crt_file = .libc_a },
});
- }
- 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.isMinGW()) {
+ 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,
+ 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,
});
- }
- 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;
+ // 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;
+ }
}
- }
- // 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 (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",
- };
+ 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.
- try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
- }
+ const tls_index_sym = switch (target.cpu.arch) {
+ .x86 => "__tls_index",
+ else => "_tls_index",
+ };
- 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;
+ try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {});
}
- }
- 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 (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.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 (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;
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(
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 {
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;
}
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;
}
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| {
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();
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;
+ }
};
}