Commit 4cfeb9a541

Andrew Kelley <andrew@ziglang.org>
2022-10-29 04:40:01
glibc: fix race condition when building stubs
Before, the code for building glibc stubs used a special case of the Cache API that did not add any file inputs, and did not use writeManifest(). This is not really how the Cache API is designed to work and it shows because there was a race condition. This commit adds as an input file the abilists file that comes with Zig's installation, which has the added benefit of making glibc stub caching properly detect cache invalidation when the user decides to overwrite their abilists file. This harmonizes with the rest of how Zig works, which intentionally allows you to hack the installation files and have it behave properly with the cache system. Finally, because of having any file inputs, the normal API flow of the Cache system can be used, eliminating the one place that used the Cache API in a special way. In other words, it uses writeManifest() now and properly obeys the cache hit/miss semantics. closes #13160
1 parent 20925b2
Changed files (2)
src/glibc.zig
@@ -50,9 +50,12 @@ pub const LoadMetaDataError = error{
     OutOfMemory,
 };
 
+pub const abilists_path = "libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++ "abilists";
+pub const abilists_max_size = 800 * 1024; // Bigger than this and something is definitely borked.
+
 /// This function will emit a log error when there is a problem with the zig
 /// installation and then return `error.ZigInstallationCorrupt`.
-pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI {
+pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -60,22 +63,6 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI
     errdefer arena_allocator.deinit();
     const arena = arena_allocator.allocator();
 
-    var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| {
-        log.err("unable to open glibc dir: {s}", .{@errorName(err)});
-        return error.ZigInstallationCorrupt;
-    };
-    defer glibc_dir.close();
-
-    const max_size = 500 * 1024; // Bigger than this and something is definitely borked.
-    const contents = glibc_dir.readFileAlloc(arena, "abilists", max_size) catch |err| switch (err) {
-        error.OutOfMemory => return error.OutOfMemory,
-        else => {
-            log.err("unable to read libc" ++ path.sep_str ++ "glibc" ++ path.sep_str ++
-                "abilists: {s}", .{@errorName(err)});
-            return error.ZigInstallationCorrupt;
-        },
-    };
-
     var index: usize = 0;
 
     {
@@ -662,386 +649,383 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
     const target_version = target.os.version_range.linux.glibc;
 
     // Use the global cache directory.
-    var cache_parent: Cache = .{
+    var cache: Cache = .{
         .gpa = comp.gpa,
         .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
     };
-    defer cache_parent.manifest_dir.close();
-
-    var cache = cache_parent.obtain();
-    defer cache.deinit();
-    cache.hash.addBytes(build_options.version);
-    cache.hash.addBytes(comp.zig_lib_directory.path orelse ".");
-    cache.hash.add(target.cpu.arch);
-    cache.hash.add(target.abi);
-    cache.hash.add(target_version);
-
-    const hit = try cache.hit();
-    const digest = cache.final();
-    const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
+    defer cache.manifest_dir.close();
+
+    var man = cache.obtain();
+    defer man.deinit();
+    man.hash.addBytes(build_options.version);
+    man.hash.addBytes(comp.zig_lib_directory.path orelse ".");
+    man.hash.add(target.cpu.arch);
+    man.hash.add(target.abi);
+    man.hash.add(target_version);
+
+    const full_abilists_path = try comp.zig_lib_directory.join(arena, &.{abilists_path});
+    const abilists_index = try man.addFile(full_abilists_path, abilists_max_size);
+
+    if (try man.hit()) {
+        const digest = man.final();
+
+        assert(comp.glibc_so_files == null);
+        comp.glibc_so_files = BuiltSharedObjects{
+            .lock = man.toOwnedLock(),
+            .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
+        };
+        return;
+    }
 
-    // Even if we get a hit, it doesn't guarantee that we finished the job last time.
-    // We use the presence of an "ok" file to determine if it is a true hit.
+    const digest = man.final();
+    const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
 
     var o_directory: Compilation.Directory = .{
         .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
-        .path = try path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }),
+        .path = try comp.global_cache_directory.join(arena, &.{o_sub_path}),
     };
     defer o_directory.handle.close();
 
-    const ok_basename = "ok";
-    const actual_hit = if (hit) blk: {
-        o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) {
-            error.FileNotFound => break :blk false,
-            else => |e| return e,
-        };
-        break :blk true;
-    } else false;
-
-    if (!actual_hit) {
-        const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle);
-        defer metadata.destroy(comp.gpa);
+    const abilists_contents = man.files.items[abilists_index].contents.?;
+    const metadata = try loadMetaData(comp.gpa, abilists_contents);
+    defer metadata.destroy(comp.gpa);
 
