zig

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

blob 6baa8990 (41922B) - Raw


      1 const std = @import("std");
      2 const Allocator = std.mem.Allocator;
      3 const mem = std.mem;
      4 const log = std.log;
      5 const fs = std.fs;
      6 const path = fs.path;
      7 const assert = std.debug.assert;
      8 const Version = std.SemanticVersion;
      9 const Path = std.Build.Cache.Path;
     10 
     11 const Compilation = @import("../Compilation.zig");
     12 const build_options = @import("build_options");
     13 const trace = @import("../tracy.zig").trace;
     14 const Cache = std.Build.Cache;
     15 const Module = @import("../Package/Module.zig");
     16 const link = @import("../link.zig");
     17 
     18 pub const CrtFile = enum {
     19     scrt1_o,
     20 };
     21 
     22 pub fn needsCrt0(output_mode: std.builtin.OutputMode) ?CrtFile {
     23     // For shared libraries and PIC executables, we should actually link in a variant of crt1 that
     24     // is built with `-DSHARED` so that it calls `__cxa_finalize` in an ELF destructor. However, we
     25     // currently make no effort to respect `__cxa_finalize` on any other targets, so for now, we're
     26     // not doing it here either.
     27     //
     28     // See: https://github.com/ziglang/zig/issues/23574#issuecomment-2869089897
     29     return switch (output_mode) {
     30         .Obj, .Lib => null,
     31         .Exe => .scrt1_o,
     32     };
     33 }
     34 
     35 fn includePath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
     36     return path.join(arena, &.{
     37         comp.dirs.zig_lib.path.?,
     38         "libc" ++ path.sep_str ++ "include",
     39         sub_path,
     40     });
     41 }
     42 
     43 fn csuPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
     44     return path.join(arena, &.{
     45         comp.dirs.zig_lib.path.?,
     46         "libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "csu",
     47         sub_path,
     48     });
     49 }
     50 
     51 fn libcPath(comp: *Compilation, arena: Allocator, sub_path: []const u8) ![]const u8 {
     52     return path.join(arena, &.{
     53         comp.dirs.zig_lib.path.?,
     54         "libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ "lib" ++ path.sep_str ++ "libc",
     55         sub_path,
     56     });
     57 }
     58 
     59 /// TODO replace anyerror with explicit error set, recording user-friendly errors with
     60 /// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
     61 pub fn buildCrtFile(comp: *Compilation, crt_file: CrtFile, prog_node: std.Progress.Node) anyerror!void {
     62     if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions;
     63 
     64     const gpa = comp.gpa;
     65     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
     66     defer arena_allocator.deinit();
     67     const arena = arena_allocator.allocator();
     68 
     69     const target = &comp.root_mod.resolved_target.result;
     70 
     71     // In all cases in this function, we add the C compiler flags to
     72     // cache_exempt_flags rather than extra_flags, because these arguments
     73     // depend on only properties that are already covered by the cache
     74     // manifest. Including these arguments in the cache could only possibly
     75     // waste computation and create false negatives.
     76 
     77     switch (crt_file) {
     78         .scrt1_o => {
     79             var cflags = std.ArrayList([]const u8).init(arena);
     80             try cflags.appendSlice(&.{
     81                 "-O2",
     82                 "-fno-common",
     83                 "-std=gnu99",
     84                 "-DPIC",
     85                 "-w", // Disable all warnings.
     86             });
     87 
     88             if (target.cpu.arch.isPowerPC64()) {
     89                 try cflags.append("-mlongcall");
     90             }
     91 
     92             var acflags = std.ArrayList([]const u8).init(arena);
     93             try acflags.appendSlice(&.{
     94                 "-DLOCORE",
     95                 // See `Compilation.addCCArgs`.
     96                 try std.fmt.allocPrint(arena, "-D__FreeBSD_version={d}", .{target.os.version_range.semver.min.major * 100_000}),
     97             });
     98 
     99             inline for (.{ &cflags, &acflags }) |flags| {
    100                 try flags.appendSlice(&.{
    101                     "-DSTRIP_FBSDID",
    102                     "-I",
    103                     try includePath(comp, arena, try std.fmt.allocPrint(arena, "{s}-{s}-{s}", .{
    104                         std.zig.target.freebsdArchNameHeaders(target.cpu.arch),
    105                         @tagName(target.os.tag),
    106                         @tagName(target.abi),
    107                     })),
    108                     "-I",
    109                     try includePath(comp, arena, "generic-freebsd"),
    110                     "-I",
    111                     try csuPath(comp, arena, switch (target.cpu.arch) {
    112                         .arm => "arm",
    113                         .aarch64 => "aarch64",
    114                         .powerpc => "powerpc",
    115                         .powerpc64, .powerpc64le => "powerpc64",
    116                         .riscv64 => "riscv",
    117                         .x86 => "i386",
    118                         .x86_64 => "amd64",
    119                         else => unreachable,
    120                     }),
    121                     "-I",
    122                     try csuPath(comp, arena, "common"),
    123                     "-I",
    124                     try libcPath(comp, arena, "include"),
    125                     "-Qunused-arguments",
    126                 });
    127             }
    128 
    129             const sources = [_]struct {
    130                 path: []const u8,
    131                 flags: []const []const u8,
    132                 condition: bool = true,
    133             }{
    134                 .{
    135                     .path = "common" ++ path.sep_str ++ "crtbegin.c",
    136                     .flags = cflags.items,
    137                 },
    138                 .{
    139                     .path = "common" ++ path.sep_str ++ "crtbrand.S",
    140                     .flags = acflags.items,
    141                 },
    142                 .{
    143                     .path = "common" ++ path.sep_str ++ "crtend.c",
    144                     .flags = cflags.items,
    145                 },
    146                 .{
    147                     .path = "common" ++ path.sep_str ++ "feature_note.S",
    148                     .flags = acflags.items,
    149                 },
    150                 .{
    151                     .path = "common" ++ path.sep_str ++ "ignore_init_note.S",
    152                     .flags = acflags.items,
    153                 },
    154 
    155                 .{
    156                     .path = "arm" ++ path.sep_str ++ "crt1_c.c",
    157                     .flags = cflags.items,
    158                     .condition = target.cpu.arch == .arm,
    159                 },
    160                 .{
    161                     .path = "arm" ++ path.sep_str ++ "crt1_s.S",
    162                     .flags = acflags.items,
    163                     .condition = target.cpu.arch == .arm,
    164                 },
    165 
    166                 .{
    167                     .path = "aarch64" ++ path.sep_str ++ "crt1_c.c",
    168                     .flags = cflags.items,
    169                     .condition = target.cpu.arch == .aarch64,
    170                 },
    171                 .{
    172                     .path = "aarch64" ++ path.sep_str ++ "crt1_s.S",
    173                     .flags = acflags.items,
    174                     .condition = target.cpu.arch == .aarch64,
    175                 },
    176 
    177                 .{
    178                     .path = "powerpc" ++ path.sep_str ++ "crt1_c.c",
    179                     .flags = cflags.items,
    180                     .condition = target.cpu.arch == .powerpc,
    181                 },
    182                 .{
    183                     .path = "powerpc" ++ path.sep_str ++ "crtsavres.S",
    184                     .flags = acflags.items,
    185                     .condition = target.cpu.arch == .powerpc,
    186                 },
    187 
    188                 .{
    189                     .path = "powerpc64" ++ path.sep_str ++ "crt1_c.c",
    190                     .flags = cflags.items,
    191                     .condition = target.cpu.arch.isPowerPC64(),
    192                 },
    193 
    194                 .{
    195                     .path = "riscv" ++ path.sep_str ++ "crt1_c.c",
    196                     .flags = cflags.items,
    197                     .condition = target.cpu.arch == .riscv64,
    198                 },
    199                 .{
    200                     .path = "riscv" ++ path.sep_str ++ "crt1_s.S",
    201                     .flags = acflags.items,
    202                     .condition = target.cpu.arch == .riscv64,
    203                 },
    204 
    205                 .{
    206                     .path = "i386" ++ path.sep_str ++ "crt1_c.c",
    207                     .flags = cflags.items,
    208                     .condition = target.cpu.arch == .x86,
    209                 },
    210                 .{
    211                     .path = "i386" ++ path.sep_str ++ "crt1_s.S",
    212                     .flags = acflags.items,
    213                     .condition = target.cpu.arch == .x86,
    214                 },
    215 
    216                 .{
    217                     .path = "amd64" ++ path.sep_str ++ "crt1_c.c",
    218                     .flags = cflags.items,
    219                     .condition = target.cpu.arch == .x86_64,
    220                 },
    221                 .{
    222                     .path = "amd64" ++ path.sep_str ++ "crt1_s.S",
    223                     .flags = acflags.items,
    224                     .condition = target.cpu.arch == .x86_64,
    225                 },
    226             };
    227 
    228             var files_buf: [sources.len]Compilation.CSourceFile = undefined;
    229             var files_index: usize = 0;
    230             for (sources) |file| {
    231                 if (!file.condition) continue;
    232 
    233                 files_buf[files_index] = .{
    234                     .src_path = try csuPath(comp, arena, file.path),
    235                     .cache_exempt_flags = file.flags,
    236                     .owner = undefined,
    237                 };
    238                 files_index += 1;
    239             }
    240             const files = files_buf[0..files_index];
    241 
    242             return comp.build_crt_file(
    243                 if (comp.config.pie) "Scrt1" else "crt1",
    244                 .Obj,
    245                 .@"freebsd libc Scrt1.o",
    246                 prog_node,
    247                 files,
    248                 .{
    249                     .omit_frame_pointer = false,
    250                     .pic = true,
    251                 },
    252             );
    253         },
    254     }
    255 }
    256 
    257 pub const Lib = struct {
    258     name: []const u8,
    259     sover: u8,
    260 };
    261 
    262 pub const libs = [_]Lib{
    263     .{ .name = "m", .sover = 5 },
    264     .{ .name = "stdthreads", .sover = 0 },
    265     .{ .name = "thr", .sover = 3 },
    266     .{ .name = "c", .sover = 7 },
    267     .{ .name = "dl", .sover = 1 },
    268     .{ .name = "rt", .sover = 1 },
    269     .{ .name = "ld", .sover = 1 },
    270     .{ .name = "util", .sover = 9 },
    271     .{ .name = "execinfo", .sover = 1 },
    272 };
    273 
    274 pub const ABI = struct {
    275     all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
    276     all_targets: []const std.zig.target.ArchOsAbi,
    277     /// The bytes from the file verbatim, starting from the u16 number
    278     /// of function inclusions.
    279     inclusions: []const u8,
    280     arena_state: std.heap.ArenaAllocator.State,
    281 
    282     pub fn destroy(abi: *ABI, gpa: Allocator) void {
    283         abi.arena_state.promote(gpa).deinit();
    284     }
    285 };
    286 
    287 pub const LoadMetaDataError = error{
    288     /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
    289     ZigInstallationCorrupt,
    290     OutOfMemory,
    291 };
    292 
    293 pub const abilists_path = "libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++ "abilists";
    294 pub const abilists_max_size = 150 * 1024; // Bigger than this and something is definitely borked.
    295 
    296 /// This function will emit a log error when there is a problem with the zig
    297 /// installation and then return `error.ZigInstallationCorrupt`.
    298 pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
    299     const tracy = trace(@src());
    300     defer tracy.end();
    301 
    302     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
    303     errdefer arena_allocator.deinit();
    304     const arena = arena_allocator.allocator();
    305 
    306     var index: usize = 0;
    307 
    308     {
    309         const libs_len = contents[index];
    310         index += 1;
    311 
    312         var i: u8 = 0;
    313         while (i < libs_len) : (i += 1) {
    314             const lib_name = mem.sliceTo(contents[index..], 0);
    315             index += lib_name.len + 1;
    316 
    317             if (i >= libs.len or !mem.eql(u8, libs[i].name, lib_name)) {
    318                 log.err("libc" ++ path.sep_str ++ "freebsd" ++ path.sep_str ++
    319                     "abilists: invalid library name or index ({d}): '{s}'", .{ i, lib_name });
    320                 return error.ZigInstallationCorrupt;
    321             }
    322         }
    323     }
    324 
    325     const versions = b: {
    326         const versions_len = contents[index];
    327         index += 1;
    328 
    329         const versions = try arena.alloc(Version, versions_len);
    330         var i: u8 = 0;
    331         while (i < versions.len) : (i += 1) {
    332             versions[i] = .{
    333                 .major = contents[index + 0],
    334                 .minor = contents[index + 1],
    335                 .patch = contents[index + 2],
    336             };
    337             index += 3;
    338         }
    339         break :b versions;
    340     };
    341 
    342     const targets = b: {
    343         const targets_len = contents[index];
    344         index += 1;
    345 
    346         const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
    347         var i: u8 = 0;
    348         while (i < targets.len) : (i += 1) {
    349             const target_name = mem.sliceTo(contents[index..], 0);
    350             index += target_name.len + 1;
    351 
    352             var component_it = mem.tokenizeScalar(u8, target_name, '-');
    353             const arch_name = component_it.next() orelse {
    354                 log.err("abilists: expected arch name", .{});
    355                 return error.ZigInstallationCorrupt;
    356             };
    357             const os_name = component_it.next() orelse {
    358                 log.err("abilists: expected OS name", .{});
    359                 return error.ZigInstallationCorrupt;
    360             };
    361             const abi_name = component_it.next() orelse {
    362                 log.err("abilists: expected ABI name", .{});
    363                 return error.ZigInstallationCorrupt;
    364             };
    365             const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
    366                 log.err("abilists: unrecognized arch: '{s}'", .{arch_name});
    367                 return error.ZigInstallationCorrupt;
    368             };
    369             if (!mem.eql(u8, os_name, "freebsd")) {
    370                 log.err("abilists: expected OS 'freebsd', found '{s}'", .{os_name});
    371                 return error.ZigInstallationCorrupt;
    372             }
    373             const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
    374                 log.err("abilists: unrecognized ABI: '{s}'", .{abi_name});
    375                 return error.ZigInstallationCorrupt;
    376             };
    377 
    378             targets[i] = .{
    379                 .arch = arch_tag,
    380                 .os = .freebsd,
    381                 .abi = abi_tag,
    382             };
    383         }
    384         break :b targets;
    385     };
    386 
    387     const abi = try arena.create(ABI);
    388     abi.* = .{
    389         .all_versions = versions,
    390         .all_targets = targets,
    391         .inclusions = contents[index..],
    392         .arena_state = arena_allocator.state,
    393     };
    394     return abi;
    395 }
    396 
    397 pub const BuiltSharedObjects = struct {
    398     lock: Cache.Lock,
    399     dir_path: Path,
    400 
    401     pub fn deinit(self: *BuiltSharedObjects, gpa: Allocator) void {
    402         self.lock.release();
    403         gpa.free(self.dir_path.sub_path);
    404         self.* = undefined;
    405     }
    406 };
    407 
    408 const all_map_basename = "all.map";
    409 
    410 fn wordDirective(target: *const std.Target) []const u8 {
    411     // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
    412     // according to the target word size. But no; that would just make too much sense.
    413     return if (target.ptrBitWidth() == 64) ".quad" else ".long";
    414 }
    415 
    416 /// TODO replace anyerror with explicit error set, recording user-friendly errors with
    417 /// setMiscFailure and returning error.SubCompilationFailed. see libcxx.zig for example.
    418 pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
    419     // See also glibc.zig which this code is based on.
    420 
    421     const tracy = trace(@src());
    422     defer tracy.end();
    423 
    424     if (!build_options.have_llvm) {
    425         return error.ZigCompilerNotBuiltWithLLVMExtensions;
    426     }
    427 
    428     const gpa = comp.gpa;
    429 
    430     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
    431     defer arena_allocator.deinit();
    432     const arena = arena_allocator.allocator();
    433 
    434     const target = comp.getTarget();
    435     // FreeBSD 7 == FBSD_1.0, ..., FreeBSD 14 == FBSD_1.7
    436     const target_version: Version = .{ .major = 1, .minor = target.os.version_range.semver.min.major - 7, .patch = 0 };
    437 
    438     // Use the global cache directory.
    439     var cache: Cache = .{
    440         .gpa = gpa,
    441         .manifest_dir = try comp.dirs.global_cache.handle.makeOpenPath("h", .{}),
    442     };
    443     cache.addPrefix(.{ .path = null, .handle = fs.cwd() });
    444     cache.addPrefix(comp.dirs.zig_lib);
    445     cache.addPrefix(comp.dirs.global_cache);
    446     defer cache.manifest_dir.close();
    447 
    448     var man = cache.obtain();
    449     defer man.deinit();
    450     man.hash.addBytes(build_options.version);
    451     man.hash.add(target.cpu.arch);
    452     man.hash.add(target.abi);
    453     man.hash.add(target_version);
    454 
    455     const full_abilists_path = try comp.dirs.zig_lib.join(arena, &.{abilists_path});
    456     const abilists_index = try man.addFile(full_abilists_path, abilists_max_size);
    457 
    458     if (try man.hit()) {
    459         const digest = man.final();
    460 
    461         return queueSharedObjects(comp, .{
    462             .lock = man.toOwnedLock(),
    463             .dir_path = .{
    464                 .root_dir = comp.dirs.global_cache,
    465                 .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
    466             },
    467         });
    468     }
    469 
    470     const digest = man.final();
    471     const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
    472 
    473     var o_directory: Cache.Directory = .{
    474         .handle = try comp.dirs.global_cache.handle.makeOpenPath(o_sub_path, .{}),
    475         .path = try comp.dirs.global_cache.join(arena, &.{o_sub_path}),
    476     };
    477     defer o_directory.handle.close();
    478 
    479     const abilists_contents = man.files.keys()[abilists_index].contents.?;
    480     const metadata = try loadMetaData(gpa, abilists_contents);
    481     defer metadata.destroy(gpa);
    482 
    483     const target_targ_index = for (metadata.all_targets, 0..) |targ, i| {
    484         if (targ.arch == target.cpu.arch and
    485             targ.os == target.os.tag and
    486             targ.abi == target.abi)
    487         {
    488             break i;
    489         }
    490     } else {
    491         unreachable; // std.zig.target.available_libcs prevents us from getting here
    492     };
    493 
    494     const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
    495         switch (ver.order(target_version)) {
    496             .eq => break i,
    497             .lt => continue,
    498             .gt => {
    499                 // TODO Expose via compile error mechanism instead of log.
    500                 log.warn("invalid target FreeBSD libc version: {f}", .{target_version});
    501                 return error.InvalidTargetLibCVersion;
    502             },
    503         }
    504     } else blk: {
    505         const latest_index = metadata.all_versions.len - 1;
    506         log.warn("zig cannot build new FreeBSD libc version {f}; providing instead {f}", .{
    507             target_version, metadata.all_versions[latest_index],
    508         });
    509         break :blk latest_index;
    510     };
    511 
    512     {
    513         var map_contents = std.ArrayList(u8).init(arena);
    514         for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| {
    515             try map_contents.writer().print("FBSD_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
    516         }
    517         try o_directory.handle.writeFile(.{ .sub_path = all_map_basename, .data = map_contents.items });
    518         map_contents.deinit();
    519     }
    520 
    521     var stubs_asm = std.ArrayList(u8).init(gpa);
    522     defer stubs_asm.deinit();
    523 
    524     for (libs, 0..) |lib, lib_i| {
    525         stubs_asm.shrinkRetainingCapacity(0);
    526 
    527         const stubs_writer = stubs_asm.writer();
    528 
    529         try stubs_writer.writeAll(".text\n");
    530 
    531         var sym_i: usize = 0;
    532         var sym_name_buf = std.ArrayList(u8).init(arena);
    533         var opt_symbol_name: ?[]const u8 = null;
    534         var versions = try std.DynamicBitSetUnmanaged.initEmpty(arena, metadata.all_versions.len);
    535         var weak_linkages = try std.DynamicBitSetUnmanaged.initEmpty(arena, metadata.all_versions.len);
    536 
    537         var inc_fbs = std.io.fixedBufferStream(metadata.inclusions);
    538         var inc_reader = inc_fbs.reader();
    539 
    540         const fn_inclusions_len = try inc_reader.readInt(u16, .little);
    541 
    542         // Pick the default symbol version:
    543         // - If there are no versions, don't emit it
    544         // - Take the greatest one <= than the target one
    545         // - If none of them is <= than the
    546         //   specified one don't pick any default version
    547         var chosen_def_ver_index: usize = 255;
    548         var chosen_unversioned_ver_index: usize = 255;
    549 
    550         while (sym_i < fn_inclusions_len) : (sym_i += 1) {
    551             const sym_name = opt_symbol_name orelse n: {
    552                 sym_name_buf.clearRetainingCapacity();
    553                 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null);
    554 
    555                 opt_symbol_name = sym_name_buf.items;
    556                 versions.unsetAll();
    557                 weak_linkages.unsetAll();
    558                 chosen_def_ver_index = 255;
    559                 chosen_unversioned_ver_index = 255;
    560 
    561                 break :n sym_name_buf.items;
    562             };
    563             {
    564                 const targets = try std.leb.readUleb128(u64, inc_reader);
    565                 var lib_index = try inc_reader.readByte();
    566 
    567                 const is_unversioned = (lib_index & (1 << 5)) != 0;
    568                 const is_weak = (lib_index & (1 << 6)) != 0;
    569                 const is_terminal = (lib_index & (1 << 7)) != 0;
    570 
    571                 lib_index = @as(u5, @truncate(lib_index));
    572 
    573                 // Test whether the inclusion applies to our current library and target.
    574                 const ok_lib_and_target =
    575                     (lib_index == lib_i) and
    576                     ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
    577 
    578                 while (true) {
    579                     const byte = try inc_reader.readByte();
    580                     const last = (byte & 0b1000_0000) != 0;
    581                     const ver_i = @as(u7, @truncate(byte));
    582                     if (ok_lib_and_target and ver_i <= target_ver_index) {
    583                         if (is_unversioned) {
    584                             if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) {
    585                                 chosen_unversioned_ver_index = ver_i;
    586                             }
    587                         } else {
    588                             if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) {
    589                                 chosen_def_ver_index = ver_i;
    590                             }
    591 
    592                             versions.set(ver_i);
    593                         }
    594 
    595                         weak_linkages.setValue(ver_i, is_weak);
    596                     }
    597                     if (last) break;
    598                 }
    599 
    600                 if (is_terminal) {
    601                     opt_symbol_name = null;
    602                 } else continue;
    603             }
    604 
    605             if (chosen_unversioned_ver_index != 255) {
    606                 // Example:
    607                 // .balign 4
    608                 // .globl _Exit
    609                 // .type _Exit, %function
    610                 // _Exit: .long 0
    611                 try stubs_writer.print(
    612                     \\.balign {d}
    613                     \\.{s} {s}
    614                     \\.type {s}, %function
    615                     \\{s}: {s} 0
    616                     \\
    617                 , .{
    618                     target.ptrBitWidth() / 8,
    619                     if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl",
    620                     sym_name,
    621                     sym_name,
    622                     sym_name,
    623                     wordDirective(target),
    624                 });
    625             }
    626 
    627             {
    628                 var versions_iter = versions.iterator(.{});
    629                 while (versions_iter.next()) |ver_index| {
    630                     // Example:
    631                     // .balign 4
    632                     // .globl _Exit_1_0
    633                     // .type _Exit_1_0, %function
    634                     // .symver _Exit_1_0, _Exit@@FBSD_1.0, remove
    635                     // _Exit_1_0: .long 0
    636                     const ver = metadata.all_versions[ver_index];
    637                     const sym_plus_ver = try std.fmt.allocPrint(
    638                         arena,
    639                         "{s}_FBSD_{d}_{d}",
    640                         .{ sym_name, ver.major, ver.minor },
    641                     );
    642 
    643                     try stubs_writer.print(
    644                         \\.balign {d}
    645                         \\.{s} {s}
    646                         \\.type {s}, %function
    647                         \\.symver {s}, {s}{s}FBSD_{d}.{d}, remove
    648                         \\{s}: {s} 0
    649                         \\
    650                     , .{
    651                         target.ptrBitWidth() / 8,
    652                         if (weak_linkages.isSet(ver_index)) "weak" else "globl",
    653                         sym_plus_ver,
    654                         sym_plus_ver,
    655                         sym_plus_ver,
    656                         sym_name,
    657                         // Default symbol version definition vs normal symbol version definition
    658                         if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@",
    659                         ver.major,
    660                         ver.minor,
    661                         sym_plus_ver,
    662                         wordDirective(target),
    663                     });
    664                 }
    665             }
    666         }
    667 
    668         try stubs_writer.writeAll(".data\n");
    669 
    670         // FreeBSD's `libc.so.7` contains strong references to `__progname` and `environ` which are
    671         // defined in the statically-linked startup code. Those references cause the linker to put
    672         // the symbols in the dynamic symbol table. We need to create dummy references to them here
    673         // to get the same effect.
    674         if (std.mem.eql(u8, lib.name, "c")) {
    675             try stubs_writer.print(
    676                 \\.balign {d}
    677                 \\.globl __progname
    678                 \\.globl environ
    679                 \\{s} __progname
    680                 \\{s} environ
    681                 \\
    682             , .{
    683                 target.ptrBitWidth() / 8,
    684                 wordDirective(target),
    685                 wordDirective(target),
    686             });
    687         }
    688 
    689         const obj_inclusions_len = try inc_reader.readInt(u16, .little);
    690 
    691         var sizes = try arena.alloc(u16, metadata.all_versions.len);
    692 
    693         sym_i = 0;
    694         opt_symbol_name = null;
    695 
    696         while (sym_i < obj_inclusions_len) : (sym_i += 1) {
    697             const sym_name = opt_symbol_name orelse n: {
    698                 sym_name_buf.clearRetainingCapacity();
    699                 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null);
    700 
    701                 opt_symbol_name = sym_name_buf.items;
    702                 versions.unsetAll();
    703                 weak_linkages.unsetAll();
    704                 chosen_def_ver_index = 255;
    705                 chosen_unversioned_ver_index = 255;
    706 
    707                 break :n sym_name_buf.items;
    708             };
    709 
    710             {
    711                 const targets = try std.leb.readUleb128(u64, inc_reader);
    712                 const size = try std.leb.readUleb128(u16, inc_reader);
    713                 var lib_index = try inc_reader.readByte();
    714 
    715                 const is_unversioned = (lib_index & (1 << 5)) != 0;
    716                 const is_weak = (lib_index & (1 << 6)) != 0;
    717                 const is_terminal = (lib_index & (1 << 7)) != 0;
    718 
    719                 lib_index = @as(u5, @truncate(lib_index));
    720 
    721                 // Test whether the inclusion applies to our current library and target.
    722                 const ok_lib_and_target =
    723                     (lib_index == lib_i) and
    724                     ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
    725 
    726                 while (true) {
    727                     const byte = try inc_reader.readByte();
    728                     const last = (byte & 0b1000_0000) != 0;
    729                     const ver_i = @as(u7, @truncate(byte));
    730                     if (ok_lib_and_target and ver_i <= target_ver_index) {
    731                         if (is_unversioned) {
    732                             if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) {
    733                                 chosen_unversioned_ver_index = ver_i;
    734                             }
    735                         } else {
    736                             if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) {
    737                                 chosen_def_ver_index = ver_i;
    738                             }
    739 
    740                             versions.set(ver_i);
    741                         }
    742 
    743                         sizes[ver_i] = size;
    744                         weak_linkages.setValue(ver_i, is_weak);
    745                     }
    746                     if (last) break;
    747                 }
    748 
    749                 if (is_terminal) {
    750                     opt_symbol_name = null;
    751                 } else continue;
    752             }
    753 
    754             if (chosen_unversioned_ver_index != 255) {
    755                 // Example:
    756                 // .balign 4
    757                 // .globl malloc_conf
    758                 // .type malloc_conf, %object
    759                 // .size malloc_conf, 4
    760                 // malloc_conf: .fill 4, 1, 0
    761                 try stubs_writer.print(
    762                     \\.balign {d}
    763                     \\.{s} {s}
    764                     \\.type {s}, %object
    765                     \\.size {s}, {d}
    766                     \\{s}: {s} 0
    767                     \\
    768                 , .{
    769                     target.ptrBitWidth() / 8,
    770                     if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl",
    771                     sym_name,
    772                     sym_name,
    773                     sym_name,
    774                     sizes[chosen_unversioned_ver_index],
    775                     sym_name,
    776                     wordDirective(target),
    777                 });
    778             }
    779 
    780             {
    781                 var versions_iter = versions.iterator(.{});
    782                 while (versions_iter.next()) |ver_index| {
    783                     // Example:
    784                     // .balign 4
    785                     // .globl malloc_conf_1_3
    786                     // .type malloc_conf_1_3, %object
    787                     // .size malloc_conf_1_3, 4
    788                     // .symver malloc_conf_1_3, malloc_conf@@FBSD_1.3
    789                     // malloc_conf_1_3: .fill 4, 1, 0
    790                     const ver = metadata.all_versions[ver_index];
    791                     const sym_plus_ver = try std.fmt.allocPrint(
    792                         arena,
    793                         "{s}_FBSD_{d}_{d}",
    794                         .{ sym_name, ver.major, ver.minor },
    795                     );
    796 
    797                     try stubs_asm.writer().print(
    798                         \\.balign {d}
    799                         \\.{s} {s}
    800                         \\.type {s}, %object
    801                         \\.size {s}, {d}
    802                         \\.symver {s}, {s}{s}FBSD_{d}.{d}
    803                         \\{s}: .fill {d}, 1, 0
    804                         \\
    805                     , .{
    806                         target.ptrBitWidth() / 8,
    807                         if (weak_linkages.isSet(ver_index)) "weak" else "globl",
    808                         sym_plus_ver,
    809                         sym_plus_ver,
    810                         sym_plus_ver,
    811                         sizes[ver_index],
    812                         sym_plus_ver,
    813                         sym_name,
    814                         // Default symbol version definition vs normal symbol version definition
    815                         if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@",
    816                         ver.major,
    817                         ver.minor,
    818                         sym_plus_ver,
    819                         sizes[ver_index],
    820                     });
    821                 }
    822             }
    823         }
    824 
    825         try stubs_writer.writeAll(".tdata\n");
    826 
    827         const tls_inclusions_len = try inc_reader.readInt(u16, .little);
    828 
    829         sym_i = 0;
    830         opt_symbol_name = null;
    831 
    832         while (sym_i < tls_inclusions_len) : (sym_i += 1) {
    833             const sym_name = opt_symbol_name orelse n: {
    834                 sym_name_buf.clearRetainingCapacity();
    835                 try inc_reader.streamUntilDelimiter(sym_name_buf.writer(), 0, null);
    836 
    837                 opt_symbol_name = sym_name_buf.items;
    838                 versions.unsetAll();
    839                 weak_linkages.unsetAll();
    840                 chosen_def_ver_index = 255;
    841                 chosen_unversioned_ver_index = 255;
    842 
    843                 break :n sym_name_buf.items;
    844             };
    845 
    846             {
    847                 const targets = try std.leb.readUleb128(u64, inc_reader);
    848                 const size = try std.leb.readUleb128(u16, inc_reader);
    849                 var lib_index = try inc_reader.readByte();
    850 
    851                 const is_unversioned = (lib_index & (1 << 5)) != 0;
    852                 const is_weak = (lib_index & (1 << 6)) != 0;
    853                 const is_terminal = (lib_index & (1 << 7)) != 0;
    854 
    855                 lib_index = @as(u5, @truncate(lib_index));
    856 
    857                 // Test whether the inclusion applies to our current library and target.
    858                 const ok_lib_and_target =
    859                     (lib_index == lib_i) and
    860                     ((targets & (@as(u64, 1) << @as(u6, @intCast(target_targ_index)))) != 0);
    861 
    862                 while (true) {
    863                     const byte = try inc_reader.readByte();
    864                     const last = (byte & 0b1000_0000) != 0;
    865                     const ver_i = @as(u7, @truncate(byte));
    866                     if (ok_lib_and_target and ver_i <= target_ver_index) {
    867                         if (is_unversioned) {
    868                             if (chosen_unversioned_ver_index == 255 or ver_i > chosen_unversioned_ver_index) {
    869                                 chosen_unversioned_ver_index = ver_i;
    870                             }
    871                         } else {
    872                             if (chosen_def_ver_index == 255 or ver_i > chosen_def_ver_index) {
    873                                 chosen_def_ver_index = ver_i;
    874                             }
    875 
    876                             versions.set(ver_i);
    877                         }
    878 
    879                         sizes[ver_i] = size;
    880                         weak_linkages.setValue(ver_i, is_weak);
    881                     }
    882                     if (last) break;
    883                 }
    884 
    885                 if (is_terminal) {
    886                     opt_symbol_name = null;
    887                 } else continue;
    888             }
    889 
    890             if (chosen_unversioned_ver_index != 255) {
    891                 // Example:
    892                 // .balign 4
    893                 // .globl _ThreadRuneLocale
    894                 // .type _ThreadRuneLocale, %object
    895                 // .size _ThreadRuneLocale, 4
    896                 // _ThreadRuneLocale: .fill 4, 1, 0
    897                 try stubs_writer.print(
    898                     \\.balign {d}
    899                     \\.{s} {s}
    900                     \\.type {s}, %tls_object
    901                     \\.size {s}, {d}
    902                     \\{s}: {s} 0
    903                     \\
    904                 , .{
    905                     target.ptrBitWidth() / 8,
    906                     if (weak_linkages.isSet(chosen_unversioned_ver_index)) "weak" else "globl",
    907                     sym_name,
    908                     sym_name,
    909                     sym_name,
    910                     sizes[chosen_unversioned_ver_index],
    911                     sym_name,
    912                     wordDirective(target),
    913                 });
    914             }
    915 
    916             {
    917                 var versions_iter = versions.iterator(.{});
    918                 while (versions_iter.next()) |ver_index| {
    919                     // Example:
    920                     // .balign 4
    921                     // .globl _ThreadRuneLocale_1_3
    922                     // .type _ThreadRuneLocale_1_3, %tls_object
    923                     // .size _ThreadRuneLocale_1_3, 4
    924                     // .symver _ThreadRuneLocale_1_3, _ThreadRuneLocale@@FBSD_1.3
    925                     // _ThreadRuneLocale_1_3: .fill 4, 1, 0
    926                     const ver = metadata.all_versions[ver_index];
    927                     const sym_plus_ver = try std.fmt.allocPrint(
    928                         arena,
    929                         "{s}_FBSD_{d}_{d}",
    930                         .{ sym_name, ver.major, ver.minor },
    931                     );
    932 
    933                     try stubs_writer.print(
    934                         \\.balign {d}
    935                         \\.{s} {s}
    936                         \\.type {s}, %tls_object
    937                         \\.size {s}, {d}
    938                         \\.symver {s}, {s}{s}FBSD_{d}.{d}
    939                         \\{s}: .fill {d}, 1, 0
    940                         \\
    941                     , .{
    942                         target.ptrBitWidth() / 8,
    943                         if (weak_linkages.isSet(ver_index)) "weak" else "globl",
    944                         sym_plus_ver,
    945                         sym_plus_ver,
    946                         sym_plus_ver,
    947                         sizes[ver_index],
    948                         sym_plus_ver,
    949                         sym_name,
    950                         // Default symbol version definition vs normal symbol version definition
    951                         if (chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index) "@@" else "@",
    952                         ver.major,
    953                         ver.minor,
    954                         sym_plus_ver,
    955                         sizes[ver_index],
    956                     });
    957                 }
    958             }
    959         }
    960 
    961         var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "stdthreads", etc.
    962         const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
    963         try o_directory.handle.writeFile(.{ .sub_path = asm_file_basename, .data = stubs_asm.items });
    964         try buildSharedLib(comp, arena, o_directory, asm_file_basename, lib, prog_node);
    965     }
    966 
    967     man.writeManifest() catch |err| {
    968         log.warn("failed to write cache manifest for FreeBSD libc stubs: {s}", .{@errorName(err)});
    969     };
    970 
    971     return queueSharedObjects(comp, .{
    972         .lock = man.toOwnedLock(),
    973         .dir_path = .{
    974             .root_dir = comp.dirs.global_cache,
    975             .sub_path = try gpa.dupe(u8, "o" ++ fs.path.sep_str ++ digest),
    976         },
    977     });
    978 }
    979 
    980 pub fn sharedObjectsCount() u8 {
    981     return libs.len;
    982 }
    983 
    984 fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
    985     assert(comp.freebsd_so_files == null);
    986     comp.freebsd_so_files = so_files;
    987 
    988     var task_buffer: [libs.len]link.PrelinkTask = undefined;
    989     var task_buffer_i: usize = 0;
    990 
    991     {
    992         comp.mutex.lock(); // protect comp.arena
    993         defer comp.mutex.unlock();
    994 
    995         for (libs) |lib| {
    996             const so_path: Path = .{
    997                 .root_dir = so_files.dir_path.root_dir,
    998                 .sub_path = std.fmt.allocPrint(comp.arena, "{s}{c}lib{s}.so.{d}", .{
    999                     so_files.dir_path.sub_path, fs.path.sep, lib.name, lib.sover,
   1000                 }) catch return comp.setAllocFailure(),
   1001             };
   1002             task_buffer[task_buffer_i] = .{ .load_dso = so_path };
   1003             task_buffer_i += 1;
   1004         }
   1005     }
   1006 
   1007     comp.queuePrelinkTasks(task_buffer[0..task_buffer_i]);
   1008 }
   1009 
   1010 fn buildSharedLib(
   1011     comp: *Compilation,
   1012     arena: Allocator,
   1013     bin_directory: Cache.Directory,
   1014     asm_file_basename: []const u8,
   1015     lib: Lib,
   1016     prog_node: std.Progress.Node,
   1017 ) !void {
   1018     const tracy = trace(@src());
   1019     defer tracy.end();
   1020 
   1021     const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
   1022     const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
   1023     const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
   1024     const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
   1025     const map_file_path = try path.join(arena, &.{ bin_directory.path.?, all_map_basename });
   1026 
   1027     const optimize_mode = comp.compilerRtOptMode();
   1028     const strip = comp.compilerRtStrip();
   1029     const config = try Compilation.Config.resolve(.{
   1030         .output_mode = .Lib,
   1031         .link_mode = .dynamic,
   1032         .resolved_target = comp.root_mod.resolved_target,
   1033         .is_test = false,
   1034         .have_zcu = false,
   1035         .emit_bin = true,
   1036         .root_optimize_mode = optimize_mode,
   1037         .root_strip = strip,
   1038         .link_libc = false,
   1039     });
   1040 
   1041     const root_mod = try Module.create(arena, .{
   1042         .paths = .{
   1043             .root = .zig_lib_root,
   1044             .root_src_path = "",
   1045         },
   1046         .fully_qualified_name = "root",
   1047         .inherited = .{
   1048             .resolved_target = comp.root_mod.resolved_target,
   1049             .strip = strip,
   1050             .stack_check = false,
   1051             .stack_protector = 0,
   1052             .sanitize_c = .off,
   1053             .sanitize_thread = false,
   1054             .red_zone = comp.root_mod.red_zone,
   1055             .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
   1056             .valgrind = false,
   1057             .optimize_mode = optimize_mode,
   1058             .structured_cfg = comp.root_mod.structured_cfg,
   1059         },
   1060         .global = config,
   1061         .cc_argv = &.{},
   1062         .parent = null,
   1063     });
   1064 
   1065     const c_source_files = [1]Compilation.CSourceFile{
   1066         .{
   1067             .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }),
   1068             .owner = root_mod,
   1069         },
   1070     };
   1071 
   1072     const sub_compilation = try Compilation.create(comp.gpa, arena, .{
   1073         .dirs = comp.dirs.withoutLocalCache(),
   1074         .thread_pool = comp.thread_pool,
   1075         .self_exe_path = comp.self_exe_path,
   1076         // Because we manually cache the whole set of objects, we don't cache the individual objects
   1077         // within it. In fact, we *can't* do that, because we need `emit_bin` to specify the path.
   1078         .cache_mode = .none,
   1079         .config = config,
   1080         .root_mod = root_mod,
   1081         .root_name = lib.name,
   1082         .libc_installation = comp.libc_installation,
   1083         .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) },
   1084         .verbose_cc = comp.verbose_cc,
   1085         .verbose_link = comp.verbose_link,
   1086         .verbose_air = comp.verbose_air,
   1087         .verbose_llvm_ir = comp.verbose_llvm_ir,
   1088         .verbose_llvm_bc = comp.verbose_llvm_bc,
   1089         .verbose_cimport = comp.verbose_cimport,
   1090         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
   1091         .clang_passthrough_mode = comp.clang_passthrough_mode,
   1092         .version = version,
   1093         .version_script = map_file_path,
   1094         .soname = soname,
   1095         .c_source_files = &c_source_files,
   1096         .skip_linker_dependencies = true,
   1097     });
   1098     defer sub_compilation.destroy();
   1099 
   1100     try comp.updateSubCompilation(sub_compilation, .@"freebsd libc shared object", prog_node);
   1101 }