commit 22a22ceaeb3f693207fc4821b55959ba0732d7ed (tree)
parent 2be291c98773e25d4a47e5c35744084627d11f15
Author: kcbanner <kcbanner@gmail.com>
Date: Fri, 5 Jun 2026 01:55:35 -0400
Prelink tasks for MingGW implibs and MappedFile fixes
- Add MappedFile.realign
- Fix the case of MappedFile.addNode adding a node in between nodes that have lower alignment than it (by realigning the following node)
- Fixup the capacity reservation in addNode to occur after the resize (which may have consumed that capacity)
- Remove incorrect path in `.load_host_libc` that was loading mingw libs, they were already being loaded via their build tasks
- Generate mingw implibs as a prelink task, so they can be supplied to the linker before prelink()
- Any other libraries discovered during Sema are have their implibs generated after, but the self-hosted linker is not passed these. Lld can still use this path.
- mingw implib generation now interacts with the progress system
- Supply `__ImageBase` for mingw
Diffstat:
6 files changed, 161 insertions(+), 61 deletions(-)
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -4475,18 +4475,10 @@ fn performAllTheWork(
comp.link_queue.finishZcuQueue(comp);
- // This has to happen after the main semantic analysis loop because it is possible for Sema to
+ // This has to happen again after the main semantic analysis loop because it is possible for Sema to
// call `addLinkLib` and hence add more items to `comp.windows_libs`.
- for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |link_lib| {
- mingw.buildImportLib(comp, link_lib) catch |err| {
- // TODO Surface more error details.
- comp.lockAndSetMiscFailure(
- .windows_import_lib,
- "unable to generate DLL import .lib file for {s}: {t}",
- .{ link_lib, err },
- );
- };
- }
+ for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |lib_name|
+ comp.buildMingwImportLib(lib_name, false, main_progress_node);
comp.windows_libs_num_done = @intCast(comp.windows_libs.count());
// Main thread work is all done, now just wait for all async work.
@@ -4692,6 +4684,15 @@ fn dispatchPrelinkWork(comp: *Compilation, main_progress_node: std.Progress.Node
});
}
+ while (comp.windows_libs_num_done < comp.windows_libs.count()) {
+ prelink_group.async(
+ io,
+ buildMingwImportLib,
+ .{ comp, comp.windows_libs.keys()[comp.windows_libs_num_done], true, main_progress_node },
+ );
+ comp.windows_libs_num_done += 1;
+ }
+
prelink_group.await(io) catch |err| switch (err) {
error.Canceled => unreachable, // see swapCancelProtection above
};
@@ -5377,6 +5378,34 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std
}
}
+fn buildMingwImportLib(comp: *Compilation, lib_name: []const u8, is_prelink: bool, prog_node: std.Progress.Node) void {
+ const crt_file_path = mingw.buildImportLib(comp, lib_name, prog_node) catch |err| switch (err) {
+ // TODO: This isn't actually true for self-hosted
+ // In the non-prelink case we will end up putting foo.lib onto the linker line and letting the linker
+ // use its library paths to look for libraries and report any problems.
+ error.DefNotFound => return if (is_prelink) {
+ comp.lockAndSetMiscFailure(
+ .windows_import_lib,
+ "definition not found for required mingw DLL import .lib {s}",
+ .{lib_name},
+ );
+ },
+ // TODO Surface more error details.
+ else => |e| return comp.lockAndSetMiscFailure(
+ .windows_import_lib,
+ "unable to generate mingw DLL import .lib file for {s}: {t}",
+ .{ lib_name, e },
+ ),
+ };
+
+ if (is_prelink)
+ comp.queuePrelinkTasks(&.{.{ .load_archive = crt_file_path }}) catch |err| comp.lockAndSetMiscFailure(
+ .windows_import_lib,
+ "unable to queue prelink task for mingw import lib {f}: {t}",
+ .{ crt_file_path, err },
+ );
+}
+
fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
diff --git a/src/libs/mingw.zig b/src/libs/mingw.zig
@@ -207,9 +207,12 @@ fn addCrtCcArgs(
});
}
-pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
+pub fn buildImportLib(comp: *Compilation, lib_name: []const u8, prog_node: std.Progress.Node) !Cache.Path {
dev.check(.build_import_lib);
+ const sub_node = prog_node.start(lib_name, 0);
+ defer sub_node.end();
+
const gpa = comp.gpa;
const io = comp.io;
@@ -218,12 +221,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
const arena = arena_allocator.allocator();
const def_file_path = findDef(arena, io, comp.getTarget(), comp.dirs.zig_lib, lib_name) catch |err| switch (err) {
- error.FileNotFound => {
- log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
- // In this case we will end up putting foo.lib onto the linker line and letting the linker
- // use its library paths to look for libraries and report any problems.
- return;
- },
+ error.FileNotFound => return error.DefNotFound,
else => |e| return e,
};
// Only .def.in files need preprocessing
@@ -263,14 +261,16 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
try comp.crt_files.ensureUnusedCapacity(gpa, 1);
+
+ const crt_file_path: Cache.Path = .{
+ .root_dir = comp.dirs.global_cache,
+ .sub_path = sub_path,
+ };
comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
- .full_object_path = .{
- .root_dir = comp.dirs.global_cache,
- .sub_path = sub_path,
- },
+ .full_object_path = crt_file_path,
.lock = man.toOwnedLock(),
});
- return;
+ return crt_file_path;
}
const digest = man.final();
@@ -294,6 +294,9 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
}
const members = members: {
+ const members_node = sub_node.start("Members", 0);
+ defer members_node.end();
+
const input = switch (def_needs_preprocessing) {
true => pp: {
var aw: Io.Writer.Allocating = .init(gpa);
@@ -357,13 +360,15 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
comp.mutex.lockUncancelable(io);
defer comp.mutex.unlock(io);
+ const crt_file_path: Cache.Path = .{
+ .root_dir = comp.dirs.global_cache,
+ .sub_path = lib_final_path,
+ };
try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{
- .full_object_path = .{
- .root_dir = comp.dirs.global_cache,
- .sub_path = lib_final_path,
- },
+ .full_object_path = crt_file_path,
.lock = man.toOwnedLock(),
});
+ return crt_file_path;
}
pub fn libExists(
diff --git a/src/link.zig b/src/link.zig
@@ -482,7 +482,7 @@ pub const File = struct {
rpath_list: []const []const u8,
/// Zig compiler development linker flags.
- /// Enable dumping of linker's state as JSON.
+ /// Enable dumping of linker's state.
enable_link_snapshots: bool,
/// Darwin-specific linker flags:
@@ -1527,20 +1527,11 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
}
}
- if (target.os.tag == .windows) {
+ if (target.os.tag == .windows and target.abi == .msvc) {
const inputs: []const struct {
dir: enum { crt, msvc_lib, kernel32_lib },
name: []const u8,
- } = if (target.abi.isGnu()) switch (comp.config.link_mode) {
- .dynamic => &.{
- .{ .dir = .crt, .name = "dllcrt2.obj" },
- .{ .dir = .crt, .name = "libmingw32.lib" },
- },
- .static => &.{
- .{ .dir = .crt, .name = "crt2.obj" },
- .{ .dir = .crt, .name = "libmingw32.lib" },
- },
- } else switch (comp.config.link_mode) {
+ } = switch (comp.config.link_mode) {
.dynamic => &.{
.{ .dir = .msvc_lib, .name = "msvcrt.lib" },
.{ .dir = .msvc_lib, .name = "vcruntime.lib" },
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -2017,6 +2017,16 @@ fn initHeaders(
.{ .read = true, .write = !is_image },
);
}
+
+ // Linker-supplied symbols
+ {
+ const target = &comp.root_mod.resolved_target.result;
+ if (is_image and target.isMinGW()) {
+ const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
+ const sym = si.get(coff);
+ sym.ni = Node.known.header;
+ }
+ }
}
pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
@@ -2044,7 +2054,7 @@ pub fn endProgress(coff: *Coff) void {
coff.mf.update_prog_node = .none;
coff.input_prog_node.end();
coff.input_prog_node = .none;
- if (!isImage(coff)) {
+ if (!coff.isImage()) {
coff.member_prog_node.end();
coff.member_prog_node = .none;
coff.symbol_prog_node.end();
@@ -3103,7 +3113,7 @@ fn objectSectionMapIndex(
const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
- const sn = if (!object_section_gop.found_existing) sn: {
+ const sym = if (!object_section_gop.found_existing) sn: {
try coff.ensureUnusedStringCapacity(name_slice.len);
const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
@@ -3140,14 +3150,24 @@ fn objectSectionMapIndex(
assert(sym.loc_relocs == .none);
sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
- break :sn sym.section_number;
- } else object_section_gop.value_ptr.get(coff).section_number;
+ break :sn sym;
+ } else object_section_gop.value_ptr.get(coff);
+
+ const parent_ni = sym.ni.parent(&coff.mf);
+ const parent_alignment = parent_ni.alignment(&coff.mf);
+ if (alignment.compare(.gt, parent_alignment)) {
+ log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
+ parent_ni.realign(&coff.mf, gpa, alignment, true) catch |err| switch (err) {
+ error.Unimplemented => unreachable,
+ else => |e| return e,
+ };
+ }
try coff.verifyParentSectionAttributes(
.object,
- sn.name(coff),
+ sym.section_number.name(coff),
name,
- .fromFlags(sn.header(coff).flags),
+ .fromFlags(sym.section_number.header(coff).flags),
effective_attributes,
);
@@ -4135,7 +4155,7 @@ fn loadObject(
},
.weak_external => unreachable,
});
- sym.section_number = symbol.section_number;
+ sym.section_number = section.si.get(coff).section_number;
}
log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
@@ -4988,7 +5008,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
const other_ioi = isi.input(coff);
if (loc_sym.gmi == .none) {
// TODO: We could report the name here if we interned it in loadObject
- err.addNote("referenced internally by input '{f}{f}'", .{
+ err.addNote("referenced by input '{f}{f}'", .{
other_ioi.path(coff).fmtEscapeString(),
fmtMemberNameString(other_ioi.memberName(coff)),
});
@@ -5430,6 +5450,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
return true;
}
+ // TODO: Only do this if actually referenced? Might have to do on-demand?
{
// Resolve unresolved .WEAK_EXTERNAL symbols to their aliases
const alias_si = sym.weakAlias();
diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig
@@ -351,15 +351,17 @@ pub const Node = extern struct {
}
/// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
- ///
+ /// If it is possible to move the node backwards, this will be done instead of moving it forward.
+ /// If `set_alignment` is set, persists `new_alignment` as the node's alignment for future operations.
/// Asserts that `ni` is not `Node.Index.root`.
pub fn realign(
ni: Node.Index,
mf: *MappedFile,
gpa: std.mem.Allocator,
new_alignment: std.mem.Alignment,
+ set_alignment: bool,
) Error!void {
- mf.realignNode(gpa, ni, new_alignment) catch |err| switch (err) {
+ mf.realignNode(gpa, ni, new_alignment, true, set_alignment) catch |err| switch (err) {
error.OutOfMemory,
error.Canceled,
=> |e| return e,
@@ -554,6 +556,24 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
}) Error!Node.Index {
if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
+ if (opts.parent != .none) {
+ const new_end = offset + opts.add_node.size;
+ switch (opts.next) {
+ .none => {
+ _, const parent_size = opts.parent.location(mf).resolve(mf);
+ if (new_end > parent_size)
+ try opts.parent.resize(mf, gpa, new_end);
+ },
+ else => |next_ni| {
+ const next_offset, _ = next_ni.location(mf).resolve(mf);
+ if (new_end > next_offset)
+ mf.realignNode(gpa, next_ni, opts.add_node.alignment, false, false) catch |err| switch (err) {
+ error.Unimplemented => unreachable,
+ else => |e| return e,
+ };
+ },
+ }
+ }
const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
if (std.math.cast(u32, offset)) |small_offset| break :location .{ .small, .{
.small = .{ .offset = small_offset, .size = 0 },
@@ -595,16 +615,12 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
},
.location_payload = location_payload,
};
+
{
- defer {
- free_node.flags.moved = false;
- free_node.flags.resized = false;
- }
- _, const parent_size = opts.parent.location(mf).resolve(mf);
- const required_parent_size = offset + opts.add_node.size;
- if (required_parent_size > parent_size)
- try opts.parent.resize(mf, gpa, required_parent_size);
try free_ni.resize(mf, gpa, opts.add_node.size);
+ if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
+ free_node.flags.moved = false;
+ free_node.flags.resized = false;
}
if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
@@ -703,6 +719,7 @@ fn shrinkNode(
// This would require unmapping first
if (ni == Node.Index.root) return error.Unimplemented;
+ defer if (std.debug.runtime_safety) mf.verify();
if (node.last != .none) {
const last = node.last.get(mf);
@@ -725,9 +742,10 @@ fn shrinkNode(
const old_file_offset = node.next.fileLocation(mf, false).offset;
const new_file_offset = (old_file_offset - old_next_offset) + new_next_offset;
@memmove(
- mf.memory_map.memory[new_file_offset..][0..next_size],
- mf.memory_map.memory[old_file_offset..][0..next_size],
+ mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(next_size)],
+ mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(next_size)],
);
+ @memset(mf.memory_map.memory[@intCast(new_file_offset + next_size)..@intCast(old_file_offset + next_size)], 0);
}
node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size);
@@ -999,6 +1017,8 @@ fn realignNode(
gpa: std.mem.Allocator,
ni: Node.Index,
new_alignment: std.mem.Alignment,
+ try_backward: bool,
+ set_alignment: bool,
) (Allocator.Error || Io.Cancelable || IoError)!void {
assert(ni != Node.Index.root); // currently unsupported
@@ -1009,7 +1029,12 @@ fn realignNode(
defer if (std.debug.runtime_safety) mf.verify();
+ const prev_alignment = node.flags.alignment;
node.flags.alignment = new_alignment;
+ defer {
+ // alignment needs to be temporarily set for the resizes below
+ if (!set_alignment) node.flags.alignment = prev_alignment;
+ }
const new_size = node.flags.alignment.forward(@intCast(size));
if (new_alignment.check(@intCast(old_offset))) {
@@ -1026,6 +1051,37 @@ fn realignNode(
},
};
+ if (try_backward) {
+ const backward_offset = new_alignment.backward(old_offset);
+ const prev_end = if (node.prev == .none) 0 else prev: {
+ const prev_offset, const prev_size = node.prev.location(mf).resolve(mf);
+ break :prev prev_offset + prev_size;
+ };
+
+ if (backward_offset >= prev_end) {
+ try mf.ensureCapacityForSetLocation(gpa);
+
+ if (node.flags.has_content) {
+ const old_file_offset = ni.fileLocation(mf, false).offset;
+ const new_file_offset = (old_file_offset - old_offset) + backward_offset;
+ @memmove(
+ mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
+ mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
+ );
+ @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..@intCast(old_file_offset + size)], 0);
+ }
+
+ if (backward_offset + new_size <= trailing_end) {
+ ni.setLocationAssumeCapacity(mf, backward_offset, new_size);
+ } else {
+ ni.setLocationAssumeCapacity(mf, backward_offset, size);
+ try mf.resizeNode(gpa, ni, new_size);
+ }
+
+ return;
+ }
+ }
+
const forward_offset = new_alignment.forward(@intCast(old_offset));
if (forward_offset + new_size <= trailing_end) {
// Shift into the free space if possible
diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig
@@ -20,8 +20,6 @@ pub fn build(b: *std.Build) void {
if (!use_llvm and target.result.cpu.arch == .loongarch64) continue; // TODO
if (!use_llvm and target.result.cpu.arch == .powerpc64le) continue; // TODO
if (!use_llvm and target.result.cpu.arch == .s390x) continue; // TODO
- if (!use_llvm and target.result.os.tag == .windows and target.result.abi == .gnu and dyn_libc)
- continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)
const lib = b.addLibrary(.{
.linkage = .dynamic,