diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index df1b5127dfb8..d0f982d6f2a4 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -1,7 +1,7 @@ //! To get started, run this tool with no args and read the help message. //! -//! This tool extracts the Linux syscall numbers from the Linux source tree -//! directly, and emits an enumerated list per supported Zig arch. +//! This tool extracts syscall numbers from the Linux source tree +//! and emits an enumerated list per arch. const std = @import("std"); const mem = std.mem; @@ -26,676 +26,240 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ .{ "mmap_pgoff", "mmap2" }, }); -// Only for newer architectures where we use the C preprocessor. -const stdlib_renames_new = std.StaticStringMap([]const u8).initComptime(.{ - .{ "newuname", "uname" }, - .{ "umount", "umount2" }, -}); - -// We use this to deal with the fact that multiple syscalls can be mapped to sys_ni_syscall. -// Thankfully it's only 2 well-known syscalls in newer kernel ports at the moment. -fn getOverridenNameNew(value: []const u8) ?[]const u8 { - if (mem.eql(u8, value, "18")) { - return "sys_lookup_dcookie"; - } else if (mem.eql(u8, value, "42")) { - return "sys_nfsservctl"; - } else { - return null; - } -} - -fn isReservedNameOld(name: []const u8) bool { +/// Filter syscalls that aren't actually syscalls. +fn isReserved(name: []const u8) bool { return std.mem.startsWith(u8, name, "available") or std.mem.startsWith(u8, name, "reserved") or std.mem.startsWith(u8, name, "unused"); } -const default_args: []const []const u8 = &.{ - "-E", - // -dM is cleaner, but -dD preserves iteration order. - "-dD", - // No need for line-markers. - "-P", - "-nostdinc", - // Using -I=[dir] includes the zig linux headers, which we don't want. - "-Itools/include", - "-Itools/include/uapi", - // Output the syscall in a format we can easily recognize. - "-D __SYSCALL(nr, nm)=zigsyscall nm nr", -}; - -const ProcessPreprocessedFileFn = *const fn (bytes: []const u8, writer: anytype) anyerror!void; -const ProcessTableBasedArchFileFn = *const fn ( - bytes: []const u8, - filters: Filters, - writer: anytype, - optional_writer: anytype, -) anyerror!void; - -const FlowControl = enum { - @"break", - @"continue", - none, -}; - -const AbiCheckParams = struct { abi: []const u8, flow: FlowControl }; - -const Filters = struct { - abiCheckParams: ?AbiCheckParams, - fixedName: ?*const fn (name: []const u8) []const u8, - isReservedNameOld: ?*const fn (name: []const u8) bool, -}; - -fn abiCheck(abi: []const u8, params: *const AbiCheckParams) FlowControl { - if (mem.eql(u8, abi, params.abi)) return params.flow; - return .none; +fn abiGen(comptime fields: []const []const u8) fn ([]const u8) bool { + const common = [_][]const u8{"common"} ++ fields; + return struct { + fn gen(abi: []const u8) bool { + for (common) |f| + if (mem.eql(u8, abi, f)) return true; + return false; + } + }.gen; } -fn fixedName(name: []const u8) []const u8 { - return if (stdlib_renames.get(name)) |fixed| fixed else name; +/// Used when the abi column is the same value. +fn everythingAbi(_: []const u8) bool { + return true; } - -const ArchInfo = union(enum) { - table: struct { - name: []const u8, - enum_name: []const u8, - file_path: []const u8, - header: ?[]const u8, - extra_values: ?[]const u8, - process_file: ProcessTableBasedArchFileFn, - filters: Filters, - additional_enum: ?[]const u8, - }, - preprocessor: struct { - name: []const u8, - enum_name: []const u8, - file_path: []const u8, - child_options: struct { - comptime additional_args: ?[]const []const u8 = null, - target: []const u8, - - pub inline fn getArgs(self: *const @This(), zig_exe: []const u8, file_path: []const u8) []const []const u8 { - const additional_args: []const []const u8 = self.additional_args orelse &.{}; - return .{ zig_exe, "cc" } ++ additional_args ++ .{ "-target", self.target } ++ default_args ++ .{file_path}; - } - }, - header: ?[]const u8, - extra_values: ?[]const u8, - process_file: ProcessPreprocessedFileFn, - additional_enum: ?[]const u8, - }, -}; - -const arch_infos = [_]ArchInfo{ - .{ - // These architectures have their syscall definitions generated from a TSV - // file, processed via scripts/syscallhdr.sh. - .table = .{ - .name = "x86", - .enum_name = "X86", - .file_path = "arch/x86/entry/syscalls/syscall_32.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - .abiCheckParams = null, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "x64", - .enum_name = "X64", - .file_path = "arch/x86/entry/syscalls/syscall_64.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - // The x32 abi syscalls are always at the end. - .abiCheckParams = .{ .abi = "x32", .flow = .@"break" }, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "arm", - .enum_name = "Arm", - .file_path = "arch/arm/tools/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - .abiCheckParams = .{ .abi = "oabi", .flow = .@"continue" }, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = " const arm_base = 0x0f0000;\n\n", - // TODO: maybe extract these from arch/arm/include/uapi/asm/unistd.h - .extra_values = - \\ - \\ breakpoint = arm_base + 1, - \\ cacheflush = arm_base + 2, - \\ usr26 = arm_base + 3, - \\ usr32 = arm_base + 4, - \\ set_tls = arm_base + 5, - \\ get_tls = arm_base + 6, - \\ - , - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "sparc", - .enum_name = "Sparc", - .file_path = "arch/sparc/kernel/syscalls/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - .abiCheckParams = .{ .abi = "64", .flow = .@"continue" }, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "sparc64", - .enum_name = "Sparc64", - .file_path = "arch/sparc/kernel/syscalls/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - .abiCheckParams = .{ .abi = "32", .flow = .@"continue" }, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "m68k", - .enum_name = "M68k", - .file_path = "arch/m68k/kernel/syscalls/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - // abi is always common - .abiCheckParams = null, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "mips_o32", - .enum_name = "MipsO32", - .file_path = "arch/mips/kernel/syscalls/syscall_o32.tbl", - .process_file = &processMipsBasedArch, - .filters = .{ - // abi is always o32 - .abiCheckParams = null, - .fixedName = &fixedName, - .isReservedNameOld = &isReservedNameOld, - }, - .header = " const linux_base = 4000;\n\n", - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "mips_n64", - .enum_name = "MipsN64", - .file_path = "arch/mips/kernel/syscalls/syscall_n64.tbl", - .process_file = &processMipsBasedArch, - .filters = .{ - // abi is always n64 - .abiCheckParams = null, - .fixedName = &fixedName, - .isReservedNameOld = &isReservedNameOld, - }, - .header = " const linux_base = 5000;\n\n", - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "mips_n32", - .enum_name = "MipsN32", - .file_path = "arch/mips/kernel/syscalls/syscall_n32.tbl", - .process_file = &processMipsBasedArch, - .filters = .{ - // abi is always n32 - .abiCheckParams = null, - .fixedName = &fixedName, - .isReservedNameOld = &isReservedNameOld, - }, - .header = " const linux_base = 6000;\n\n", - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "powerpc", - .enum_name = "PowerPC", - .file_path = "arch/powerpc/kernel/syscalls/syscall.tbl", - .process_file = &processPowerPcBasedArch, - .filters = .{ - .abiCheckParams = null, - .fixedName = null, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = "PowerPC64", - }, - }, - .{ - .table = .{ - .name = "s390x", - .enum_name = "S390x", - .file_path = "arch/s390/kernel/syscalls/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - // 32-bit s390 support in linux is deprecated - .abiCheckParams = .{ .abi = "32", .flow = .@"continue" }, - .fixedName = &fixedName, - .isReservedNameOld = null, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .table = .{ - .name = "xtensa", - .enum_name = "Xtensa", - .file_path = "arch/xtensa/kernel/syscalls/syscall.tbl", - .process_file = &processTableBasedArch, - .filters = .{ - // abi is always common - .abiCheckParams = null, - .fixedName = fixedName, - .isReservedNameOld = &isReservedNameOld, - }, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "arm64", - .enum_name = "Arm64", - .file_path = "arch/arm64/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "aarch64-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, +/// "common" or "32" +const abi32 = abiGen(&.{"32"}); +/// "common" or "64" +const abi64 = abiGen(&.{"64"}); +/// "common" or "eabi" +const abiArm = abiGen(&.{"eabi"}); +/// "common", "32" or "nospu" +const abiPpc32 = abiGen(&.{ "32", "nospu" }); +/// "common", "64" or "nospu" +const abiPpc64 = abiGen(&.{ "64", "nospu" }); + +/// These architectures have custom syscall numbers defined in arch-specific tables. +const specific = [_]struct { + var_name: []const u8, + table: []const u8, + abi: *const fn (abi: []const u8) bool, + header: ?[]const u8 = null, + footer: ?[]const u8 = null, +}{ + .{ .var_name = "X86", .table = "arch/x86/entry/syscalls/syscall_32.tbl", .abi = everythingAbi }, + .{ .var_name = "X64", .table = "arch/x86/entry/syscalls/syscall_64.tbl", .abi = abi64 }, .{ - .preprocessor = .{ - .name = "riscv32", - .enum_name = "RiscV32", - .file_path = "arch/riscv/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "riscv32-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "riscv64", - .enum_name = "RiscV64", - .file_path = "arch/riscv/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "riscv64-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "loongarch", - .enum_name = "LoongArch64", - .file_path = "arch/loongarch/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "loongarch64-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "arc", - .enum_name = "Arc", - .file_path = "arch/arc/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "arc-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "csky", - .enum_name = "CSky", - .file_path = "arch/csky/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "csky-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, - }, - .{ - .preprocessor = .{ - .name = "hexagon", - .enum_name = "Hexagon", - .file_path = "arch/hexagon/include/uapi/asm/unistd.h", - .child_options = .{ - .additional_args = null, - .target = "hexagon-freestanding-none", - }, - .process_file = &processPreprocessedFile, - .header = null, - .extra_values = null, - .additional_enum = null, - }, + .var_name = "Arm", + .table = "arch/arm/tools/syscall.tbl", + .abi = abiArm, + // TODO: These values haven't been brought over from `arch/arm/include/uapi/asm/unistd.h`. + .header = " const arm_base = 0x0f0000;\n\n", + .footer = + \\ + \\ breakpoint = arm_base + 1, + \\ cacheflush = arm_base + 2, + \\ usr26 = arm_base + 3, + \\ usr32 = arm_base + 4, + \\ set_tls = arm_base + 5, + \\ get_tls = arm_base + 6, + \\ + , }, + .{ .var_name = "Sparc", .table = "arch/sparc/kernel/syscalls/syscall.tbl", .abi = abi32 }, + .{ .var_name = "Sparc64", .table = "arch/sparc/kernel/syscalls/syscall.tbl", .abi = abi64 }, + .{ .var_name = "M68k", .table = "arch/m68k/kernel/syscalls/syscall.tbl", .abi = everythingAbi }, + .{ .var_name = "PowerPC", .table = "arch/powerpc/kernel/syscalls/syscall.tbl", .abi = abiPpc32 }, + .{ .var_name = "PowerPC64", .table = "arch/powerpc/kernel/syscalls/syscall.tbl", .abi = abiPpc64 }, + .{ .var_name = "S390x", .table = "arch/s390/kernel/syscalls/syscall.tbl", .abi = abi64 }, + .{ .var_name = "Xtensa", .table = "arch/xtensa/kernel/syscalls/syscall.tbl", .abi = everythingAbi }, + // TODO: Enable these when a backend is available + // .{ .var_name = "SuperH", .table = "arch/sh/kernel/syscalls/syscall.tbl", .abi = everythingAbi }, + // .{ .var_name = "Alpha", .table = "arch/alpha/kernel/syscalls/syscall.tbl", .abi = everythingAbi }, + // .{ .var_name = "PARisc", .table = "arch/parisc/kernel/syscalls/syscall.tbl", .abi = abi32 }, + // .{ .var_name = "PARisc64", .table = "arch/parisc/kernel/syscalls/syscall.tbl", .abi = abi64 }, }; -fn processPreprocessedFile( - bytes: []const u8, - writer: anytype, -) !void { - var lines = mem.tokenizeScalar(u8, bytes, '\n'); - while (lines.next()) |line| { - var fields = mem.tokenizeAny(u8, line, " "); - const prefix = fields.next() orelse return error.Incomplete; +/// The MIPS-based architectures are similar to the specific ones, except that the abi +/// is always the same and syscall numbers are offset by a number specific to the arch. +const mips = [_]struct { + var_name: []const u8, + table: []const u8, + base: usize, +}{ + .{ .var_name = "MipsO32", .table = "arch/mips/kernel/syscalls/syscall_o32.tbl", .base = 4000 }, + .{ .var_name = "MipsN64", .table = "arch/mips/kernel/syscalls/syscall_n64.tbl", .base = 5000 }, + .{ .var_name = "MipsN32", .table = "arch/mips/kernel/syscalls/syscall_n32.tbl", .base = 6000 }, +}; - if (!mem.eql(u8, prefix, "zigsyscall")) continue; +/// These architectures have their syscall numbers defined using the generic syscall +/// list introduced in c. 2012 for AArch64. +/// The 6.11 release converted this list into a single table, where parts of the +/// syscall ABI are enabled based on the presence of certain abi fields: +/// - 32: Syscalls using 64-bit types on 32-bit targets. +/// - 64: 64-bit native syscalls. +/// - time32: 32-bit time syscalls. +/// - renameat: Supports the older renameat syscall along with renameat2. +/// - rlimit: Supports the {get,set}rlimit syscalls. +/// - memfd_secret: Has an implementation of `memfd_secret`. +/// +/// Arch-specfic syscalls between [244...259] are also enabled by adding the arch name as an abi. +/// +/// The abi fields are sourced from the respectiev `arch/{arch}/kernel/Makefile.syscalls` files +/// in the kernel tree. +const generic = [_]struct { + var_name: []const u8, + abi: *const fn (abi: []const u8) bool, +}{ + .{ .var_name = "Arm64", .abi = abiGen(&.{ "64", "renameat", "rlimit", "memfd_secret" }) }, + .{ .var_name = "RiscV32", .abi = abiGen(&.{ "32", "riscv", "memfd_secret" }) }, + .{ .var_name = "RiscV64", .abi = abiGen(&.{ "64", "riscv", "rlimit", "memfd_secret" }) }, + .{ .var_name = "LoongArch64", .abi = abi64 }, + .{ .var_name = "Arc", .abi = abiGen(&.{ "32", "arc", "time32", "renameat", "stat64", "rlimit" }) }, + .{ .var_name = "CSky", .abi = abiGen(&.{ "32", "csky", "time32", "stat64", "rlimit" }) }, + .{ .var_name = "Hexagon", .abi = abiGen(&.{ "32", "hexagon", "time32", "stat64", "rlimit", "renameat" }) }, + // .{ .var_name = "OpenRisc", .abi = abiGen(&.{ "32", "or1k", "time32", "stat64", "rlimit", "renameat" }) }, + // .{ .var_name = "Nios2", .abi = abiGen(&.{ "32", "nios2", "time32", "stat64", "renameat", "rlimit" }) }, +}; +const generic_table = "scripts/syscall.tbl"; - const sys_name = fields.next() orelse return error.Incomplete; - const value = fields.rest(); - const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..]; - const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name; +fn splitLine(line: []const u8) [3][]const u8 { + var fields = mem.tokenizeAny(u8, line, " \t"); + const number = fields.next() orelse @panic("Bad field"); + const abi = fields.next() orelse @panic("Bad field"); + const name = fields.next() orelse @panic("Bad field"); - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value }); - } + return .{ number, abi, name }; } -fn processTableBasedArch( - bytes: []const u8, - filters: Filters, - writer: anytype, - optional_writer: anytype, -) !void { - _ = optional_writer; - - var lines = mem.tokenizeScalar(u8, bytes, '\n'); - while (lines.next()) |line| { - if (line[0] == '#') continue; - - var fields = mem.tokenizeAny(u8, line, " \t"); - const number = fields.next() orelse return error.Incomplete; - - const abi = fields.next() orelse return error.Incomplete; - if (filters.abiCheckParams) |*params| { - switch (abiCheck(abi, params)) { - .none => {}, - .@"break" => break, - .@"continue" => continue, - } - } - const name = fields.next() orelse return error.Incomplete; - if (filters.isReservedNameOld) |isReservedNameOldFn| { - if (isReservedNameOldFn(name)) continue; - } - const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name; - - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); - } -} +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); -fn processMipsBasedArch( - bytes: []const u8, - filters: Filters, - writer: anytype, - optional_writer: anytype, -) !void { - _ = optional_writer; - - var lines = mem.tokenizeScalar(u8, bytes, '\n'); - while (lines.next()) |line| { - if (line[0] == '#') continue; - - var fields = mem.tokenizeAny(u8, line, " \t"); - const number = fields.next() orelse return error.Incomplete; - - const abi = fields.next() orelse return error.Incomplete; - if (filters.abiCheckParams) |*params| { - switch (abiCheck(abi, params)) { - .none => {}, - .@"break" => break, - .@"continue" => continue, - } - } - const name = fields.next() orelse return error.Incomplete; - if (filters.isReservedNameOld) |isReservedNameOldFn| { - if (isReservedNameOldFn(name)) continue; - } - const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name; + const args = try std.process.argsAlloc(allocator); + if (args.len < 2 or mem.eql(u8, args[1], "--help")) + usageAndExit(std.io.getStdErr(), args[0], 1); + const linux_path = args[1]; - try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number }); - } -} + var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer()); + const writer = buf_out.writer(); -fn processPowerPcBasedArch( - bytes: []const u8, - filters: Filters, - writer: anytype, - optional_writer: anytype, -) !void { - _ = filters; - var lines = mem.tokenizeScalar(u8, bytes, '\n'); - - while (lines.next()) |line| { - if (line[0] == '#') continue; - - var fields = mem.tokenizeAny(u8, line, " \t"); - const number = fields.next() orelse return error.Incomplete; - const abi = fields.next() orelse return error.Incomplete; - const name = fields.next() orelse return error.Incomplete; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - - if (mem.eql(u8, abi, "spu")) { - continue; - } else if (mem.eql(u8, abi, "32")) { - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); - } else if (mem.eql(u8, abi, "64")) { - try optional_writer.?.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); - } else { // common/nospu - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); - try optional_writer.?.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); - } - } -} + var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); + defer linux_dir.close(); -fn generateSyscallsFromTable( - allocator: std.mem.Allocator, - buf: []u8, - linux_dir: std.fs.Dir, - writer: anytype, - _arch_info: *const ArchInfo, -) !void { - std.debug.assert(_arch_info.* == .table); + // As of 6.11, the largest table is 24195 bytes. + // 32k should be enough for now. + const buf = try allocator.alloc(u8, 1 << 15); + defer allocator.free(buf); - const arch_info = _arch_info.table; + // Fetch the kernel version from the Makefile variables. + const version = blk: { + const head = try linux_dir.readFile("Makefile", buf[0..128]); + var lines = mem.tokenizeScalar(u8, head, '\n'); + _ = lines.next(); // Skip SPDX identifier + + var ver = mem.zeroes(std.SemanticVersion); + inline for (.{ "major", "minor", "patch" }, .{ "VERSION", "PATCHLEVEL", "SUBLEVEL" }) |f, v| { + const line = lines.next() orelse @panic("Bad line"); + const offset = (v ++ " = ").len; + @field(ver, f) = try fmt.parseInt(usize, line[offset..], 10); + } - const table = try linux_dir.readFile(arch_info.file_path, buf); + break :blk ver; + }; - var optional_array_list: ?std.ArrayList(u8) = if (arch_info.additional_enum) |_| std.ArrayList(u8).init(allocator) else null; - const optional_writer = if (optional_array_list) |_| optional_array_list.?.writer() else null; + try writer.print( + \\// This file is automatically generated, DO NOT edit it manually. + \\// See tools/generate_linux_syscalls.zig for more info. + \\// This list current as of kernel: {} + \\ + \\ + , .{version}); - try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name}); + const trailing = "};\n\n"; + for (specific) |arch| { + try writer.print("pub const {s} = enum(usize) {{\n", .{arch.var_name}); + if (arch.header) |h| + try writer.writeAll(h); + const table = try linux_dir.readFile(arch.table, buf); - if (arch_info.header) |header| { - try writer.writeAll(header); - } + var lines = mem.tokenizeScalar(u8, table, '\n'); + while (lines.next()) |line| { + if (line[0] == '#') continue; + const number, const abi, const name = splitLine(line); - try arch_info.process_file(table, arch_info.filters, writer, optional_writer); + if (!arch.abi(abi)) continue; + if (isReserved(name)) continue; - if (arch_info.extra_values) |extra_values| { - try writer.writeAll(extra_values); + const final_name = stdlib_renames.get(name) orelse name; + try writer.print(" {p} = {s},\n", .{ zig.fmtId(final_name), number }); + } + if (arch.footer) |f| + try writer.writeAll(f); + try writer.writeAll(trailing); } - try writer.writeAll("};"); - if (arch_info.additional_enum) |additional_enum| { - try writer.writeAll("\n\n"); - try writer.print("pub const {s} = enum(usize) {{\n", .{additional_enum}); - try writer.writeAll(optional_array_list.?.items); - try writer.writeAll("};"); - } -} + for (mips) |arch| { + try writer.print( + \\pub const {s} = enum(usize) {{ + \\ const linux_base = {d}; + \\ + \\ + , .{ arch.var_name, arch.base }); + const table = try linux_dir.readFile(arch.table, buf); -fn generateSyscallsFromPreprocessor( - allocator: std.mem.Allocator, - linux_dir: std.fs.Dir, - linux_path: []const u8, - zig_exe: []const u8, - writer: anytype, - _arch_info: *const ArchInfo, -) !void { - std.debug.assert(_arch_info.* == .preprocessor); - - const arch_info = _arch_info.preprocessor; - - const child_result = try std.process.Child.run(.{ - .allocator = allocator, - .argv = arch_info.child_options.getArgs(zig_exe, arch_info.file_path), - .cwd = linux_path, - .cwd_dir = linux_dir, - }); - if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr}); - - const defines = switch (child_result.term) { - .Exited => |code| if (code == 0) child_result.stdout else { - std.debug.print("zig cc exited with code {d}\n", .{code}); - std.process.exit(1); - }, - else => { - std.debug.print("zig cc crashed\n", .{}); - std.process.exit(1); - }, - }; + var lines = mem.tokenizeScalar(u8, table, '\n'); + while (lines.next()) |line| { + if (line[0] == '#') continue; + const number, _, const name = splitLine(line); - try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name}); - if (arch_info.header) |header| { - try writer.writeAll(header); - } + if (isReserved(name)) continue; - try arch_info.process_file(defines, writer); + const final_name = stdlib_renames.get(name) orelse name; + try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(final_name), number }); + } - if (arch_info.extra_values) |extra_values| { - try writer.writeAll(extra_values); + try writer.writeAll(trailing); } - try writer.writeAll("};"); -} - -pub fn main() !void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - const allocator = arena.allocator(); - - const args = try std.process.argsAlloc(allocator); - if (args.len < 3 or mem.eql(u8, args[1], "--help")) - usageAndExit(std.io.getStdErr(), args[0], 1); - const zig_exe = args[1]; - const linux_path = args[2]; - - var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer()); - const writer = buf_out.writer(); + const table = try linux_dir.readFile(generic_table, buf); + for (generic, 0..) |arch, i| { + try writer.print("pub const {s} = enum(usize) {{\n", .{arch.var_name}); - var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); - defer linux_dir.close(); + var lines = mem.tokenizeScalar(u8, table, '\n'); + while (lines.next()) |line| { + if (line[0] == '#') continue; + const number, const abi, const name = splitLine(line); - try writer.writeAll( - \\// This file is automatically generated. - \\// See tools/generate_linux_syscalls.zig for more info. - \\ - \\ - ); + if (!arch.abi(abi)) continue; + if (isReserved(name)) continue; - // As of 5.17.1, the largest table is 23467 bytes. - // 32k should be enough for now. - const buf = try allocator.alloc(u8, 1 << 15); - defer allocator.free(buf); - - inline for (arch_infos, 0..) |arch_info, i| { - switch (arch_info) { - .table => try generateSyscallsFromTable( - allocator, - buf, - linux_dir, - writer, - &arch_info, - ), - .preprocessor => try generateSyscallsFromPreprocessor( - allocator, - linux_dir, - linux_path, - zig_exe, - writer, - &arch_info, - ), - } - if (i < arch_infos.len - 1) { - try writer.writeAll("\n\n"); - } else { - try writer.writeAll("\n"); + const final_name = stdlib_renames.get(name) orelse name; + try writer.print(" {p} = {s},\n", .{ zig.fmtId(final_name), number }); } + + try writer.writeAll(trailing[0 .. 3 + @as(usize, @intFromBool(i < generic.len - 1))]); } try buf_out.flush();