| 1 | //! This script updates the .c, .h, .s, and .S files that make up the start |
| 2 | //! files such as crt1.o. Not to be confused with |
| 3 | //! https://codeberg.org/ziglang/libc-abi-tools which updates the `abilists` |
| 4 | //! file. |
| 5 | //! |
| 6 | //! Example usage: |
| 7 | //! `zig run ../tools/update_glibc.zig -- ~/Downloads/glibc ..` |
| 8 | |
| 9 | const std = @import("std"); |
| 10 | const Io = std.Io; |
| 11 | const Dir = std.Io.Dir; |
| 12 | const mem = std.mem; |
| 13 | const log = std.log; |
| 14 | const fatal = std.process.fatal; |
| 15 | |
| 16 | const exempt_files = [_][]const u8{ |
| 17 | // This file is maintained by a separate project and does not come from glibc. |
| 18 | "abilists", |
| 19 | |
| 20 | // Generated files. |
| 21 | "include/libc-modules.h", |
| 22 | "include/config.h", |
| 23 | |
| 24 | // These are easier to maintain like this, without updating to the abi-note.c |
| 25 | // that glibc did upstream. |
| 26 | "csu/abi-tag.h", |
| 27 | "csu/abi-note.S", |
| 28 | |
| 29 | // We have patched these files to require fewer includes. |
| 30 | "stdlib/at_quick_exit.c", |
| 31 | "stdlib/atexit.c", |
| 32 | "sysdeps/pthread/pthread_atfork.c", |
| 33 | }; |
| 34 | |
| 35 | const exempt_extensions = [_][]const u8{ |
| 36 | // These are the start files we use when targeting glibc <= 2.33. |
| 37 | "-2.33.S", |
| 38 | "-2.33.c", |
| 39 | "-2.32.c", |
| 40 | }; |
| 41 | |
| 42 | pub fn main(init: std.process.Init) !void { |
| 43 | const arena = init.arena.allocator(); |
| 44 | const io = init.io; |
| 45 | const args = try init.minimal.args.toSlice(arena); |
| 46 | |
| 47 | const glibc_src_path = args[1]; |
| 48 | const zig_src_path = args[2]; |
| 49 | |
| 50 | const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path}); |
| 51 | |
| 52 | var dest_dir = Dir.cwd().openDir(io, dest_dir_path, .{ .iterate = true }) catch |err| { |
| 53 | fatal("unable to open destination directory '{s}': {t}", .{ dest_dir_path, err }); |
| 54 | }; |
| 55 | defer dest_dir.close(io); |
| 56 | |
| 57 | var glibc_src_dir = try Dir.cwd().openDir(io, glibc_src_path, .{}); |
| 58 | defer glibc_src_dir.close(io); |
| 59 | |
| 60 | // Copy updated files from upstream. |
| 61 | { |
| 62 | var walker = try dest_dir.walk(arena); |
| 63 | defer walker.deinit(); |
| 64 | |
| 65 | walk: while (try walker.next(io)) |entry| { |
| 66 | if (entry.kind != .file) continue; |
| 67 | if (mem.startsWith(u8, entry.basename, ".")) continue; |
| 68 | for (exempt_files) |p| { |
| 69 | if (mem.eql(u8, entry.path, p)) continue :walk; |
| 70 | } |
| 71 | for (exempt_extensions) |ext| { |
| 72 | if (mem.endsWith(u8, entry.path, ext)) continue :walk; |
| 73 | } |
| 74 | |
| 75 | glibc_src_dir.copyFile(entry.path, dest_dir, entry.path, io, .{}) catch |err| { |
| 76 | log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {t}", .{ |
| 77 | glibc_src_path, entry.path, dest_dir_path, entry.path, err, |
| 78 | }); |
| 79 | if (err == error.FileNotFound) { |
| 80 | try dest_dir.deleteFile(io, entry.path); |
| 81 | } |
| 82 | }; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | // Warn about duplicated files inside glibc/include/* that can be omitted |
| 87 | // because they are already in generic-glibc/*. |
| 88 | |
| 89 | var include_dir = dest_dir.openDir(io, "include", .{ .iterate = true }) catch |err| { |
| 90 | fatal("unable to open directory '{s}/include': {t}", .{ dest_dir_path, err }); |
| 91 | }; |
| 92 | defer include_dir.close(io); |
| 93 | |
| 94 | const generic_glibc_path = try std.fmt.allocPrint( |
| 95 | arena, |
| 96 | "{s}/lib/libc/include/generic-glibc", |
| 97 | .{zig_src_path}, |
| 98 | ); |
| 99 | var generic_glibc_dir = try Dir.cwd().openDir(io, generic_glibc_path, .{}); |
| 100 | defer generic_glibc_dir.close(io); |
| 101 | |
| 102 | var walker = try include_dir.walk(arena); |
| 103 | defer walker.deinit(); |
| 104 | |
| 105 | walk: while (try walker.next(io)) |entry| { |
| 106 | if (entry.kind != .file) continue; |
| 107 | if (mem.startsWith(u8, entry.basename, ".")) continue; |
| 108 | for (exempt_files) |p| { |
| 109 | if (mem.eql(u8, entry.path, p)) continue :walk; |
| 110 | } |
| 111 | |
| 112 | const max_file_size = 10 * 1024 * 1024; |
| 113 | |
| 114 | const generic_glibc_contents = generic_glibc_dir.readFileAlloc( |
| 115 | io, |
| 116 | entry.path, |
| 117 | arena, |
| 118 | .limited(max_file_size), |
| 119 | ) catch |err| switch (err) { |
| 120 | error.FileNotFound => continue, |
| 121 | else => |e| fatal("unable to load '{s}/include/{s}': {t}", .{ generic_glibc_path, entry.path, e }), |
| 122 | }; |
| 123 | const glibc_include_contents = include_dir.readFileAlloc( |
| 124 | io, |
| 125 | entry.path, |
| 126 | arena, |
| 127 | .limited(max_file_size), |
| 128 | ) catch |err| { |
| 129 | fatal("unable to load '{s}/include/{s}': {t}", .{ dest_dir_path, entry.path, err }); |
| 130 | }; |
| 131 | |
| 132 | const whitespace = " \r\n\t"; |
| 133 | const generic_glibc_trimmed = mem.trim(u8, generic_glibc_contents, whitespace); |
| 134 | const glibc_include_trimmed = mem.trim(u8, glibc_include_contents, whitespace); |
| 135 | if (mem.eql(u8, generic_glibc_trimmed, glibc_include_trimmed)) { |
| 136 | log.warn("same contents: '{s}/include/{s}' and '{s}/include/{s}'", .{ |
| 137 | generic_glibc_path, entry.path, dest_dir_path, entry.path, |
| 138 | }); |
| 139 | } |
| 140 | } |
| 141 | } |