-        const target_targ_index = for (metadata.all_targets) |targ, i| {
-            if (targ.arch == target.cpu.arch and
-                targ.os == target.os.tag and
-                targ.abi == target.abi)
-            {
-                break i;
-            }
-        } else {
-            unreachable; // target_util.available_libcs prevents us from getting here
-        };
+    const target_targ_index = for (metadata.all_targets) |targ, i| {
+        if (targ.arch == target.cpu.arch and
+            targ.os == target.os.tag and
+            targ.abi == target.abi)
+        {
+            break i;
+        }
+    } else {
+        unreachable; // target_util.available_libcs prevents us from getting here
+    };
 
-        const target_ver_index = for (metadata.all_versions) |ver, i| {
-            switch (ver.order(target_version)) {
-                .eq => break i,
-                .lt => continue,
-                .gt => {
-                    // TODO Expose via compile error mechanism instead of log.
-                    log.warn("invalid target glibc version: {}", .{target_version});
-                    return error.InvalidTargetGLibCVersion;
-                },
-            }
-        } else blk: {
-            const latest_index = metadata.all_versions.len - 1;
-            log.warn("zig cannot build new glibc version {}; providing instead {}", .{
-                target_version, metadata.all_versions[latest_index],
-            });
-            break :blk latest_index;
-        };
+    const target_ver_index = for (metadata.all_versions) |ver, i| {
+        switch (ver.order(target_version)) {
+            .eq => break i,
+            .lt => continue,
+            .gt => {
+                // TODO Expose via compile error mechanism instead of log.
+                log.warn("invalid target glibc version: {}", .{target_version});
+                return error.InvalidTargetGLibCVersion;
+            },
+        }
+    } else blk: {
+        const latest_index = metadata.all_versions.len - 1;
+        log.warn("zig cannot build new glibc version {}; providing instead {}", .{
+            target_version, metadata.all_versions[latest_index],
+        });
+        break :blk latest_index;
+    };
 
