authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:54-04:00
log22a22ceaeb3f693207fc4821b55959ba0732d7ed
treefeb6df740e4d0a2fbe767b48d92518484bb57619
parent2be291c98773e25d4a47e5c35744084627d11f15

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

6 files changed, 161 insertions(+), 61 deletions(-)

src/Compilation.zig+40-11
......@@ -4475,18 +4475,10 @@ fn performAllTheWork(
44754475
44764476 comp.link_queue.finishZcuQueue(comp);
44774477
4478 // This has to happen after the main semantic analysis loop because it is possible for Sema to
4478 // This has to happen again after the main semantic analysis loop because it is possible for Sema to
44794479 // call `addLinkLib` and hence add more items to `comp.windows_libs`.
4480 for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |link_lib| {
4481 mingw.buildImportLib(comp, link_lib) catch |err| {
4482 // TODO Surface more error details.
4483 comp.lockAndSetMiscFailure(
4484 .windows_import_lib,
4485 "unable to generate DLL import .lib file for {s}: {t}",
4486 .{ link_lib, err },
4487 );
4488 };
4489 }
4480 for (comp.windows_libs.keys()[comp.windows_libs_num_done..]) |lib_name|
4481 comp.buildMingwImportLib(lib_name, false, main_progress_node);
44904482 comp.windows_libs_num_done = @intCast(comp.windows_libs.count());
44914483
44924484 // 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
46924684 });
46934685 }
46944686
4687 while (comp.windows_libs_num_done < comp.windows_libs.count()) {
4688 prelink_group.async(
4689 io,
4690 buildMingwImportLib,
4691 .{ comp, comp.windows_libs.keys()[comp.windows_libs_num_done], true, main_progress_node },
4692 );
4693 comp.windows_libs_num_done += 1;
4694 }
4695
46954696 prelink_group.await(io) catch |err| switch (err) {
46964697 error.Canceled => unreachable, // see swapCancelProtection above
46974698 };
......@@ -5377,6 +5378,34 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std
53775378 }
53785379}
53795380
5381fn buildMingwImportLib(comp: *Compilation, lib_name: []const u8, is_prelink: bool, prog_node: std.Progress.Node) void {
5382 const crt_file_path = mingw.buildImportLib(comp, lib_name, prog_node) catch |err| switch (err) {
5383 // TODO: This isn't actually true for self-hosted
5384 // In the non-prelink case we will end up putting foo.lib onto the linker line and letting the linker
5385 // use its library paths to look for libraries and report any problems.
5386 error.DefNotFound => return if (is_prelink) {
5387 comp.lockAndSetMiscFailure(
5388 .windows_import_lib,
5389 "definition not found for required mingw DLL import .lib {s}",
5390 .{lib_name},
5391 );
5392 },
5393 // TODO Surface more error details.
5394 else => |e| return comp.lockAndSetMiscFailure(
5395 .windows_import_lib,
5396 "unable to generate mingw DLL import .lib file for {s}: {t}",
5397 .{ lib_name, e },
5398 ),
5399 };
5400
5401 if (is_prelink)
5402 comp.queuePrelinkTasks(&.{.{ .load_archive = crt_file_path }}) catch |err| comp.lockAndSetMiscFailure(
5403 .windows_import_lib,
5404 "unable to queue prelink task for mingw import lib {f}: {t}",
5405 .{ crt_file_path, err },
5406 );
5407}
5408
53805409fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
53815410 if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
53825411 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
src/libs/mingw.zig+21-16
......@@ -207,9 +207,12 @@ fn addCrtCcArgs(
207207 });
208208}
209209
210pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
210pub fn buildImportLib(comp: *Compilation, lib_name: []const u8, prog_node: std.Progress.Node) !Cache.Path {
211211 dev.check(.build_import_lib);
212212
213 const sub_node = prog_node.start(lib_name, 0);
214 defer sub_node.end();
215
213216 const gpa = comp.gpa;
214217 const io = comp.io;
215218
......@@ -218,12 +221,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
218221 const arena = arena_allocator.allocator();
219222
220223 const def_file_path = findDef(arena, io, comp.getTarget(), comp.dirs.zig_lib, lib_name) catch |err| switch (err) {
221 error.FileNotFound => {
222 log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name });
223 // In this case we will end up putting foo.lib onto the linker line and letting the linker
224 // use its library paths to look for libraries and report any problems.
225 return;
226 },
224 error.FileNotFound => return error.DefNotFound,
227225 else => |e| return e,
228226 };
229227 // Only .def.in files need preprocessing
......@@ -263,14 +261,16 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
263261 comp.mutex.lockUncancelable(io);
264262 defer comp.mutex.unlock(io);
265263 try comp.crt_files.ensureUnusedCapacity(gpa, 1);
264
265 const crt_file_path: Cache.Path = .{
266 .root_dir = comp.dirs.global_cache,
267 .sub_path = sub_path,
268 };
266269 comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{
267 .full_object_path = .{
268 .root_dir = comp.dirs.global_cache,
269 .sub_path = sub_path,
270 },
270 .full_object_path = crt_file_path,
271271 .lock = man.toOwnedLock(),
272272 });
273 return;
273 return crt_file_path;
274274 }
275275
276276 const digest = man.final();
......@@ -294,6 +294,9 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
294294 }
295295
296296 const members = members: {
297 const members_node = sub_node.start("Members", 0);
298 defer members_node.end();
299
297300 const input = switch (def_needs_preprocessing) {
298301 true => pp: {
299302 var aw: Io.Writer.Allocating = .init(gpa);
......@@ -357,13 +360,15 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
357360
358361 comp.mutex.lockUncancelable(io);
359362 defer comp.mutex.unlock(io);
363 const crt_file_path: Cache.Path = .{
364 .root_dir = comp.dirs.global_cache,
365 .sub_path = lib_final_path,
366 };
360367 try comp.crt_files.putNoClobber(gpa, final_lib_basename, .{
361 .full_object_path = .{
362 .root_dir = comp.dirs.global_cache,
363 .sub_path = lib_final_path,
364 },
368 .full_object_path = crt_file_path,
365369 .lock = man.toOwnedLock(),
366370 });
371 return crt_file_path;
367372}
368373
369374pub fn libExists(
src/link.zig+3-12
......@@ -482,7 +482,7 @@ pub const File = struct {
482482 rpath_list: []const []const u8,
483483
484484 /// Zig compiler development linker flags.
485 /// Enable dumping of linker's state as JSON.
485 /// Enable dumping of linker's state.
486486 enable_link_snapshots: bool,
487487
488488 /// Darwin-specific linker flags:
......@@ -1527,20 +1527,11 @@ pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
15271527 }
15281528 }
15291529
1530 if (target.os.tag == .windows) {
1530 if (target.os.tag == .windows and target.abi == .msvc) {
15311531 const inputs: []const struct {
15321532 dir: enum { crt, msvc_lib, kernel32_lib },
15331533 name: []const u8,
1534 } = if (target.abi.isGnu()) switch (comp.config.link_mode) {
1535 .dynamic => &.{
1536 .{ .dir = .crt, .name = "dllcrt2.obj" },
1537 .{ .dir = .crt, .name = "libmingw32.lib" },
1538 },
1539 .static => &.{
1540 .{ .dir = .crt, .name = "crt2.obj" },
1541 .{ .dir = .crt, .name = "libmingw32.lib" },
1542 },
1543 } else switch (comp.config.link_mode) {
1534 } = switch (comp.config.link_mode) {
15441535 .dynamic => &.{
15451536 .{ .dir = .msvc_lib, .name = "msvcrt.lib" },
15461537 .{ .dir = .msvc_lib, .name = "vcruntime.lib" },
src/link/Coff.zig+29-8
......@@ -2017,6 +2017,16 @@ fn initHeaders(
20172017 .{ .read = true, .write = !is_image },
20182018 );
20192019 }
2020
2021 // Linker-supplied symbols
2022 {
2023 const target = &comp.root_mod.resolved_target.result;
2024 if (is_image and target.isMinGW()) {
2025 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
2026 const sym = si.get(coff);
2027 sym.ni = Node.known.header;
2028 }
2029 }
20202030}
20212031
20222032pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
......@@ -2044,7 +2054,7 @@ pub fn endProgress(coff: *Coff) void {
20442054 coff.mf.update_prog_node = .none;
20452055 coff.input_prog_node.end();
20462056 coff.input_prog_node = .none;
2047 if (!isImage(coff)) {
2057 if (!coff.isImage()) {
20482058 coff.member_prog_node.end();
20492059 coff.member_prog_node = .none;
20502060 coff.symbol_prog_node.end();
......@@ -3103,7 +3113,7 @@ fn objectSectionMapIndex(
31033113
31043114 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
31053115 const osmi: Node.ObjectSectionMapIndex = @enumFromInt(object_section_gop.index);
3106 const sn = if (!object_section_gop.found_existing) sn: {
3116 const sym = if (!object_section_gop.found_existing) sn: {
31073117 try coff.ensureUnusedStringCapacity(name_slice.len);
31083118 const parent_name = coff.getOrPutStringAssumeCapacity(coff.objectSectionParentName(name_slice));
31093119 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, effective_attributes)).symbol(coff);
......@@ -3140,14 +3150,24 @@ fn objectSectionMapIndex(
31403150 assert(sym.loc_relocs == .none);
31413151 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
31423152 coff.nodes.appendAssumeCapacity(.{ .object_section = osmi });
3143 break :sn sym.section_number;
3144 } else object_section_gop.value_ptr.get(coff).section_number;
3153 break :sn sym;
3154 } else object_section_gop.value_ptr.get(coff);
3155
3156 const parent_ni = sym.ni.parent(&coff.mf);
3157 const parent_alignment = parent_ni.alignment(&coff.mf);
3158 if (alignment.compare(.gt, parent_alignment)) {
3159 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
3160 parent_ni.realign(&coff.mf, gpa, alignment, true) catch |err| switch (err) {
3161 error.Unimplemented => unreachable,
3162 else => |e| return e,
3163 };
3164 }
31453165
31463166 try coff.verifyParentSectionAttributes(
31473167 .object,
3148 sn.name(coff),
3168 sym.section_number.name(coff),
31493169 name,
3150 .fromFlags(sn.header(coff).flags),
3170 .fromFlags(sym.section_number.header(coff).flags),
31513171 effective_attributes,
31523172 );
31533173
......@@ -4135,7 +4155,7 @@ fn loadObject(
41354155 },
41364156 .weak_external => unreachable,
41374157 });
4138 sym.section_number = symbol.section_number;
4158 sym.section_number = section.si.get(coff).section_number;
41394159 }
41404160
41414161 log.debug("addInputSymbol({s}, 0x{x}, {t}=0x{x}) = {d}@{d}", .{
......@@ -4988,7 +5008,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
49885008 const other_ioi = isi.input(coff);
49895009 if (loc_sym.gmi == .none) {
49905010 // TODO: We could report the name here if we interned it in loadObject
4991 err.addNote("referenced internally by input '{f}{f}'", .{
5011 err.addNote("referenced by input '{f}{f}'", .{
49925012 other_ioi.path(coff).fmtEscapeString(),
49935013 fmtMemberNameString(other_ioi.memberName(coff)),
49945014 });
......@@ -5430,6 +5450,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
54305450 return true;
54315451 }
54325452
5453 // TODO: Only do this if actually referenced? Might have to do on-demand?
54335454 {
54345455 // Resolve unresolved .WEAK_EXTERNAL symbols to their aliases
54355456 const alias_si = sym.weakAlias();
src/link/MappedFile.zig+68-12
......@@ -351,15 +351,17 @@ pub const Node = extern struct {
351351 }
352352
353353 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
354 ///
354 /// If it is possible to move the node backwards, this will be done instead of moving it forward.
355 /// If `set_alignment` is set, persists `new_alignment` as the node's alignment for future operations.
355356 /// Asserts that `ni` is not `Node.Index.root`.
356357 pub fn realign(
357358 ni: Node.Index,
358359 mf: *MappedFile,
359360 gpa: std.mem.Allocator,
360361 new_alignment: std.mem.Alignment,
362 set_alignment: bool,
361363 ) Error!void {
362 mf.realignNode(gpa, ni, new_alignment) catch |err| switch (err) {
364 mf.realignNode(gpa, ni, new_alignment, true, set_alignment) catch |err| switch (err) {
363365 error.OutOfMemory,
364366 error.Canceled,
365367 => |e| return e,
......@@ -554,6 +556,24 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
554556}) Error!Node.Index {
555557 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
556558 const offset = opts.add_node.alignment.forward(@intCast(opts.offset));
559 if (opts.parent != .none) {
560 const new_end = offset + opts.add_node.size;
561 switch (opts.next) {
562 .none => {
563 _, const parent_size = opts.parent.location(mf).resolve(mf);
564 if (new_end > parent_size)
565 try opts.parent.resize(mf, gpa, new_end);
566 },
567 else => |next_ni| {
568 const next_offset, _ = next_ni.location(mf).resolve(mf);
569 if (new_end > next_offset)
570 mf.realignNode(gpa, next_ni, opts.add_node.alignment, false, false) catch |err| switch (err) {
571 error.Unimplemented => unreachable,
572 else => |e| return e,
573 };
574 },
575 }
576 }
557577 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
558578 if (std.math.cast(u32, offset)) |small_offset| break :location .{ .small, .{
559579 .small = .{ .offset = small_offset, .size = 0 },
......@@ -595,16 +615,12 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
595615 },
596616 .location_payload = location_payload,
597617 };
618
598619 {
599 defer {
600 free_node.flags.moved = false;
601 free_node.flags.resized = false;
602 }
603 _, const parent_size = opts.parent.location(mf).resolve(mf);
604 const required_parent_size = offset + opts.add_node.size;
605 if (required_parent_size > parent_size)
606 try opts.parent.resize(mf, gpa, required_parent_size);
607620 try free_ni.resize(mf, gpa, opts.add_node.size);
621 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
622 free_node.flags.moved = false;
623 free_node.flags.resized = false;
608624 }
609625 if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
610626 if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
......@@ -703,6 +719,7 @@ fn shrinkNode(
703719
704720 // This would require unmapping first
705721 if (ni == Node.Index.root) return error.Unimplemented;
722 defer if (std.debug.runtime_safety) mf.verify();
706723
707724 if (node.last != .none) {
708725 const last = node.last.get(mf);
......@@ -725,9 +742,10 @@ fn shrinkNode(
725742 const old_file_offset = node.next.fileLocation(mf, false).offset;
726743 const new_file_offset = (old_file_offset - old_next_offset) + new_next_offset;
727744 @memmove(
728 mf.memory_map.memory[new_file_offset..][0..next_size],
729 mf.memory_map.memory[old_file_offset..][0..next_size],
745 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(next_size)],
746 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(next_size)],
730747 );
748 @memset(mf.memory_map.memory[@intCast(new_file_offset + next_size)..@intCast(old_file_offset + next_size)], 0);
731749 }
732750
733751 node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size);
......@@ -999,6 +1017,8 @@ fn realignNode(
9991017 gpa: std.mem.Allocator,
10001018 ni: Node.Index,
10011019 new_alignment: std.mem.Alignment,
1020 try_backward: bool,
1021 set_alignment: bool,
10021022) (Allocator.Error || Io.Cancelable || IoError)!void {
10031023 assert(ni != Node.Index.root); // currently unsupported
10041024
......@@ -1009,7 +1029,12 @@ fn realignNode(
10091029
10101030 defer if (std.debug.runtime_safety) mf.verify();
10111031
1032 const prev_alignment = node.flags.alignment;
10121033 node.flags.alignment = new_alignment;
1034 defer {
1035 // alignment needs to be temporarily set for the resizes below
1036 if (!set_alignment) node.flags.alignment = prev_alignment;
1037 }
10131038
10141039 const new_size = node.flags.alignment.forward(@intCast(size));
10151040 if (new_alignment.check(@intCast(old_offset))) {
......@@ -1026,6 +1051,37 @@ fn realignNode(
10261051 },
10271052 };
10281053
1054 if (try_backward) {
1055 const backward_offset = new_alignment.backward(old_offset);
1056 const prev_end = if (node.prev == .none) 0 else prev: {
1057 const prev_offset, const prev_size = node.prev.location(mf).resolve(mf);
1058 break :prev prev_offset + prev_size;
1059 };
1060
1061 if (backward_offset >= prev_end) {
1062 try mf.ensureCapacityForSetLocation(gpa);
1063
1064 if (node.flags.has_content) {
1065 const old_file_offset = ni.fileLocation(mf, false).offset;
1066 const new_file_offset = (old_file_offset - old_offset) + backward_offset;
1067 @memmove(
1068 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
1069 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
1070 );
1071 @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..@intCast(old_file_offset + size)], 0);
1072 }
1073
1074 if (backward_offset + new_size <= trailing_end) {
1075 ni.setLocationAssumeCapacity(mf, backward_offset, new_size);
1076 } else {
1077 ni.setLocationAssumeCapacity(mf, backward_offset, size);
1078 try mf.resizeNode(gpa, ni, new_size);
1079 }
1080
1081 return;
1082 }
1083 }
1084
10291085 const forward_offset = new_alignment.forward(@intCast(old_offset));
10301086 if (forward_offset + new_size <= trailing_end) {
10311087 // Shift into the free space if possible
test/standalone/shared_library/build.zig-2
......@@ -20,8 +20,6 @@ pub fn build(b: *std.Build) void {
2020 if (!use_llvm and target.result.cpu.arch == .loongarch64) continue; // TODO
2121 if (!use_llvm and target.result.cpu.arch == .powerpc64le) continue; // TODO
2222 if (!use_llvm and target.result.cpu.arch == .s390x) continue; // TODO
23 if (!use_llvm and target.result.os.tag == .windows and target.result.abi == .gnu and dyn_libc)
24 continue; // TODO: sub-compilation of compiler_rt failed (failed to link with LLD: LibCInstallationNotAvailable)
2523
2624 const lib = b.addLibrary(.{
2725 .linkage = .dynamic,