zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Msrc/Compilation.zig | 51++++++++++++++++++++++++++++++++++++++++-----------
Msrc/libs/mingw.zig | 37+++++++++++++++++++++----------------
Msrc/link.zig | 15+++------------
Msrc/link/Coff.zig | 37+++++++++++++++++++++++++++++--------
Msrc/link/MappedFile.zig | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtest/standalone/shared_library/build.zig | 2--
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,