master
   1base: link.File,
   2ofmt: union(enum) {
   3    elf: Elf,
   4    coff: Coff,
   5    wasm: Wasm,
   6},
   7
   8const Coff = struct {
   9    image_base: u64,
  10    entry: link.File.OpenOptions.Entry,
  11    pdb_out_path: ?[]const u8,
  12    repro: bool,
  13    tsaware: bool,
  14    nxcompat: bool,
  15    dynamicbase: bool,
  16    /// TODO this and minor_subsystem_version should be combined into one property and left as
  17    /// default or populated together. They should not be separate fields.
  18    major_subsystem_version: u16,
  19    minor_subsystem_version: u16,
  20    lib_directories: []const Cache.Directory,
  21    module_definition_file: ?[]const u8,
  22    subsystem: ?std.zig.Subsystem,
  23    /// These flags are populated by `codegen.llvm.updateExports` to allow us to guess the subsystem.
  24    lld_export_flags: struct {
  25        c_main: bool,
  26        winmain: bool,
  27        wwinmain: bool,
  28        winmain_crt_startup: bool,
  29        wwinmain_crt_startup: bool,
  30        dllmain_crt_startup: bool,
  31    },
  32    fn init(comp: *Compilation, options: link.File.OpenOptions) !Coff {
  33        const target = &comp.root_mod.resolved_target.result;
  34        const output_mode = comp.config.output_mode;
  35        return .{
  36            .image_base = options.image_base orelse switch (output_mode) {
  37                .Exe => switch (target.cpu.arch) {
  38                    .aarch64, .x86_64 => 0x140000000,
  39                    .thumb, .x86 => 0x400000,
  40                    else => return error.UnsupportedCoffArchitecture,
  41                },
  42                .Lib => switch (target.cpu.arch) {
  43                    .aarch64, .x86_64 => 0x180000000,
  44                    .thumb, .x86 => 0x10000000,
  45                    else => return error.UnsupportedCoffArchitecture,
  46                },
  47                .Obj => 0,
  48            },
  49            .entry = options.entry,
  50            .pdb_out_path = options.pdb_out_path,
  51            .repro = options.repro,
  52            .tsaware = options.tsaware,
  53            .nxcompat = options.nxcompat,
  54            .dynamicbase = options.dynamicbase,
  55            .major_subsystem_version = options.major_subsystem_version orelse 6,
  56            .minor_subsystem_version = options.minor_subsystem_version orelse 0,
  57            .lib_directories = options.lib_directories,
  58            .module_definition_file = options.module_definition_file,
  59            // Subsystem depends on the set of public symbol names from linked objects.
  60            // See LinkerDriver::inferSubsystem from the LLD project for the flow chart.
  61            .subsystem = options.subsystem,
  62            // These flags are initially all `false`; the LLVM backend populates them when it learns about exports.
  63            .lld_export_flags = .{
  64                .c_main = false,
  65                .winmain = false,
  66                .wwinmain = false,
  67                .winmain_crt_startup = false,
  68                .wwinmain_crt_startup = false,
  69                .dllmain_crt_startup = false,
  70            },
  71        };
  72    }
  73};
  74pub const Elf = struct {
  75    entry_name: ?[]const u8,
  76    hash_style: HashStyle,
  77    image_base: u64,
  78    linker_script: ?[]const u8,
  79    version_script: ?[]const u8,
  80    sort_section: ?SortSection,
  81    print_icf_sections: bool,
  82    print_map: bool,
  83    emit_relocs: bool,
  84    z_nodelete: bool,
  85    z_notext: bool,
  86    z_defs: bool,
  87    z_origin: bool,
  88    z_nocopyreloc: bool,
  89    z_now: bool,
  90    z_relro: bool,
  91    z_common_page_size: ?u64,
  92    z_max_page_size: ?u64,
  93    rpath_list: []const []const u8,
  94    symbol_wrap_set: []const []const u8,
  95    soname: ?[]const u8,
  96    allow_undefined_version: bool,
  97    enable_new_dtags: ?bool,
  98    compress_debug_sections: std.zig.CompressDebugSections,
  99    bind_global_refs_locally: bool,
 100    pub const HashStyle = enum { sysv, gnu, both };
 101    pub const SortSection = enum { name, alignment };
 102    /// Deprecated; use 'std.zig.CompressDebugSections' instead. To be removed after 0.16.0 is tagged.
 103    pub const CompressDebugSections = std.zig.CompressDebugSections;
 104
 105    fn init(comp: *Compilation, options: link.File.OpenOptions) !Elf {
 106        const PtrWidth = enum { p32, p64 };
 107        const target = &comp.root_mod.resolved_target.result;
 108        const output_mode = comp.config.output_mode;
 109        const is_dyn_lib = output_mode == .Lib and comp.config.link_mode == .dynamic;
 110        const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
 111            0...32 => .p32,
 112            33...64 => .p64,
 113            else => return error.UnsupportedElfArchitecture,
 114        };
 115        const default_entry_name: []const u8 = switch (target.cpu.arch) {
 116            .mips, .mipsel, .mips64, .mips64el => "__start",
 117            else => "_start",
 118        };
 119        return .{
 120            .entry_name = switch (options.entry) {
 121                .disabled => null,
 122                .default => if (output_mode != .Exe) null else default_entry_name,
 123                .enabled => default_entry_name,
 124                .named => |name| name,
 125            },
 126            .hash_style = options.hash_style,
 127            .image_base = b: {
 128                if (is_dyn_lib) break :b 0;
 129                if (output_mode == .Exe and comp.config.pie) break :b 0;
 130                break :b options.image_base orelse switch (ptr_width) {
 131                    .p32 => 0x10000,
 132                    .p64 => 0x1000000,
 133                };
 134            },
 135            .linker_script = options.linker_script,
 136            .version_script = options.version_script,
 137            .sort_section = options.sort_section,
 138            .print_icf_sections = options.print_icf_sections,
 139            .print_map = options.print_map,
 140            .emit_relocs = options.emit_relocs,
 141            .z_nodelete = options.z_nodelete,
 142            .z_notext = options.z_notext,
 143            .z_defs = options.z_defs,
 144            .z_origin = options.z_origin,
 145            .z_nocopyreloc = options.z_nocopyreloc,
 146            .z_now = options.z_now,
 147            .z_relro = options.z_relro,
 148            .z_common_page_size = options.z_common_page_size,
 149            .z_max_page_size = options.z_max_page_size,
 150            .rpath_list = options.rpath_list,
 151            .symbol_wrap_set = options.symbol_wrap_set.keys(),
 152            .soname = options.soname,
 153            .allow_undefined_version = options.allow_undefined_version,
 154            .enable_new_dtags = options.enable_new_dtags,
 155            .compress_debug_sections = options.compress_debug_sections,
 156            .bind_global_refs_locally = options.bind_global_refs_locally,
 157        };
 158    }
 159};
 160const Wasm = struct {
 161    /// Symbol name of the entry function to export
 162    entry_name: ?[]const u8,
 163    /// When true, will import the function table from the host environment.
 164    import_table: bool,
 165    /// When true, will export the function table to the host environment.
 166    export_table: bool,
 167    /// When defined, sets the initial memory size of the memory.
 168    initial_memory: ?u64,
 169    /// When defined, sets the maximum memory size of the memory.
 170    max_memory: ?u64,
 171    /// When defined, sets the start of the data section.
 172    global_base: ?u64,
 173    /// Set of *global* symbol names to export to the host environment.
 174    export_symbol_names: []const []const u8,
 175    /// When true, will allow undefined symbols
 176    import_symbols: bool,
 177    fn init(comp: *Compilation, options: link.File.OpenOptions) !Wasm {
 178        const default_entry_name: []const u8 = switch (comp.config.wasi_exec_model) {
 179            .reactor => "_initialize",
 180            .command => "_start",
 181        };
 182        return .{
 183            .entry_name = switch (options.entry) {
 184                .disabled => null,
 185                .default => if (comp.config.output_mode != .Exe) null else default_entry_name,
 186                .enabled => default_entry_name,
 187                .named => |name| name,
 188            },
 189            .import_table = options.import_table,
 190            .export_table = options.export_table,
 191            .initial_memory = options.initial_memory,
 192            .max_memory = options.max_memory,
 193            .global_base = options.global_base,
 194            .export_symbol_names = options.export_symbol_names,
 195            .import_symbols = options.import_symbols,
 196        };
 197    }
 198};
 199
 200pub fn createEmpty(
 201    arena: Allocator,
 202    comp: *Compilation,
 203    emit: Cache.Path,
 204    options: link.File.OpenOptions,
 205) !*Lld {
 206    const target = &comp.root_mod.resolved_target.result;
 207    const output_mode = comp.config.output_mode;
 208    const optimize_mode = comp.root_mod.optimize_mode;
 209
 210    const obj_file_ext: []const u8 = switch (target.ofmt) {
 211        .coff => "obj",
 212        .elf, .wasm => "o",
 213        else => unreachable,
 214    };
 215    const gc_sections: bool = options.gc_sections orelse switch (target.ofmt) {
 216        .coff => optimize_mode != .Debug,
 217        .elf => optimize_mode != .Debug and output_mode != .Obj,
 218        .wasm => output_mode != .Obj,
 219        else => unreachable,
 220    };
 221    const stack_size: u64 = options.stack_size orelse default: {
 222        if (target.ofmt == .wasm and target.os.tag == .freestanding)
 223            break :default 1 * 1024 * 1024; // 1 MiB
 224        break :default 16 * 1024 * 1024; // 16 MiB
 225    };
 226
 227    const lld = try arena.create(Lld);
 228    lld.* = .{
 229        .base = .{
 230            .tag = .lld,
 231            .comp = comp,
 232            .emit = emit,
 233            .zcu_object_basename = try allocPrint(arena, "{s}_zcu.{s}", .{ fs.path.stem(emit.sub_path), obj_file_ext }),
 234            .gc_sections = gc_sections,
 235            .print_gc_sections = options.print_gc_sections,
 236            .stack_size = stack_size,
 237            .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
 238            .file = null,
 239            .build_id = options.build_id,
 240        },
 241        .ofmt = switch (target.ofmt) {
 242            .coff => .{ .coff = try .init(comp, options) },
 243            .elf => .{ .elf = try .init(comp, options) },
 244            .wasm => .{ .wasm = try .init(comp, options) },
 245            else => unreachable,
 246        },
 247    };
 248    return lld;
 249}
 250pub fn deinit(lld: *Lld) void {
 251    _ = lld;
 252}
 253pub fn flush(
 254    lld: *Lld,
 255    arena: Allocator,
 256    tid: Zcu.PerThread.Id,
 257    prog_node: std.Progress.Node,
 258) link.File.FlushError!void {
 259    dev.check(.lld_linker);
 260    _ = tid;
 261
 262    const tracy = trace(@src());
 263    defer tracy.end();
 264
 265    const sub_prog_node = prog_node.start("LLD Link", 0);
 266    defer sub_prog_node.end();
 267
 268    const comp = lld.base.comp;
 269    const result = if (comp.config.output_mode == .Lib and comp.config.link_mode == .static) r: {
 270        if (!@import("build_options").have_llvm or !comp.config.use_lib_llvm) {
 271            return lld.base.comp.link_diags.fail("using lld without libllvm not implemented", .{});
 272        }
 273        break :r linkAsArchive(lld, arena);
 274    } else switch (lld.ofmt) {
 275        .coff => coffLink(lld, arena),
 276        .elf => elfLink(lld, arena),
 277        .wasm => wasmLink(lld, arena),
 278    };
 279    result catch |err| switch (err) {
 280        error.OutOfMemory, error.LinkFailure => |e| return e,
 281        else => |e| return lld.base.comp.link_diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
 282    };
 283}
 284
 285fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
 286    const base = &lld.base;
 287    const comp = base.comp;
 288    const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
 289    const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
 290    const full_out_path_z = try arena.dupeZ(u8, full_out_path);
 291    const opt_zcu = comp.zcu;
 292
 293    const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: {
 294        break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
 295    } else null;
 296
 297    log.debug("zcu_obj_path={?f}", .{zcu_obj_path});
 298
 299    const compiler_rt_path: ?Cache.Path = if (comp.compiler_rt_strat == .obj)
 300        comp.compiler_rt_obj.?.full_object_path
 301    else
 302        null;
 303
 304    const ubsan_rt_path: ?Cache.Path = if (comp.ubsan_rt_strat == .obj)
 305        comp.ubsan_rt_obj.?.full_object_path
 306    else
 307        null;
 308
 309    // This function follows the same pattern as link.Elf.linkWithLLD so if you want some
 310    // insight as to what's going on here you can read that function body which is more
 311    // well-commented.
 312
 313    const link_inputs = comp.link_inputs;
 314
 315    var object_files: std.ArrayList([*:0]const u8) = .empty;
 316
 317    try object_files.ensureUnusedCapacity(arena, link_inputs.len);
 318    for (link_inputs) |input| {
 319        object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena));
 320    }
 321
 322    try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() +
 323        comp.win32_resource_table.count() + 2);
 324
 325    for (comp.c_object_table.keys()) |key| {
 326        object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena));
 327    }
 328    for (comp.win32_resource_table.keys()) |key| {
 329        object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path));
 330    }
 331    if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
 332    if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
 333    if (ubsan_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena));
 334
 335    if (comp.verbose_link) {
 336        std.debug.print("ar rcs {s}", .{full_out_path_z});
 337        for (object_files.items) |arg| {
 338            std.debug.print(" {s}", .{arg});
 339        }
 340        std.debug.print("\n", .{});
 341    }
 342
 343    const llvm_bindings = @import("../codegen/llvm/bindings.zig");
 344    const llvm = @import("../codegen/llvm.zig");
 345    const target = &comp.root_mod.resolved_target.result;
 346    llvm.initializeLLVMTarget(target.cpu.arch);
 347    const bad = llvm_bindings.WriteArchive(
 348        full_out_path_z,
 349        object_files.items.ptr,
 350        object_files.items.len,
 351        switch (target.os.tag) {
 352            .windows => .COFF,
 353            else => if (target.os.tag.isDarwin()) .DARWIN else .GNU,
 354        },
 355    );
 356    if (bad) return error.UnableToWriteArchive;
 357}
 358
 359fn coffLink(lld: *Lld, arena: Allocator) !void {
 360    const comp = lld.base.comp;
 361    const gpa = comp.gpa;
 362    const base = &lld.base;
 363    const coff = &lld.ofmt.coff;
 364
 365    const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
 366    const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
 367
 368    const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
 369        break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
 370    } else null;
 371
 372    const is_lib = comp.config.output_mode == .Lib;
 373    const is_dyn_lib = comp.config.link_mode == .dynamic and is_lib;
 374    const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe;
 375    const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib;
 376    const target = &comp.root_mod.resolved_target.result;
 377    const optimize_mode = comp.root_mod.optimize_mode;
 378    const entry_name: ?[]const u8 = switch (coff.entry) {
 379        // This logic isn't quite right for disabled or enabled. No point in fixing it
 380        // when the goal is to eliminate dependency on LLD anyway.
 381        // https://github.com/ziglang/zig/issues/17751
 382        .disabled, .default, .enabled => null,
 383        .named => |name| name,
 384    };
 385
 386    if (comp.config.output_mode == .Obj) {
 387        // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
 388        // here. TODO: think carefully about how we can avoid this redundant operation when doing
 389        // build-obj. See also the corresponding TODO in linkAsArchive.
 390        const the_object_path = blk: {
 391            if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
 392
 393            if (comp.c_object_table.count() != 0)
 394                break :blk comp.c_object_table.keys()[0].status.success.object_path;
 395
 396            if (zcu_obj_path) |p|
 397                break :blk p;
 398
 399            // TODO I think this is unreachable. Audit this situation when solving the above TODO
 400            // regarding eliding redundant object -> object transformations.
 401            return error.NoObjectsToLink;
 402        };
 403        try std.fs.Dir.copyFile(
 404            the_object_path.root_dir.handle,
 405            the_object_path.sub_path,
 406            directory.handle,
 407            base.emit.sub_path,
 408            .{},
 409        );
 410    } else {
 411        // Create an LLD command line and invoke it.
 412        var argv = std.array_list.Managed([]const u8).init(gpa);
 413        defer argv.deinit();
 414        // We will invoke ourselves as a child process to gain access to LLD.
 415        // This is necessary because LLD does not behave properly as a library -
 416        // it calls exit() and does not reset all global data between invocations.
 417        const linker_command = "lld-link";
 418        try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
 419
 420        if (target.isMinGW()) {
 421            try argv.append("-lldmingw");
 422        }
 423
 424        try argv.append("-ERRORLIMIT:0");
 425        try argv.append("-NOLOGO");
 426        if (comp.config.debug_format != .strip) {
 427            try argv.append("-DEBUG");
 428
 429            const out_ext = std.fs.path.extension(full_out_path);
 430            const out_pdb = coff.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{
 431                full_out_path[0 .. full_out_path.len - out_ext.len],
 432            });
 433            const out_pdb_basename = std.fs.path.basename(out_pdb);
 434
 435            try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb}));
 436            try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb_basename}));
 437        }
 438        if (comp.version) |version| {
 439            try argv.append(try allocPrint(arena, "-VERSION:{d}.{d}", .{ version.major, version.minor }));
 440        }
 441
 442        if (target_util.llvmMachineAbi(target)) |mabi| {
 443            try argv.append(try allocPrint(arena, "-MLLVM:-target-abi={s}", .{mabi}));
 444        }
 445
 446        try argv.append(try allocPrint(arena, "-MLLVM:-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}));
 447
 448        if (comp.config.lto != .none) {
 449            switch (optimize_mode) {
 450                .Debug => {},
 451                .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
 452                .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
 453            }
 454        }
 455        if (comp.config.output_mode == .Exe) {
 456            try argv.append(try allocPrint(arena, "-STACK:{d}", .{base.stack_size}));
 457        }
 458        try argv.append(try allocPrint(arena, "-BASE:{d}", .{coff.image_base}));
 459
 460        switch (base.build_id) {
 461            .none => try argv.append("-BUILD-ID:NO"),
 462            .fast => try argv.append("-BUILD-ID"),
 463            .uuid, .sha1, .md5, .hexstring => {},
 464        }
 465
 466        if (target.cpu.arch == .x86) {
 467            try argv.append("-MACHINE:X86");
 468        } else if (target.cpu.arch == .x86_64) {
 469            try argv.append("-MACHINE:X64");
 470        } else if (target.cpu.arch == .thumb) {
 471            try argv.append("-MACHINE:ARM");
 472        } else if (target.cpu.arch == .aarch64) {
 473            try argv.append("-MACHINE:ARM64");
 474        }
 475
 476        for (comp.force_undefined_symbols.keys()) |symbol| {
 477            try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol}));
 478        }
 479
 480        if (is_dyn_lib) {
 481            try argv.append("-DLL");
 482        }
 483
 484        if (entry_name) |name| {
 485            try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name}));
 486        }
 487
 488        if (coff.repro) {
 489            try argv.append("-BREPRO");
 490        }
 491
 492        if (coff.tsaware) {
 493            try argv.append("-tsaware");
 494        }
 495        if (coff.nxcompat) {
 496            try argv.append("-nxcompat");
 497        }
 498        if (!coff.dynamicbase) {
 499            try argv.append("-dynamicbase:NO");
 500        }
 501        if (base.allow_shlib_undefined) {
 502            try argv.append("-FORCE:UNRESOLVED");
 503        }
 504
 505        try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
 506
 507        if (comp.emit_implib) |raw_emit_path| {
 508            const path = try comp.resolveEmitPathFlush(arena, .artifact, raw_emit_path);
 509            try argv.append(try allocPrint(arena, "-IMPLIB:{f}", .{path}));
 510        }
 511
 512        if (comp.config.link_libc) {
 513            if (comp.libc_installation) |libc_installation| {
 514                try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
 515
 516                if (target.abi == .msvc or target.abi == .itanium) {
 517                    try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
 518                    try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
 519                }
 520            }
 521        }
 522
 523        for (coff.lib_directories) |lib_directory| {
 524            try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_directory.path orelse "."}));
 525        }
 526
 527        try argv.ensureUnusedCapacity(comp.link_inputs.len);
 528        for (comp.link_inputs) |link_input| switch (link_input) {
 529            .dso_exact => unreachable, // not applicable to PE/COFF
 530            inline .dso, .res => |x| {
 531                argv.appendAssumeCapacity(try x.path.toString(arena));
 532            },
 533            .object, .archive => |obj| {
 534                if (obj.must_link) {
 535                    argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{f}", .{@as(Cache.Path, obj.path)}));
 536                } else {
 537                    argv.appendAssumeCapacity(try obj.path.toString(arena));
 538                }
 539            },
 540        };
 541
 542        for (comp.c_object_table.keys()) |key| {
 543            try argv.append(try key.status.success.object_path.toString(arena));
 544        }
 545
 546        for (comp.win32_resource_table.keys()) |key| {
 547            try argv.append(key.status.success.res_path);
 548        }
 549
 550        if (zcu_obj_path) |p| {
 551            try argv.append(try p.toString(arena));
 552        }
 553
 554        if (coff.module_definition_file) |def| {
 555            try argv.append(try allocPrint(arena, "-DEF:{s}", .{def}));
 556        }
 557
 558        const resolved_subsystem: ?std.zig.Subsystem = blk: {
 559            if (coff.subsystem) |explicit| break :blk explicit;
 560            switch (target.os.tag) {
 561                .windows => {
 562                    if (comp.zcu != null) {
 563                        if (coff.lld_export_flags.dllmain_crt_startup or is_dyn_lib)
 564                            break :blk null;
 565                        if (coff.lld_export_flags.c_main or comp.config.is_test or
 566                            coff.lld_export_flags.winmain_crt_startup or
 567                            coff.lld_export_flags.wwinmain_crt_startup)
 568                        {
 569                            break :blk .console;
 570                        }
 571                        if (coff.lld_export_flags.winmain or coff.lld_export_flags.wwinmain)
 572                            break :blk .windows;
 573                    }
 574                },
 575                .uefi => break :blk .efi_application,
 576                else => {},
 577            }
 578            break :blk null;
 579        };
 580
 581        const Mode = enum { uefi, win32 };
 582        const mode: Mode = mode: {
 583            if (resolved_subsystem) |subsystem| {
 584                try argv.append(try allocPrint(arena, "-SUBSYSTEM:{s},{d}.{d}", .{
 585                    @tagName(subsystem),
 586                    coff.major_subsystem_version,
 587                    coff.minor_subsystem_version,
 588                }));
 589                break :mode switch (subsystem) {
 590                    .console,
 591                    .windows,
 592                    .posix,
 593                    .native,
 594                    => .win32,
 595                    .efi_application,
 596                    .efi_boot_service_driver,
 597                    .efi_rom,
 598                    .efi_runtime_driver,
 599                    => .uefi,
 600                };
 601            } else if (target.os.tag == .uefi) {
 602                break :mode .uefi;
 603            } else {
 604                break :mode .win32;
 605            }
 606        };
 607
 608        switch (mode) {
 609            .uefi => try argv.appendSlice(&[_][]const u8{
 610                "-BASE:0",
 611                "-ENTRY:EfiMain",
 612                "-OPT:REF",
 613                "-SAFESEH:NO",
 614                "-MERGE:.rdata=.data",
 615                "-NODEFAULTLIB",
 616                "-SECTION:.xdata,D",
 617            }),
 618            .win32 => {
 619                if (link_in_crt) {
 620                    if (target.abi.isGnu()) {
 621                        if (target.cpu.arch == .x86) {
 622                            try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
 623                        } else {
 624                            try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
 625                        }
 626
 627                        if (is_dyn_lib) {
 628                            try argv.append(try comp.crtFileAsString(arena, "dllcrt2.obj"));
 629                            if (target.cpu.arch == .x86) {
 630                                try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
 631                            } else {
 632                                try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
 633                            }
 634                        } else {
 635                            try argv.append(try comp.crtFileAsString(arena, "crt2.obj"));
 636                        }
 637
 638                        try argv.append(try comp.crtFileAsString(arena, "libmingw32.lib"));
 639                    } else {
 640                        try argv.append(switch (comp.config.link_mode) {
 641                            .static => "libcmt.lib",
 642                            .dynamic => "msvcrt.lib",
 643                        });
 644
 645                        const lib_str = switch (comp.config.link_mode) {
 646                            .static => "lib",
 647                            .dynamic => "",
 648                        };
 649                        try argv.append(try allocPrint(arena, "{s}vcruntime.lib", .{lib_str}));
 650                        try argv.append(try allocPrint(arena, "{s}ucrt.lib", .{lib_str}));
 651
 652                        //Visual C++ 2015 Conformance Changes
 653                        //https://msdn.microsoft.com/en-us/library/bb531344.aspx
 654                        try argv.append("legacy_stdio_definitions.lib");
 655
 656                        // msvcrt depends on kernel32 and ntdll
 657                        try argv.append("kernel32.lib");
 658                        try argv.append("ntdll.lib");
 659                    }
 660                } else {
 661                    try argv.append("-NODEFAULTLIB");
 662                    if (!is_lib and entry_name == null) {
 663                        if (comp.zcu != null) {
 664                            if (coff.lld_export_flags.winmain_crt_startup) {
 665                                try argv.append("-ENTRY:WinMainCRTStartup");
 666                            } else {
 667                                try argv.append("-ENTRY:wWinMainCRTStartup");
 668                            }
 669                        } else {
 670                            try argv.append("-ENTRY:wWinMainCRTStartup");
 671                        }
 672                    }
 673                }
 674            },
 675        }
 676
 677        if (comp.config.link_libc and link_in_crt) {
 678            if (comp.zigc_static_lib) |zigc| {
 679                try argv.append(try zigc.full_object_path.toString(arena));
 680            }
 681        }
 682
 683        // libc++ dep
 684        if (comp.config.link_libcpp) {
 685            try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
 686            try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
 687        }
 688
 689        // libunwind dep
 690        if (comp.config.link_libunwind) {
 691            try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
 692        }
 693
 694        if (comp.config.any_fuzz) {
 695            try argv.append(try comp.fuzzer_lib.?.full_object_path.toString(arena));
 696        }
 697
 698        const ubsan_rt_path: ?Cache.Path = blk: {
 699            if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
 700            if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
 701            break :blk null;
 702        };
 703        if (ubsan_rt_path) |path| {
 704            try argv.append(try path.toString(arena));
 705        }
 706
 707        if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) {
 708            // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
 709            // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
 710            if (comp.compiler_rt_obj) |obj| try argv.append(try obj.full_object_path.toString(arena));
 711            if (comp.compiler_rt_lib) |lib| try argv.append(try lib.full_object_path.toString(arena));
 712        }
 713
 714        try argv.ensureUnusedCapacity(comp.windows_libs.count());
 715        for (comp.windows_libs.keys()) |key| {
 716            const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
 717            if (comp.crt_files.get(lib_basename)) |crt_file| {
 718                argv.appendAssumeCapacity(try crt_file.full_object_path.toString(arena));
 719                continue;
 720            }
 721            if (try findLib(arena, lib_basename, coff.lib_directories)) |full_path| {
 722                argv.appendAssumeCapacity(full_path);
 723                continue;
 724            }
 725            if (target.abi.isGnu()) {
 726                const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
 727                if (try findLib(arena, fallback_name, coff.lib_directories)) |full_path| {
 728                    argv.appendAssumeCapacity(full_path);
 729                    continue;
 730                }
 731            }
 732            if (target.abi == .msvc or target.abi == .itanium) {
 733                argv.appendAssumeCapacity(lib_basename);
 734                continue;
 735            }
 736
 737            log.err("DLL import library for -l{s} not found", .{key});
 738            return error.DllImportLibraryNotFound;
 739        }
 740
 741        try spawnLld(comp, arena, argv.items);
 742    }
 743}
 744fn findLib(arena: Allocator, name: []const u8, lib_directories: []const Cache.Directory) !?[]const u8 {
 745    for (lib_directories) |lib_directory| {
 746        lib_directory.handle.access(name, .{}) catch |err| switch (err) {
 747            error.FileNotFound => continue,
 748            else => |e| return e,
 749        };
 750        return try lib_directory.join(arena, &.{name});
 751    }
 752    return null;
 753}
 754
 755fn elfLink(lld: *Lld, arena: Allocator) !void {
 756    const comp = lld.base.comp;
 757    const gpa = comp.gpa;
 758    const diags = &comp.link_diags;
 759    const base = &lld.base;
 760    const elf = &lld.ofmt.elf;
 761
 762    const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
 763    const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
 764
 765    const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
 766        break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
 767    } else null;
 768
 769    const output_mode = comp.config.output_mode;
 770    const is_obj = output_mode == .Obj;
 771    const is_lib = output_mode == .Lib;
 772    const link_mode = comp.config.link_mode;
 773    const is_dyn_lib = link_mode == .dynamic and is_lib;
 774    const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
 775    const target = &comp.root_mod.resolved_target.result;
 776    const compiler_rt_path: ?Cache.Path = blk: {
 777        if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
 778        if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
 779        break :blk null;
 780    };
 781    const ubsan_rt_path: ?Cache.Path = blk: {
 782        if (comp.ubsan_rt_lib) |x| break :blk x.full_object_path;
 783        if (comp.ubsan_rt_obj) |x| break :blk x.full_object_path;
 784        break :blk null;
 785    };
 786
 787    // Due to a deficiency in LLD, we need to special-case BPF to a simple file
 788    // copy when generating relocatables. Normally, we would expect `lld -r` to work.
 789    // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
 790    // before even generating the relocatable.
 791    //
 792    // For m68k, we go through this path because LLD doesn't support it yet, but LLVM can
 793    // produce usable object files.
 794    if (output_mode == .Obj and
 795        (comp.config.lto != .none or
 796            target.cpu.arch.isBpf() or
 797            target.cpu.arch == .lanai or
 798            target.cpu.arch == .m68k or
 799            target.cpu.arch.isSPARC() or
 800            target.cpu.arch == .ve or
 801            target.cpu.arch == .xcore))
 802    {
 803        // In this case we must do a simple file copy
 804        // here. TODO: think carefully about how we can avoid this redundant operation when doing
 805        // build-obj. See also the corresponding TODO in linkAsArchive.
 806        const the_object_path = blk: {
 807            if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
 808
 809            if (comp.c_object_table.count() != 0)
 810                break :blk comp.c_object_table.keys()[0].status.success.object_path;
 811
 812            if (zcu_obj_path) |p|
 813                break :blk p;
 814
 815            // TODO I think this is unreachable. Audit this situation when solving the above TODO
 816            // regarding eliding redundant object -> object transformations.
 817            return error.NoObjectsToLink;
 818        };
 819        try std.fs.Dir.copyFile(
 820            the_object_path.root_dir.handle,
 821            the_object_path.sub_path,
 822            directory.handle,
 823            base.emit.sub_path,
 824            .{},
 825        );
 826    } else {
 827        // Create an LLD command line and invoke it.
 828        var argv = std.array_list.Managed([]const u8).init(gpa);
 829        defer argv.deinit();
 830        // We will invoke ourselves as a child process to gain access to LLD.
 831        // This is necessary because LLD does not behave properly as a library -
 832        // it calls exit() and does not reset all global data between invocations.
 833        const linker_command = "ld.lld";
 834        try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
 835        if (is_obj) {
 836            try argv.append("-r");
 837        }
 838
 839        try argv.append("--error-limit=0");
 840
 841        if (comp.sysroot) |sysroot| {
 842            try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
 843        }
 844
 845        if (target_util.llvmMachineAbi(target)) |mabi| {
 846            try argv.appendSlice(&.{
 847                "-mllvm",
 848                try std.fmt.allocPrint(arena, "-target-abi={s}", .{mabi}),
 849            });
 850        }
 851
 852        try argv.appendSlice(&.{
 853            "-mllvm",
 854            try std.fmt.allocPrint(arena, "-float-abi={s}", .{if (target.abi.float() == .hard) "hard" else "soft"}),
 855        });
 856
 857        if (comp.config.lto != .none) {
 858            switch (comp.root_mod.optimize_mode) {
 859                .Debug => {},
 860                .ReleaseSmall => try argv.append("--lto-O2"),
 861                .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
 862            }
 863        }
 864        switch (comp.root_mod.optimize_mode) {
 865            .Debug => {},
 866            .ReleaseSmall => try argv.append("-O2"),
 867            .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
 868        }
 869
 870        if (elf.entry_name) |name| {
 871            try argv.appendSlice(&.{ "--entry", name });
 872        }
 873
 874        for (comp.force_undefined_symbols.keys()) |sym| {
 875            try argv.append("-u");
 876            try argv.append(sym);
 877        }
 878
 879        switch (elf.hash_style) {
 880            .gnu => try argv.append("--hash-style=gnu"),
 881            .sysv => try argv.append("--hash-style=sysv"),
 882            .both => {}, // this is the default
 883        }
 884
 885        if (output_mode == .Exe) {
 886            try argv.appendSlice(&.{
 887                "-z",
 888                try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}),
 889            });
 890        }
 891
 892        switch (base.build_id) {
 893            .none => try argv.append("--build-id=none"),
 894            .fast, .uuid, .sha1, .md5 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
 895                @tagName(base.build_id),
 896            })),
 897            .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
 898        }
 899
 900        try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{elf.image_base}));
 901
 902        if (elf.linker_script) |linker_script| {
 903            try argv.append("-T");
 904            try argv.append(linker_script);
 905        }
 906
 907        if (elf.sort_section) |how| {
 908            const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
 909            try argv.append(arg);
 910        }
 911
 912        if (base.gc_sections) {
 913            try argv.append("--gc-sections");
 914        }
 915
 916        if (base.print_gc_sections) {
 917            try argv.append("--print-gc-sections");
 918        }
 919
 920        if (elf.print_icf_sections) {
 921            try argv.append("--print-icf-sections");
 922        }
 923
 924        if (elf.print_map) {
 925            try argv.append("--print-map");
 926        }
 927
 928        if (comp.link_eh_frame_hdr) {
 929            try argv.append("--eh-frame-hdr");
 930        }
 931
 932        if (elf.emit_relocs) {
 933            try argv.append("--emit-relocs");
 934        }
 935
 936        if (comp.config.rdynamic) {
 937            try argv.append("--export-dynamic");
 938        }
 939
 940        if (comp.config.debug_format == .strip) {
 941            try argv.append("-s");
 942        }
 943
 944        if (elf.z_nodelete) {
 945            try argv.append("-z");
 946            try argv.append("nodelete");
 947        }
 948        if (elf.z_notext) {
 949            try argv.append("-z");
 950            try argv.append("notext");
 951        }
 952        if (elf.z_defs) {
 953            try argv.append("-z");
 954            try argv.append("defs");
 955        }
 956        if (elf.z_origin) {
 957            try argv.append("-z");
 958            try argv.append("origin");
 959        }
 960        if (elf.z_nocopyreloc) {
 961            try argv.append("-z");
 962            try argv.append("nocopyreloc");
 963        }
 964        if (elf.z_now) {
 965            // LLD defaults to -zlazy
 966            try argv.append("-znow");
 967        }
 968        if (!elf.z_relro) {
 969            // LLD defaults to -zrelro
 970            try argv.append("-znorelro");
 971        }
 972        if (elf.z_common_page_size) |size| {
 973            try argv.append("-z");
 974            try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
 975        }
 976        if (elf.z_max_page_size) |size| {
 977            try argv.append("-z");
 978            try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
 979        }
 980
 981        if (getLDMOption(target)) |ldm| {
 982            try argv.append("-m");
 983            try argv.append(ldm);
 984        }
 985
 986        if (link_mode == .static) {
 987            if (target.cpu.arch.isArm()) {
 988                try argv.append("-Bstatic");
 989            } else {
 990                try argv.append("-static");
 991            }
 992        } else if (switch (target.os.tag) {
 993            else => is_dyn_lib,
 994            .haiku => is_exe_or_dyn_lib,
 995        }) {
 996            try argv.append("-shared");
 997        }
 998
 999        if (comp.config.pie and output_mode == .Exe) {
1000            try argv.append("-pie");
1001        }
1002
1003        if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
1004            // Add options to produce shared objects with only 2 PT_LOAD segments.
1005            // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
1006            // ld.elf_so fails loading dynamic libraries with "not found" error.
1007            // See https://github.com/ziglang/zig/issues/9109 .
1008            try argv.append("--no-rosegment");
1009            try argv.append("-znorelro");
1010        }
1011
1012        try argv.append("-o");
1013        try argv.append(full_out_path);
1014
1015        // csu prelude
1016        const csu = try comp.getCrtPaths(arena);
1017        if (csu.crt0) |p| try argv.append(try p.toString(arena));
1018        if (csu.crti) |p| try argv.append(try p.toString(arena));
1019        if (csu.crtbegin) |p| try argv.append(try p.toString(arena));
1020
1021        for (elf.rpath_list) |rpath| {
1022            try argv.appendSlice(&.{ "-rpath", rpath });
1023        }
1024
1025        for (elf.symbol_wrap_set) |symbol_name| {
1026            try argv.appendSlice(&.{ "-wrap", symbol_name });
1027        }
1028
1029        if (comp.config.link_libc) {
1030            if (comp.libc_installation) |libc_installation| {
1031                try argv.append("-L");
1032                try argv.append(libc_installation.crt_dir.?);
1033            }
1034        }
1035
1036        if (output_mode == .Exe and link_mode == .dynamic) {
1037            if (target.dynamic_linker.get()) |dynamic_linker| {
1038                try argv.append("--dynamic-linker");
1039                try argv.append(dynamic_linker);
1040            } else {
1041                try argv.append("--no-dynamic-linker");
1042            }
1043        }
1044
1045        if (is_dyn_lib) {
1046            if (elf.soname) |soname| {
1047                try argv.append("-soname");
1048                try argv.append(soname);
1049            }
1050            if (elf.version_script) |version_script| {
1051                try argv.append("-version-script");
1052                try argv.append(version_script);
1053            }
1054            if (elf.allow_undefined_version) {
1055                try argv.append("--undefined-version");
1056            } else {
1057                try argv.append("--no-undefined-version");
1058            }
1059            if (elf.enable_new_dtags) |enable_new_dtags| {
1060                if (enable_new_dtags) {
1061                    try argv.append("--enable-new-dtags");
1062                } else {
1063                    try argv.append("--disable-new-dtags");
1064                }
1065            }
1066        }
1067
1068        // Positional arguments to the linker such as object files.
1069        var whole_archive = false;
1070
1071        for (base.comp.link_inputs) |link_input| switch (link_input) {
1072            .res => unreachable, // Windows-only
1073            .dso => continue,
1074            .object, .archive => |obj| {
1075                if (obj.must_link and !whole_archive) {
1076                    try argv.append("-whole-archive");
1077                    whole_archive = true;
1078                } else if (!obj.must_link and whole_archive) {
1079                    try argv.append("-no-whole-archive");
1080                    whole_archive = false;
1081                }
1082                try argv.append(try obj.path.toString(arena));
1083            },
1084            .dso_exact => |dso_exact| {
1085                assert(dso_exact.name[0] == ':');
1086                try argv.appendSlice(&.{ "-l", dso_exact.name });
1087            },
1088        };
1089
1090        if (whole_archive) {
1091            try argv.append("-no-whole-archive");
1092            whole_archive = false;
1093        }
1094
1095        for (comp.c_object_table.keys()) |key| {
1096            try argv.append(try key.status.success.object_path.toString(arena));
1097        }
1098
1099        if (zcu_obj_path) |p| {
1100            try argv.append(try p.toString(arena));
1101        }
1102
1103        if (comp.tsan_lib) |lib| {
1104            assert(comp.config.any_sanitize_thread);
1105            try argv.append(try lib.full_object_path.toString(arena));
1106        }
1107
1108        if (comp.fuzzer_lib) |lib| {
1109            assert(comp.config.any_fuzz);
1110            try argv.append(try lib.full_object_path.toString(arena));
1111        }
1112
1113        if (ubsan_rt_path) |p| {
1114            try argv.append(try p.toString(arena));
1115        }
1116
1117        // Shared libraries.
1118        if (is_exe_or_dyn_lib) {
1119            // Worst-case, we need an --as-needed argument for every lib, as well
1120            // as one before and one after.
1121            try argv.ensureUnusedCapacity(2 * base.comp.link_inputs.len + 2);
1122            argv.appendAssumeCapacity("--as-needed");
1123            var as_needed = true;
1124
1125            for (base.comp.link_inputs) |link_input| switch (link_input) {
1126                .res => unreachable, // Windows-only
1127                .object, .archive, .dso_exact => continue,
1128                .dso => |dso| {
1129                    const lib_as_needed = !dso.needed;
1130                    switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1131                        0b00, 0b11 => {},
1132                        0b01 => {
1133                            argv.appendAssumeCapacity("--no-as-needed");
1134                            as_needed = false;
1135                        },
1136                        0b10 => {
1137                            argv.appendAssumeCapacity("--as-needed");
1138                            as_needed = true;
1139                        },
1140                    }
1141
1142                    // By this time, we depend on these libs being dynamically linked
1143                    // libraries and not static libraries (the check for that needs to be earlier),
1144                    // but they could be full paths to .so files, in which case we
1145                    // want to avoid prepending "-l".
1146                    argv.appendAssumeCapacity(try dso.path.toString(arena));
1147                },
1148            };
1149
1150            if (!as_needed) {
1151                argv.appendAssumeCapacity("--as-needed");
1152                as_needed = true;
1153            }
1154
1155            // libc++ dep
1156            if (comp.config.link_libcpp) {
1157                try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1158                try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1159            }
1160
1161            // libunwind dep
1162            if (comp.config.link_libunwind) {
1163                try argv.append(try comp.libunwind_static_lib.?.full_object_path.toString(arena));
1164            }
1165
1166            // libc dep
1167            diags.flags.missing_libc = false;
1168            if (comp.config.link_libc) {
1169                if (comp.libc_installation != null) {
1170                    const needs_grouping = link_mode == .static;
1171                    if (needs_grouping) try argv.append("--start-group");
1172                    try argv.appendSlice(target_util.libcFullLinkFlags(target));
1173                    if (needs_grouping) try argv.append("--end-group");
1174                } else if (target.isGnuLibC()) {
1175                    for (glibc.libs) |lib| {
1176                        if (lib.removed_in) |rem_in| {
1177                            if (target.os.versionRange().gnuLibCVersion().?.order(rem_in) != .lt) continue;
1178                        }
1179
1180                        const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
1181                            comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1182                        });
1183                        try argv.append(lib_path);
1184                    }
1185                    try argv.append(try comp.crtFileAsString(arena, "libc_nonshared.a"));
1186                } else if (target.isMuslLibC()) {
1187                    try argv.append(try comp.crtFileAsString(arena, switch (link_mode) {
1188                        .static => "libc.a",
1189                        .dynamic => "libc.so",
1190                    }));
1191                } else if (target.isFreeBSDLibC()) {
1192                    for (freebsd.libs) |lib| {
1193                        const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
1194                            comp.freebsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1195                        });
1196                        try argv.append(lib_path);
1197                    }
1198                } else if (target.isNetBSDLibC()) {
1199                    for (netbsd.libs) |lib| {
1200                        const lib_path = try std.fmt.allocPrint(arena, "{f}{c}lib{s}.so.{d}", .{
1201                            comp.netbsd_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1202                        });
1203                        try argv.append(lib_path);
1204                    }
1205                } else {
1206                    diags.flags.missing_libc = true;
1207                }
1208
1209                if (comp.zigc_static_lib) |zigc| {
1210                    try argv.append(try zigc.full_object_path.toString(arena));
1211                }
1212            }
1213        }
1214
1215        // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
1216        // to be after the shared libraries, so they are picked up from the shared
1217        // libraries, not libcompiler_rt.
1218        if (compiler_rt_path) |p| {
1219            try argv.append(try p.toString(arena));
1220        }
1221
1222        // crt postlude
1223        if (csu.crtend) |p| try argv.append(try p.toString(arena));
1224        if (csu.crtn) |p| try argv.append(try p.toString(arena));
1225
1226        if (base.allow_shlib_undefined) {
1227            try argv.append("--allow-shlib-undefined");
1228        }
1229
1230        switch (elf.compress_debug_sections) {
1231            .none => {},
1232            .zlib => try argv.append("--compress-debug-sections=zlib"),
1233            .zstd => try argv.append("--compress-debug-sections=zstd"),
1234        }
1235
1236        if (elf.bind_global_refs_locally) {
1237            try argv.append("-Bsymbolic");
1238        }
1239
1240        try spawnLld(comp, arena, argv.items);
1241    }
1242}
1243fn getLDMOption(target: *const std.Target) ?[]const u8 {
1244    // This should only return emulations understood by LLD's parseEmulation().
1245    return switch (target.cpu.arch) {
1246        .aarch64 => switch (target.os.tag) {
1247            .linux => "aarch64linux",
1248            else => "aarch64elf",
1249        },
1250        .aarch64_be => switch (target.os.tag) {
1251            .linux => "aarch64linuxb",
1252            else => "aarch64elfb",
1253        },
1254        .amdgcn => "elf64_amdgpu",
1255        .arm, .thumb => switch (target.os.tag) {
1256            .linux => "armelf_linux_eabi",
1257            else => "armelf",
1258        },
1259        .armeb, .thumbeb => switch (target.os.tag) {
1260            .linux => "armelfb_linux_eabi",
1261            else => "armelfb",
1262        },
1263        .hexagon => "hexagonelf",
1264        .loongarch32 => "elf32loongarch",
1265        .loongarch64 => "elf64loongarch",
1266        .mips => switch (target.os.tag) {
1267            .freebsd => "elf32btsmip_fbsd",
1268            else => "elf32btsmip",
1269        },
1270        .mipsel => switch (target.os.tag) {
1271            .freebsd => "elf32ltsmip_fbsd",
1272            else => "elf32ltsmip",
1273        },
1274        .mips64 => switch (target.os.tag) {
1275            .freebsd => switch (target.abi) {
1276                .gnuabin32, .muslabin32 => "elf32btsmipn32_fbsd",
1277                else => "elf64btsmip_fbsd",
1278            },
1279            else => switch (target.abi) {
1280                .gnuabin32, .muslabin32 => "elf32btsmipn32",
1281                else => "elf64btsmip",
1282            },
1283        },
1284        .mips64el => switch (target.os.tag) {
1285            .freebsd => switch (target.abi) {
1286                .gnuabin32, .muslabin32 => "elf32ltsmipn32_fbsd",
1287                else => "elf64ltsmip_fbsd",
1288            },
1289            else => switch (target.abi) {
1290                .gnuabin32, .muslabin32 => "elf32ltsmipn32",
1291                else => "elf64ltsmip",
1292            },
1293        },
1294        .msp430 => "msp430elf",
1295        .powerpc => switch (target.os.tag) {
1296            .freebsd => "elf32ppc_fbsd",
1297            .linux => "elf32ppclinux",
1298            else => "elf32ppc",
1299        },
1300        .powerpcle => switch (target.os.tag) {
1301            .linux => "elf32lppclinux",
1302            else => "elf32lppc",
1303        },
1304        .powerpc64 => "elf64ppc",
1305        .powerpc64le => "elf64lppc",
1306        .riscv32 => "elf32lriscv",
1307        .riscv32be => "elf32briscv",
1308        .riscv64 => "elf64lriscv",
1309        .riscv64be => "elf64briscv",
1310        .s390x => "elf64_s390",
1311        .sparc64 => "elf64_sparc",
1312        .x86 => switch (target.os.tag) {
1313            .freebsd => "elf_i386_fbsd",
1314            else => "elf_i386",
1315        },
1316        .x86_64 => switch (target.abi) {
1317            .gnux32, .muslx32 => "elf32_x86_64",
1318            else => "elf_x86_64",
1319        },
1320        else => null,
1321    };
1322}
1323fn wasmLink(lld: *Lld, arena: Allocator) !void {
1324    const comp = lld.base.comp;
1325    const shared_memory = comp.config.shared_memory;
1326    const export_memory = comp.config.export_memory;
1327    const import_memory = comp.config.import_memory;
1328    const target = &comp.root_mod.resolved_target.result;
1329    const base = &lld.base;
1330    const wasm = &lld.ofmt.wasm;
1331
1332    const gpa = comp.gpa;
1333
1334    const directory = base.emit.root_dir; // Just an alias to make it shorter to type.
1335    const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path});
1336
1337    const zcu_obj_path: ?Cache.Path = if (comp.zcu != null) p: {
1338        break :p try comp.resolveEmitPathFlush(arena, .temp, base.zcu_object_basename.?);
1339    } else null;
1340
1341    const is_obj = comp.config.output_mode == .Obj;
1342    const compiler_rt_path: ?Cache.Path = blk: {
1343        if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path;
1344        if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path;
1345        break :blk null;
1346    };
1347    const ubsan_rt_path: ?Cache.Path = blk: {
1348        if (comp.ubsan_rt_lib) |lib| break :blk lib.full_object_path;
1349        if (comp.ubsan_rt_obj) |obj| break :blk obj.full_object_path;
1350        break :blk null;
1351    };
1352
1353    if (is_obj) {
1354        // LLD's WASM driver does not support the equivalent of `-r` so we do a simple file copy
1355        // here. TODO: think carefully about how we can avoid this redundant operation when doing
1356        // build-obj. See also the corresponding TODO in linkAsArchive.
1357        const the_object_path = blk: {
1358            if (link.firstObjectInput(comp.link_inputs)) |obj| break :blk obj.path;
1359
1360            if (comp.c_object_table.count() != 0)
1361                break :blk comp.c_object_table.keys()[0].status.success.object_path;
1362
1363            if (zcu_obj_path) |p|
1364                break :blk p;
1365
1366            // TODO I think this is unreachable. Audit this situation when solving the above TODO
1367            // regarding eliding redundant object -> object transformations.
1368            return error.NoObjectsToLink;
1369        };
1370        try fs.Dir.copyFile(
1371            the_object_path.root_dir.handle,
1372            the_object_path.sub_path,
1373            directory.handle,
1374            base.emit.sub_path,
1375            .{},
1376        );
1377    } else {
1378        // Create an LLD command line and invoke it.
1379        var argv = std.array_list.Managed([]const u8).init(gpa);
1380        defer argv.deinit();
1381        // We will invoke ourselves as a child process to gain access to LLD.
1382        // This is necessary because LLD does not behave properly as a library -
1383        // it calls exit() and does not reset all global data between invocations.
1384        const linker_command = "wasm-ld";
1385        try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1386        try argv.append("--error-limit=0");
1387
1388        if (comp.config.lto != .none) {
1389            switch (comp.root_mod.optimize_mode) {
1390                .Debug => {},
1391                .ReleaseSmall => try argv.append("-O2"),
1392                .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
1393            }
1394        }
1395
1396        if (import_memory) {
1397            try argv.append("--import-memory");
1398        }
1399
1400        if (export_memory) {
1401            try argv.append("--export-memory");
1402        }
1403
1404        if (wasm.import_table) {
1405            assert(!wasm.export_table);
1406            try argv.append("--import-table");
1407        }
1408
1409        if (wasm.export_table) {
1410            assert(!wasm.import_table);
1411            try argv.append("--export-table");
1412        }
1413
1414        // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly
1415        // specified it as garbage collection is enabled by default.
1416        if (!base.gc_sections) {
1417            try argv.append("--no-gc-sections");
1418        }
1419
1420        if (comp.config.debug_format == .strip) {
1421            try argv.append("-s");
1422        }
1423
1424        if (wasm.initial_memory) |initial_memory| {
1425            const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory});
1426            try argv.append(arg);
1427        }
1428
1429        if (wasm.max_memory) |max_memory| {
1430            const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory});
1431            try argv.append(arg);
1432        }
1433
1434        if (shared_memory) {
1435            try argv.append("--shared-memory");
1436        }
1437
1438        if (wasm.global_base) |global_base| {
1439            const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base});
1440            try argv.append(arg);
1441        } else {
1442            // We prepend it by default, so when a stack overflow happens the runtime will trap correctly,
1443            // rather than silently overwrite all global declarations. See https://github.com/ziglang/zig/issues/4496
1444            //
1445            // The user can overwrite this behavior by setting the global-base
1446            try argv.append("--stack-first");
1447        }
1448
1449        // Users are allowed to specify which symbols they want to export to the wasm host.
1450        for (wasm.export_symbol_names) |symbol_name| {
1451            const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
1452            try argv.append(arg);
1453        }
1454
1455        if (comp.config.rdynamic) {
1456            try argv.append("--export-dynamic");
1457        }
1458
1459        if (wasm.entry_name) |entry_name| {
1460            try argv.appendSlice(&.{ "--entry", entry_name });
1461        } else {
1462            try argv.append("--no-entry");
1463        }
1464
1465        try argv.appendSlice(&.{
1466            "-z",
1467            try std.fmt.allocPrint(arena, "stack-size={d}", .{base.stack_size}),
1468        });
1469
1470        switch (base.build_id) {
1471            .none => try argv.append("--build-id=none"),
1472            .fast, .uuid, .sha1 => try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
1473                @tagName(base.build_id),
1474            })),
1475            .hexstring => |hs| try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{x}", .{hs.toSlice()})),
1476            .md5 => {},
1477        }
1478
1479        if (wasm.import_symbols) {
1480            try argv.append("--allow-undefined");
1481        }
1482
1483        if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) {
1484            try argv.append("--shared");
1485        }
1486        if (comp.config.pie) {
1487            try argv.append("--pie");
1488        }
1489
1490        try argv.appendSlice(&.{ "-o", full_out_path });
1491
1492        if (target.cpu.arch == .wasm64) {
1493            try argv.append("-mwasm64");
1494        }
1495
1496        const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or
1497            (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic);
1498
1499        if (comp.config.link_libc and is_exe_or_dyn_lib) {
1500            if (target.os.tag == .wasi) {
1501                try argv.append(try comp.crtFileAsString(
1502                    arena,
1503                    wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model),
1504                ));
1505                try argv.append(try comp.crtFileAsString(arena, "libc.a"));
1506            }
1507
1508            if (comp.zigc_static_lib) |zigc| {
1509                try argv.append(try zigc.full_object_path.toString(arena));
1510            }
1511
1512            if (comp.config.link_libcpp) {
1513                try argv.append(try comp.libcxx_static_lib.?.full_object_path.toString(arena));
1514                try argv.append(try comp.libcxxabi_static_lib.?.full_object_path.toString(arena));
1515            }
1516        }
1517
1518        // Positional arguments to the linker such as object files.
1519        var whole_archive = false;
1520        for (comp.link_inputs) |link_input| switch (link_input) {
1521            .object, .archive => |obj| {
1522                if (obj.must_link and !whole_archive) {
1523                    try argv.append("-whole-archive");
1524                    whole_archive = true;
1525                } else if (!obj.must_link and whole_archive) {
1526                    try argv.append("-no-whole-archive");
1527                    whole_archive = false;
1528                }
1529                try argv.append(try obj.path.toString(arena));
1530            },
1531            .dso => |dso| {
1532                try argv.append(try dso.path.toString(arena));
1533            },
1534            .dso_exact => unreachable,
1535            .res => unreachable,
1536        };
1537        if (whole_archive) {
1538            try argv.append("-no-whole-archive");
1539            whole_archive = false;
1540        }
1541
1542        for (comp.c_object_table.keys()) |key| {
1543            try argv.append(try key.status.success.object_path.toString(arena));
1544        }
1545        if (zcu_obj_path) |p| {
1546            try argv.append(try p.toString(arena));
1547        }
1548
1549        if (compiler_rt_path) |p| {
1550            try argv.append(try p.toString(arena));
1551        }
1552
1553        if (ubsan_rt_path) |p| {
1554            try argv.append(try p.toStringZ(arena));
1555        }
1556
1557        try spawnLld(comp, arena, argv.items);
1558
1559        // Give +x to the .wasm file if it is an executable and the OS is WASI.
1560        // Some systems may be configured to execute such binaries directly. Even if that
1561        // is not the case, it means we will get "exec format error" when trying to run
1562        // it, and then can react to that in the same way as trying to run an ELF file
1563        // from a foreign CPU architecture.
1564        if (fs.has_executable_bit and target.os.tag == .wasi and
1565            comp.config.output_mode == .Exe)
1566        {
1567            // TODO: what's our strategy for reporting linker errors from this function?
1568            // report a nice error here with the file path if it fails instead of
1569            // just returning the error code.
1570            // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
1571            std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) {
1572                error.OperationNotSupported => unreachable, // Not a symlink.
1573                else => |e| return e,
1574            };
1575        }
1576    }
1577}
1578
1579fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !void {
1580    const io = comp.io;
1581
1582    if (comp.verbose_link) {
1583        // Skip over our own name so that the LLD linker name is the first argv item.
1584        Compilation.dump_argv(argv[1..]);
1585    }
1586
1587    // If possible, we run LLD as a child process because it does not always
1588    // behave properly as a library, unfortunately.
1589    // https://github.com/ziglang/zig/issues/3825
1590    if (!std.process.can_spawn) {
1591        const exit_code = try lldMain(arena, argv, false);
1592        if (exit_code == 0) return;
1593        if (comp.clang_passthrough_mode) std.process.exit(exit_code);
1594        return error.LinkFailure;
1595    }
1596
1597    var stderr: []u8 = &.{};
1598    defer comp.gpa.free(stderr);
1599
1600    var child = std.process.Child.init(argv, arena);
1601    const term = (if (comp.clang_passthrough_mode) term: {
1602        child.stdin_behavior = .Inherit;
1603        child.stdout_behavior = .Inherit;
1604        child.stderr_behavior = .Inherit;
1605
1606        break :term child.spawnAndWait();
1607    } else term: {
1608        child.stdin_behavior = .Ignore;
1609        child.stdout_behavior = .Ignore;
1610        child.stderr_behavior = .Pipe;
1611
1612        child.spawn() catch |err| break :term err;
1613        var stderr_reader = child.stderr.?.readerStreaming(io, &.{});
1614        stderr = try stderr_reader.interface.allocRemaining(comp.gpa, .unlimited);
1615        break :term child.wait();
1616    }) catch |first_err| term: {
1617        const err = switch (first_err) {
1618            error.NameTooLong => err: {
1619                const s = fs.path.sep_str;
1620                const rand_int = std.crypto.random.int(u64);
1621                const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
1622
1623                const rsp_file = try comp.dirs.local_cache.handle.createFile(rsp_path, .{});
1624                defer comp.dirs.local_cache.handle.deleteFileZ(rsp_path) catch |err|
1625                    log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) });
1626                {
1627                    defer rsp_file.close();
1628                    var rsp_file_buffer: [1024]u8 = undefined;
1629                    var rsp_file_writer = rsp_file.writer(&rsp_file_buffer);
1630                    const rsp_writer = &rsp_file_writer.interface;
1631                    for (argv[2..]) |arg| {
1632                        try rsp_writer.writeByte('"');
1633                        for (arg) |c| {
1634                            switch (c) {
1635                                '\"', '\\' => try rsp_writer.writeByte('\\'),
1636                                else => {},
1637                            }
1638                            try rsp_writer.writeByte(c);
1639                        }
1640                        try rsp_writer.writeByte('"');
1641                        try rsp_writer.writeByte('\n');
1642                    }
1643                    try rsp_writer.flush();
1644                }
1645
1646                var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint(
1647                    arena,
1648                    "@{s}",
1649                    .{try comp.dirs.local_cache.join(arena, &.{rsp_path})},
1650                ) }, arena);
1651                if (comp.clang_passthrough_mode) {
1652                    rsp_child.stdin_behavior = .Inherit;
1653                    rsp_child.stdout_behavior = .Inherit;
1654                    rsp_child.stderr_behavior = .Inherit;
1655
1656                    break :term rsp_child.spawnAndWait() catch |err| break :err err;
1657                } else {
1658                    rsp_child.stdin_behavior = .Ignore;
1659                    rsp_child.stdout_behavior = .Ignore;
1660                    rsp_child.stderr_behavior = .Pipe;
1661
1662                    rsp_child.spawn() catch |err| break :err err;
1663                    var stderr_reader = rsp_child.stderr.?.readerStreaming(io, &.{});
1664                    stderr = try stderr_reader.interface.allocRemaining(comp.gpa, .unlimited);
1665                    break :term rsp_child.wait() catch |err| break :err err;
1666                }
1667            },
1668            else => first_err,
1669        };
1670        log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) });
1671        return error.UnableToSpawnSelf;
1672    };
1673
1674    const diags = &comp.link_diags;
1675    switch (term) {
1676        .Exited => |code| if (code != 0) {
1677            if (comp.clang_passthrough_mode) std.process.exit(code);
1678            diags.lockAndParseLldStderr(argv[1], stderr);
1679            return error.LinkFailure;
1680        },
1681        else => {
1682            if (comp.clang_passthrough_mode) std.process.abort();
1683            return diags.fail("{s} terminated with stderr:\n{s}", .{ argv[0], stderr });
1684        },
1685    }
1686
1687    if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1688}
1689
1690const std = @import("std");
1691const Allocator = std.mem.Allocator;
1692const Cache = std.Build.Cache;
1693const allocPrint = std.fmt.allocPrint;
1694const assert = std.debug.assert;
1695const fs = std.fs;
1696const log = std.log.scoped(.link);
1697const mem = std.mem;
1698
1699const Compilation = @import("../Compilation.zig");
1700const Zcu = @import("../Zcu.zig");
1701const dev = @import("../dev.zig");
1702const freebsd = @import("../libs/freebsd.zig");
1703const glibc = @import("../libs/glibc.zig");
1704const netbsd = @import("../libs/netbsd.zig");
1705const wasi_libc = @import("../libs/wasi_libc.zig");
1706const link = @import("../link.zig");
1707const lldMain = @import("../main.zig").lldMain;
1708const target_util = @import("../target.zig");
1709const trace = @import("../tracy.zig").trace;
1710const Lld = @This();