zig

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

blob 9be5b9ca (48961B) - Raw


      1 const std = @import("std");
      2 const build_options = @import("build_options");
      3 const builtin = @import("builtin");
      4 const assert = std.debug.assert;
      5 const fs = std.fs;
      6 const mem = std.mem;
      7 const log = std.log.scoped(.link);
      8 const trace = @import("tracy.zig").trace;
      9 const wasi_libc = @import("wasi_libc.zig");
     10 
     11 const Air = @import("Air.zig");
     12 const Allocator = std.mem.Allocator;
     13 const Cache = @import("Cache.zig");
     14 const Compilation = @import("Compilation.zig");
     15 const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
     16 const Liveness = @import("Liveness.zig");
     17 const Module = @import("Module.zig");
     18 const Package = @import("Package.zig");
     19 const Type = @import("type.zig").Type;
     20 const TypedValue = @import("TypedValue.zig");
     21 
     22 /// When adding a new field, remember to update `hashAddSystemLibs`.
     23 pub const SystemLib = struct {
     24     needed: bool = false,
     25     weak: bool = false,
     26 };
     27 
     28 pub const CacheMode = enum { incremental, whole };
     29 
     30 pub fn hashAddSystemLibs(
     31     hh: *Cache.HashHelper,
     32     hm: std.StringArrayHashMapUnmanaged(SystemLib),
     33 ) void {
     34     const keys = hm.keys();
     35     hh.add(keys.len);
     36     hh.addListOfBytes(keys);
     37     for (hm.values()) |value| {
     38         hh.add(value.needed);
     39         hh.add(value.weak);
     40     }
     41 }
     42 
     43 pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
     44 
     45 pub const Emit = struct {
     46     /// Where the output will go.
     47     directory: Compilation.Directory,
     48     /// Path to the output file, relative to `directory`.
     49     sub_path: []const u8,
     50 
     51     /// Returns the full path to `basename` if it were in the same directory as the
     52     /// `Emit` sub_path.
     53     pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
     54         const full_path = if (emit.directory.path) |p|
     55             try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
     56         else
     57             emit.sub_path;
     58 
     59         if (fs.path.dirname(full_path)) |dirname| {
     60             return try fs.path.joinZ(arena, &.{ dirname, basename });
     61         } else {
     62             return basename;
     63         }
     64     }
     65 };
     66 
     67 pub const Options = struct {
     68     /// This is `null` when `-fno-emit-bin` is used.
     69     emit: ?Emit,
     70     /// This is `null` not building a Windows DLL, or when `-fno-emit-implib` is used.
     71     implib_emit: ?Emit,
     72     target: std.Target,
     73     output_mode: std.builtin.OutputMode,
     74     link_mode: std.builtin.LinkMode,
     75     optimize_mode: std.builtin.Mode,
     76     machine_code_model: std.builtin.CodeModel,
     77     root_name: [:0]const u8,
     78     /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
     79     module: ?*Module,
     80     dynamic_linker: ?[]const u8,
     81     /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
     82     sysroot: ?[]const u8,
     83     /// Used for calculating how much space to reserve for symbols in case the binary file
     84     /// does not already have a symbol table.
     85     symbol_count_hint: u64 = 32,
     86     /// Used for calculating how much space to reserve for executable program code in case
     87     /// the binary file does not already have such a section.
     88     program_code_size_hint: u64 = 256 * 1024,
     89     entry_addr: ?u64 = null,
     90     entry: ?[]const u8,
     91     stack_size_override: ?u64,
     92     image_base_override: ?u64,
     93     /// 0 means no stack protector
     94     /// other value means stack protector with that buffer size.
     95     stack_protector: u32,
     96     cache_mode: CacheMode,
     97     include_compiler_rt: bool,
     98     /// Set to `true` to omit debug info.
     99     strip: bool,
    100     /// If this is true then this link code is responsible for outputting an object
    101     /// file and then using LLD to link it together with the link options and other objects.
    102     /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
    103     use_lld: bool,
    104     /// If this is true then this link code is responsible for making an LLVM IR Module,
    105     /// outputting it to an object file, and then linking that together with link options and
    106     /// other objects.
    107     /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
    108     use_llvm: bool,
    109     link_libc: bool,
    110     link_libcpp: bool,
    111     link_libunwind: bool,
    112     function_sections: bool,
    113     no_builtin: bool,
    114     eh_frame_hdr: bool,
    115     emit_relocs: bool,
    116     rdynamic: bool,
    117     z_nodelete: bool,
    118     z_notext: bool,
    119     z_defs: bool,
    120     z_origin: bool,
    121     z_nocopyreloc: bool,
    122     z_now: bool,
    123     z_relro: bool,
    124     z_common_page_size: ?u64,
    125     z_max_page_size: ?u64,
    126     tsaware: bool,
    127     nxcompat: bool,
    128     dynamicbase: bool,
    129     linker_optimization: u8,
    130     compress_debug_sections: CompressDebugSections,
    131     bind_global_refs_locally: bool,
    132     import_memory: bool,
    133     import_symbols: bool,
    134     import_table: bool,
    135     export_table: bool,
    136     initial_memory: ?u64,
    137     max_memory: ?u64,
    138     shared_memory: bool,
    139     export_symbol_names: []const []const u8,
    140     global_base: ?u64,
    141     is_native_os: bool,
    142     is_native_abi: bool,
    143     pic: bool,
    144     pie: bool,
    145     lto: bool,
    146     valgrind: bool,
    147     tsan: bool,
    148     stack_check: bool,
    149     red_zone: bool,
    150     omit_frame_pointer: bool,
    151     single_threaded: bool,
    152     verbose_link: bool,
    153     dll_export_fns: bool,
    154     error_return_tracing: bool,
    155     skip_linker_dependencies: bool,
    156     parent_compilation_link_libc: bool,
    157     each_lib_rpath: bool,
    158     build_id: bool,
    159     disable_lld_caching: bool,
    160     is_test: bool,
    161     hash_style: HashStyle,
    162     major_subsystem_version: ?u32,
    163     minor_subsystem_version: ?u32,
    164     gc_sections: ?bool = null,
    165     allow_shlib_undefined: ?bool,
    166     subsystem: ?std.Target.SubSystem,
    167     linker_script: ?[]const u8,
    168     version_script: ?[]const u8,
    169     soname: ?[]const u8,
    170     llvm_cpu_features: ?[*:0]const u8,
    171     print_gc_sections: bool,
    172     print_icf_sections: bool,
    173     print_map: bool,
    174     opt_bisect_limit: i32,
    175 
    176     objects: []Compilation.LinkObject,
    177     framework_dirs: []const []const u8,
    178     frameworks: std.StringArrayHashMapUnmanaged(SystemLib),
    179     system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
    180     wasi_emulated_libs: []const wasi_libc.CRTFile,
    181     lib_dirs: []const []const u8,
    182     rpath_list: []const []const u8,
    183 
    184     /// List of symbols forced as undefined in the symbol table
    185     /// thus forcing their resolution by the linker.
    186     /// Corresponds to `-u <symbol>` for ELF and `/include:<symbol>` for COFF/PE.
    187     /// TODO add handling for MachO.
    188     force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
    189 
    190     version: ?std.builtin.Version,
    191     compatibility_version: ?std.builtin.Version,
    192     libc_installation: ?*const LibCInstallation,
    193 
    194     /// WASI-only. Type of WASI execution model ("command" or "reactor").
    195     wasi_exec_model: std.builtin.WasiExecModel = undefined,
    196 
    197     /// (Zig compiler development) Enable dumping of linker's state as JSON.
    198     enable_link_snapshots: bool = false,
    199 
    200     /// (Darwin) Path and version of the native SDK if detected.
    201     native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null,
    202 
    203     /// (Darwin) Install name for the dylib
    204     install_name: ?[]const u8 = null,
    205 
    206     /// (Darwin) Path to entitlements file
    207     entitlements: ?[]const u8 = null,
    208 
    209     /// (Darwin) size of the __PAGEZERO segment
    210     pagezero_size: ?u64 = null,
    211 
    212     /// (Darwin) search strategy for system libraries
    213     search_strategy: ?File.MachO.SearchStrategy = null,
    214 
    215     /// (Darwin) set minimum space for future expansion of the load commands
    216     headerpad_size: ?u32 = null,
    217 
    218     /// (Darwin) set enough space as if all paths were MATPATHLEN
    219     headerpad_max_install_names: bool = false,
    220 
    221     /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
    222     dead_strip_dylibs: bool = false,
    223 
    224     /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
    225     /// paths when consolidating CodeView streams into a single PDB file.
    226     pdb_source_path: ?[]const u8 = null,
    227 
    228     /// (Windows) PDB output path
    229     pdb_out_path: ?[]const u8 = null,
    230 
    231     /// (Windows) .def file to specify when linking
    232     module_definition_file: ?[]const u8 = null,
    233 
    234     pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode {
    235         return if (options.use_lld) .Obj else options.output_mode;
    236     }
    237 
    238     pub fn move(self: *Options) Options {
    239         const copied_state = self.*;
    240         self.frameworks = .{};
    241         self.system_libs = .{};
    242         self.force_undefined_symbols = .{};
    243         return copied_state;
    244     }
    245 };
    246 
    247 pub const HashStyle = enum { sysv, gnu, both };
    248 
    249 pub const CompressDebugSections = enum { none, zlib };
    250 
    251 pub const File = struct {
    252     tag: Tag,
    253     options: Options,
    254     file: ?fs.File,
    255     allocator: Allocator,
    256     /// When linking with LLD, this linker code will output an object file only at
    257     /// this location, and then this path can be placed on the LLD linker line.
    258     intermediary_basename: ?[]const u8 = null,
    259 
    260     /// Prevents other processes from clobbering files in the output directory
    261     /// of this linking operation.
    262     lock: ?Cache.Lock = null,
    263 
    264     pub const LinkBlock = union {
    265         elf: Elf.TextBlock,
    266         coff: Coff.Atom,
    267         macho: MachO.Atom,
    268         plan9: Plan9.DeclBlock,
    269         c: void,
    270         wasm: Wasm.DeclBlock,
    271         spirv: void,
    272         nvptx: void,
    273     };
    274 
    275     pub const LinkFn = union {
    276         elf: Dwarf.SrcFn,
    277         coff: Coff.SrcFn,
    278         macho: Dwarf.SrcFn,
    279         plan9: void,
    280         c: void,
    281         wasm: Wasm.FnData,
    282         spirv: SpirV.FnData,
    283         nvptx: void,
    284     };
    285 
    286     pub const Export = union {
    287         elf: Elf.Export,
    288         coff: Coff.Export,
    289         macho: MachO.Export,
    290         plan9: Plan9.Export,
    291         c: void,
    292         wasm: Wasm.Export,
    293         spirv: void,
    294         nvptx: void,
    295     };
    296 
    297     /// Attempts incremental linking, if the file already exists. If
    298     /// incremental linking fails, falls back to truncating the file and
    299     /// rewriting it. A malicious file is detected as incremental link failure
    300     /// and does not cause Illegal Behavior. This operation is not atomic.
    301     pub fn openPath(allocator: Allocator, options: Options) !*File {
    302         const have_macho = !build_options.only_c;
    303         if (have_macho and options.target.ofmt == .macho) {
    304             return &(try MachO.openPath(allocator, options)).base;
    305         }
    306 
    307         if (options.emit == null) {
    308             return switch (options.target.ofmt) {
    309                 .coff => &(try Coff.createEmpty(allocator, options)).base,
    310                 .elf => &(try Elf.createEmpty(allocator, options)).base,
    311                 .macho => unreachable,
    312                 .wasm => &(try Wasm.createEmpty(allocator, options)).base,
    313                 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base,
    314                 .c => unreachable, // Reported error earlier.
    315                 .spirv => &(try SpirV.createEmpty(allocator, options)).base,
    316                 .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
    317                 .hex => return error.HexObjectFormatUnimplemented,
    318                 .raw => return error.RawObjectFormatUnimplemented,
    319                 .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
    320             };
    321         }
    322         const emit = options.emit.?;
    323         const use_lld = build_options.have_llvm and options.use_lld; // comptime-known false when !have_llvm
    324         const sub_path = if (use_lld) blk: {
    325             if (options.module == null) {
    326                 // No point in opening a file, we would not write anything to it.
    327                 // Initialize with empty.
    328                 return switch (options.target.ofmt) {
    329                     .coff => &(try Coff.createEmpty(allocator, options)).base,
    330                     .elf => &(try Elf.createEmpty(allocator, options)).base,
    331                     .macho => unreachable,
    332                     .plan9 => &(try Plan9.createEmpty(allocator, options)).base,
    333                     .wasm => &(try Wasm.createEmpty(allocator, options)).base,
    334                     .c => unreachable, // Reported error earlier.
    335                     .spirv => &(try SpirV.createEmpty(allocator, options)).base,
    336                     .nvptx => &(try NvPtx.createEmpty(allocator, options)).base,
    337                     .hex => return error.HexObjectFormatUnimplemented,
    338                     .raw => return error.RawObjectFormatUnimplemented,
    339                     .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
    340                 };
    341             }
    342             // Open a temporary object file, not the final output file because we
    343             // want to link with LLD.
    344             break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{
    345                 emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
    346             });
    347         } else emit.sub_path;
    348         errdefer if (use_lld) allocator.free(sub_path);
    349 
    350         const file: *File = f: {
    351             switch (options.target.ofmt) {
    352                 .coff => {
    353                     if (build_options.only_c) unreachable;
    354                     break :f &(try Coff.openPath(allocator, sub_path, options)).base;
    355                 },
    356                 .elf => {
    357                     if (build_options.only_c) unreachable;
    358                     break :f &(try Elf.openPath(allocator, sub_path, options)).base;
    359                 },
    360                 .macho => unreachable,
    361                 .plan9 => {
    362                     if (build_options.only_c) unreachable;
    363                     break :f &(try Plan9.openPath(allocator, sub_path, options)).base;
    364                 },
    365                 .wasm => {
    366                     if (build_options.only_c) unreachable;
    367                     break :f &(try Wasm.openPath(allocator, sub_path, options)).base;
    368                 },
    369                 .c => {
    370                     break :f &(try C.openPath(allocator, sub_path, options)).base;
    371                 },
    372                 .spirv => {
    373                     if (build_options.only_c) unreachable;
    374                     break :f &(try SpirV.openPath(allocator, sub_path, options)).base;
    375                 },
    376                 .nvptx => {
    377                     if (build_options.only_c) unreachable;
    378                     break :f &(try NvPtx.openPath(allocator, sub_path, options)).base;
    379                 },
    380                 .hex => return error.HexObjectFormatUnimplemented,
    381                 .raw => return error.RawObjectFormatUnimplemented,
    382                 .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented,
    383             }
    384         };
    385 
    386         if (use_lld) {
    387             // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`,
    388             // we also want to put the intermediary object file in the cache while the
    389             // main emit directory is the cwd.
    390             file.intermediary_basename = sub_path;
    391         }
    392 
    393         return file;
    394     }
    395 
    396     pub fn cast(base: *File, comptime T: type) ?*T {
    397         if (base.tag != T.base_tag)
    398             return null;
    399 
    400         return @fieldParentPtr(T, "base", base);
    401     }
    402 
    403     pub fn makeWritable(base: *File) !void {
    404         switch (base.tag) {
    405             .coff, .elf, .macho, .plan9, .wasm => {
    406                 if (build_options.only_c) unreachable;
    407                 if (base.file != null) return;
    408                 const emit = base.options.emit orelse return;
    409                 base.file = try emit.directory.handle.createFile(emit.sub_path, .{
    410                     .truncate = false,
    411                     .read = true,
    412                     .mode = determineMode(base.options),
    413                 });
    414             },
    415             .c, .spirv, .nvptx => {},
    416         }
    417     }
    418 
    419     pub fn makeExecutable(base: *File) !void {
    420         switch (base.options.output_mode) {
    421             .Obj => return,
    422             .Lib => switch (base.options.link_mode) {
    423                 .Static => return,
    424                 .Dynamic => {},
    425             },
    426             .Exe => {},
    427         }
    428         switch (base.tag) {
    429             .macho => if (base.file) |f| {
    430                 if (build_options.only_c) unreachable;
    431                 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
    432                     if (base.options.target.cpu.arch == .aarch64) {
    433                         // XNU starting with Big Sur running on arm64 is caching inodes of running binaries.
    434                         // Any change to the binary will effectively invalidate the kernel's cache
    435                         // resulting in a SIGKILL on each subsequent run. Since when doing incremental
    436                         // linking we're modifying a binary in-place, this will end up with the kernel
    437                         // killing it on every subsequent run. To circumvent it, we will copy the file
    438                         // into a new inode, remove the original file, and rename the copy to match
    439                         // the original file. This is super messy, but there doesn't seem any other
    440                         // way to please the XNU.
    441                         const emit = base.options.emit orelse return;
    442                         try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{});
    443                     }
    444                 }
    445                 f.close();
    446                 base.file = null;
    447             },
    448             .coff, .elf, .plan9, .wasm => if (base.file) |f| {
    449                 if (build_options.only_c) unreachable;
    450                 if (base.intermediary_basename != null) {
    451                     // The file we have open is not the final file that we want to
    452                     // make executable, so we don't have to close it.
    453                     return;
    454                 }
    455                 f.close();
    456                 base.file = null;
    457             },
    458             .c, .spirv, .nvptx => {},
    459         }
    460     }
    461 
    462     pub const UpdateDeclError = error{
    463         OutOfMemory,
    464         Overflow,
    465         Underflow,
    466         FileTooBig,
    467         InputOutput,
    468         FilesOpenedWithWrongFlags,
    469         IsDir,
    470         NoSpaceLeft,
    471         Unseekable,
    472         PermissionDenied,
    473         SwapFile,
    474         CorruptedData,
    475         SystemResources,
    476         OperationAborted,
    477         BrokenPipe,
    478         ConnectionResetByPeer,
    479         ConnectionTimedOut,
    480         NotOpenForReading,
    481         WouldBlock,
    482         AccessDenied,
    483         Unexpected,
    484         DiskQuota,
    485         NotOpenForWriting,
    486         AnalysisFail,
    487         CodegenFail,
    488         EmitFail,
    489         NameTooLong,
    490         CurrentWorkingDirectoryUnlinked,
    491         LockViolation,
    492         NetNameDeleted,
    493     };
    494 
    495     /// Called from within the CodeGen to lower a local variable instantion as an unnamed
    496     /// constant. Returns the symbol index of the lowered constant in the read-only section
    497     /// of the final binary.
    498     pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: Module.Decl.Index) UpdateDeclError!u32 {
    499         if (build_options.only_c) @compileError("unreachable");
    500         const decl = base.options.module.?.declPtr(decl_index);
    501         log.debug("lowerUnnamedConst {*} ({s})", .{ decl, decl.name });
    502         switch (base.tag) {
    503             // zig fmt: off
    504             .coff  => return @fieldParentPtr(Coff,  "base", base).lowerUnnamedConst(tv, decl_index),
    505             .elf   => return @fieldParentPtr(Elf,   "base", base).lowerUnnamedConst(tv, decl_index),
    506             .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index),
    507             .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index),
    508             .spirv => unreachable,
    509             .c     => unreachable,
    510             .wasm  => return @fieldParentPtr(Wasm,  "base", base).lowerUnnamedConst(tv, decl_index),
    511             .nvptx => unreachable,
    512             // zig fmt: on
    513         }
    514     }
    515 
    516     /// Called from within CodeGen to retrieve the symbol index of a global symbol.
    517     /// If no symbol exists yet with this name, a new undefined global symbol will
    518     /// be created. This symbol may get resolved once all relocatables are (re-)linked.
    519     pub fn getGlobalSymbol(base: *File, name: []const u8) UpdateDeclError!u32 {
    520         if (build_options.only_c) @compileError("unreachable");
    521         log.debug("getGlobalSymbol '{s}'", .{name});
    522         switch (base.tag) {
    523             // zig fmt: off
    524             .coff  => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name),
    525             .elf   => unreachable,
    526             .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name),
    527             .plan9 => unreachable,
    528             .spirv => unreachable,
    529             .c     => unreachable,
    530             .wasm  => return @fieldParentPtr(Wasm,  "base", base).getGlobalSymbol(name),
    531             .nvptx => unreachable,
    532             // zig fmt: on
    533         }
    534     }
    535 
    536     /// May be called before or after updateDeclExports but must be called
    537     /// after allocateDeclIndexes for any given Decl.
    538     pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
    539         const decl = module.declPtr(decl_index);
    540         log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() });
    541         assert(decl.has_tv);
    542         if (build_options.only_c) {
    543             assert(base.tag == .c);
    544             return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index);
    545         }
    546         switch (base.tag) {
    547             // zig fmt: off
    548             .coff  => return @fieldParentPtr(Coff,  "base", base).updateDecl(module, decl_index),
    549             .elf   => return @fieldParentPtr(Elf,   "base", base).updateDecl(module, decl_index),
    550             .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index),
    551             .c     => return @fieldParentPtr(C,     "base", base).updateDecl(module, decl_index),
    552             .wasm  => return @fieldParentPtr(Wasm,  "base", base).updateDecl(module, decl_index),
    553             .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index),
    554             .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index),
    555             .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index),
    556             // zig fmt: on
    557         }
    558     }
    559 
    560     /// May be called before or after updateDeclExports but must be called
    561     /// after allocateDeclIndexes for any given Decl.
    562     pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void {
    563         const owner_decl = module.declPtr(func.owner_decl);
    564         log.debug("updateFunc {*} ({s}), type={}", .{
    565             owner_decl, owner_decl.name, owner_decl.ty.fmtDebug(),
    566         });
    567         if (build_options.only_c) {
    568             assert(base.tag == .c);
    569             return @fieldParentPtr(C, "base", base).updateFunc(module, func, air, liveness);
    570         }
    571         switch (base.tag) {
    572             // zig fmt: off
    573             .coff  => return @fieldParentPtr(Coff,  "base", base).updateFunc(module, func, air, liveness),
    574             .elf   => return @fieldParentPtr(Elf,   "base", base).updateFunc(module, func, air, liveness),
    575             .macho => return @fieldParentPtr(MachO, "base", base).updateFunc(module, func, air, liveness),
    576             .c     => return @fieldParentPtr(C,     "base", base).updateFunc(module, func, air, liveness),
    577             .wasm  => return @fieldParentPtr(Wasm,  "base", base).updateFunc(module, func, air, liveness),
    578             .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func, air, liveness),
    579             .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func, air, liveness),
    580             .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func, air, liveness),
    581             // zig fmt: on
    582         }
    583     }
    584 
    585     pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void {
    586         log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
    587             decl, decl.name, decl.src_line + 1,
    588         });
    589         assert(decl.has_tv);
    590         if (build_options.only_c) {
    591             assert(base.tag == .c);
    592             return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);
    593         }
    594         switch (base.tag) {
    595             .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
    596             .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
    597             .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
    598             .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
    599             .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),
    600             .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),
    601             .spirv, .nvptx => {},
    602         }
    603     }
    604 
    605     /// Must be called before any call to updateDecl or updateDeclExports for
    606     /// any given Decl.
    607     /// TODO we're transitioning to deleting this function and instead having
    608     /// each linker backend notice the first time updateDecl or updateFunc is called, or
    609     /// a callee referenced from AIR.
    610     pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void {
    611         const decl = base.options.module.?.declPtr(decl_index);
    612         log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
    613         if (build_options.only_c) {
    614             assert(base.tag == .c);
    615             return;
    616         }
    617         switch (base.tag) {
    618             .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
    619             .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
    620 
    621             .coff,
    622             .elf,
    623             .macho,
    624             .c,
    625             .spirv,
    626             .nvptx,
    627             => {},
    628         }
    629     }
    630 
    631     pub fn releaseLock(self: *File) void {
    632         if (self.lock) |*lock| {
    633             lock.release();
    634             self.lock = null;
    635         }
    636     }
    637 
    638     pub fn toOwnedLock(self: *File) Cache.Lock {
    639         const lock = self.lock.?;
    640         self.lock = null;
    641         return lock;
    642     }
    643 
    644     pub fn destroy(base: *File) void {
    645         base.releaseLock();
    646         if (base.file) |f| f.close();
    647         if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path);
    648         base.options.frameworks.deinit(base.allocator);
    649         base.options.system_libs.deinit(base.allocator);
    650         base.options.force_undefined_symbols.deinit(base.allocator);
    651         switch (base.tag) {
    652             .coff => {
    653                 if (build_options.only_c) unreachable;
    654                 const parent = @fieldParentPtr(Coff, "base", base);
    655                 parent.deinit();
    656                 base.allocator.destroy(parent);
    657             },
    658             .elf => {
    659                 if (build_options.only_c) unreachable;
    660                 const parent = @fieldParentPtr(Elf, "base", base);
    661                 parent.deinit();
    662                 base.allocator.destroy(parent);
    663             },
    664             .macho => {
    665                 if (build_options.only_c) unreachable;
    666                 const parent = @fieldParentPtr(MachO, "base", base);
    667                 parent.deinit();
    668                 base.allocator.destroy(parent);
    669             },
    670             .c => {
    671                 const parent = @fieldParentPtr(C, "base", base);
    672                 parent.deinit();
    673                 base.allocator.destroy(parent);
    674             },
    675             .wasm => {
    676                 if (build_options.only_c) unreachable;
    677                 const parent = @fieldParentPtr(Wasm, "base", base);
    678                 parent.deinit();
    679                 base.allocator.destroy(parent);
    680             },
    681             .spirv => {
    682                 if (build_options.only_c) unreachable;
    683                 const parent = @fieldParentPtr(SpirV, "base", base);
    684                 parent.deinit();
    685                 base.allocator.destroy(parent);
    686             },
    687             .plan9 => {
    688                 if (build_options.only_c) unreachable;
    689                 const parent = @fieldParentPtr(Plan9, "base", base);
    690                 parent.deinit();
    691                 base.allocator.destroy(parent);
    692             },
    693             .nvptx => {
    694                 if (build_options.only_c) unreachable;
    695                 const parent = @fieldParentPtr(NvPtx, "base", base);
    696                 parent.deinit();
    697                 base.allocator.destroy(parent);
    698             },
    699         }
    700     }
    701 
    702     /// TODO audit this error set. most of these should be collapsed into one error,
    703     /// and ErrorFlags should be updated to convey the meaning to the user.
    704     pub const FlushError = error{
    705         BadDwarfCfi,
    706         CacheUnavailable,
    707         CurrentWorkingDirectoryUnlinked,
    708         DivisionByZero,
    709         DllImportLibraryNotFound,
    710         EmptyStubFile,
    711         ExpectedFuncType,
    712         FailedToEmit,
    713         FailedToResolveRelocationTarget,
    714         FileSystem,
    715         FilesOpenedWithWrongFlags,
    716         FlushFailure,
    717         FrameworkNotFound,
    718         FunctionSignatureMismatch,
    719         GlobalTypeMismatch,
    720         InvalidCharacter,
    721         InvalidEntryKind,
    722         InvalidFeatureSet,
    723         InvalidFormat,
    724         InvalidIndex,
    725         InvalidInitFunc,
    726         InvalidMagicByte,
    727         InvalidWasmVersion,
    728         LLDCrashed,
    729         LLDReportedFailure,
    730         LLD_LinkingIsTODO_ForSpirV,
    731         LibCInstallationMissingCRTDir,
    732         LibCInstallationNotAvailable,
    733         LibraryNotFound,
    734         LinkingWithoutZigSourceUnimplemented,
    735         MalformedArchive,
    736         MalformedDwarf,
    737         MalformedSection,
    738         MemoryTooBig,
    739         MemoryTooSmall,
    740         MismatchedCpuArchitecture,
    741         MissAlignment,
    742         MissingEndForBody,
    743         MissingEndForExpression,
    744         /// TODO: this should be removed from the error set in favor of using ErrorFlags
    745         MissingMainEntrypoint,
    746         /// TODO: this should be removed from the error set in favor of using ErrorFlags
    747         MissingSection,
    748         MissingSymbol,
    749         MissingTableSymbols,
    750         ModuleNameMismatch,
    751         MultipleSymbolDefinitions,
    752         NoObjectsToLink,
    753         NotObject,
    754         NotObjectFile,
    755         NotSupported,
    756         OutOfMemory,
    757         Overflow,
    758         PermissionDenied,
    759         StreamTooLong,
    760         SwapFile,
    761         SymbolCollision,
    762         SymbolMismatchingType,
    763         TODOImplementPlan9Objs,
    764         TODOImplementWritingLibFiles,
    765         TODOImplementWritingStaticLibFiles,
    766         UnableToSpawnSelf,
    767         UnableToSpawnWasm,
    768         UnableToWriteArchive,
    769         UndefinedLocal,
    770         /// TODO: merge with UndefinedSymbolReference
    771         UndefinedSymbol,
    772         /// TODO: merge with UndefinedSymbol
    773         UndefinedSymbolReference,
    774         Underflow,
    775         UnexpectedRemainder,
    776         UnexpectedTable,
    777         UnexpectedValue,
    778         UnhandledDwFormValue,
    779         UnhandledSymbolType,
    780         UnknownFeature,
    781         Unseekable,
    782         UnsupportedCpuArchitecture,
    783         UnsupportedVersion,
    784     } ||
    785         fs.File.WriteFileError ||
    786         fs.File.OpenError ||
    787         std.ChildProcess.SpawnError ||
    788         fs.Dir.CopyFileError;
    789 
    790     /// Commit pending changes and write headers. Takes into account final output mode
    791     /// and `use_lld`, not only `effectiveOutputMode`.
    792     pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void {
    793         if (build_options.only_c) {
    794             assert(base.tag == .c);
    795             return @fieldParentPtr(C, "base", base).flush(comp, prog_node);
    796         }
    797         if (comp.clang_preprocessor_mode == .yes) {
    798             const emit = base.options.emit orelse return; // -fno-emit-bin
    799             // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case)
    800             // Until then, we do `lld -r -o output.o input.o` even though the output is the same
    801             // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file
    802             // to the final location. See also the corresponding TODO in Coff linking.
    803             const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path});
    804             defer comp.gpa.free(full_out_path);
    805             assert(comp.c_object_table.count() == 1);
    806             const the_key = comp.c_object_table.keys()[0];
    807             const cached_pp_file_path = the_key.status.success.object_path;
    808             try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{});
    809             return;
    810         }
    811 
    812         const use_lld = build_options.have_llvm and base.options.use_lld;
    813         if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) {
    814             return base.linkAsArchive(comp, prog_node);
    815         }
    816         switch (base.tag) {
    817             .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node),
    818             .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node),
    819             .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node),
    820             .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node),
    821             .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node),
    822             .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node),
    823             .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node),
    824             .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node),
    825         }
    826     }
    827 
    828     /// Commit pending changes and write headers. Works based on `effectiveOutputMode`
    829     /// rather than final output mode.
    830     pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void {
    831         if (build_options.only_c) {
    832             assert(base.tag == .c);
    833             return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node);
    834         }
    835         switch (base.tag) {
    836             .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node),
    837             .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node),
    838             .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node),
    839             .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node),
    840             .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node),
    841             .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node),
    842             .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node),
    843             .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node),
    844         }
    845     }
    846 
    847     /// Called when a Decl is deleted from the Module.
    848     pub fn freeDecl(base: *File, decl_index: Module.Decl.Index) void {
    849         if (build_options.only_c) {
    850             assert(base.tag == .c);
    851             return @fieldParentPtr(C, "base", base).freeDecl(decl_index);
    852         }
    853         switch (base.tag) {
    854             .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index),
    855             .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index),
    856             .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index),
    857             .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index),
    858             .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index),
    859             .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index),
    860             .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index),
    861             .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index),
    862         }
    863     }
    864 
    865     pub fn errorFlags(base: *File) ErrorFlags {
    866         switch (base.tag) {
    867             .coff => return @fieldParentPtr(Coff, "base", base).error_flags,
    868             .elf => return @fieldParentPtr(Elf, "base", base).error_flags,
    869             .macho => return @fieldParentPtr(MachO, "base", base).error_flags,
    870             .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags,
    871             .c => return .{ .no_entry_point_found = false },
    872             .wasm, .spirv, .nvptx => return ErrorFlags{},
    873         }
    874     }
    875 
    876     pub const UpdateDeclExportsError = error{
    877         OutOfMemory,
    878         AnalysisFail,
    879     };
    880 
    881     /// May be called before or after updateDecl, but must be called after
    882     /// allocateDeclIndexes for any given Decl.
    883     pub fn updateDeclExports(
    884         base: *File,
    885         module: *Module,
    886         decl_index: Module.Decl.Index,
    887         exports: []const *Module.Export,
    888     ) UpdateDeclExportsError!void {
    889         const decl = module.declPtr(decl_index);
    890         log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name });
    891         assert(decl.has_tv);
    892         if (build_options.only_c) {
    893             assert(base.tag == .c);
    894             return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports);
    895         }
    896         switch (base.tag) {
    897             .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports),
    898             .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports),
    899             .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports),
    900             .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports),
    901             .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports),
    902             .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports),
    903             .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports),
    904             .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports),
    905         }
    906     }
    907 
    908     pub const RelocInfo = struct {
    909         parent_atom_index: u32,
    910         offset: u64,
    911         addend: u32,
    912     };
    913 
    914     /// Get allocated `Decl`'s address in virtual memory.
    915     /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
    916     /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
    917     /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
    918     /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate
    919     /// the block/atom.
    920     pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 {
    921         if (build_options.only_c) unreachable;
    922         switch (base.tag) {
    923             .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info),
    924             .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info),
    925             .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info),
    926             .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info),
    927             .c => unreachable,
    928             .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info),
    929             .spirv => unreachable,
    930             .nvptx => unreachable,
    931         }
    932     }
    933 
    934     /// This function is called by the frontend before flush(). It communicates that
    935     /// `options.bin_file.emit` directory needs to be renamed from
    936     /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.
    937     /// The frontend would like to simply perform a file system rename, however,
    938     /// some linker backends care about the file paths of the objects they are linking.
    939     /// So this function call tells linker backends to rename the paths of object files
    940     /// to observe the new directory path.
    941     /// Linker backends which do not have this requirement can fall back to the simple
    942     /// implementation at the bottom of this function.
    943     /// This function is only called when CacheMode is `whole`.
    944     pub fn renameTmpIntoCache(
    945         base: *File,
    946         cache_directory: Compilation.Directory,
    947         tmp_dir_sub_path: []const u8,
    948         o_sub_path: []const u8,
    949     ) !void {
    950         // So far, none of the linker backends need to respond to this event, however,
    951         // it makes sense that they might want to. So we leave this mechanism here
    952         // for now. Once the linker backends get more mature, if it turns out this
    953         // is not needed we can refactor this into having the frontend do the rename
    954         // directly, and remove this function from link.zig.
    955         _ = base;
    956         while (true) {
    957             if (builtin.os.tag == .windows) {
    958                 // Work around windows `renameW` can't fail with `PathAlreadyExists`
    959                 // See https://github.com/ziglang/zig/issues/8362
    960                 if (cache_directory.handle.access(o_sub_path, .{})) |_| {
    961                     try cache_directory.handle.deleteTree(o_sub_path);
    962                     continue;
    963                 } else |err| switch (err) {
    964                     error.FileNotFound => {},
    965                     else => |e| return e,
    966                 }
    967                 std.fs.rename(
    968                     cache_directory.handle,
    969                     tmp_dir_sub_path,
    970                     cache_directory.handle,
    971                     o_sub_path,
    972                 ) catch |err| {
    973                     log.err("unable to rename cache dir {s} to {s}: {s}", .{ tmp_dir_sub_path, o_sub_path, @errorName(err) });
    974                     return err;
    975                 };
    976                 break;
    977             } else {
    978                 std.fs.rename(
    979                     cache_directory.handle,
    980                     tmp_dir_sub_path,
    981                     cache_directory.handle,
    982                     o_sub_path,
    983                 ) catch |err| switch (err) {
    984                     error.PathAlreadyExists => {
    985                         try cache_directory.handle.deleteTree(o_sub_path);
    986                         continue;
    987                     },
    988                     else => |e| return e,
    989                 };
    990                 break;
    991             }
    992         }
    993     }
    994 
    995     pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void {
    996         const tracy = trace(@src());
    997         defer tracy.end();
    998 
    999         var arena_allocator = std.heap.ArenaAllocator.init(base.allocator);
   1000         defer arena_allocator.deinit();
   1001         const arena = arena_allocator.allocator();
   1002 
   1003         const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type.
   1004         const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path});
   1005         const full_out_path_z = try arena.dupeZ(u8, full_out_path);
   1006 
   1007         // If there is no Zig code to compile, then we should skip flushing the output file
   1008         // because it will not be part of the linker line anyway.
   1009         const module_obj_path: ?[]const u8 = if (base.options.module != null) blk: {
   1010             try base.flushModule(comp, prog_node);
   1011 
   1012             const dirname = fs.path.dirname(full_out_path_z) orelse ".";
   1013             break :blk try fs.path.join(arena, &.{ dirname, base.intermediary_basename.? });
   1014         } else null;
   1015 
   1016         log.debug("module_obj_path={s}", .{if (module_obj_path) |s| s else "(null)"});
   1017 
   1018         const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt)
   1019             comp.compiler_rt_obj.?.full_object_path
   1020         else
   1021             null;
   1022 
   1023         // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
   1024         // insight as to what's going on here you can read that function body which is more
   1025         // well-commented.
   1026 
   1027         const id_symlink_basename = "llvm-ar.id";
   1028 
   1029         var man: Cache.Manifest = undefined;
   1030         defer if (!base.options.disable_lld_caching) man.deinit();
   1031 
   1032         var digest: [Cache.hex_digest_len]u8 = undefined;
   1033 
   1034         if (!base.options.disable_lld_caching) {
   1035             man = comp.cache_parent.obtain();
   1036 
   1037             // We are about to obtain this lock, so here we give other processes a chance first.
   1038             base.releaseLock();
   1039 
   1040             for (base.options.objects) |obj| {
   1041                 _ = try man.addFile(obj.path, null);
   1042                 man.hash.add(obj.must_link);
   1043             }
   1044             for (comp.c_object_table.keys()) |key| {
   1045                 _ = try man.addFile(key.status.success.object_path, null);
   1046             }
   1047             try man.addOptionalFile(module_obj_path);
   1048             try man.addOptionalFile(compiler_rt_path);
   1049 
   1050             // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   1051             _ = try man.hit();
   1052             digest = man.final();
   1053 
   1054             var prev_digest_buf: [digest.len]u8 = undefined;
   1055             const prev_digest: []u8 = Cache.readSmallFile(
   1056                 directory.handle,
   1057                 id_symlink_basename,
   1058                 &prev_digest_buf,
   1059             ) catch |err| b: {
   1060                 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   1061                 break :b prev_digest_buf[0..0];
   1062             };
   1063             if (mem.eql(u8, prev_digest, &digest)) {
   1064                 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   1065                 base.lock = man.toOwnedLock();
   1066                 return;
   1067             }
   1068 
   1069             // We are about to change the output file to be different, so we invalidate the build hash now.
   1070             directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   1071                 error.FileNotFound => {},
   1072                 else => |e| return e,
   1073             };
   1074         }
   1075 
   1076         const num_object_files = base.options.objects.len + comp.c_object_table.count() + 2;
   1077         var object_files = try std.ArrayList([*:0]const u8).initCapacity(base.allocator, num_object_files);
   1078         defer object_files.deinit();
   1079 
   1080         for (base.options.objects) |obj| {
   1081             object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path));
   1082         }
   1083         for (comp.c_object_table.keys()) |key| {
   1084             object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path));
   1085         }
   1086         if (module_obj_path) |p| {
   1087             object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
   1088         }
   1089         if (compiler_rt_path) |p| {
   1090             object_files.appendAssumeCapacity(try arena.dupeZ(u8, p));
   1091         }
   1092 
   1093         if (base.options.verbose_link) {
   1094             std.debug.print("ar rcs {s}", .{full_out_path_z});
   1095             for (object_files.items) |arg| {
   1096                 std.debug.print(" {s}", .{arg});
   1097             }
   1098             std.debug.print("\n", .{});
   1099         }
   1100 
   1101         const llvm_bindings = @import("codegen/llvm/bindings.zig");
   1102         const llvm = @import("codegen/llvm.zig");
   1103         const os_tag = llvm.targetOs(base.options.target.os.tag);
   1104         const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag);
   1105         if (bad) return error.UnableToWriteArchive;
   1106 
   1107         if (!base.options.disable_lld_caching) {
   1108             Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   1109                 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)});
   1110             };
   1111 
   1112             man.writeManifest() catch |err| {
   1113                 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)});
   1114             };
   1115 
   1116             base.lock = man.toOwnedLock();
   1117         }
   1118     }
   1119 
   1120     pub const Tag = enum {
   1121         coff,
   1122         elf,
   1123         macho,
   1124         c,
   1125         wasm,
   1126         spirv,
   1127         plan9,
   1128         nvptx,
   1129     };
   1130 
   1131     pub const ErrorFlags = struct {
   1132         no_entry_point_found: bool = false,
   1133         missing_libc: bool = false,
   1134     };
   1135 
   1136     pub const C = @import("link/C.zig");
   1137     pub const Coff = @import("link/Coff.zig");
   1138     pub const Plan9 = @import("link/Plan9.zig");
   1139     pub const Elf = @import("link/Elf.zig");
   1140     pub const MachO = @import("link/MachO.zig");
   1141     pub const SpirV = @import("link/SpirV.zig");
   1142     pub const Wasm = @import("link/Wasm.zig");
   1143     pub const NvPtx = @import("link/NvPtx.zig");
   1144     pub const Dwarf = @import("link/Dwarf.zig");
   1145 };
   1146 
   1147 pub fn determineMode(options: Options) fs.File.Mode {
   1148     // On common systems with a 0o022 umask, 0o777 will still result in a file created
   1149     // with 0o755 permissions, but it works appropriately if the system is configured
   1150     // more leniently. As another data point, C's fopen seems to open files with the
   1151     // 666 mode.
   1152     const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777;
   1153     switch (options.effectiveOutputMode()) {
   1154         .Lib => return switch (options.link_mode) {
   1155             .Dynamic => executable_mode,
   1156             .Static => fs.File.default_mode,
   1157         },
   1158         .Exe => return executable_mode,
   1159         .Obj => return fs.File.default_mode,
   1160     }
   1161 }