-        {
-            var map_contents = std.ArrayList(u8).init(arena);
-            for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| {
-                if (ver.patch == 0) {
-                    try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
-                } else {
-                    try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
-                }
+    {
+        var map_contents = std.ArrayList(u8).init(arena);
+        for (metadata.all_versions[0 .. target_ver_index + 1]) |ver| {
+            if (ver.patch == 0) {
+                try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
+            } else {
+                try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
             }
-            try o_directory.handle.writeFile(all_map_basename, map_contents.items);
-            map_contents.deinit(); // The most recent allocation of an arena can be freed :)
         }
+        try o_directory.handle.writeFile(all_map_basename, map_contents.items);
+        map_contents.deinit(); // The most recent allocation of an arena can be freed :)
+    }
 
-        var stubs_asm = std.ArrayList(u8).init(comp.gpa);
-        defer stubs_asm.deinit();
+    var stubs_asm = std.ArrayList(u8).init(comp.gpa);
+    defer stubs_asm.deinit();
 
-        for (libs) |lib, lib_i| {
-            stubs_asm.shrinkRetainingCapacity(0);
-            try stubs_asm.appendSlice(".text\n");
+    for (libs) |lib, lib_i| {
+        stubs_asm.shrinkRetainingCapacity(0);
+        try stubs_asm.appendSlice(".text\n");
 
-            var inc_i: usize = 0;
+        var inc_i: usize = 0;
 
-            const fn_inclusions_len = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
-            inc_i += 2;
+        const fn_inclusions_len = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
+        inc_i += 2;
 
-            var sym_i: usize = 0;
-            var opt_symbol_name: ?[]const u8 = null;
-            var versions_buffer: [32]u8 = undefined;
-            var versions_len: usize = undefined;
-            while (sym_i < fn_inclusions_len) : (sym_i += 1) {
-                const sym_name = opt_symbol_name orelse n: {
-                    const name = mem.sliceTo(metadata.inclusions[inc_i..], 0);
-                    inc_i += name.len + 1;
-
-                    opt_symbol_name = name;
-                    versions_buffer = undefined;
-                    versions_len = 0;
-                    break :n name;
-                };
-                const targets = mem.readIntLittle(u32, metadata.inclusions[inc_i..][0..4]);
-                inc_i += 4;
+        var sym_i: usize = 0;
+        var opt_symbol_name: ?[]const u8 = null;
+        var versions_buffer: [32]u8 = undefined;
+        var versions_len: usize = undefined;
+        while (sym_i < fn_inclusions_len) : (sym_i += 1) {
+            const sym_name = opt_symbol_name orelse n: {
+                const name = mem.sliceTo(metadata.inclusions[inc_i..], 0);
+                inc_i += name.len + 1;
+
+                opt_symbol_name = name;
+                versions_buffer = undefined;
+                versions_len = 0;
+                break :n name;
+            };
+            const targets = mem.readIntLittle(u32, metadata.inclusions[inc_i..][0..4]);
+            inc_i += 4;
 
-                const lib_index = metadata.inclusions[inc_i];
+            const lib_index = metadata.inclusions[inc_i];
+            inc_i += 1;
+            const is_terminal = (targets & (1 << 31)) != 0;
+            if (is_terminal) opt_symbol_name = null;
+
+            // Test whether the inclusion applies to our current library and target.
+            const ok_lib_and_target =
+                (lib_index == lib_i) and
+                ((targets & (@as(u32, 1) << @intCast(u5, target_targ_index))) != 0);
+
+            while (true) {
+                const byte = metadata.inclusions[inc_i];
                 inc_i += 1;
-                const is_terminal = (targets & (1 << 31)) != 0;
-                if (is_terminal) opt_symbol_name = null;
-
-                // Test whether the inclusion applies to our current library and target.
-                const ok_lib_and_target =
-                    (lib_index == lib_i) and
-                    ((targets & (@as(u32, 1) << @intCast(u5, target_targ_index))) != 0);
-
-                while (true) {
-                    const byte = metadata.inclusions[inc_i];
-                    inc_i += 1;
-                    const last = (byte & 0b1000_0000) != 0;
-                    const ver_i = @truncate(u7, byte);
-                    if (ok_lib_and_target and ver_i <= target_ver_index) {
-                        versions_buffer[versions_len] = ver_i;
-                        versions_len += 1;
-                    }
-                    if (last) break;
+                const last = (byte & 0b1000_0000) != 0;
+                const ver_i = @truncate(u7, byte);
+                if (ok_lib_and_target and ver_i <= target_ver_index) {
+                    versions_buffer[versions_len] = ver_i;
+                    versions_len += 1;
                 }
+                if (last) break;
+            }
 
-                if (!is_terminal) continue;
-
-                // Pick the default symbol version:
-                // - If there are no versions, don't emit it
-                // - Take the greatest one <= than the target one
-                // - If none of them is <= than the
-                //   specified one don't pick any default version
-                if (versions_len == 0) continue;
-                var chosen_def_ver_index: u8 = 255;
-                {
-                    var ver_buf_i: u8 = 0;
-                    while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
-                        const ver_index = versions_buffer[ver_buf_i];
-                        if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
-                            chosen_def_ver_index = ver_index;
-                        }
+            if (!is_terminal) continue;
+
+            // Pick the default symbol version:
+            // - If there are no versions, don't emit it
+            // - Take the greatest one <= than the target one
+            // - If none of them is <= than the
+            //   specified one don't pick any default version
+            if (versions_len == 0) continue;
+            var chosen_def_ver_index: u8 = 255;
+            {
+                var ver_buf_i: u8 = 0;
+                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
+                    const ver_index = versions_buffer[ver_buf_i];
+                    if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
+                        chosen_def_ver_index = ver_index;
                     }
                 }
-                {
-                    var ver_buf_i: u8 = 0;
-                    while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
-                        // Example:
-                        // .globl _Exit_2_2_5
-                        // .type _Exit_2_2_5, %function;
-                        // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5
-                        // _Exit_2_2_5:
-                        const ver_index = versions_buffer[ver_buf_i];
-                        const ver = metadata.all_versions[ver_index];
-                        // Default symbol version definition vs normal symbol version definition
-                        const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
-                        const at_sign_str: []const u8 = if (want_default) "@@" else "@";
-                        if (ver.patch == 0) {
-                            const sym_plus_ver = if (want_default)
-                                sym_name
-                            else
-                                try std.fmt.allocPrint(
-                                    arena,
-                                    "{s}_GLIBC_{d}_{d}",
-                                    .{ sym_name, ver.major, ver.minor },
-                                );
-                            try stubs_asm.writer().print(
-                                \\.globl {s}
-                                \\.type {s}, %function;
-                                \\.symver {s}, {s}{s}GLIBC_{d}.{d}
-                                \\{s}:
-                                \\
-                            , .{
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_name,
-                                at_sign_str,
-                                ver.major,
-                                ver.minor,
-                                sym_plus_ver,
-                            });
-                        } else {
-                            const sym_plus_ver = if (want_default)
-                                sym_name
-                            else
-                                try std.fmt.allocPrint(
-                                    arena,
-                                    "{s}_GLIBC_{d}_{d}_{d}",
-                                    .{ sym_name, ver.major, ver.minor, ver.patch },
-                                );
-                            try stubs_asm.writer().print(
-                                \\.globl {s}
-                                \\.type {s}, %function;
-                                \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
-                                \\{s}:
-                                \\
-                            , .{
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_name,
-                                at_sign_str,
-                                ver.major,
-                                ver.minor,
-                                ver.patch,
-                                sym_plus_ver,
-                            });
-                        }
+            }
+            {
+                var ver_buf_i: u8 = 0;
+                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
+                    // Example:
+                    // .globl _Exit_2_2_5
+                    // .type _Exit_2_2_5, %function;
+                    // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5
+                    // _Exit_2_2_5:
+                    const ver_index = versions_buffer[ver_buf_i];
+                    const ver = metadata.all_versions[ver_index];
+                    // Default symbol version definition vs normal symbol version definition
+                    const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
+                    const at_sign_str: []const u8 = if (want_default) "@@" else "@";
+                    if (ver.patch == 0) {
+                        const sym_plus_ver = if (want_default)
+                            sym_name
+                        else
+                            try std.fmt.allocPrint(
+                                arena,
+                                "{s}_GLIBC_{d}_{d}",
+                                .{ sym_name, ver.major, ver.minor },
+                            );
+                        try stubs_asm.writer().print(
+                            \\.globl {s}
+                            \\.type {s}, %function;
+                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}
+                            \\{s}:
+                            \\
+                        , .{
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_name,
+                            at_sign_str,
+                            ver.major,
+                            ver.minor,
+                            sym_plus_ver,
+                        });
+                    } else {
+                        const sym_plus_ver = if (want_default)
+                            sym_name
+                        else
+                            try std.fmt.allocPrint(
+                                arena,
+                                "{s}_GLIBC_{d}_{d}_{d}",
+                                .{ sym_name, ver.major, ver.minor, ver.patch },
+                            );
+                        try stubs_asm.writer().print(
+                            \\.globl {s}
+                            \\.type {s}, %function;
+                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
+                            \\{s}:
+                            \\
+                        , .{
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_name,
+                            at_sign_str,
+                            ver.major,
+                            ver.minor,
+                            ver.patch,
+                            sym_plus_ver,
+                        });
                     }
                 }
             }
+        }
+
+        try stubs_asm.appendSlice(".data\n");
+
+        const obj_inclusions_len = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
+        inc_i += 2;
 
-            try stubs_asm.appendSlice(".data\n");
+        sym_i = 0;
+        opt_symbol_name = null;
+        versions_buffer = undefined;
+        versions_len = undefined;
+        while (sym_i < obj_inclusions_len) : (sym_i += 1) {
+            const sym_name = opt_symbol_name orelse n: {
+                const name = mem.sliceTo(metadata.inclusions[inc_i..], 0);
+                inc_i += name.len + 1;
 
-            const obj_inclusions_len = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
+                opt_symbol_name = name;
+                versions_buffer = undefined;
+                versions_len = 0;
+                break :n name;
+            };
+            const targets = mem.readIntLittle(u32, metadata.inclusions[inc_i..][0..4]);
+            inc_i += 4;
+
+            const size = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
             inc_i += 2;
 
-            sym_i = 0;
-            opt_symbol_name = null;
-            versions_buffer = undefined;
-            versions_len = undefined;
-            while (sym_i < obj_inclusions_len) : (sym_i += 1) {
-                const sym_name = opt_symbol_name orelse n: {
-                    const name = mem.sliceTo(metadata.inclusions[inc_i..], 0);
-                    inc_i += name.len + 1;
-
-                    opt_symbol_name = name;
-                    versions_buffer = undefined;
-                    versions_len = 0;
-                    break :n name;
-                };
-                const targets = mem.readIntLittle(u32, metadata.inclusions[inc_i..][0..4]);
-                inc_i += 4;
+            const lib_index = metadata.inclusions[inc_i];
+            inc_i += 1;
+            const is_terminal = (targets & (1 << 31)) != 0;
+            if (is_terminal) opt_symbol_name = null;
 
-                const size = mem.readIntLittle(u16, metadata.inclusions[inc_i..][0..2]);
-                inc_i += 2;
+            // Test whether the inclusion applies to our current library and target.
+            const ok_lib_and_target =
+                (lib_index == lib_i) and
+                ((targets & (@as(u32, 1) << @intCast(u5, target_targ_index))) != 0);
 
-                const lib_index = metadata.inclusions[inc_i];
+            while (true) {
+                const byte = metadata.inclusions[inc_i];
                 inc_i += 1;
-                const is_terminal = (targets & (1 << 31)) != 0;
-                if (is_terminal) opt_symbol_name = null;
-
-                // Test whether the inclusion applies to our current library and target.
-                const ok_lib_and_target =
-                    (lib_index == lib_i) and
-                    ((targets & (@as(u32, 1) << @intCast(u5, target_targ_index))) != 0);
-
-                while (true) {
-                    const byte = metadata.inclusions[inc_i];
-                    inc_i += 1;
-                    const last = (byte & 0b1000_0000) != 0;
-                    const ver_i = @truncate(u7, byte);
-                    if (ok_lib_and_target and ver_i <= target_ver_index) {
-                        versions_buffer[versions_len] = ver_i;
-                        versions_len += 1;
-                    }
-                    if (last) break;
+                const last = (byte & 0b1000_0000) != 0;
+                const ver_i = @truncate(u7, byte);
+                if (ok_lib_and_target and ver_i <= target_ver_index) {
+                    versions_buffer[versions_len] = ver_i;
+                    versions_len += 1;
                 }
+                if (last) break;
+            }
+
+            if (!is_terminal) continue;
 
-                if (!is_terminal) continue;
-
-                // Pick the default symbol version:
-                // - If there are no versions, don't emit it
-                // - Take the greatest one <= than the target one
-                // - If none of them is <= than the
-                //   specified one don't pick any default version
-                if (versions_len == 0) continue;
-                var chosen_def_ver_index: u8 = 255;
-                {
-                    var ver_buf_i: u8 = 0;
-                    while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
-                        const ver_index = versions_buffer[ver_buf_i];
-                        if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
-                            chosen_def_ver_index = ver_index;
-                        }
+            // Pick the default symbol version:
+            // - If there are no versions, don't emit it
+            // - Take the greatest one <= than the target one
+            // - If none of them is <= than the
+            //   specified one don't pick any default version
+            if (versions_len == 0) continue;
+            var chosen_def_ver_index: u8 = 255;
+            {
+                var ver_buf_i: u8 = 0;
+                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
+                    const ver_index = versions_buffer[ver_buf_i];
+                    if (chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) {
+                        chosen_def_ver_index = ver_index;
                     }
                 }
-                {
-                    var ver_buf_i: u8 = 0;
-                    while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
-                        // Example:
-                        // .globl environ_2_2_5
-                        // .type environ_2_2_5, %object;
-                        // .size environ_2_2_5, 4;
-                        // .symver environ_2_2_5, environ@@GLIBC_2.2.5
-                        // environ_2_2_5:
-                        const ver_index = versions_buffer[ver_buf_i];
-                        const ver = metadata.all_versions[ver_index];
-                        // Default symbol version definition vs normal symbol version definition
-                        const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
-                        const at_sign_str: []const u8 = if (want_default) "@@" else "@";
-                        if (ver.patch == 0) {
-                            const sym_plus_ver = if (want_default)
-                                sym_name
-                            else
-                                try std.fmt.allocPrint(
-                                    arena,
-                                    "{s}_GLIBC_{d}_{d}",
-                                    .{ sym_name, ver.major, ver.minor },
-                                );
-                            try stubs_asm.writer().print(
-                                \\.globl {s}
-                                \\.type {s}, %object;
-                                \\.size {s}, {d};
-                                \\.symver {s}, {s}{s}GLIBC_{d}.{d}
-                                \\{s}:
-                                \\
-                            , .{
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                size,
-                                sym_plus_ver,
-                                sym_name,
-                                at_sign_str,
-                                ver.major,
-                                ver.minor,
-                                sym_plus_ver,
-                            });
-                        } else {
-                            const sym_plus_ver = if (want_default)
-                                sym_name
-                            else
-                                try std.fmt.allocPrint(
-                                    arena,
-                                    "{s}_GLIBC_{d}_{d}_{d}",
-                                    .{ sym_name, ver.major, ver.minor, ver.patch },
-                                );
-                            try stubs_asm.writer().print(
-                                \\.globl {s}
-                                \\.type {s}, %object;
-                                \\.size {s}, {d};
-                                \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
-                                \\{s}:
-                                \\
-                            , .{
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                sym_plus_ver,
-                                size,
-                                sym_plus_ver,
-                                sym_name,
-                                at_sign_str,
-                                ver.major,
-                                ver.minor,
-                                ver.patch,
-                                sym_plus_ver,
-                            });
-                        }
+            }
+            {
+                var ver_buf_i: u8 = 0;
+                while (ver_buf_i < versions_len) : (ver_buf_i += 1) {
+                    // Example:
+                    // .globl environ_2_2_5
+                    // .type environ_2_2_5, %object;
+                    // .size environ_2_2_5, 4;
+                    // .symver environ_2_2_5, environ@@GLIBC_2.2.5
+                    // environ_2_2_5:
+                    const ver_index = versions_buffer[ver_buf_i];
+                    const ver = metadata.all_versions[ver_index];
+                    // Default symbol version definition vs normal symbol version definition
+                    const want_default = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
+                    const at_sign_str: []const u8 = if (want_default) "@@" else "@";
+                    if (ver.patch == 0) {
+                        const sym_plus_ver = if (want_default)
+                            sym_name
+                        else
+                            try std.fmt.allocPrint(
+                                arena,
+                                "{s}_GLIBC_{d}_{d}",
+                                .{ sym_name, ver.major, ver.minor },
+                            );
+                        try stubs_asm.writer().print(
+                            \\.globl {s}
+                            \\.type {s}, %object;
+                            \\.size {s}, {d};
+                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}
+                            \\{s}:
+                            \\
+                        , .{
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            size,
+                            sym_plus_ver,
+                            sym_name,
+                            at_sign_str,
+                            ver.major,
+                            ver.minor,
+                            sym_plus_ver,
+                        });
+                    } else {
+                        const sym_plus_ver = if (want_default)
+                            sym_name
+                        else
+                            try std.fmt.allocPrint(
+                                arena,
+                                "{s}_GLIBC_{d}_{d}_{d}",
+                                .{ sym_name, ver.major, ver.minor, ver.patch },
+                            );
+                        try stubs_asm.writer().print(
+                            \\.globl {s}
+                            \\.type {s}, %object;
+                            \\.size {s}, {d};
+                            \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
+                            \\{s}:
+                            \\
+                        , .{
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            sym_plus_ver,
+                            size,
+                            sym_plus_ver,
+                            sym_name,
+                            at_sign_str,
+                            ver.major,
+                            ver.minor,
+                            ver.patch,
+                            sym_plus_ver,
+                        });
                     }
                 }
             }
+        }
 
-            var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
-            const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
-            try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items);
+        var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
+        const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
+        try o_directory.handle.writeFile(asm_file_basename, stubs_asm.items);
 
-            try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib);
-        }
-        // No need to write the manifest because there are no file inputs associated with this cache hash.
-        // However we do need to write the ok file now.
-        if (o_directory.handle.createFile(ok_basename, .{})) |file| {
-            file.close();
-        } else |err| {
-            log.warn("glibc shared objects: failed to mark completion: {s}", .{@errorName(err)});
-        }
+        try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib);
     }
 
+    man.writeManifest() catch |err| {
+        log.warn("failed to write cache manifest for glibc stubs: {s}", .{@errorName(err)});
+    };
+
     assert(comp.glibc_so_files == null);
     comp.glibc_so_files = BuiltSharedObjects{
-        .lock = cache.toOwnedLock(),
-        .dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }),
+        .lock = man.toOwnedLock(),
+        .dir_path = try comp.global_cache_directory.join(comp.gpa, &.{ "o", &digest }),
     };
 }
 
src/print_targets.zig
@@ -25,7 +25,17 @@ pub fn cmdTargets(
     defer zig_lib_directory.handle.close();
     defer allocator.free(zig_lib_directory.path.?);
 
-    const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle);
+    const abilists_contents = zig_lib_directory.handle.readFileAlloc(
+        allocator,
+        glibc.abilists_path,
+        glibc.abilists_max_size,
+    ) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
+        else => fatal("unable to read " ++ glibc.abilists_path ++ ": {s}", .{@errorName(err)}),
+    };
+    defer allocator.free(abilists_contents);
+
+    const glibc_abi = try glibc.loadMetaData(allocator, abilists_contents);
     defer glibc_abi.destroy(allocator);
 
     var bw = io.bufferedWriter(stdout);