branch fixes

This commit is contained in:
Andrew Kelley 2024-10-18 22:29:50 -07:00
parent 5ca54036ca
commit c2898c436f
10 changed files with 286 additions and 429 deletions

View File

@ -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 },
};

View File

@ -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;

View File

@ -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;
}
};
}

View File

@ -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(

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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| {

View File

@ -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);

View File

@ -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();