master
   1const std = @import("std");
   2const Allocator = std.mem.Allocator;
   3const mem = std.mem;
   4const log = std.log;
   5const fs = std.fs;
   6const path = fs.path;
   7const assert = std.debug.assert;
   8const Version = std.SemanticVersion;
   9const Path = std.Build.Cache.Path;
  10
  11const Compilation = @import("../Compilation.zig");
  12const build_options = @import("build_options");
  13const trace = @import("../tracy.zig").trace;
  14const Cache = std.Build.Cache;
  15const Module = @import("../Package/Module.zig");
  16const link = @import("../link.zig");
  17
  18pub const Lib = struct {
  19    name: []const u8,
  20    sover: u8,
  21    removed_in: ?Version = null,
  22};
  23
  24pub const ABI = struct {
  25    all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
  26    all_targets: []const std.zig.target.ArchOsAbi,
  27    /// The bytes from the file verbatim, starting from the u16 number
  28    /// of function inclusions.
  29    inclusions: []const u8,
  30    arena_state: std.heap.ArenaAllocator.State,
  31
  32    pub fn destroy(abi: *ABI, gpa: Allocator) void {
  33        abi.arena_state.promote(gpa).deinit();
  34    }
  35};
  36
  37// The order of the elements in this array defines the linking order.
  38pub const libs = [_]Lib{
  39    .{ .name = "m", .sover = 6 },
  40    .{ .name = "c", .sover = 6 },
  41    .{ .name = "ld", .sover = 2 },
  42    .{ .name = "resolv", .sover = 2 },
  43    .{ .name = "pthread", .sover = 0, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
  44    .{ .name = "dl", .sover = 2, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
  45    .{ .name = "rt", .sover = 1, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
  46    .{ .name = "util", .sover = 1, .removed_in = .{ .major = 2, .minor = 34, .patch = 0 } },
  47};
  48
  49pub const LoadMetaDataError = error{
  50    /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
  51    ZigInstallationCorrupt,
  52    OutOfMemory,
  53};
  54
  55pub const abilists_path = "libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++ "abilists";
  56pub const abilists_max_size = 800 * 1024; // Bigger than this and something is definitely borked.
  57
  58/// This function will emit a log error when there is a problem with the zig
  59/// installation and then return `error.ZigInstallationCorrupt`.
  60pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
  61    const tracy = trace(@src());
  62    defer tracy.end();
  63
  64    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
  65    errdefer arena_allocator.deinit();
  66    const arena = arena_allocator.allocator();
  67
  68    var index: usize = 0;
  69
  70    {
  71        const libs_len = contents[index];
  72        index += 1;
  73
  74        var i: u8 = 0;
  75        while (i < libs_len) : (i += 1) {
  76            const lib_name = mem.sliceTo(contents[index..], 0);
  77            index += lib_name.len + 1;
  78
  79            if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
  80                log.err("libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++
  81                    "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
  82                return error.ZigInstallationCorrupt;
  83            }
  84        }
  85    }
  86
  87    const versions = b: {
  88        const versions_len = contents[index];
  89        index += 1;
  90
  91        const versions = try arena.alloc(Version, versions_len);
  92        var i: u8 = 0;
  93        while (i < versions.len) : (i += 1) {
  94            versions[i] = .{
  95                .major = contents[index + 0],
  96                .minor = contents[index + 1],
  97                .patch = contents[index + 2],
  98            };
  99            index += 3;
 100        }
 101        break :b versions;
 102    };
 103
 104    const targets = b: {
 105        const targets_len = contents[index];
 106        index += 1;
 107
 108        const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
 109        var i: u8 = 0;
 110        while (i < targets.len) : (i += 1) {
 111            const target_name = mem.sliceTo(contents[index..], 0);
 112            index += target_name.len + 1;
 113
 114            var component_it = mem.tokenizeScalar(u8, target_name, '-');
 115            const arch_name = component_it.next() orelse {
 116                log.err("abilists: expected arch name", .{});
 117                return error.ZigInstallationCorrupt;
 118            };
 119            const os_name = component_it.next() orelse {
 120                log.err("abilists: expected OS name", .{});
 121                return error.ZigInstallationCorrupt;
 122            };
 123            const abi_name = component_it.next() orelse {
 124                log.err("abilists: expected ABI name", .{});
 125                return error.ZigInstallationCorrupt;
 126            };
 127            const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
 128                log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
 129                return error.ZigInstallationCorrupt;
 130            };
 131            if (!mem.eql(u8, os_name, "linux")) {
 132                log.err("abilists: expected OS 'linux', found '{s}'", .{os_name});
 133                return error.ZigInstallationCorrupt;
 134            }
 135            const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
 136                log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
 137                return error.ZigInstallationCorrupt;
 138            };
 139
 140            targets[i] = .{
 141                .arch = arch_tag,
 142                .os = .linux,
 143                .abi = abi_tag,
 144            };
 145        }
 146        break :b targets;
 147    };
 148
 149    const abi = try arena.create(ABI);
 150    abi.* = .{
 151        .all_versions = versions,
 152        .all_targets = targets,
 153        .inclusions = contents[index..],
 154        .arena_state = arena_allocator.state,
 155    };
 156    return abi;
 157}
 158
 159pub const CrtFile = enum {
 160    scrt1_o,
 161    libc_nonshared_a,
 162};
 163
 164/// TODO replace anyerror with explicit error set, recording user-friendly errors with
 165/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
 166pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
 167    if (!build_options.have_llvm) {
 168        return error.ZigCompilerNotBuiltWithLLVMExtensions;
 169    }
 170    const gpa = comp.gpa;
 171    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
 172    defer arena_allocator.deinit();
 173    const arena = arena_allocator.allocator();
 174
 175    const target = &comp.root_mod.resolved_target.result;
 176    const target_ver = target.os.versionRange().gnuLibCVersion().?;
 177    const nonshared_stat = target_ver.order(.{ .major = 2, .minor = 32, .patch = 0 }) != .gt;
 178    const start_old_init_fini = target_ver.order(.{ .major = 2, .minor = 33, .patch = 0 }) != .gt;
 179
 180    // In all cases in this function, we add the C compiler flags to
 181    // cache_exempt_flags rather than extra_flags, because these arguments
 182    // depend on only properties that are already covered by the cache
 183    // manifest. Including these arguments in the cache could only possibly
 184    // waste computation and create false negatives.
 185
 186    switch (crt_file) {
 187        .scrt1_o => {
 188            const start_o: Compilation.CSourceFile = blk: {
 189                var args = std.array_list.Managed([]const u8).init(arena);
 190                try add_include_dirs(comp, arena, &args);
 191                try args.appendSlice(&[_][]const u8{
 192                    "-D_LIBC_REENTRANT",
 193                    "-include",
 194                    try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
 195                    "-DMODULE_NAME=libc",
 196                    "-Wno-nonportable-include-path",
 197                    "-include",
 198                    try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
 199                    "-DPIC",
 200                    "-DSHARED",
 201                    "-DTOP_NAMESPACE=glibc",
 202                    "-DASSEMBLER",
 203                    "-Wa,--noexecstack",
 204                });
 205                const src_path = if (start_old_init_fini) "start-2.33.S" else "start.S";
 206                break :blk .{
 207                    .src_path = try start_asm_path(comp, arena, src_path),
 208                    .cache_exempt_flags = args.items,
 209                    .owner = undefined,
 210                };
 211            };
 212            const abi_note_o: Compilation.CSourceFile = blk: {
 213                var args = std.array_list.Managed([]const u8).init(arena);
 214                try args.appendSlice(&[_][]const u8{
 215                    "-I",
 216                    try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
 217                });
 218                try add_include_dirs(comp, arena, &args);
 219                try args.appendSlice(&[_][]const u8{
 220                    "-D_LIBC_REENTRANT",
 221                    "-DMODULE_NAME=libc",
 222                    "-DTOP_NAMESPACE=glibc",
 223                    "-DASSEMBLER",
 224                    "-Wa,--noexecstack",
 225                });
 226                break :blk .{
 227                    .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
 228                    .cache_exempt_flags = args.items,
 229                    .owner = undefined,
 230                };
 231            };
 232            const init_o: Compilation.CSourceFile = .{
 233                .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "init.c"),
 234                .owner = undefined,
 235            };
 236            var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o };
 237            const basename = if (comp.config.output_mode == .Exe and !comp.config.pie) "crt1" else "Scrt1";
 238            return comp.build_crt_file(basename, .Obj, .@"glibc Scrt1.o", prog_node, &files, .{});
 239        },
 240        .libc_nonshared_a => {
 241            const s = path.sep_str;
 242            const Dep = struct {
 243                path: []const u8,
 244                include: bool = true,
 245            };
 246            const deps = [_]Dep{
 247                .{ .path = lib_libc_glibc ++ "stdlib" ++ s ++ "atexit.c" },
 248                .{ .path = lib_libc_glibc ++ "stdlib" ++ s ++ "at_quick_exit.c" },
 249                .{ .path = lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread" ++ s ++ "pthread_atfork.c" },
 250                .{ .path = lib_libc_glibc ++ "debug" ++ s ++ "stack_chk_fail_local.c" },
 251
 252                // libc_nonshared.a redirected stat functions to xstat until glibc 2.33,
 253                // when they were finally versioned like other symbols.
 254                .{
 255                    .path = lib_libc_glibc ++ "io" ++ s ++ "stat-2.32.c",
 256                    .include = nonshared_stat,
 257                },
 258                .{
 259                    .path = lib_libc_glibc ++ "io" ++ s ++ "fstat-2.32.c",
 260                    .include = nonshared_stat,
 261                },
 262                .{
 263                    .path = lib_libc_glibc ++ "io" ++ s ++ "lstat-2.32.c",
 264                    .include = nonshared_stat,
 265                },
 266                .{
 267                    .path = lib_libc_glibc ++ "io" ++ s ++ "stat64-2.32.c",
 268                    .include = nonshared_stat,
 269                },
 270                .{
 271                    .path = lib_libc_glibc ++ "io" ++ s ++ "fstat64-2.32.c",
 272                    .include = nonshared_stat,
 273                },
 274                .{
 275                    .path = lib_libc_glibc ++ "io" ++ s ++ "lstat64-2.32.c",
 276                    .include = nonshared_stat,
 277                },
 278                .{
 279                    .path = lib_libc_glibc ++ "io" ++ s ++ "fstatat-2.32.c",
 280                    .include = nonshared_stat,
 281                },
 282                .{
 283                    .path = lib_libc_glibc ++ "io" ++ s ++ "fstatat64-2.32.c",
 284                    .include = nonshared_stat,
 285                },
 286                .{
 287                    .path = lib_libc_glibc ++ "io" ++ s ++ "mknodat-2.32.c",
 288                    .include = nonshared_stat,
 289                },
 290                .{
 291                    .path = lib_libc_glibc ++ "io" ++ s ++ "mknod-2.32.c",
 292                    .include = nonshared_stat,
 293                },
 294
 295                // __libc_start_main used to require statically linked init/fini callbacks
 296                // until glibc 2.34 when they were assimilated into the shared library.
 297                .{
 298                    .path = lib_libc_glibc ++ "csu" ++ s ++ "elf-init-2.33.c",
 299                    .include = start_old_init_fini,
 300                },
 301            };
 302
 303            var files_buf: [deps.len]Compilation.CSourceFile = undefined;
 304            var files_index: usize = 0;
 305
 306            for (deps) |dep| {
 307                if (!dep.include) continue;
 308
 309                var args = std.array_list.Managed([]const u8).init(arena);
 310                try args.appendSlice(&[_][]const u8{
 311                    "-std=gnu11",
 312                    "-fgnu89-inline",
 313                    "-fmerge-all-constants",
 314                    "-frounding-math",
 315                    "-Wno-unsupported-floating-point-opt", // For targets that don't support -frounding-math.
 316                    "-fno-common",
 317                    "-fmath-errno",
 318                    "-ftls-model=initial-exec",
 319                    "-Wno-ignored-attributes",
 320                    "-Qunused-arguments",
 321                });
 322                try add_include_dirs(comp, arena, &args);
 323
 324                try args.append("-DNO_INITFINI");
 325
 326                if (target.cpu.arch == .x86) {
 327                    // This prevents i386/sysdep.h from trying to do some
 328                    // silly and unnecessary inline asm hack that uses weird
 329                    // syntax that clang does not support.
 330                    try args.append("-DCAN_USE_REGISTER_ASM_EBP");
 331                }
 332
 333                try args.appendSlice(&[_][]const u8{
 334                    "-D_LIBC_REENTRANT",
 335                    "-include",
 336                    try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
 337                    "-DMODULE_NAME=libc",
 338                    "-Wno-nonportable-include-path",
 339                    "-include",
 340                    try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
 341                    "-DPIC",
 342                    "-DLIBC_NONSHARED=1",
 343                    "-DTOP_NAMESPACE=glibc",
 344                });
 345                files_buf[files_index] = .{
 346                    .src_path = try lib_path(comp, arena, dep.path),
 347                    .cache_exempt_flags = args.items,
 348                    .owner = undefined,
 349                };
 350                files_index += 1;
 351            }
 352            const files = files_buf[0..files_index];
 353            return comp.build_crt_file("c_nonshared", .Lib, .@"glibc libc_nonshared.a", prog_node, files, .{});
 354        },
 355    }
 356}
 357
 358fn start_asm_path(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
 359    const arch = comp.getTarget().cpu.arch;
 360    const is_ppc = arch.isPowerPC();
 361    const is_aarch64 = arch.isAARCH64();
 362    const is_sparc = arch.isSPARC();
 363    const is_64 = comp.getTarget().ptrBitWidth() == 64;
 364
 365    const s = path.sep_str;
 366
 367    var result = std.array_list.Managed(u8).init(arena);
 368    try result.appendSlice(comp.dirs.zig_lib.path orelse ".");
 369    try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
 370    if (is_sparc) {
 371        if (is_64) {
 372            try result.appendSlice("sparc" ++ s ++ "sparc64");
 373        } else {
 374            try result.appendSlice("sparc" ++ s ++ "sparc32");
 375        }
 376    } else if (arch.isArm()) {
 377        try result.appendSlice("arm");
 378    } else if (arch.isMIPS()) {
 379        try result.appendSlice("mips");
 380    } else if (arch == .x86_64) {
 381        try result.appendSlice("x86_64");
 382    } else if (arch == .x86) {
 383        try result.appendSlice("i386");
 384    } else if (is_aarch64) {
 385        try result.appendSlice("aarch64");
 386    } else if (arch.isRISCV()) {
 387        try result.appendSlice("riscv");
 388    } else if (is_ppc) {
 389        if (is_64) {
 390            try result.appendSlice("powerpc" ++ s ++ "powerpc64");
 391        } else {
 392            try result.appendSlice("powerpc" ++ s ++ "powerpc32");
 393        }
 394    } else if (arch == .s390x) {
 395        try result.appendSlice("s390" ++ s ++ "s390-64");
 396    } else if (arch.isLoongArch()) {
 397        try result.appendSlice("loongarch");
 398    } else if (arch == .m68k) {
 399        try result.appendSlice("m68k");
 400    } else if (arch == .arc) {
 401        try result.appendSlice("arc");
 402    } else if (arch == .csky) {
 403        try result.appendSlice("csky" ++ s ++ "abiv2");
 404    }
 405
 406    try result.appendSlice(s);
 407    try result.appendSlice(basename);
 408    return result.items;
 409}
 410
 411fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.array_list.Managed([]const u8)) error{OutOfMemory}!void {
 412    const target = comp.getTarget();
 413    const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
 414
 415    const s = path.sep_str;
 416
 417    try args.append("-I");
 418    try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
 419
 420    if (target.os.tag == .linux) {
 421        try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
 422    }
 423
 424    if (opt_nptl) |nptl| {
 425        try add_include_dirs_arch(arena, args, target, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
 426    }
 427
 428    if (target.os.tag == .linux) {
 429        try args.append("-I");
 430        try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
 431            "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
 432
 433        try args.append("-I");
 434        try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
 435            "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
 436        try args.append("-I");
 437        try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
 438            "unix" ++ s ++ "sysv" ++ s ++ "linux"));
 439    }
 440    if (opt_nptl) |nptl| {
 441        try args.append("-I");
 442        try args.append(try path.join(arena, &.{ comp.dirs.zig_lib.path orelse ".", lib_libc_glibc ++ "sysdeps", nptl }));
 443    }
 444
 445    try args.append("-I");
 446    try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
 447
 448    try args.append("-I");
 449    try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
 450
 451    try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
 452
 453    try args.append("-I");
 454    try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
 455
 456    try add_include_dirs_arch(arena, args, target, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
 457
 458    try args.append("-I");
 459    try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
 460
 461    try args.append("-I");
 462    try args.append(try path.join(arena, &[_][]const u8{ comp.dirs.zig_lib.path orelse ".", lib_libc ++ "glibc" }));
 463
 464    try args.append("-I");
 465    try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{
 466        comp.dirs.zig_lib.path orelse ".",
 467        std.zig.target.glibcArchNameHeaders(target.cpu.arch),
 468        @tagName(target.os.tag),
 469        std.zig.target.glibcAbiNameHeaders(target.abi),
 470    }));
 471
 472    try args.append("-I");
 473    try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
 474
 475    const arch_name = std.zig.target.osArchName(target);
 476    try args.append("-I");
 477    try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{
 478        comp.dirs.zig_lib.path orelse ".", arch_name,
 479    }));
 480
 481    try args.append("-I");
 482    try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
 483}
 484
 485fn add_include_dirs_arch(
 486    arena: Allocator,
 487    args: *std.array_list.Managed([]const u8),
 488    target: *const std.Target,
 489    opt_nptl: ?[]const u8,
 490    dir: []const u8,
 491) error{OutOfMemory}!void {
 492    const arch = target.cpu.arch;
 493    const is_x86 = arch.isX86();
 494    const is_aarch64 = arch.isAARCH64();
 495    const is_ppc = arch.isPowerPC();
 496    const is_sparc = arch.isSPARC();
 497    const is_64 = target.ptrBitWidth() == 64;
 498
 499    const s = path.sep_str;
 500
 501    if (is_x86) {
 502        if (arch == .x86_64) {
 503            if (opt_nptl) |nptl| {
 504                try args.append("-I");
 505                try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
 506            } else {
 507                if (target.abi == .gnux32) {
 508                    try args.append("-I");
 509                    try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", "x32" }));
 510                }
 511                try args.append("-I");
 512                try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
 513            }
 514        } else if (arch == .x86) {
 515            if (opt_nptl) |nptl| {
 516                try args.append("-I");
 517                try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
 518            } else {
 519                try args.append("-I");
 520                try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
 521            }
 522        }
 523        if (opt_nptl) |nptl| {
 524            try args.append("-I");
 525            try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
 526        } else {
 527            try args.append("-I");
 528            try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
 529        }
 530    } else if (arch.isArm()) {
 531        if (opt_nptl) |nptl| {
 532            try args.append("-I");
 533            try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
 534        } else {
 535            try args.append("-I");
 536            try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
 537        }
 538    } else if (arch.isMIPS()) {
 539        if (opt_nptl) |nptl| {
 540            try args.append("-I");
 541            try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
 542        } else {
 543            if (is_64) {
 544                try args.append("-I");
 545                try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
 546            } else {
 547                try args.append("-I");
 548                try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
 549            }
 550            try args.append("-I");
 551            try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
 552        }
 553    } else if (is_sparc) {
 554        if (opt_nptl) |nptl| {
 555            try args.append("-I");
 556            try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
 557        } else {
 558            if (is_64) {
 559                try args.append("-I");
 560                try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
 561            } else {
 562                try args.append("-I");
 563                try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
 564            }
 565            try args.append("-I");
 566            try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
 567        }
 568    } else if (is_aarch64) {
 569        if (opt_nptl) |nptl| {
 570            try args.append("-I");
 571            try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
 572        } else {
 573            try args.append("-I");
 574            try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
 575        }
 576    } else if (is_ppc) {
 577        if (opt_nptl) |nptl| {
 578            try args.append("-I");
 579            try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
 580        } else {
 581            if (is_64) {
 582                try args.append("-I");
 583                try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
 584            } else {
 585                try args.append("-I");
 586                try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
 587            }
 588            try args.append("-I");
 589            try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
 590        }
 591    } else if (arch.isRISCV()) {
 592        if (opt_nptl) |nptl| {
 593            try args.append("-I");
 594            try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
 595        } else {
 596            try args.append("-I");
 597            try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
 598        }
 599    } else if (arch == .s390x) {
 600        if (opt_nptl) |nptl| {
 601            try args.append("-I");
 602            try args.append(try path.join(arena, &[_][]const u8{ dir, "s390", nptl }));
 603        } else {
 604            try args.append("-I");
 605            try args.append(try path.join(arena, &[_][]const u8{ dir, "s390" ++ s ++ "s390-64" }));
 606            try args.append("-I");
 607            try args.append(try path.join(arena, &[_][]const u8{ dir, "s390" }));
 608        }
 609    } else if (arch.isLoongArch()) {
 610        try args.append("-I");
 611        try args.append(try path.join(arena, &[_][]const u8{ dir, "loongarch" }));
 612    } else if (arch == .m68k) {
 613        if (opt_nptl) |nptl| {
 614            try args.append("-I");
 615            try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k", nptl }));
 616        } else {
 617            // coldfire ABI support requires: https://github.com/ziglang/zig/issues/20690
 618            try args.append("-I");
 619            try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k" ++ s ++ "m680x0" }));
 620            try args.append("-I");
 621            try args.append(try path.join(arena, &[_][]const u8{ dir, "m68k" }));
 622        }
 623    } else if (arch == .arc) {
 624        try args.append("-I");
 625        try args.append(try path.join(arena, &[_][]const u8{ dir, "arc" }));
 626    } else if (arch == .csky) {
 627        try args.append("-I");
 628        try args.append(try path.join(arena, &[_][]const u8{ dir, "csky" }));
 629    }
 630}
 631
 632const lib_libc = "libc" ++ path.sep_str;
 633const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
 634
 635fn lib_path(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
 636    return path.join(arena, &.{ comp.dirs.zig_lib.path orelse ".", sub_path });
 637}
 638
 639pub const BuiltSharedObjects = struct {
 640    lock: Cache.Lock,
 641    dir_path: Path,
 642
 643    pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
 644        self.lock.release();
 645        gpa.free(self.dir_path.sub_path);
 646        self.* = undefined;
 647    }
 648};
 649
 650const all_map_basename = "all.map";
 651
 652fn wordDirective(target: *const std.Target) []const u8 {
 653    // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
 654    // according to the target word size. But no; that would just make too much sense.
 655    return if (target.ptrBitWidth() == 64) ".quad" else ".long";
 656}
 657
 658/// TODO replace anyerror with explicit error set, recording user-friendly errors with
 659/// lockAndSetMiscFailure and returning error.AlreadyReported. see libcxx.zig for example.
 660pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
 661    const tracy = trace(@src());
 662    defer tracy.end();
 663
 664    if (!build_options.have_llvm) {
 665        return error.ZigCompilerNotBuiltWithLLVMExtensions;
 666    }
 667
 668    const gpa = comp.gpa;
 669    const io = comp.io;
 670
 671    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
 672    defer arena_allocator.deinit();
 673    const arena = arena_allocator.allocator();
 674
 675    const target = comp.getTarget();
 676    const target_version = target.os.versionRange().gnuLibCVersion().?;
 677
 678    // Use the global cache directory.
 679    var cache: Cache = .{
 680        .gpa = gpa,
 681        .io = io,
 682        .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}),
 683    };
 684    cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
 685    cache.addPrefix(comp.dirs.zig_lib);
 686    cache.addPrefix(comp.dirs.global_cache);
 687    defer cache.manifest_dir.close();
 688
 689    var man = cache.obtain();
 690    defer man.deinit();
 691    man.hash.addBytes(build_options.version);
 692    man.hash.add(target.cpu.arch);
 693    man.hash.add(target.abi);
 694    man.hash.add(target_version);
 695
 696    const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path});
 697    const abilists_index = try man.addFile(full_abilists_path, abilists_max_size);
 698
 699    if (try man.hit()) {
 700        const digest = man.final();
 701
 702        return queueSharedObjects(comp, .{
 703            .lock = man.toOwnedLock(),
 704            .dir_path = .{
 705                .root_dir = comp.dirs.global_cache,
 706                .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
 707            },
 708        });
 709    }
 710
 711    const digest = man.final();
 712    const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
 713
 714    var o_directory: Cache.Directory = .{
 715        .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}),
 716        .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}),
 717    };
 718    defer o_directory.handle.close();
 719
 720    const abilists_contents = man.files.keys()[abilists_index].contents.?;
 721    const metadata = try loadMetaData(gpa, abilists_contents);
 722    defer metadata.destroy(gpa);
 723
 724    const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
 725        if (targ.arch == target.cpu.arch and
 726            targ.os == target.os.tag and
 727            targ.abi == target.abi)
 728        {
 729            break i;
 730        }
 731    } else {
 732        unreachable; // std.zig.target.available_libcs prevents us from getting here
 733    };
 734
 735    const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
 736        switch (ver.order(target_version)) {
 737            .eq => break i,
 738            .lt => continue,
 739            .gt => {
 740                // TODO Expose via compile error mechanism instead of log.
 741                log.warn("invalid target glibc version: {f}", .{target_version});
 742                return error.InvalidTargetGLibCVersion;
 743            },
 744        }
 745    } else blk: {
 746        const latest_index = metadata.all_versions.len - 1;
 747        log.warn("zig cannot build new glibc version {f}; providing instead {f}", .{
 748            target_version, metadata.all_versions[latest_index],
 749        });
 750        break :blk latest_index;
 751    };
 752
 753    {
 754        var map_contents = std.array_list.Managed(u8).init(arena);
 755        for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| {
 756            if (ver.patch == 0) {
 757                try map_contents.print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
 758            } else {
 759                try map_contents.print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
 760            }
 761        }
 762        try o_directory.handle.writeFile(.{ .sub_path = all_map_basename, .data = map_contents.items });
 763        map_contents.deinit(); // The most recent allocation of an arena can be freed :)
 764    }
 765
 766    var stubs_asm = std.array_list.Managed(u8).init(gpa);
 767    defer stubs_asm.deinit();
 768
 769    for (libs, 0..) |lib, lib_i| {
 770        if (lib.removed_in) |rem_in| {
 771            if (target_version.order(rem_in) != .lt) continue;
 772        }
 773
 774        stubs_asm.shrinkRetainingCapacity(0);
 775        try stubs_asm.appendSlice(".text\n");
 776
 777        var sym_i: usize = 0;
 778        var sym_name_buf: std.Io.Writer.Allocating = .init(arena);
 779        var opt_symbol_name: ?[]const u8 = null;
 780        var versions_buffer: [32]u8 = undefined;
 781        var versions_len: usize = undefined;
 782
 783        // There can be situations where there are multiple inclusions for the same symbol with
 784        // partially overlapping versions, due to different target lists. For example:
 785        //
 786        //  lgammal:
 787        //   library: libm.so
 788        //   versions: 2.4 2.23
 789        //   targets: ... powerpc64-linux-gnu s390x-linux-gnu
 790        //  lgammal:
 791        //   library: libm.so
 792        //   versions: 2.2 2.23
 793        //   targets: sparc64-linux-gnu s390x-linux-gnu
 794        //
 795        // If we don't handle this, we end up writing the default `lgammal` symbol for version 2.33
 796        // twice, which causes a "duplicate symbol" assembler error.
 797        var versions_written = std.AutoArrayHashMap(Version, void).init(arena);
 798
 799        var inc_reader: std.Io.Reader = .fixed(metadata.inclusions);
 800
 801        const fn_inclusions_len = try inc_reader.takeInt(u16, .little);
 802
 803        while (sym_i < fn_inclusions_len) : (sym_i += 1) {
 804            const sym_name = opt_symbol_name orelse n: {
 805                sym_name_buf.clearRetainingCapacity();
 806                _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
 807                assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
 808                inc_reader.toss(1);
 809
 810                opt_symbol_name = sym_name_buf.written();
 811                versions_buffer = undefined;
 812                versions_len = 0;
 813
 814                break :n sym_name_buf.written();
 815            };
 816            const targets = try inc_reader.takeLeb128(u64);
 817            var lib_index = try inc_reader.takeByte();
 818
 819            const is_terminal = (lib_index & (1 << 7)) != 0;
 820            if (is_terminal) {
 821                lib_index &= ~@as(u8, 1 << 7);
 822                opt_symbol_name = null;
 823            }
 824
 825            // Test whether the inclusion applies to our current library and target.
 826            const ok_lib_and_target =
 827                (lib_index == lib_i) and
 828                ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
 829
 830            while (true) {
 831                const byte = try inc_reader.takeByte();
 832                const last = (byte & 0b1000_0000) != 0;
 833                const ver_i = @as(u7, @truncate(byte));
 834                if (ok_lib_and_target and ver_i <= target_ver_index) {
 835                    versions_buffer[versions_len] = ver_i;
 836                    versions_len += 1;
 837                }
 838                if (last) break;
 839            }
 840
 841            if (!is_terminal) continue;
 842
 843            // Pick the default symbol version:
 844            // - If there are no versions, don't emit it
 845            // - Take the greatest one <= than the target one
 846            // - If none of them is <= than the
 847            //   specified one don't pick any default version
 848            if (versions_len == 0) continue;
 849            var chosen_def_ver_index: u8 = 255;
 850            {
 851                var ver_buf_i: u8 = 0;
 852                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
 853                    const ver_index = versions_buffer[ver_buf_i];
 854                    if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
 855                        chosen_def_ver_index = ver_index;
 856                    }
 857                }
 858            }
 859
 860            versions_written.clearRetainingCapacity();
 861            try versions_written.ensureTotalCapacity(versions_len);
 862
 863            {
 864                var ver_buf_i: u8 = 0;
 865                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
 866                    // Example:
 867                    // .balign 4
 868                    // .globl _Exit_2_2_5
 869                    // .type _Exit_2_2_5, %function
 870                    // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5, remove
 871                    // _Exit_2_2_5: .long 0
 872                    const ver_index = versions_buffer[ver_buf_i];
 873                    const ver = metadata.all_versions[ver_index];
 874
 875                    if (versions_written.getOrPutAssumeCapacity(ver).found_existing) continue;
 876
 877                    // Default symbol version definition vs normal symbol version definition
 878                    const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
 879                    const at_sign_str: []const u8 = if (want_default) "@@" else "@";
 880                    if (ver.patch == 0) {
 881                        const sym_plus_ver = try std.fmt.allocPrint(
 882                            arena,
 883                            "{s}_{d}_{d}",
 884                            .{ sym_name, ver.major, ver.minor },
 885                        );
 886                        try stubs_asm.print(
 887                            \\.balign {d}
 888                            \\.globl {s}
 889                            \\.type {s}, %function
 890                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}, remove
 891                            \\{s}: {s} 0
 892                            \\
 893                        , .{
 894                            target.ptrBitWidth() / 8,
 895                            sym_plus_ver,
 896                            sym_plus_ver,
 897                            sym_plus_ver,
 898                            sym_name,
 899                            at_sign_str,
 900                            ver.major,
 901                            ver.minor,
 902                            sym_plus_ver,
 903                            wordDirective(target),
 904                        });
 905                    } else {
 906                        const sym_plus_ver = try std.fmt.allocPrint(
 907                            arena,
 908                            "{s}_{d}_{d}_{d}",
 909                            .{ sym_name, ver.major, ver.minor, ver.patch },
 910                        );
 911                        try stubs_asm.print(
 912                            \\.balign {d}
 913                            \\.globl {s}
 914                            \\.type {s}, %function
 915                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}, remove
 916                            \\{s}: {s} 0
 917                            \\
 918                        , .{
 919                            target.ptrBitWidth() / 8,
 920                            sym_plus_ver,
 921                            sym_plus_ver,
 922                            sym_plus_ver,
 923                            sym_name,
 924                            at_sign_str,
 925                            ver.major,
 926                            ver.minor,
 927                            ver.patch,
 928                            sym_plus_ver,
 929                            wordDirective(target),
 930                        });
 931                    }
 932                }
 933            }
 934        }
 935
 936        try stubs_asm.appendSlice(".rodata\n");
 937
 938        // For some targets, the real `libc.so.6` will contain a weak reference to `_IO_stdin_used`,
 939        // making the linker put the symbol in the dynamic symbol table. We likewise need to emit a
 940        // reference to it here for that effect, or it will not show up, which in turn will cause
 941        // the real glibc to think that the program was built against an ancient `FILE` structure
 942        // (pre-glibc 2.1).
 943        //
 944        // Note that glibc only compiles in the legacy compatibility code for some targets; it
 945        // depends on what is defined in the `shlib-versions` file for the particular architecture
 946        // and ABI. Those files are preprocessed by 2 separate tools during the glibc build to get
 947        // the final `abi-versions.h`, so it would be quite brittle to try to condition our emission
 948        // of the `_IO_stdin_used` reference in the exact same way. The only downside of emitting
 949        // the reference unconditionally is that it ends up being unused for newer targets; it
 950        // otherwise has no negative effect.
 951        //
 952        // glibc uses a weak reference because it has to work with programs compiled against pre-2.1
 953        // versions where the symbol didn't exist. We only care about modern glibc versions, so use
 954        // a strong reference.
 955        if (std.mem.eql(u8, lib.name, "c")) {
 956            try stubs_asm.print(
 957                \\.balign {d}
 958                \\.globl _IO_stdin_used
 959                \\{s} _IO_stdin_used
 960                \\
 961            , .{
 962                target.ptrBitWidth() / 8,
 963                wordDirective(target),
 964            });
 965        }
 966
 967        try stubs_asm.appendSlice(".data\n");
 968
 969        const obj_inclusions_len = try inc_reader.takeInt(u16, .little);
 970
 971        var sizes = try arena.alloc(u16, metadata.all_versions.len);
 972
 973        sym_i = 0;
 974        opt_symbol_name = null;
 975        versions_buffer = undefined;
 976        versions_len = undefined;
 977        while (sym_i < obj_inclusions_len) : (sym_i += 1) {
 978            const sym_name = opt_symbol_name orelse n: {
 979                sym_name_buf.clearRetainingCapacity();
 980                _ = try inc_reader.streamDelimiter(&sym_name_buf.writer, 0);
 981                assert(inc_reader.buffered()[0] == 0); // TODO change streamDelimiter API
 982                inc_reader.toss(1);
 983
 984                opt_symbol_name = sym_name_buf.written();
 985                versions_buffer = undefined;
 986                versions_len = 0;
 987
 988                break :n sym_name_buf.written();
 989            };
 990            const targets = try inc_reader.takeLeb128(u64);
 991            const size = try inc_reader.takeLeb128(u16);
 992            var lib_index = try inc_reader.takeByte();
 993
 994            const is_terminal = (lib_index & (1 << 7)) != 0;
 995            if (is_terminal) {
 996                lib_index &= ~@as(u8, 1 << 7);
 997                opt_symbol_name = null;
 998            }
 999
1000            // Test whether the inclusion applies to our current library and target.
1001            const ok_lib_and_target =
1002                (lib_index == lib_i) and
1003                ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
1004
1005            while (true) {
1006                const byte = try inc_reader.takeByte();
1007                const last = (byte & 0b1000_0000) != 0;
1008                const ver_i = @as(u7, @truncate(byte));
1009                if (ok_lib_and_target and ver_i <= target_ver_index) {
1010                    versions_buffer[versions_len] = ver_i;
1011                    versions_len += 1;
1012                    sizes[ver_i] = size;
1013                }
1014                if (last) break;
1015            }
1016
1017            if (!is_terminal) continue;
1018
1019            // Pick the default symbol version:
1020            // - If there are no versions, don't emit it
1021            // - Take the greatest one <= than the target one
1022            // - If none of them is <= than the
1023            //   specified one don't pick any default version
1024            if (versions_len == 0) continue;
1025            var chosen_def_ver_index: u8 = 255;
1026            {
1027                var ver_buf_i: u8 = 0;
1028                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
1029                    const ver_index = versions_buffer[ver_buf_i];
1030                    if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
1031                        chosen_def_ver_index = ver_index;
1032                    }
1033                }
1034            }
1035
1036            versions_written.clearRetainingCapacity();
1037            try versions_written.ensureTotalCapacity(versions_len);
1038
1039            {
1040                var ver_buf_i: u8 = 0;
1041                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
1042                    // Example:
1043                    // .balign 4
1044                    // .globl environ_2_2_5
1045                    // .type environ_2_2_5, %object
1046                    // .size environ_2_2_5, 4
1047                    // .symver environ_2_2_5, environ@@GLIBC_2.2.5, remove
1048                    // environ_2_2_5: .fill 4, 1, 0
1049                    const ver_index = versions_buffer[ver_buf_i];
1050                    const ver = metadata.all_versions[ver_index];
1051
1052                    if (versions_written.getOrPutAssumeCapacity(ver).found_existing) continue;
1053
1054                    // Default symbol version definition vs normal symbol version definition
1055                    const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
1056                    const at_sign_str: []const u8 = if (want_default) "@@" else "@";
1057                    if (ver.patch == 0) {
1058                        const sym_plus_ver = try std.fmt.allocPrint(
1059                            arena,
1060                            "{s}_{d}_{d}",
1061                            .{ sym_name, ver.major, ver.minor },
1062                        );
1063                        try stubs_asm.print(
1064                            \\.balign {d}
1065                            \\.globl {s}
1066                            \\.type {s}, %object
1067                            \\.size {s}, {d}
1068                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}, remove
1069                            \\{s}: .fill {d}, 1, 0
1070                            \\
1071                        , .{
1072                            target.ptrBitWidth() / 8,
1073                            sym_plus_ver,
1074                            sym_plus_ver,
1075                            sym_plus_ver,
1076                            sizes[ver_index],
1077                            sym_plus_ver,
1078                            sym_name,
1079                            at_sign_str,
1080                            ver.major,
1081                            ver.minor,
1082                            sym_plus_ver,
1083                            sizes[ver_index],
1084                        });
1085                    } else {
1086                        const sym_plus_ver = try std.fmt.allocPrint(
1087                            arena,
1088                            "{s}_{d}_{d}_{d}",
1089                            .{ sym_name, ver.major, ver.minor, ver.patch },
1090                        );
1091                        try stubs_asm.print(
1092                            \\.balign {d}
1093                            \\.globl {s}
1094                            \\.type {s}, %object
1095                            \\.size {s}, {d}
1096                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}, remove
1097                            \\{s}: .fill {d}, 1, 0
1098                            \\
1099                        , .{
1100                            target.ptrBitWidth() / 8,
1101                            sym_plus_ver,
1102                            sym_plus_ver,
1103                            sym_plus_ver,
1104                            sizes[ver_index],
1105                            sym_plus_ver,
1106                            sym_name,
1107                            at_sign_str,
1108                            ver.major,
1109                            ver.minor,
1110                            ver.patch,
1111                            sym_plus_ver,
1112                            sizes[ver_index],
1113                        });
1114                    }
1115                }
1116            }
1117        }
1118
1119        var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
1120        const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
1121        try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
1122        try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node);
1123    }
1124
1125    man.writeManifest() catch |err| {
1126        log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
1127    };
1128
1129    return queueSharedObjects(comp, .{
1130        .lock = man.toOwnedLock(),
1131        .dir_path = .{
1132            .root_dir = comp.dirs.global_cache,
1133            .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
1134        },
1135    });
1136}
1137
1138fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
1139    const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
1140
1141    assert(comp.glibc_so_files == null);
1142    comp.glibc_so_files = so_files;
1143
1144    var task_buffer: [libs.len]link.PrelinkTask = undefined;
1145    var task_buffer_i: usize = 0;
1146
1147    {
1148        comp.mutex.lock(); // protect comp.arena
1149        defer comp.mutex.unlock();
1150
1151        for (libs) |lib| {
1152            if (lib.removed_in) |rem_in| {
1153                if (target_version.order(rem_in) != .lt) continue;
1154            }
1155            const so_path: Path = .{
1156                .root_dir = so_files.dir_path.root_dir,
1157                .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
1158                    so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover,
1159                }) catch return comp.setAllocFailure(),
1160            };
1161            task_buffer[task_buffer_i] = .{ .load_dso = so_path };
1162            task_buffer_i += 1;
1163        }
1164    }
1165
1166    comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
1167}
1168
1169fn buildSharedLib(
1170    comp: *Compilation,
1171    arena: Allocator,
1172    bin_directory: Cache.Directory,
1173    asm_file_basename: []const u8,
1174    lib: Lib,
1175    prog_node: std.Progress.Node,
1176) !void {
1177    const tracy = trace(@src());
1178    defer tracy.end();
1179
1180    const io = comp.io;
1181    const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
1182    const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
1183    const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
1184    const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
1185    const map_file_path = try path.join(arena, &.{ bin_directory.path.?, all_map_basename });
1186
1187    const optimize_mode = comp.compilerRtOptMode();
1188    const strip = comp.compilerRtStrip();
1189    const config = try Compilation.Config.resolve(.{
1190        .output_mode = .Lib,
1191        .link_mode = .dynamic,
1192        .resolved_target = comp.root_mod.resolved_target,
1193        .is_test = false,
1194        .have_zcu = false,
1195        .emit_bin = true,
1196        .root_optimize_mode = optimize_mode,
1197        .root_strip = strip,
1198        .link_libc = false,
1199    });
1200
1201    const root_mod = try Module.create(arena, .{
1202        .paths = .{
1203            .root = .zig_lib_root,
1204            .root_src_path = "",
1205        },
1206        .fully_qualified_name = "root",
1207        .inherited = .{
1208            .resolved_target = comp.root_mod.resolved_target,
1209            .strip = strip,
1210            .stack_check = false,
1211            .stack_protector = 0,
1212            .sanitize_c = .off,
1213            .sanitize_thread = false,
1214            .red_zone = comp.root_mod.red_zone,
1215            .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
1216            .valgrind = false,
1217            .optimize_mode = optimize_mode,
1218            .structured_cfg = comp.root_mod.structured_cfg,
1219        },
1220        .global = config,
1221        .cc_argv = &.{},
1222        .parent = null,
1223    });
1224
1225    const c_source_files = [1]Compilation.CSourceFile{
1226        .{
1227            .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
1228            .owner = root_mod,
1229        },
1230    };
1231
1232    const misc_task: Compilation.MiscTask = .@"glibc shared object";
1233
1234    var sub_create_diag: Compilation.CreateDiagnostic = undefined;
1235    const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
1236        .dirs = comp.dirs.withoutLocalCache(),
1237        .thread_pool = comp.thread_pool,
1238        .self_exe_path = comp.self_exe_path,
1239        // Because we manually cache the whole set of objects, we don't cache the individual objects
1240        // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path.
1241        .cache_mode = .none,
1242        .config = config,
1243        .root_mod = root_mod,
1244        .root_name = lib.name,
1245        .libc_installation = comp.libc_installation,
1246        .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) },
1247        .verbose_cc = comp.verbose_cc,
1248        .verbose_link = comp.verbose_link,
1249        .verbose_air = comp.verbose_air,
1250        .verbose_llvm_ir = comp.verbose_llvm_ir,
1251        .verbose_llvm_bc = comp.verbose_llvm_bc,
1252        .verbose_cimport = comp.verbose_cimport,
1253        .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
1254        .clang_passthrough_mode = comp.clang_passthrough_mode,
1255        .version = version,
1256        .version_script = map_file_path,
1257        .soname = soname,
1258        .c_source_files = &c_source_files,
1259        .skip_linker_dependencies = true,
1260    }) catch |err| switch (err) {
1261        error.CreateFail => {
1262            comp.lockAndSetMiscFailure(misc_task, "sub-compilation of {t} failed: {f}", .{ misc_task, sub_create_diag });
1263            return error.AlreadyReported;
1264        },
1265        else => |e| return e,
1266    };
1267    defer sub_compilation.destroy();
1268
1269    try comp.updateSubCompilation(sub_compilation, misc_task, prog_node);
1270}
1271
1272pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile {
1273    return switch (output_mode) {
1274        .Obj, .Lib => null,
1275        .Exe => .scrt1_o,
1276    };
1277}