master
   1const builtin = @import("builtin");
   2const build_options = @import("build_options");
   3
   4const std = @import("std");
   5const Io = std.Io;
   6const assert = std.debug.assert;
   7const fs = std.fs;
   8const mem = std.mem;
   9const log = std.log.scoped(.link);
  10const Allocator = std.mem.Allocator;
  11const Cache = std.Build.Cache;
  12const Path = std.Build.Cache.Path;
  13const Directory = std.Build.Cache.Directory;
  14const Compilation = @import("Compilation.zig");
  15const LibCInstallation = std.zig.LibCInstallation;
  16
  17const trace = @import("tracy.zig").trace;
  18const wasi_libc = @import("libs/wasi_libc.zig");
  19
  20const Zcu = @import("Zcu.zig");
  21const InternPool = @import("InternPool.zig");
  22const Type = @import("Type.zig");
  23const Value = @import("Value.zig");
  24const Package = @import("Package.zig");
  25const dev = @import("dev.zig");
  26const target_util = @import("target.zig");
  27const codegen = @import("codegen.zig");
  28
  29pub const aarch64 = @import("link/aarch64.zig");
  30pub const LdScript = @import("link/LdScript.zig");
  31pub const Queue = @import("link/Queue.zig");
  32
  33pub const Diags = struct {
  34    /// Stored here so that function definitions can distinguish between
  35    /// needing an allocator for things besides error reporting.
  36    gpa: Allocator,
  37    mutex: std.Thread.Mutex,
  38    msgs: std.ArrayList(Msg),
  39    flags: Flags,
  40    lld: std.ArrayList(Lld),
  41
  42    pub const SourceLocation = union(enum) {
  43        none,
  44        wasm: File.Wasm.SourceLocation,
  45    };
  46
  47    pub const Flags = packed struct {
  48        no_entry_point_found: bool = false,
  49        missing_libc: bool = false,
  50        alloc_failure_occurred: bool = false,
  51
  52        const Int = blk: {
  53            const bits = @typeInfo(@This()).@"struct".fields.len;
  54            break :blk @Int(.unsigned, bits);
  55        };
  56
  57        pub fn anySet(ef: Flags) bool {
  58            return @as(Int, @bitCast(ef)) > 0;
  59        }
  60    };
  61
  62    pub const Lld = struct {
  63        /// Allocated with gpa.
  64        msg: []const u8,
  65        context_lines: []const []const u8 = &.{},
  66
  67        pub fn deinit(self: *Lld, gpa: Allocator) void {
  68            for (self.context_lines) |line| gpa.free(line);
  69            gpa.free(self.context_lines);
  70            gpa.free(self.msg);
  71            self.* = undefined;
  72        }
  73    };
  74
  75    pub const Msg = struct {
  76        source_location: SourceLocation = .none,
  77        msg: []const u8,
  78        notes: []Msg = &.{},
  79
  80        fn string(
  81            msg: *const Msg,
  82            bundle: *std.zig.ErrorBundle.Wip,
  83            base: ?*File,
  84        ) Allocator.Error!std.zig.ErrorBundle.String {
  85            return switch (msg.source_location) {
  86                .none => try bundle.addString(msg.msg),
  87                .wasm => |sl| {
  88                    dev.check(.wasm_linker);
  89                    const wasm = base.?.cast(.wasm).?;
  90                    return sl.string(msg.msg, bundle, wasm);
  91                },
  92            };
  93        }
  94
  95        pub fn deinit(self: *Msg, gpa: Allocator) void {
  96            for (self.notes) |*note| note.deinit(gpa);
  97            gpa.free(self.notes);
  98            gpa.free(self.msg);
  99        }
 100    };
 101
 102    pub const ErrorWithNotes = struct {
 103        diags: *Diags,
 104        /// Allocated index in diags.msgs array.
 105        index: usize,
 106        /// Next available note slot.
 107        note_slot: usize = 0,
 108
 109        pub fn addMsg(
 110            err: ErrorWithNotes,
 111            comptime format: []const u8,
 112            args: anytype,
 113        ) error{OutOfMemory}!void {
 114            const gpa = err.diags.gpa;
 115            const err_msg = &err.diags.msgs.items[err.index];
 116            err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
 117        }
 118
 119        pub fn addNote(err: *ErrorWithNotes, comptime format: []const u8, args: anytype) void {
 120            const gpa = err.diags.gpa;
 121            const msg = std.fmt.allocPrint(gpa, format, args) catch return err.diags.setAllocFailure();
 122            const err_msg = &err.diags.msgs.items[err.index];
 123            assert(err.note_slot < err_msg.notes.len);
 124            err_msg.notes[err.note_slot] = .{ .msg = msg };
 125            err.note_slot += 1;
 126        }
 127    };
 128
 129    pub fn init(gpa: Allocator) Diags {
 130        return .{
 131            .gpa = gpa,
 132            .mutex = .{},
 133            .msgs = .empty,
 134            .flags = .{},
 135            .lld = .empty,
 136        };
 137    }
 138
 139    pub fn deinit(diags: *Diags) void {
 140        const gpa = diags.gpa;
 141
 142        for (diags.msgs.items) |*item| item.deinit(gpa);
 143        diags.msgs.deinit(gpa);
 144
 145        for (diags.lld.items) |*item| item.deinit(gpa);
 146        diags.lld.deinit(gpa);
 147
 148        diags.* = undefined;
 149    }
 150
 151    pub fn hasErrors(diags: *Diags) bool {
 152        return diags.msgs.items.len > 0 or diags.flags.anySet();
 153    }
 154
 155    pub fn lockAndParseLldStderr(diags: *Diags, prefix: []const u8, stderr: []const u8) void {
 156        diags.mutex.lock();
 157        defer diags.mutex.unlock();
 158
 159        diags.parseLldStderr(prefix, stderr) catch diags.setAllocFailure();
 160    }
 161
 162    fn parseLldStderr(
 163        diags: *Diags,
 164        prefix: []const u8,
 165        stderr: []const u8,
 166    ) Allocator.Error!void {
 167        const gpa = diags.gpa;
 168
 169        var context_lines = std.array_list.Managed([]const u8).init(gpa);
 170        defer context_lines.deinit();
 171
 172        var current_err: ?*Lld = null;
 173        var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n");
 174        while (lines.next()) |line| {
 175            if (line.len > prefix.len + ":".len and
 176                mem.eql(u8, line[0..prefix.len], prefix) and line[prefix.len] == ':')
 177            {
 178                if (current_err) |err| {
 179                    err.context_lines = try context_lines.toOwnedSlice();
 180                }
 181
 182                var split = mem.splitSequence(u8, line, "error: ");
 183                _ = split.first();
 184
 185                const duped_msg = try std.fmt.allocPrint(gpa, "{s}: {s}", .{ prefix, split.rest() });
 186                errdefer gpa.free(duped_msg);
 187
 188                current_err = try diags.lld.addOne(gpa);
 189                current_err.?.* = .{ .msg = duped_msg };
 190            } else if (current_err != null) {
 191                const context_prefix = ">>> ";
 192                var trimmed = mem.trimEnd(u8, line, &std.ascii.whitespace);
 193                if (mem.startsWith(u8, trimmed, context_prefix)) {
 194                    trimmed = trimmed[context_prefix.len..];
 195                }
 196
 197                if (trimmed.len > 0) {
 198                    const duped_line = try gpa.dupe(u8, trimmed);
 199                    try context_lines.append(duped_line);
 200                }
 201            }
 202        }
 203
 204        if (current_err) |err| {
 205            err.context_lines = try context_lines.toOwnedSlice();
 206        }
 207    }
 208
 209    pub fn fail(diags: *Diags, comptime format: []const u8, args: anytype) error{LinkFailure} {
 210        @branchHint(.cold);
 211        addError(diags, format, args);
 212        return error.LinkFailure;
 213    }
 214
 215    pub fn failSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) error{LinkFailure} {
 216        @branchHint(.cold);
 217        addErrorSourceLocation(diags, sl, format, args);
 218        return error.LinkFailure;
 219    }
 220
 221    pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void {
 222        @branchHint(.cold);
 223        return addErrorSourceLocation(diags, .none, format, args);
 224    }
 225
 226    pub fn addErrorSourceLocation(diags: *Diags, sl: SourceLocation, comptime format: []const u8, args: anytype) void {
 227        @branchHint(.cold);
 228        const gpa = diags.gpa;
 229        const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
 230        diags.mutex.lock();
 231        defer diags.mutex.unlock();
 232        addErrorLockedFallible(diags, sl, eu_main_msg) catch |err| switch (err) {
 233            error.OutOfMemory => diags.setAllocFailureLocked(),
 234        };
 235    }
 236
 237    fn addErrorLockedFallible(diags: *Diags, sl: SourceLocation, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void {
 238        const gpa = diags.gpa;
 239        const main_msg = try eu_main_msg;
 240        errdefer gpa.free(main_msg);
 241        try diags.msgs.append(gpa, .{
 242            .msg = main_msg,
 243            .source_location = sl,
 244        });
 245    }
 246
 247    pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
 248        @branchHint(.cold);
 249        const gpa = diags.gpa;
 250        diags.mutex.lock();
 251        defer diags.mutex.unlock();
 252        try diags.msgs.ensureUnusedCapacity(gpa, 1);
 253        return addErrorWithNotesAssumeCapacity(diags, note_count);
 254    }
 255
 256    pub fn addErrorWithNotesAssumeCapacity(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
 257        @branchHint(.cold);
 258        const gpa = diags.gpa;
 259        const index = diags.msgs.items.len;
 260        const err = diags.msgs.addOneAssumeCapacity();
 261        err.* = .{
 262            .msg = undefined,
 263            .notes = try gpa.alloc(Msg, note_count),
 264        };
 265        return .{
 266            .diags = diags,
 267            .index = index,
 268        };
 269    }
 270
 271    pub fn addMissingLibraryError(
 272        diags: *Diags,
 273        checked_paths: []const []const u8,
 274        comptime format: []const u8,
 275        args: anytype,
 276    ) void {
 277        @branchHint(.cold);
 278        const gpa = diags.gpa;
 279        const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
 280        diags.mutex.lock();
 281        defer diags.mutex.unlock();
 282        addMissingLibraryErrorLockedFallible(diags, checked_paths, eu_main_msg) catch |err| switch (err) {
 283            error.OutOfMemory => diags.setAllocFailureLocked(),
 284        };
 285    }
 286
 287    fn addMissingLibraryErrorLockedFallible(
 288        diags: *Diags,
 289        checked_paths: []const []const u8,
 290        eu_main_msg: Allocator.Error![]u8,
 291    ) Allocator.Error!void {
 292        const gpa = diags.gpa;
 293        const main_msg = try eu_main_msg;
 294        errdefer gpa.free(main_msg);
 295        try diags.msgs.ensureUnusedCapacity(gpa, 1);
 296        const notes = try gpa.alloc(Msg, checked_paths.len);
 297        errdefer gpa.free(notes);
 298        for (checked_paths, notes) |path, *note| {
 299            note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) };
 300        }
 301        diags.msgs.appendAssumeCapacity(.{
 302            .msg = main_msg,
 303            .notes = notes,
 304        });
 305    }
 306
 307    pub fn addParseError(
 308        diags: *Diags,
 309        path: Path,
 310        comptime format: []const u8,
 311        args: anytype,
 312    ) void {
 313        @branchHint(.cold);
 314        const gpa = diags.gpa;
 315        const eu_main_msg = std.fmt.allocPrint(gpa, format, args);
 316        diags.mutex.lock();
 317        defer diags.mutex.unlock();
 318        addParseErrorLockedFallible(diags, path, eu_main_msg) catch |err| switch (err) {
 319            error.OutOfMemory => diags.setAllocFailureLocked(),
 320        };
 321    }
 322
 323    fn addParseErrorLockedFallible(diags: *Diags, path: Path, m: Allocator.Error![]u8) Allocator.Error!void {
 324        const gpa = diags.gpa;
 325        const main_msg = try m;
 326        errdefer gpa.free(main_msg);
 327        try diags.msgs.ensureUnusedCapacity(gpa, 1);
 328        const note = try std.fmt.allocPrint(gpa, "while parsing {f}", .{path});
 329        errdefer gpa.free(note);
 330        const notes = try gpa.create([1]Msg);
 331        errdefer gpa.destroy(notes);
 332        notes.* = .{.{ .msg = note }};
 333        diags.msgs.appendAssumeCapacity(.{
 334            .msg = main_msg,
 335            .notes = notes,
 336        });
 337    }
 338
 339    pub fn failParse(
 340        diags: *Diags,
 341        path: Path,
 342        comptime format: []const u8,
 343        args: anytype,
 344    ) error{LinkFailure} {
 345        @branchHint(.cold);
 346        addParseError(diags, path, format, args);
 347        return error.LinkFailure;
 348    }
 349
 350    pub fn setAllocFailure(diags: *Diags) void {
 351        @branchHint(.cold);
 352        diags.mutex.lock();
 353        defer diags.mutex.unlock();
 354        setAllocFailureLocked(diags);
 355    }
 356
 357    fn setAllocFailureLocked(diags: *Diags) void {
 358        log.debug("memory allocation failure", .{});
 359        diags.flags.alloc_failure_occurred = true;
 360    }
 361
 362    pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip, base: ?*File) Allocator.Error!void {
 363        for (diags.msgs.items) |link_err| {
 364            try bundle.addRootErrorMessage(.{
 365                .msg = try link_err.string(bundle, base),
 366                .notes_len = @intCast(link_err.notes.len),
 367            });
 368            const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len));
 369            for (link_err.notes, 0..) |note, i| {
 370                bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{
 371                    .msg = try note.string(bundle, base),
 372                }));
 373            }
 374        }
 375    }
 376};
 377
 378pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
 379
 380pub const File = struct {
 381    tag: Tag,
 382
 383    /// The owner of this output File.
 384    comp: *Compilation,
 385    emit: Path,
 386
 387    file: ?fs.File,
 388    /// When using the LLVM backend, the emitted object is written to a file with this name. This
 389    /// object file then becomes a normal link input to LLD or a self-hosted linker.
 390    ///
 391    /// To convert this to an actual path, see `Compilation.resolveEmitPath` (with `kind == .temp`).
 392    zcu_object_basename: ?[]const u8 = null,
 393    gc_sections: bool,
 394    print_gc_sections: bool,
 395    build_id: std.zig.BuildId,
 396    allow_shlib_undefined: bool,
 397    stack_size: u64,
 398    post_prelink: bool = false,
 399
 400    /// Prevents other processes from clobbering files in the output directory
 401    /// of this linking operation.
 402    lock: ?Cache.Lock = null,
 403    child_pid: ?std.process.Child.Id = null,
 404
 405    pub const OpenOptions = struct {
 406        symbol_count_hint: u64 = 32,
 407        program_code_size_hint: u64 = 256 * 1024,
 408
 409        /// This may depend on what symbols are found during the linking process.
 410        entry: Entry,
 411        /// Virtual address of the entry point procedure relative to image base.
 412        entry_addr: ?u64,
 413        stack_size: ?u64,
 414        image_base: ?u64,
 415        emit_relocs: bool,
 416        z_nodelete: bool,
 417        z_notext: bool,
 418        z_defs: bool,
 419        z_origin: bool,
 420        z_nocopyreloc: bool,
 421        z_now: bool,
 422        z_relro: bool,
 423        z_common_page_size: ?u64,
 424        z_max_page_size: ?u64,
 425        tsaware: bool,
 426        nxcompat: bool,
 427        dynamicbase: bool,
 428        compress_debug_sections: std.zig.CompressDebugSections,
 429        bind_global_refs_locally: bool,
 430        import_symbols: bool,
 431        import_table: bool,
 432        export_table: bool,
 433        initial_memory: ?u64,
 434        max_memory: ?u64,
 435        object_host_name: ?[]const u8,
 436        export_symbol_names: []const []const u8,
 437        global_base: ?u64,
 438        build_id: std.zig.BuildId,
 439        hash_style: Lld.Elf.HashStyle,
 440        sort_section: ?Lld.Elf.SortSection,
 441        major_subsystem_version: ?u16,
 442        minor_subsystem_version: ?u16,
 443        gc_sections: ?bool,
 444        repro: bool,
 445        allow_shlib_undefined: ?bool,
 446        allow_undefined_version: bool,
 447        enable_new_dtags: ?bool,
 448        subsystem: ?std.zig.Subsystem,
 449        linker_script: ?[]const u8,
 450        version_script: ?[]const u8,
 451        soname: ?[]const u8,
 452        print_gc_sections: bool,
 453        print_icf_sections: bool,
 454        print_map: bool,
 455
 456        /// Use a wrapper function for symbol. Any undefined reference to symbol
 457        /// will be resolved to __wrap_symbol. Any undefined reference to
 458        /// __real_symbol will be resolved to symbol. This can be used to provide a
 459        /// wrapper for a system function. The wrapper function should be called
 460        /// __wrap_symbol. If it wishes to call the system function, it should call
 461        /// __real_symbol.
 462        symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
 463
 464        compatibility_version: ?std.SemanticVersion,
 465
 466        // TODO: remove this. libraries are resolved by the frontend.
 467        lib_directories: []const Directory,
 468        framework_dirs: []const []const u8,
 469        rpath_list: []const []const u8,
 470
 471        /// Zig compiler development linker flags.
 472        /// Enable dumping of linker's state as JSON.
 473        enable_link_snapshots: bool,
 474
 475        /// Darwin-specific linker flags:
 476        /// Install name for the dylib
 477        install_name: ?[]const u8,
 478        /// Path to entitlements file
 479        entitlements: ?[]const u8,
 480        /// size of the __PAGEZERO segment
 481        pagezero_size: ?u64,
 482        /// Set minimum space for future expansion of the load commands
 483        headerpad_size: ?u32,
 484        /// Set enough space as if all paths were MATPATHLEN
 485        headerpad_max_install_names: bool,
 486        /// Remove dylibs that are unreachable by the entry point or exported symbols
 487        dead_strip_dylibs: bool,
 488        frameworks: []const MachO.Framework,
 489        darwin_sdk_layout: ?MachO.SdkLayout,
 490        /// Force load all members of static archives that implement an
 491        /// Objective-C class or category
 492        force_load_objc: bool,
 493        /// Whether local symbols should be discarded from the symbol table.
 494        discard_local_symbols: bool,
 495
 496        /// Windows-specific linker flags:
 497        /// PDB source path prefix to instruct the linker how to resolve relative
 498        /// paths when consolidating CodeView streams into a single PDB file.
 499        pdb_source_path: ?[]const u8,
 500        /// PDB output path
 501        pdb_out_path: ?[]const u8,
 502        /// .def file to specify when linking
 503        module_definition_file: ?[]const u8,
 504
 505        pub const Entry = union(enum) {
 506            default,
 507            disabled,
 508            enabled,
 509            named: []const u8,
 510        };
 511    };
 512
 513    pub const OpenError = @typeInfo(@typeInfo(@TypeOf(open)).@"fn".return_type.?).error_union.error_set;
 514
 515    /// Attempts incremental linking, if the file already exists. If
 516    /// incremental linking fails, falls back to truncating the file and
 517    /// rewriting it. A malicious file is detected as incremental link failure
 518    /// and does not cause Illegal Behavior. This operation is not atomic.
 519    /// `arena` is used for allocations with the same lifetime as the created File.
 520    pub fn open(
 521        arena: Allocator,
 522        comp: *Compilation,
 523        emit: Path,
 524        options: OpenOptions,
 525    ) !*File {
 526        if (comp.config.use_lld) {
 527            dev.check(.lld_linker);
 528            assert(comp.zcu == null or comp.config.use_llvm);
 529            // LLD does not support incremental linking.
 530            const lld: *Lld = try .createEmpty(arena, comp, emit, options);
 531            return &lld.base;
 532        }
 533        switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
 534            .plan9 => return error.UnsupportedObjectFormat,
 535            inline else => |tag| {
 536                dev.check(tag.devFeature());
 537                const ptr = try tag.Type().open(arena, comp, emit, options);
 538                return &ptr.base;
 539            },
 540            .lld => unreachable, // not known from ofmt
 541        }
 542    }
 543
 544    pub fn createEmpty(
 545        arena: Allocator,
 546        comp: *Compilation,
 547        emit: Path,
 548        options: OpenOptions,
 549    ) !*File {
 550        if (comp.config.use_lld) {
 551            dev.check(.lld_linker);
 552            assert(comp.zcu == null or comp.config.use_llvm);
 553            const lld: *Lld = try .createEmpty(arena, comp, emit, options);
 554            return &lld.base;
 555        }
 556        switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt, comp.config.use_new_linker)) {
 557            .plan9 => return error.UnsupportedObjectFormat,
 558            inline else => |tag| {
 559                dev.check(tag.devFeature());
 560                const ptr = try tag.Type().createEmpty(arena, comp, emit, options);
 561                return &ptr.base;
 562            },
 563            .lld => unreachable, // not known from ofmt
 564        }
 565    }
 566
 567    pub fn cast(base: *File, comptime tag: Tag) if (dev.env.supports(tag.devFeature())) ?*tag.Type() else ?noreturn {
 568        return if (dev.env.supports(tag.devFeature()) and base.tag == tag) @fieldParentPtr("base", base) else null;
 569    }
 570
 571    pub fn startProgress(base: *File, prog_node: std.Progress.Node) void {
 572        switch (base.tag) {
 573            else => {},
 574            inline .elf2, .coff2 => |tag| {
 575                dev.check(tag.devFeature());
 576                return @as(*tag.Type(), @fieldParentPtr("base", base)).startProgress(prog_node);
 577            },
 578        }
 579    }
 580
 581    pub fn endProgress(base: *File) void {
 582        switch (base.tag) {
 583            else => {},
 584            inline .elf2, .coff2 => |tag| {
 585                dev.check(tag.devFeature());
 586                return @as(*tag.Type(), @fieldParentPtr("base", base)).endProgress();
 587            },
 588        }
 589    }
 590
 591    pub fn makeWritable(base: *File) !void {
 592        dev.check(.make_writable);
 593        const comp = base.comp;
 594        const gpa = comp.gpa;
 595        const io = comp.io;
 596        switch (base.tag) {
 597            .lld => assert(base.file == null),
 598            .elf, .macho, .wasm => {
 599                if (base.file != null) return;
 600                dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker });
 601                const emit = base.emit;
 602                if (base.child_pid) |pid| {
 603                    if (builtin.os.tag == .windows) {
 604                        return error.HotSwapUnavailableOnHostOperatingSystem;
 605                    } else {
 606                        // If we try to open the output file in write mode while it is running,
 607                        // it will return ETXTBSY. So instead, we copy the file, atomically rename it
 608                        // over top of the exe path, and then proceed normally. This changes the inode,
 609                        // avoiding the error.
 610                        const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{
 611                            emit.sub_path, std.crypto.random.int(u32),
 612                        });
 613                        defer gpa.free(tmp_sub_path);
 614                        try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, .{});
 615                        try emit.root_dir.handle.rename(tmp_sub_path, emit.sub_path);
 616                        switch (builtin.os.tag) {
 617                            .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
 618                                log.warn("ptrace failure: {s}", .{@errorName(err)});
 619                            },
 620                            .maccatalyst, .macos => {
 621                                const macho_file = base.cast(.macho).?;
 622                                macho_file.ptraceAttach(pid) catch |err| {
 623                                    log.warn("attaching failed with error: {s}", .{@errorName(err)});
 624                                };
 625                            },
 626                            .windows => unreachable,
 627                            else => return error.HotSwapUnavailableOnHostOperatingSystem,
 628                        }
 629                    }
 630                }
 631                base.file = try emit.root_dir.handle.openFile(emit.sub_path, .{ .mode = .read_write });
 632            },
 633            .elf2, .coff2 => if (base.file == null) {
 634                const mf = if (base.cast(.elf2)) |elf|
 635                    &elf.mf
 636                else if (base.cast(.coff2)) |coff|
 637                    &coff.mf
 638                else
 639                    unreachable;
 640                mf.file = try base.emit.root_dir.handle.adaptToNewApi().openFile(io, base.emit.sub_path, .{
 641                    .mode = .read_write,
 642                });
 643                base.file = .adaptFromNewApi(mf.file);
 644                try mf.ensureTotalCapacity(@intCast(mf.nodes.items[0].location().resolve(mf)[1]));
 645            },
 646            .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
 647            .plan9 => unreachable,
 648        }
 649    }
 650
 651    /// Some linkers create a separate file for debug info, which we might need to temporarily close
 652    /// when moving the compilation result directory due to the host OS not allowing moving a
 653    /// file/directory while a handle remains open.
 654    /// Returns `true` if a debug info file was closed. In that case, `reopenDebugInfo` may be called.
 655    pub fn closeDebugInfo(base: *File) bool {
 656        const macho = base.cast(.macho) orelse return false;
 657        return macho.closeDebugInfo();
 658    }
 659
 660    pub fn reopenDebugInfo(base: *File) !void {
 661        const macho = base.cast(.macho).?;
 662        return macho.reopenDebugInfo();
 663    }
 664
 665    pub fn makeExecutable(base: *File) !void {
 666        dev.check(.make_executable);
 667        const comp = base.comp;
 668        const io = comp.io;
 669        switch (comp.config.output_mode) {
 670            .Obj => return,
 671            .Lib => switch (comp.config.link_mode) {
 672                .static => return,
 673                .dynamic => {},
 674            },
 675            .Exe => {},
 676        }
 677        switch (base.tag) {
 678            .lld => assert(base.file == null),
 679            .elf => if (base.file) |f| {
 680                dev.check(.elf_linker);
 681                f.close();
 682                base.file = null;
 683
 684                if (base.child_pid) |pid| {
 685                    switch (builtin.os.tag) {
 686                        .linux => std.posix.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| {
 687                            log.warn("ptrace failure: {s}", .{@errorName(err)});
 688                        },
 689                        else => return error.HotSwapUnavailableOnHostOperatingSystem,
 690                    }
 691                }
 692            },
 693            .macho, .wasm => if (base.file) |f| {
 694                dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker });
 695                f.close();
 696                base.file = null;
 697
 698                if (base.child_pid) |pid| {
 699                    switch (builtin.os.tag) {
 700                        .maccatalyst, .macos => {
 701                            const macho_file = base.cast(.macho).?;
 702                            macho_file.ptraceDetach(pid) catch |err| {
 703                                log.warn("detaching failed with error: {s}", .{@errorName(err)});
 704                            };
 705                        },
 706                        else => return error.HotSwapUnavailableOnHostOperatingSystem,
 707                    }
 708                }
 709            },
 710            .elf2, .coff2 => if (base.file) |f| {
 711                const mf = if (base.cast(.elf2)) |elf|
 712                    &elf.mf
 713                else if (base.cast(.coff2)) |coff|
 714                    &coff.mf
 715                else
 716                    unreachable;
 717                mf.unmap();
 718                assert(mf.file.handle == f.handle);
 719                mf.file.close(io);
 720                mf.file = undefined;
 721                base.file = null;
 722            },
 723            .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
 724            .plan9 => unreachable,
 725        }
 726    }
 727
 728    pub const DebugInfoOutput = union(enum) {
 729        dwarf: *Dwarf.WipNav,
 730        none,
 731    };
 732    pub const UpdateDebugInfoError = Dwarf.UpdateError;
 733    pub const FlushDebugInfoError = Dwarf.FlushError;
 734
 735    /// Note that `LinkFailure` is not a member of this error set because the error message
 736    /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`.
 737    pub const UpdateNavError = codegen.CodeGenError;
 738
 739    /// Called from within CodeGen to retrieve the symbol index of a global symbol.
 740    /// If no symbol exists yet with this name, a new undefined global symbol will
 741    /// be created. This symbol may get resolved once all relocatables are (re-)linked.
 742    /// Optionally, it is possible to specify where to expect the symbol defined if it
 743    /// is an import.
 744    pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!u32 {
 745        log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name });
 746        switch (base.tag) {
 747            .lld => unreachable,
 748            .spirv => unreachable,
 749            .c => unreachable,
 750            inline else => |tag| {
 751                dev.check(tag.devFeature());
 752                return @as(*tag.Type(), @fieldParentPtr("base", base)).getGlobalSymbol(name, lib_name);
 753            },
 754        }
 755    }
 756
 757    /// May be called before or after updateExports for any given Nav.
 758    /// Asserts that the ZCU is not using the LLVM backend.
 759    fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void {
 760        assert(base.comp.zcu.?.llvm_object == null);
 761        const nav = pt.zcu.intern_pool.getNav(nav_index);
 762        assert(nav.status == .fully_resolved);
 763        switch (base.tag) {
 764            .lld => unreachable,
 765            .plan9 => unreachable,
 766            inline else => |tag| {
 767                dev.check(tag.devFeature());
 768                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNav(pt, nav_index);
 769            },
 770        }
 771    }
 772
 773    pub const UpdateContainerTypeError = error{
 774        OutOfMemory,
 775        /// `Zcu.failed_types` is already populated with the error message.
 776        TypeFailureReported,
 777    };
 778
 779    /// Never called when LLVM is codegenning the ZCU.
 780    fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void {
 781        assert(base.comp.zcu.?.llvm_object == null);
 782        switch (base.tag) {
 783            .lld => unreachable,
 784            else => {},
 785            inline .elf => |tag| {
 786                dev.check(tag.devFeature());
 787                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty);
 788            },
 789        }
 790    }
 791
 792    /// May be called before or after updateExports for any given Decl.
 793    /// The active tag of `mir` is determined by the backend used for the module this function is in.
 794    /// Never called when LLVM is codegenning the ZCU.
 795    fn updateFunc(
 796        base: *File,
 797        pt: Zcu.PerThread,
 798        func_index: InternPool.Index,
 799        /// This is owned by the caller, but the callee is permitted to mutate it provided
 800        /// that `mir.deinit` remains legal for the caller. For instance, the callee can
 801        /// take ownership of an embedded slice and replace it with `&.{}` in `mir`.
 802        mir: *codegen.AnyMir,
 803    ) UpdateNavError!void {
 804        assert(base.comp.zcu.?.llvm_object == null);
 805        switch (base.tag) {
 806            .lld => unreachable,
 807            .spirv => unreachable, // see corresponding special case in `Zcu.PerThread.runCodegenInner`
 808            .plan9 => unreachable,
 809            inline else => |tag| {
 810                dev.check(tag.devFeature());
 811                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir);
 812            },
 813        }
 814    }
 815
 816    pub const UpdateLineNumberError = error{
 817        OutOfMemory,
 818        Overflow,
 819        LinkFailure,
 820    };
 821
 822    /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because
 823    /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`.
 824    /// Never called when LLVM is codegenning the ZCU.
 825    fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateLineNumberError!void {
 826        assert(base.comp.zcu.?.llvm_object == null);
 827        {
 828            const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
 829            const file = pt.zcu.fileByIndex(ti.file);
 830            const inst = file.zir.?.instructions.get(@intFromEnum(ti.inst));
 831            assert(inst.tag == .declaration);
 832        }
 833
 834        switch (base.tag) {
 835            .lld => unreachable,
 836            .spirv => {},
 837            .plan9 => unreachable,
 838            .elf2, .coff2 => {},
 839            inline else => |tag| {
 840                dev.check(tag.devFeature());
 841                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
 842            },
 843        }
 844    }
 845
 846    pub fn releaseLock(self: *File) void {
 847        if (self.lock) |*lock| {
 848            lock.release();
 849            self.lock = null;
 850        }
 851    }
 852
 853    pub fn toOwnedLock(self: *File) Cache.Lock {
 854        const lock = self.lock.?;
 855        self.lock = null;
 856        return lock;
 857    }
 858
 859    pub fn destroy(base: *File) void {
 860        base.releaseLock();
 861        if (base.file) |f| f.close();
 862        switch (base.tag) {
 863            .plan9 => unreachable,
 864            inline else => |tag| {
 865                dev.check(tag.devFeature());
 866                @as(*tag.Type(), @fieldParentPtr("base", base)).deinit();
 867            },
 868        }
 869    }
 870
 871    pub fn idle(base: *File, tid: Zcu.PerThread.Id) !bool {
 872        switch (base.tag) {
 873            else => return false,
 874            inline .elf2, .coff2 => |tag| {
 875                dev.check(tag.devFeature());
 876                return @as(*tag.Type(), @fieldParentPtr("base", base)).idle(tid);
 877            },
 878        }
 879    }
 880
 881    pub fn updateErrorData(base: *File, pt: Zcu.PerThread) !void {
 882        switch (base.tag) {
 883            else => {},
 884            inline .elf2, .coff2 => |tag| {
 885                dev.check(tag.devFeature());
 886                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateErrorData(pt);
 887            },
 888        }
 889    }
 890
 891    pub const FlushError = error{
 892        /// Indicates an error will be present in `Compilation.link_diags`.
 893        LinkFailure,
 894        OutOfMemory,
 895    };
 896
 897    /// Commit pending changes and write headers. Takes into account final output mode.
 898    /// `arena` has the lifetime of the call to `Compilation.update`.
 899    pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void {
 900        const comp = base.comp;
 901        if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) {
 902            dev.check(.clang_command);
 903            const emit = base.emit;
 904            // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
 905            // Until then, we do `lld -r -o output.o input.o` even though the output is the same
 906            // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
 907            // to the final location. See also the corresponding TODO in Coff linking.
 908            assert(comp.c_object_table.count() == 1);
 909            const the_key = comp.c_object_table.keys()[0];
 910            const cached_pp_file_path = the_key.status.success.object_path;
 911            cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| {
 912                const diags = &base.comp.link_diags;
 913                return diags.fail("failed to copy '{f}' to '{f}': {s}", .{
 914                    std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar),
 915                    std.fmt.alt(@as(Path, emit), .formatEscapeChar),
 916                    @errorName(err),
 917                });
 918            };
 919            return;
 920        }
 921        assert(base.post_prelink);
 922        switch (base.tag) {
 923            .plan9 => unreachable,
 924            inline else => |tag| {
 925                dev.check(tag.devFeature());
 926                return @as(*tag.Type(), @fieldParentPtr("base", base)).flush(arena, tid, prog_node);
 927            },
 928        }
 929    }
 930
 931    pub const UpdateExportsError = error{
 932        OutOfMemory,
 933        AnalysisFail,
 934    };
 935
 936    /// This is called for every exported thing. `exports` is almost always
 937    /// a list of size 1, meaning that `exported` is exported once. However, it is possible
 938    /// to export the same thing with multiple different symbol names (aliases).
 939    /// May be called before or after updateDecl for any given Decl.
 940    /// Never called when LLVM is codegenning the ZCU.
 941    pub fn updateExports(
 942        base: *File,
 943        pt: Zcu.PerThread,
 944        exported: Zcu.Exported,
 945        export_indices: []const Zcu.Export.Index,
 946    ) UpdateExportsError!void {
 947        assert(base.comp.zcu.?.llvm_object == null);
 948        switch (base.tag) {
 949            .lld => unreachable,
 950            .plan9 => unreachable,
 951            inline else => |tag| {
 952                dev.check(tag.devFeature());
 953                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices);
 954            },
 955        }
 956    }
 957
 958    pub const RelocInfo = struct {
 959        parent: Parent,
 960        offset: u64,
 961        addend: u32,
 962
 963        pub const Parent = union(enum) {
 964            none,
 965            atom_index: u32,
 966            debug_output: DebugInfoOutput,
 967        };
 968    };
 969
 970    /// Get allocated `Nav`'s address in virtual memory.
 971    /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
 972    /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
 973    /// `Nav`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
 974    /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate
 975    /// the block/atom.
 976    /// Never called when LLVM is codegenning the ZCU.
 977    pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) !u64 {
 978        assert(base.comp.zcu.?.llvm_object == null);
 979        switch (base.tag) {
 980            .lld => unreachable,
 981            .c => unreachable,
 982            .spirv => unreachable,
 983            .wasm => unreachable,
 984            .plan9 => unreachable,
 985            inline else => |tag| {
 986                dev.check(tag.devFeature());
 987                return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
 988            },
 989        }
 990    }
 991
 992    /// Never called when LLVM is codegenning the ZCU.
 993    pub fn lowerUav(
 994        base: *File,
 995        pt: Zcu.PerThread,
 996        decl_val: InternPool.Index,
 997        decl_align: InternPool.Alignment,
 998        src_loc: Zcu.LazySrcLoc,
 999    ) !codegen.SymbolResult {
1000        assert(base.comp.zcu.?.llvm_object == null);
1001        switch (base.tag) {
1002            .lld => unreachable,
1003            .c => unreachable,
1004            .spirv => unreachable,
1005            .wasm => unreachable,
1006            .plan9 => unreachable,
1007            inline else => |tag| {
1008                dev.check(tag.devFeature());
1009                return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
1010            },
1011        }
1012    }
1013
1014    /// Never called when LLVM is codegenning the ZCU.
1015    pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 {
1016        assert(base.comp.zcu.?.llvm_object == null);
1017        switch (base.tag) {
1018            .lld => unreachable,
1019            .c => unreachable,
1020            .spirv => unreachable,
1021            .wasm => unreachable,
1022            .plan9 => unreachable,
1023            inline else => |tag| {
1024                dev.check(tag.devFeature());
1025                return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
1026            },
1027        }
1028    }
1029
1030    /// Never called when LLVM is codegenning the ZCU.
1031    pub fn deleteExport(
1032        base: *File,
1033        exported: Zcu.Exported,
1034        name: InternPool.NullTerminatedString,
1035    ) void {
1036        assert(base.comp.zcu.?.llvm_object == null);
1037        switch (base.tag) {
1038            .lld => unreachable,
1039            .plan9 => unreachable,
1040
1041            .spirv,
1042            => {},
1043
1044            inline else => |tag| {
1045                dev.check(tag.devFeature());
1046                return @as(*tag.Type(), @fieldParentPtr("base", base)).deleteExport(exported, name);
1047            },
1048        }
1049    }
1050
1051    /// Opens a path as an object file and parses it into the linker.
1052    fn openLoadObject(base: *File, path: Path) anyerror!void {
1053        if (base.tag == .lld) return;
1054        const diags = &base.comp.link_diags;
1055        const input = try openObjectInput(diags, path);
1056        errdefer input.object.file.close();
1057        try loadInput(base, input);
1058    }
1059
1060    /// Opens a path as a static library and parses it into the linker.
1061    /// If `query` is non-null, allows GNU ld scripts.
1062    fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void {
1063        if (base.tag == .lld) return;
1064        if (opt_query) |query| {
1065            const archive = try openObject(path, query.must_link, query.hidden);
1066            errdefer archive.file.close();
1067            loadInput(base, .{ .archive = archive }) catch |err| switch (err) {
1068                error.BadMagic, error.UnexpectedEndOfFile => {
1069                    if (base.tag != .elf and base.tag != .elf2) return err;
1070                    try loadGnuLdScript(base, path, query, archive.file);
1071                    archive.file.close();
1072                    return;
1073                },
1074                else => return err,
1075            };
1076        } else {
1077            const archive = try openObject(path, false, false);
1078            errdefer archive.file.close();
1079            try loadInput(base, .{ .archive = archive });
1080        }
1081    }
1082
1083    /// Opens a path as a shared library and parses it into the linker.
1084    /// Handles GNU ld scripts.
1085    fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void {
1086        if (base.tag == .lld) return;
1087        const dso = try openDso(path, query.needed, query.weak, query.reexport);
1088        errdefer dso.file.close();
1089        loadInput(base, .{ .dso = dso }) catch |err| switch (err) {
1090            error.BadMagic, error.UnexpectedEndOfFile => {
1091                if (base.tag != .elf and base.tag != .elf2) return err;
1092                try loadGnuLdScript(base, path, query, dso.file);
1093                dso.file.close();
1094                return;
1095            },
1096            else => return err,
1097        };
1098    }
1099
1100    fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: fs.File) anyerror!void {
1101        const comp = base.comp;
1102        const diags = &comp.link_diags;
1103        const gpa = comp.gpa;
1104        const stat = try file.stat();
1105        const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig;
1106        const buf = try gpa.alloc(u8, size);
1107        defer gpa.free(buf);
1108        const n = try file.preadAll(buf, 0);
1109        if (buf.len != n) return error.UnexpectedEndOfFile;
1110        var ld_script = try LdScript.parse(gpa, diags, path, buf);
1111        defer ld_script.deinit(gpa);
1112        for (ld_script.args) |arg| {
1113            const query: UnresolvedInput.Query = .{
1114                .needed = arg.needed or parent_query.needed,
1115                .weak = parent_query.weak,
1116                .reexport = parent_query.reexport,
1117                .preferred_mode = parent_query.preferred_mode,
1118                .search_strategy = parent_query.search_strategy,
1119                .allow_so_scripts = parent_query.allow_so_scripts,
1120            };
1121            if (mem.startsWith(u8, arg.path, "-l")) {
1122                @panic("TODO");
1123            } else {
1124                if (fs.path.isAbsolute(arg.path)) {
1125                    const new_path = Path.initCwd(path: {
1126                        comp.mutex.lock();
1127                        defer comp.mutex.unlock();
1128                        break :path try comp.arena.dupe(u8, arg.path);
1129                    });
1130                    switch (Compilation.classifyFileExt(arg.path)) {
1131                        .shared_library => try openLoadDso(base, new_path, query),
1132                        .object => try openLoadObject(base, new_path),
1133                        .static_library => try openLoadArchive(base, new_path, query),
1134                        else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}),
1135                    }
1136                } else {
1137                    @panic("TODO");
1138                }
1139            }
1140        }
1141    }
1142
1143    pub fn loadInput(base: *File, input: Input) anyerror!void {
1144        if (base.tag == .lld) return;
1145        switch (base.tag) {
1146            inline .elf, .elf2, .wasm => |tag| {
1147                dev.check(tag.devFeature());
1148                return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input);
1149            },
1150            else => {},
1151        }
1152    }
1153
1154    /// Called when all linker inputs have been sent via `loadInput`. After
1155    /// this, `loadInput` will not be called anymore.
1156    pub fn prelink(base: *File) FlushError!void {
1157        assert(!base.post_prelink);
1158
1159        // In this case, an object file is created by the LLVM backend, so
1160        // there is no prelink phase. The Zig code is linked as a standard
1161        // object along with the others.
1162        if (base.zcu_object_basename != null) return;
1163
1164        switch (base.tag) {
1165            inline .elf2, .coff2, .wasm => |tag| {
1166                dev.check(tag.devFeature());
1167                return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(base.comp.link_prog_node);
1168            },
1169            else => {},
1170        }
1171    }
1172
1173    pub const Tag = enum {
1174        coff2,
1175        elf,
1176        elf2,
1177        macho,
1178        c,
1179        wasm,
1180        spirv,
1181        plan9,
1182        lld,
1183
1184        pub fn Type(comptime tag: Tag) type {
1185            return switch (tag) {
1186                .coff2 => Coff2,
1187                .elf => Elf,
1188                .elf2 => Elf2,
1189                .macho => MachO,
1190                .c => C,
1191                .wasm => Wasm,
1192                .spirv => SpirV,
1193                .lld => Lld,
1194                .plan9 => comptime unreachable,
1195            };
1196        }
1197
1198        fn fromObjectFormat(ofmt: std.Target.ObjectFormat, use_new_linker: bool) Tag {
1199            return switch (ofmt) {
1200                .coff => .coff2,
1201                .elf => if (use_new_linker) .elf2 else .elf,
1202                .macho => .macho,
1203                .wasm => .wasm,
1204                .plan9 => .plan9,
1205                .c => .c,
1206                .spirv => .spirv,
1207                .hex => @panic("TODO implement hex object format"),
1208                .raw => @panic("TODO implement raw object format"),
1209            };
1210        }
1211
1212        pub fn devFeature(tag: Tag) dev.Feature {
1213            return @field(dev.Feature, @tagName(tag) ++ "_linker");
1214        }
1215    };
1216
1217    pub const LazySymbol = struct {
1218        pub const Kind = enum { code, const_data };
1219
1220        kind: Kind,
1221        ty: InternPool.Index,
1222    };
1223
1224    pub fn determineMode(
1225        output_mode: std.builtin.OutputMode,
1226        link_mode: std.builtin.LinkMode,
1227    ) fs.File.Mode {
1228        // On common systems with a 0o022 umask, 0o777 will still result in a file created
1229        // with 0o755 permissions, but it works appropriately if the system is configured
1230        // more leniently. As another data point, C's fopen seems to open files with the
1231        // 666 mode.
1232        const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
1233        switch (output_mode) {
1234            .Lib => return switch (link_mode) {
1235                .dynamic => executable_mode,
1236                .static => fs.File.default_mode,
1237            },
1238            .Exe => return executable_mode,
1239            .Obj => return fs.File.default_mode,
1240        }
1241    }
1242
1243    pub fn isStatic(self: File) bool {
1244        return self.comp.config.link_mode == .static;
1245    }
1246
1247    pub fn isObject(self: File) bool {
1248        const output_mode = self.comp.config.output_mode;
1249        return output_mode == .Obj;
1250    }
1251
1252    pub fn isExe(self: File) bool {
1253        const output_mode = self.comp.config.output_mode;
1254        return output_mode == .Exe;
1255    }
1256
1257    pub fn isStaticLib(self: File) bool {
1258        const output_mode = self.comp.config.output_mode;
1259        return output_mode == .Lib and self.isStatic();
1260    }
1261
1262    pub fn isRelocatable(self: File) bool {
1263        return self.isObject() or self.isStaticLib();
1264    }
1265
1266    pub fn isDynLib(self: File) bool {
1267        const output_mode = self.comp.config.output_mode;
1268        return output_mode == .Lib and !self.isStatic();
1269    }
1270
1271    pub fn cgFail(
1272        base: *File,
1273        nav_index: InternPool.Nav.Index,
1274        comptime format: []const u8,
1275        args: anytype,
1276    ) error{ CodegenFail, OutOfMemory } {
1277        @branchHint(.cold);
1278        return base.comp.zcu.?.codegenFail(nav_index, format, args);
1279    }
1280
1281    pub const Lld = @import("link/Lld.zig");
1282    pub const C = @import("link/C.zig");
1283    pub const Coff2 = @import("link/Coff.zig");
1284    pub const Elf = @import("link/Elf.zig");
1285    pub const Elf2 = @import("link/Elf2.zig");
1286    pub const MachO = @import("link/MachO.zig");
1287    pub const SpirV = @import("link/SpirV.zig");
1288    pub const Wasm = @import("link/Wasm.zig");
1289    pub const Dwarf = @import("link/Dwarf.zig");
1290};
1291
1292pub const PrelinkTask = union(enum) {
1293    /// Loads the objects, shared objects, and archives that are already
1294    /// known from the command line.
1295    load_explicitly_provided,
1296    /// Loads the shared objects and archives by resolving
1297    /// `target_util.libcFullLinkFlags()` against the host libc
1298    /// installation.
1299    load_host_libc,
1300    /// Tells the linker to load an object file by path.
1301    load_object: Path,
1302    /// Tells the linker to load a static library by path.
1303    load_archive: Path,
1304    /// Tells the linker to load a shared library, possibly one that is a
1305    /// GNU ld script.
1306    load_dso: Path,
1307};
1308pub const ZcuTask = union(enum) {
1309    /// Write the constant value for a Decl to the output file.
1310    link_nav: InternPool.Nav.Index,
1311    /// Write the machine code for a function to the output file.
1312    link_func: LinkFunc,
1313    link_type: InternPool.Index,
1314    update_line_number: InternPool.TrackedInst.Index,
1315    pub fn deinit(task: ZcuTask, zcu: *const Zcu) void {
1316        switch (task) {
1317            .link_nav,
1318            .link_type,
1319            .update_line_number,
1320            => {},
1321            .link_func => |link_func| {
1322                switch (link_func.mir.status.load(.acquire)) {
1323                    .pending => unreachable, // cannot deinit until MIR done
1324                    .failed => {}, // MIR not populated so doesn't need freeing
1325                    .ready => link_func.mir.value.deinit(zcu),
1326                }
1327                zcu.gpa.destroy(link_func.mir);
1328            },
1329        }
1330    }
1331    pub const LinkFunc = struct {
1332        /// This will either be a non-generic `func_decl` or a `func_instance`.
1333        func: InternPool.Index,
1334        /// This pointer is allocated into `gpa` and must be freed when the `ZcuTask` is processed.
1335        /// The pointer is shared with the codegen worker, which will populate the MIR inside once
1336        /// it has been generated. It's important that the `link_func` is queued at the same time as
1337        /// the codegen job to ensure that the linker receives functions in a deterministic order,
1338        /// allowing reproducible builds.
1339        mir: *SharedMir,
1340        /// This is not actually used by `doZcuTask`. Instead, `Queue` uses this value as a heuristic
1341        /// to avoid queueing too much AIR/MIR for codegen/link at a time. Essentially, we cap the
1342        /// total number of AIR bytes which are being processed at once, preventing unbounded memory
1343        /// usage when AIR is produced faster than it is processed.
1344        air_bytes: u32,
1345
1346        pub const SharedMir = struct {
1347            /// This is initially `.pending`. When `value` is populated, the codegen thread will set
1348            /// this to `.ready`, and alert the queue if needed. It could also end up `.failed`.
1349            /// The action of storing a value (other than `.pending`) to this atomic transfers
1350            /// ownership of memory assoicated with `value` to this `ZcuTask`.
1351            status: std.atomic.Value(enum(u8) {
1352                /// We are waiting on codegen to generate MIR (or die trying).
1353                pending,
1354                /// `value` is not populated and will not be populated. Just drop the task from the queue and move on.
1355                failed,
1356                /// `value` is populated with the MIR from the backend in use, which is not LLVM.
1357                ready,
1358            }),
1359            /// This is `undefined` until `ready` is set to `true`. Once populated, this MIR belongs
1360            /// to the `ZcuTask`, and must be `deinit`ed when it is processed. Allocated into `gpa`.
1361            value: codegen.AnyMir,
1362        };
1363    };
1364};
1365
1366pub fn doPrelinkTask(comp: *Compilation, task: PrelinkTask) void {
1367    const diags = &comp.link_diags;
1368    const base = comp.bin_file orelse {
1369        comp.link_prog_node.completeOne();
1370        return;
1371    };
1372
1373    var timer = comp.startTimer();
1374    defer if (timer.finish()) |ns| {
1375        comp.mutex.lock();
1376        defer comp.mutex.unlock();
1377        comp.time_report.?.stats.cpu_ns_link += ns;
1378    };
1379
1380    switch (task) {
1381        .load_explicitly_provided => {
1382            const prog_node = comp.link_prog_node.start("Parse Inputs", comp.link_inputs.len);
1383            defer prog_node.end();
1384            for (comp.link_inputs) |input| {
1385                base.loadInput(input) catch |err| switch (err) {
1386                    error.LinkFailure => return, // error reported via diags
1387                    else => |e| switch (input) {
1388                        .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}),
1389                        .object => |obj| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}),
1390                        .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}),
1391                        .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {s}", .{@errorName(e)}),
1392                        .dso_exact => diags.addError("failed to handle dso_exact: {s}", .{@errorName(e)}),
1393                    },
1394                };
1395                prog_node.completeOne();
1396            }
1397        },
1398        .load_host_libc => {
1399            const prog_node = comp.link_prog_node.start("Parse Host libc", 0);
1400            defer prog_node.end();
1401
1402            const target = &comp.root_mod.resolved_target.result;
1403            const flags = target_util.libcFullLinkFlags(target);
1404            const crt_dir = comp.libc_installation.?.crt_dir.?;
1405            const sep = std.fs.path.sep_str;
1406            for (flags) |flag| {
1407                assert(mem.startsWith(u8, flag, "-l"));
1408                const lib_name = flag["-l".len..];
1409                switch (comp.config.link_mode) {
1410                    .dynamic => {
1411                        const dso_path = Path.initCwd(
1412                            std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1413                                crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(),
1414                            }) catch return diags.setAllocFailure(),
1415                        );
1416                        base.openLoadDso(dso_path, .{
1417                            .preferred_mode = .dynamic,
1418                            .search_strategy = .paths_first,
1419                        }) catch |err| switch (err) {
1420                            error.FileNotFound => {
1421                                // Also try static.
1422                                const archive_path = Path.initCwd(
1423                                    std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1424                                        crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1425                                    }) catch return diags.setAllocFailure(),
1426                                );
1427                                base.openLoadArchive(archive_path, .{
1428                                    .preferred_mode = .dynamic,
1429                                    .search_strategy = .paths_first,
1430                                }) catch |archive_err| switch (archive_err) {
1431                                    error.LinkFailure => return, // error reported via diags
1432                                    else => |e| diags.addParseError(dso_path, "failed to parse archive {f}: {s}", .{ archive_path, @errorName(e) }),
1433                                };
1434                            },
1435                            error.LinkFailure => return, // error reported via diags
1436                            else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}),
1437                        };
1438                    },
1439                    .static => {
1440                        const path = Path.initCwd(
1441                            std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{
1442                                crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(),
1443                            }) catch return diags.setAllocFailure(),
1444                        );
1445                        // glibc sometimes makes even archive files GNU ld scripts.
1446                        base.openLoadArchive(path, .{
1447                            .preferred_mode = .static,
1448                            .search_strategy = .no_fallback,
1449                        }) catch |err| switch (err) {
1450                            error.LinkFailure => return, // error reported via diags
1451                            else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1452                        };
1453                    },
1454                }
1455            }
1456        },
1457        .load_object => |path| {
1458            const prog_node = comp.link_prog_node.start("Parse Object", 0);
1459            defer prog_node.end();
1460            base.openLoadObject(path) catch |err| switch (err) {
1461                error.LinkFailure => return, // error reported via diags
1462                else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}),
1463            };
1464        },
1465        .load_archive => |path| {
1466            const prog_node = comp.link_prog_node.start("Parse Archive", 0);
1467            defer prog_node.end();
1468            base.openLoadArchive(path, null) catch |err| switch (err) {
1469                error.LinkFailure => return, // error reported via link_diags
1470                else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}),
1471            };
1472        },
1473        .load_dso => |path| {
1474            const prog_node = comp.link_prog_node.start("Parse Shared Library", 0);
1475            defer prog_node.end();
1476            base.openLoadDso(path, .{
1477                .preferred_mode = .dynamic,
1478                .search_strategy = .paths_first,
1479            }) catch |err| switch (err) {
1480                error.LinkFailure => return, // error reported via link_diags
1481                else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}),
1482            };
1483        },
1484    }
1485}
1486pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
1487    const diags = &comp.link_diags;
1488    const zcu = comp.zcu.?;
1489    const ip = &zcu.intern_pool;
1490    const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid));
1491    defer pt.deactivate();
1492
1493    var timer = comp.startTimer();
1494
1495    switch (task) {
1496        .link_nav => |nav_index| {
1497            const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
1498            const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0);
1499            defer nav_prog_node.end();
1500            if (zcu.llvm_object) |llvm_object| {
1501                llvm_object.updateNav(pt, nav_index) catch |err| switch (err) {
1502                    error.OutOfMemory => diags.setAllocFailure(),
1503                };
1504            } else if (comp.bin_file) |lf| {
1505                lf.updateNav(pt, nav_index) catch |err| switch (err) {
1506                    error.OutOfMemory => diags.setAllocFailure(),
1507                    error.CodegenFail => zcu.assertCodegenFailed(nav_index),
1508                    error.Overflow, error.RelocationNotByteAligned => {
1509                        switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) {
1510                            error.CodegenFail => return,
1511                            error.OutOfMemory => return diags.setAllocFailure(),
1512                        }
1513                        // Not a retryable failure.
1514                    },
1515                };
1516            }
1517        },
1518        .link_func => |func| {
1519            const nav = zcu.funcInfo(func.func).owner_nav;
1520            const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
1521            const nav_prog_node = comp.link_prog_node.start(fqn_slice, 0);
1522            defer nav_prog_node.end();
1523            switch (func.mir.status.load(.acquire)) {
1524                .pending => unreachable,
1525                .ready => {},
1526                .failed => return,
1527            }
1528            assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
1529            const mir = &func.mir.value;
1530            if (comp.bin_file) |lf| {
1531                lf.updateFunc(pt, func.func, mir) catch |err| switch (err) {
1532                    error.OutOfMemory => return diags.setAllocFailure(),
1533                    error.CodegenFail => return zcu.assertCodegenFailed(nav),
1534                    error.Overflow, error.RelocationNotByteAligned => {
1535                        switch (zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)})) {
1536                            error.OutOfMemory => return diags.setAllocFailure(),
1537                            error.CodegenFail => return,
1538                        }
1539                    },
1540                };
1541            }
1542        },
1543        .link_type => |ty| {
1544            const name = Type.fromInterned(ty).containerTypeName(ip).toSlice(ip);
1545            const nav_prog_node = comp.link_prog_node.start(name, 0);
1546            defer nav_prog_node.end();
1547            if (zcu.llvm_object == null) {
1548                if (comp.bin_file) |lf| {
1549                    lf.updateContainerType(pt, ty) catch |err| switch (err) {
1550                        error.OutOfMemory => diags.setAllocFailure(),
1551                        error.TypeFailureReported => assert(zcu.failed_types.contains(ty)),
1552                    };
1553                }
1554            }
1555        },
1556        .update_line_number => |ti| {
1557            const nav_prog_node = comp.link_prog_node.start("Update line number", 0);
1558            defer nav_prog_node.end();
1559            if (pt.zcu.llvm_object == null) {
1560                if (comp.bin_file) |lf| {
1561                    lf.updateLineNumber(pt, ti) catch |err| switch (err) {
1562                        error.OutOfMemory => diags.setAllocFailure(),
1563                        else => |e| log.err("update line number failed: {s}", .{@errorName(e)}),
1564                    };
1565                }
1566            }
1567        },
1568    }
1569
1570    if (timer.finish()) |ns_link| report_time: {
1571        const zir_decl: ?InternPool.TrackedInst.Index = switch (task) {
1572            .link_type, .update_line_number => null,
1573            .link_nav => |nav| ip.getNav(nav).srcInst(ip),
1574            .link_func => |f| ip.getNav(ip.indexToKey(f.func).func.owner_nav).srcInst(ip),
1575        };
1576        comp.mutex.lock();
1577        defer comp.mutex.unlock();
1578        const tr = &zcu.comp.time_report.?;
1579        tr.stats.cpu_ns_link += ns_link;
1580        if (zir_decl) |inst| {
1581            const gop = tr.decl_link_ns.getOrPut(zcu.gpa, inst) catch |err| switch (err) {
1582                error.OutOfMemory => {
1583                    zcu.comp.setAllocFailure();
1584                    break :report_time;
1585                },
1586            };
1587            if (!gop.found_existing) gop.value_ptr.* = 0;
1588            gop.value_ptr.* += ns_link;
1589        }
1590    }
1591}
1592pub fn doIdleTask(comp: *Compilation, tid: usize) error{ OutOfMemory, LinkFailure }!bool {
1593    return if (comp.bin_file) |lf| lf.idle(@enumFromInt(tid)) else false;
1594}
1595/// After the main pipeline is done, but before flush, the compilation may need to link one final
1596/// `Nav` into the binary: the `builtin.test_functions` value. Since the link thread isn't running
1597/// by then, we expose this function which can be called directly.
1598pub fn linkTestFunctionsNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) void {
1599    const zcu = pt.zcu;
1600    const comp = zcu.comp;
1601    const diags = &comp.link_diags;
1602    if (zcu.llvm_object) |llvm_object| {
1603        llvm_object.updateNav(pt, nav_index) catch |err| switch (err) {
1604            error.OutOfMemory => diags.setAllocFailure(),
1605        };
1606    } else if (comp.bin_file) |lf| {
1607        lf.updateNav(pt, nav_index) catch |err| switch (err) {
1608            error.OutOfMemory => diags.setAllocFailure(),
1609            error.CodegenFail => zcu.assertCodegenFailed(nav_index),
1610            error.Overflow, error.RelocationNotByteAligned => {
1611                switch (zcu.codegenFail(nav_index, "unable to codegen: {s}", .{@errorName(err)})) {
1612                    error.CodegenFail => return,
1613                    error.OutOfMemory => return diags.setAllocFailure(),
1614                }
1615                // Not a retryable failure.
1616            },
1617        };
1618    }
1619}
1620pub fn updateErrorData(pt: Zcu.PerThread) void {
1621    const comp = pt.zcu.comp;
1622    if (comp.bin_file) |lf| lf.updateErrorData(pt) catch |err| switch (err) {
1623        error.OutOfMemory => comp.link_diags.setAllocFailure(),
1624        error.LinkFailure => {},
1625    };
1626}
1627
1628/// Provided by the CLI, processed into `LinkInput` instances at the start of
1629/// the compilation pipeline.
1630pub const UnresolvedInput = union(enum) {
1631    /// A library name that could potentially be dynamic or static depending on
1632    /// query parameters, resolved according to library directories.
1633    /// This could potentially resolve to a GNU ld script, resulting in more
1634    /// library dependencies.
1635    name_query: NameQuery,
1636    /// When a file path is provided, query info is still needed because the
1637    /// path may point to a .so file which may actually be a GNU ld script that
1638    /// references library names which need to be resolved.
1639    path_query: PathQuery,
1640    /// Strings that come from GNU ld scripts. Is it a filename? Is it a path?
1641    /// Who knows! Fuck around and find out.
1642    ambiguous_name: NameQuery,
1643    /// Put exactly this string in the dynamic section, no rpath.
1644    dso_exact: Input.DsoExact,
1645
1646    pub const NameQuery = struct {
1647        name: []const u8,
1648        query: Query,
1649    };
1650
1651    pub const PathQuery = struct {
1652        path: Path,
1653        query: Query,
1654    };
1655
1656    pub const Query = struct {
1657        needed: bool = false,
1658        weak: bool = false,
1659        reexport: bool = false,
1660        must_link: bool = false,
1661        hidden: bool = false,
1662        allow_so_scripts: bool = false,
1663        preferred_mode: std.builtin.LinkMode,
1664        search_strategy: SearchStrategy,
1665
1666        fn fallbackMode(q: Query) std.builtin.LinkMode {
1667            assert(q.search_strategy != .no_fallback);
1668            return switch (q.preferred_mode) {
1669                .dynamic => .static,
1670                .static => .dynamic,
1671            };
1672        }
1673    };
1674
1675    pub const SearchStrategy = enum {
1676        paths_first,
1677        mode_first,
1678        no_fallback,
1679    };
1680};
1681
1682pub const Input = union(enum) {
1683    object: Object,
1684    archive: Object,
1685    res: Res,
1686    /// May not be a GNU ld script. Those are resolved when converting from
1687    /// `UnresolvedInput` to `Input` values.
1688    dso: Dso,
1689    dso_exact: DsoExact,
1690
1691    pub const Object = struct {
1692        path: Path,
1693        file: fs.File,
1694        must_link: bool,
1695        hidden: bool,
1696    };
1697
1698    pub const Res = struct {
1699        path: Path,
1700        file: fs.File,
1701    };
1702
1703    pub const Dso = struct {
1704        path: Path,
1705        file: fs.File,
1706        needed: bool,
1707        weak: bool,
1708        reexport: bool,
1709    };
1710
1711    pub const DsoExact = struct {
1712        /// Includes the ":" prefix. This is intended to be put into the DSO
1713        /// section verbatim with no corresponding rpaths.
1714        name: []const u8,
1715    };
1716
1717    /// Returns `null` in the case of `dso_exact`.
1718    pub fn path(input: Input) ?Path {
1719        return switch (input) {
1720            .object, .archive => |obj| obj.path,
1721            inline .res, .dso => |x| x.path,
1722            .dso_exact => null,
1723        };
1724    }
1725
1726    /// Returns `null` in the case of `dso_exact`.
1727    pub fn pathAndFile(input: Input) ?struct { Path, fs.File } {
1728        return switch (input) {
1729            .object, .archive => |obj| .{ obj.path, obj.file },
1730            inline .res, .dso => |x| .{ x.path, x.file },
1731            .dso_exact => null,
1732        };
1733    }
1734
1735    pub fn taskName(input: Input) []const u8 {
1736        return switch (input) {
1737            .object, .archive => |obj| obj.path.basename(),
1738            inline .res, .dso => |x| x.path.basename(),
1739            .dso_exact => "dso_exact",
1740        };
1741    }
1742};
1743
1744pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void {
1745    for (link_inputs) |link_input| {
1746        man.hash.add(@as(@typeInfo(Input).@"union".tag_type.?, link_input));
1747        switch (link_input) {
1748            .object, .archive => |obj| {
1749                _ = try man.addOpenedFile(obj.path, obj.file, null);
1750                man.hash.add(obj.must_link);
1751                man.hash.add(obj.hidden);
1752            },
1753            .res => |res| {
1754                _ = try man.addOpenedFile(res.path, res.file, null);
1755            },
1756            .dso => |dso| {
1757                _ = try man.addOpenedFile(dso.path, dso.file, null);
1758                man.hash.add(dso.needed);
1759                man.hash.add(dso.weak);
1760                man.hash.add(dso.reexport);
1761            },
1762            .dso_exact => |dso_exact| {
1763                man.hash.addBytes(dso_exact.name);
1764            },
1765        }
1766    }
1767}
1768
1769pub fn resolveInputs(
1770    gpa: Allocator,
1771    arena: Allocator,
1772    target: *const std.Target,
1773    /// This function mutates this array but does not take ownership.
1774    /// Allocated with `gpa`.
1775    unresolved_inputs: *std.ArrayList(UnresolvedInput),
1776    /// Allocated with `gpa`.
1777    resolved_inputs: *std.ArrayList(Input),
1778    lib_directories: []const Cache.Directory,
1779    color: std.zig.Color,
1780) Allocator.Error!void {
1781    var checked_paths: std.ArrayList(u8) = .empty;
1782    defer checked_paths.deinit(gpa);
1783
1784    var ld_script_bytes: std.ArrayList(u8) = .empty;
1785    defer ld_script_bytes.deinit(gpa);
1786
1787    var failed_libs: std.ArrayList(struct {
1788        name: []const u8,
1789        strategy: UnresolvedInput.SearchStrategy,
1790        checked_paths: []const u8,
1791        preferred_mode: std.builtin.LinkMode,
1792    }) = .empty;
1793
1794    // Convert external system libs into a stack so that items can be
1795    // pushed to it.
1796    //
1797    // This is necessary because shared objects might turn out to be
1798    // "linker scripts" that in fact resolve to one or more other
1799    // external system libs, including parameters such as "needed".
1800    //
1801    // Unfortunately, such files need to be detected immediately, so
1802    // that this library search logic can be applied to them.
1803    mem.reverse(UnresolvedInput, unresolved_inputs.items);
1804
1805    syslib: while (unresolved_inputs.pop()) |unresolved_input| {
1806        switch (unresolved_input) {
1807            .name_query => |name_query| {
1808                const query = name_query.query;
1809
1810                // Checked in the first pass above while looking for libc libraries.
1811                assert(!fs.path.isAbsolute(name_query.name));
1812
1813                checked_paths.clearRetainingCapacity();
1814
1815                switch (query.search_strategy) {
1816                    .mode_first, .no_fallback => {
1817                        // check for preferred mode
1818                        for (lib_directories) |lib_directory| switch (try resolveLibInput(
1819                            gpa,
1820                            arena,
1821                            unresolved_inputs,
1822                            resolved_inputs,
1823                            &checked_paths,
1824                            &ld_script_bytes,
1825                            lib_directory,
1826                            name_query,
1827                            target,
1828                            query.preferred_mode,
1829                            color,
1830                        )) {
1831                            .ok => continue :syslib,
1832                            .no_match => {},
1833                        };
1834                        // check for fallback mode
1835                        if (query.search_strategy == .no_fallback) {
1836                            try failed_libs.append(arena, .{
1837                                .name = name_query.name,
1838                                .strategy = query.search_strategy,
1839                                .checked_paths = try arena.dupe(u8, checked_paths.items),
1840                                .preferred_mode = query.preferred_mode,
1841                            });
1842                            continue :syslib;
1843                        }
1844                        for (lib_directories) |lib_directory| switch (try resolveLibInput(
1845                            gpa,
1846                            arena,
1847                            unresolved_inputs,
1848                            resolved_inputs,
1849                            &checked_paths,
1850                            &ld_script_bytes,
1851                            lib_directory,
1852                            name_query,
1853                            target,
1854                            query.fallbackMode(),
1855                            color,
1856                        )) {
1857                            .ok => continue :syslib,
1858                            .no_match => {},
1859                        };
1860                        try failed_libs.append(arena, .{
1861                            .name = name_query.name,
1862                            .strategy = query.search_strategy,
1863                            .checked_paths = try arena.dupe(u8, checked_paths.items),
1864                            .preferred_mode = query.preferred_mode,
1865                        });
1866                        continue :syslib;
1867                    },
1868                    .paths_first => {
1869                        for (lib_directories) |lib_directory| {
1870                            // check for preferred mode
1871                            switch (try resolveLibInput(
1872                                gpa,
1873                                arena,
1874                                unresolved_inputs,
1875                                resolved_inputs,
1876                                &checked_paths,
1877                                &ld_script_bytes,
1878                                lib_directory,
1879                                name_query,
1880                                target,
1881                                query.preferred_mode,
1882                                color,
1883                            )) {
1884                                .ok => continue :syslib,
1885                                .no_match => {},
1886                            }
1887
1888                            // check for fallback mode
1889                            switch (try resolveLibInput(
1890                                gpa,
1891                                arena,
1892                                unresolved_inputs,
1893                                resolved_inputs,
1894                                &checked_paths,
1895                                &ld_script_bytes,
1896                                lib_directory,
1897                                name_query,
1898                                target,
1899                                query.fallbackMode(),
1900                                color,
1901                            )) {
1902                                .ok => continue :syslib,
1903                                .no_match => {},
1904                            }
1905                        }
1906                        try failed_libs.append(arena, .{
1907                            .name = name_query.name,
1908                            .strategy = query.search_strategy,
1909                            .checked_paths = try arena.dupe(u8, checked_paths.items),
1910                            .preferred_mode = query.preferred_mode,
1911                        });
1912                        continue :syslib;
1913                    },
1914                }
1915            },
1916            .ambiguous_name => |an| {
1917                // First check the path relative to the current working directory.
1918                // If the file is a library and is not found there, check the library search paths as well.
1919                // This is consistent with the behavior of GNU ld.
1920                if (try resolvePathInput(
1921                    gpa,
1922                    arena,
1923                    unresolved_inputs,
1924                    resolved_inputs,
1925                    &ld_script_bytes,
1926                    target,
1927                    .{
1928                        .path = Path.initCwd(an.name),
1929                        .query = an.query,
1930                    },
1931                    color,
1932                )) |lib_result| {
1933                    switch (lib_result) {
1934                        .ok => continue :syslib,
1935                        .no_match => {
1936                            for (lib_directories) |lib_directory| {
1937                                switch ((try resolvePathInput(
1938                                    gpa,
1939                                    arena,
1940                                    unresolved_inputs,
1941                                    resolved_inputs,
1942                                    &ld_script_bytes,
1943                                    target,
1944                                    .{
1945                                        .path = .{
1946                                            .root_dir = lib_directory,
1947                                            .sub_path = an.name,
1948                                        },
1949                                        .query = an.query,
1950                                    },
1951                                    color,
1952                                )).?) {
1953                                    .ok => continue :syslib,
1954                                    .no_match => {},
1955                                }
1956                            }
1957                            fatal("{s}: file listed in linker script not found", .{an.name});
1958                        },
1959                    }
1960                }
1961                continue;
1962            },
1963            .path_query => |pq| {
1964                if (try resolvePathInput(
1965                    gpa,
1966                    arena,
1967                    unresolved_inputs,
1968                    resolved_inputs,
1969                    &ld_script_bytes,
1970                    target,
1971                    pq,
1972                    color,
1973                )) |lib_result| {
1974                    switch (lib_result) {
1975                        .ok => {},
1976                        .no_match => fatal("{f}: file not found", .{pq.path}),
1977                    }
1978                }
1979                continue;
1980            },
1981            .dso_exact => |dso_exact| {
1982                try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact });
1983                continue;
1984            },
1985        }
1986        @compileError("unreachable");
1987    }
1988
1989    if (failed_libs.items.len > 0) {
1990        for (failed_libs.items) |f| {
1991            const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
1992            std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{
1993                @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths,
1994            });
1995        }
1996        std.process.exit(1);
1997    }
1998}
1999
2000const ResolveLibInputResult = enum { ok, no_match };
2001const fatal = std.process.fatal;
2002
2003fn resolveLibInput(
2004    gpa: Allocator,
2005    arena: Allocator,
2006    /// Allocated via `gpa`.
2007    unresolved_inputs: *std.ArrayList(UnresolvedInput),
2008    /// Allocated via `gpa`.
2009    resolved_inputs: *std.ArrayList(Input),
2010    /// Allocated via `gpa`.
2011    checked_paths: *std.ArrayList(u8),
2012    /// Allocated via `gpa`.
2013    ld_script_bytes: *std.ArrayList(u8),
2014    lib_directory: Directory,
2015    name_query: UnresolvedInput.NameQuery,
2016    target: *const std.Target,
2017    link_mode: std.builtin.LinkMode,
2018    color: std.zig.Color,
2019) Allocator.Error!ResolveLibInputResult {
2020    try resolved_inputs.ensureUnusedCapacity(gpa, 1);
2021
2022    const lib_name = name_query.name;
2023
2024    if (target.os.tag.isDarwin() and link_mode == .dynamic) tbd: {
2025        // Prefer .tbd over .dylib.
2026        const test_path: Path = .{
2027            .root_dir = lib_directory,
2028            .sub_path = try std.fmt.allocPrint(arena, "lib{s}.tbd", .{lib_name}),
2029        };
2030        try checked_paths.print(gpa, "\n  {f}", .{test_path});
2031        var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2032            error.FileNotFound => break :tbd,
2033            else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }),
2034        };
2035        errdefer file.close();
2036        return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2037    }
2038
2039    {
2040        const test_path: Path = .{
2041            .root_dir = lib_directory,
2042            .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
2043                target.libPrefix(), lib_name, switch (link_mode) {
2044                    .static => target.staticLibSuffix(),
2045                    .dynamic => target.dynamicLibSuffix(),
2046                },
2047            }),
2048        };
2049        try checked_paths.print(gpa, "\n  {f}", .{test_path});
2050        switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{
2051            .path = test_path,
2052            .query = name_query.query,
2053        }, link_mode, color)) {
2054            .no_match => {},
2055            .ok => return .ok,
2056        }
2057    }
2058
2059    // In the case of Darwin, the main check will be .dylib, so here we
2060    // additionally check for .so files.
2061    if (target.os.tag.isDarwin() and link_mode == .dynamic) so: {
2062        const test_path: Path = .{
2063            .root_dir = lib_directory,
2064            .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}),
2065        };
2066        try checked_paths.print(gpa, "\n  {f}", .{test_path});
2067        var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2068            error.FileNotFound => break :so,
2069            else => |e| fatal("unable to search for so library '{f}': {s}", .{
2070                test_path, @errorName(e),
2071            }),
2072        };
2073        errdefer file.close();
2074        return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2075    }
2076
2077    // In the case of MinGW, the main check will be .lib but we also need to
2078    // look for `libfoo.a`.
2079    if (target.isMinGW() and link_mode == .static) mingw: {
2080        const test_path: Path = .{
2081            .root_dir = lib_directory,
2082            .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}),
2083        };
2084        try checked_paths.print(gpa, "\n  {f}", .{test_path});
2085        var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2086            error.FileNotFound => break :mingw,
2087            else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }),
2088        };
2089        errdefer file.close();
2090        return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query);
2091    }
2092
2093    return .no_match;
2094}
2095
2096fn finishResolveLibInput(
2097    resolved_inputs: *std.ArrayList(Input),
2098    path: Path,
2099    file: std.fs.File,
2100    link_mode: std.builtin.LinkMode,
2101    query: UnresolvedInput.Query,
2102) ResolveLibInputResult {
2103    switch (link_mode) {
2104        .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{
2105            .path = path,
2106            .file = file,
2107            .must_link = query.must_link,
2108            .hidden = query.hidden,
2109        } }),
2110        .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{
2111            .path = path,
2112            .file = file,
2113            .needed = query.needed,
2114            .weak = query.weak,
2115            .reexport = query.reexport,
2116        } }),
2117    }
2118    return .ok;
2119}
2120
2121fn resolvePathInput(
2122    gpa: Allocator,
2123    arena: Allocator,
2124    /// Allocated with `gpa`.
2125    unresolved_inputs: *std.ArrayList(UnresolvedInput),
2126    /// Allocated with `gpa`.
2127    resolved_inputs: *std.ArrayList(Input),
2128    /// Allocated via `gpa`.
2129    ld_script_bytes: *std.ArrayList(u8),
2130    target: *const std.Target,
2131    pq: UnresolvedInput.PathQuery,
2132    color: std.zig.Color,
2133) Allocator.Error!?ResolveLibInputResult {
2134    switch (Compilation.classifyFileExt(pq.path.sub_path)) {
2135        .static_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color),
2136        .shared_library => return try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color),
2137        .object => {
2138            var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
2139                fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) });
2140            errdefer file.close();
2141            try resolved_inputs.append(gpa, .{ .object = .{
2142                .path = pq.path,
2143                .file = file,
2144                .must_link = pq.query.must_link,
2145                .hidden = pq.query.hidden,
2146            } });
2147            return null;
2148        },
2149        .res => {
2150            var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err|
2151                fatal("failed to open windows resource {f}: {s}", .{ pq.path, @errorName(err) });
2152            errdefer file.close();
2153            try resolved_inputs.append(gpa, .{ .res = .{
2154                .path = pq.path,
2155                .file = file,
2156            } });
2157            return null;
2158        },
2159        else => fatal("{f}: unrecognized file extension", .{pq.path}),
2160    }
2161}
2162
2163fn resolvePathInputLib(
2164    gpa: Allocator,
2165    arena: Allocator,
2166    /// Allocated with `gpa`.
2167    unresolved_inputs: *std.ArrayList(UnresolvedInput),
2168    /// Allocated with `gpa`.
2169    resolved_inputs: *std.ArrayList(Input),
2170    /// Allocated via `gpa`.
2171    ld_script_bytes: *std.ArrayList(u8),
2172    target: *const std.Target,
2173    pq: UnresolvedInput.PathQuery,
2174    link_mode: std.builtin.LinkMode,
2175    color: std.zig.Color,
2176) Allocator.Error!ResolveLibInputResult {
2177    try resolved_inputs.ensureUnusedCapacity(gpa, 1);
2178
2179    const test_path: Path = pq.path;
2180    // In the case of shared libraries, they might actually be "linker scripts"
2181    // that contain references to other libraries.
2182    if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) {
2183        .static_library, .shared_library => true,
2184        else => false,
2185    }) {
2186        var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2187            error.FileNotFound => return .no_match,
2188            else => |e| fatal("unable to search for {s} library '{f}': {s}", .{
2189                @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e),
2190            }),
2191        };
2192        errdefer file.close();
2193        try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len));
2194        const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f}': {s}", .{
2195            std.fmt.alt(test_path, .formatEscapeChar), @errorName(err),
2196        });
2197        const buf = ld_script_bytes.items[0..n];
2198        if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) {
2199            // Appears to be an ELF or archive file.
2200            return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2201        }
2202        const stat = file.stat() catch |err|
2203            fatal("failed to stat {f}: {s}", .{ test_path, @errorName(err) });
2204        const size = std.math.cast(u32, stat.size) orelse
2205            fatal("{f}: linker script too big", .{test_path});
2206        try ld_script_bytes.resize(gpa, size);
2207        const buf2 = ld_script_bytes.items[n..];
2208        const n2 = file.preadAll(buf2, n) catch |err|
2209            fatal("failed to read {f}: {s}", .{ test_path, @errorName(err) });
2210        if (n2 != buf2.len) fatal("failed to read {f}: unexpected end of file", .{test_path});
2211        var diags = Diags.init(gpa);
2212        defer diags.deinit();
2213        const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);
2214        if (diags.hasErrors()) {
2215            var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2216            try wip_errors.init(gpa);
2217            defer wip_errors.deinit();
2218
2219            try diags.addMessagesToBundle(&wip_errors, null);
2220
2221            var error_bundle = try wip_errors.toOwnedBundle("");
2222            defer error_bundle.deinit(gpa);
2223
2224            error_bundle.renderToStdErr(.{}, color);
2225
2226            std.process.exit(1);
2227        }
2228
2229        var ld_script = ld_script_result catch |err|
2230            fatal("{f}: failed to parse linker script: {s}", .{ test_path, @errorName(err) });
2231        defer ld_script.deinit(gpa);
2232
2233        try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len);
2234        for (ld_script.args) |arg| {
2235            const query: UnresolvedInput.Query = .{
2236                .needed = arg.needed or pq.query.needed,
2237                .weak = pq.query.weak,
2238                .reexport = pq.query.reexport,
2239                .preferred_mode = pq.query.preferred_mode,
2240                .search_strategy = pq.query.search_strategy,
2241                .allow_so_scripts = pq.query.allow_so_scripts,
2242            };
2243            if (mem.startsWith(u8, arg.path, "-l")) {
2244                unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{
2245                    .name = try arena.dupe(u8, arg.path["-l".len..]),
2246                    .query = query,
2247                } });
2248            } else {
2249                unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{
2250                    .name = try arena.dupe(u8, arg.path),
2251                    .query = query,
2252                } });
2253            }
2254        }
2255        file.close();
2256        return .ok;
2257    }
2258
2259    var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) {
2260        error.FileNotFound => return .no_match,
2261        else => |e| fatal("unable to search for {s} library {f}: {s}", .{
2262            @tagName(link_mode), test_path, @errorName(e),
2263        }),
2264    };
2265    errdefer file.close();
2266    return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query);
2267}
2268
2269pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object {
2270    var file = try path.root_dir.handle.openFile(path.sub_path, .{});
2271    errdefer file.close();
2272    return .{
2273        .path = path,
2274        .file = file,
2275        .must_link = must_link,
2276        .hidden = hidden,
2277    };
2278}
2279
2280pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso {
2281    var file = try path.root_dir.handle.openFile(path.sub_path, .{});
2282    errdefer file.close();
2283    return .{
2284        .path = path,
2285        .file = file,
2286        .needed = needed,
2287        .weak = weak,
2288        .reexport = reexport,
2289    };
2290}
2291
2292pub fn openObjectInput(diags: *Diags, path: Path) error{LinkFailure}!Input {
2293    return .{ .object = openObject(path, false, false) catch |err| {
2294        return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) });
2295    } };
2296}
2297
2298pub fn openArchiveInput(diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input {
2299    return .{ .archive = openObject(path, must_link, hidden) catch |err| {
2300        return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) });
2301    } };
2302}
2303
2304pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input {
2305    return .{ .dso = openDso(path, needed, weak, reexport) catch |err| {
2306        return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) });
2307    } };
2308}
2309
2310/// Returns true if and only if there is at least one input of type object,
2311/// archive, or Windows resource file.
2312pub fn anyObjectInputs(inputs: []const Input) bool {
2313    return countObjectInputs(inputs) != 0;
2314}
2315
2316/// Returns the number of inputs of type object, archive, or Windows resource file.
2317pub fn countObjectInputs(inputs: []const Input) usize {
2318    var count: usize = 0;
2319    for (inputs) |input| switch (input) {
2320        .dso, .dso_exact => continue,
2321        .res, .object, .archive => count += 1,
2322    };
2323    return count;
2324}
2325
2326/// Returns the first input of type object or archive.
2327pub fn firstObjectInput(inputs: []const Input) ?Input.Object {
2328    for (inputs) |input| switch (input) {
2329        .object, .archive => |obj| return obj,
2330        .res, .dso, .dso_exact => continue,
2331    };
2332    return null;
2333}