| ... | @@ -2,12 +2,18 @@ | ... | @@ -2,12 +2,18 @@ |
| 2 | //! | 2 | //! |
| 3 | //! This tool extracts the Linux syscall numbers from the Linux source tree | 3 | //! This tool extracts the Linux syscall numbers from the Linux source tree |
| 4 | //! directly, and emits an enumerated list per supported Zig arch. | 4 | //! directly, and emits an enumerated list per supported Zig arch. |
| | 5 | //! |
| | 6 | //! As of kernel version 6.11, all supported architectures have their syscalls |
| | 7 | //! defined in files with the following tabular format: |
| | 8 | //! |
| | 9 | //! # Comment |
| | 10 | //! <number> <abi> <name> ... |
| | 11 | //! |
| | 12 | //! Everything after `name` is ignored for the purposes of this tool. |
| 5 | | 13 | |
| 6 | const std = @import("std"); | 14 | const std = @import("std"); |
| | 15 | const Io = std.Io; |
| 7 | const mem = std.mem; | 16 | const mem = std.mem; |
| 8 | const fmt = std.fmt; | | |
| 9 | const zig = std.zig; | | |
| 10 | const fs = std.fs; | | |
| 11 | | 17 | |
| 12 | const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ | 18 | const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ |
| 13 | // Remove underscore prefix. | 19 | // Remove underscore prefix. |
| ... | @@ -22,702 +28,224 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ | ... | @@ -22,702 +28,224 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ |
| 22 | // ARM EABI/Thumb. | 28 | // ARM EABI/Thumb. |
| 23 | .{ "arm_sync_file_range", "sync_file_range" }, | 29 | .{ "arm_sync_file_range", "sync_file_range" }, |
| 24 | .{ "arm_fadvise64_64", "fadvise64_64" }, | 30 | .{ "arm_fadvise64_64", "fadvise64_64" }, |
| 25 | // ARC and Hexagon. | | |
| 26 | .{ "mmap_pgoff", "mmap2" }, | | |
| 27 | }); | | |
| 28 | | | |
| 29 | // Only for newer architectures where we use the C preprocessor. | | |
| 30 | const stdlib_renames_new = std.StaticStringMap([]const u8).initComptime(.{ | | |
| 31 | .{ "newuname", "uname" }, | | |
| 32 | .{ "umount", "umount2" }, | | |
| 33 | }); | 31 | }); |
| 34 | | 32 | |
| 35 | // We use this to deal with the fact that multiple syscalls can be mapped to sys_ni_syscall. | 33 | /// Filter syscalls that aren't actually syscalls. |
| 36 | // Thankfully it's only 2 well-known syscalls in newer kernel ports at the moment. | 34 | fn isReserved(name: []const u8) bool { |
| 37 | fn getOverridenNameNew(value: []const u8) ?[]const u8 { | 35 | return mem.startsWith(u8, name, "available") or |
| 38 | if (mem.eql(u8, value, "18")) { | 36 | mem.startsWith(u8, name, "reserved") or |
| 39 | return "sys_lookup_dcookie"; | 37 | mem.startsWith(u8, name, "unused"); |
| 40 | } else if (mem.eql(u8, value, "42")) { | | |
| 41 | return "sys_nfsservctl"; | | |
| 42 | } else { | | |
| 43 | return null; | | |
| 44 | } | | |
| 45 | } | | |
| 46 | | | |
| 47 | fn isReservedNameOld(name: []const u8) bool { | | |
| 48 | return std.mem.startsWith(u8, name, "available") or | | |
| 49 | std.mem.startsWith(u8, name, "reserved") or | | |
| 50 | std.mem.startsWith(u8, name, "unused"); | | |
| 51 | } | 38 | } |
| 52 | | 39 | |
| 53 | const default_args: []const []const u8 = &.{ | 40 | /// Values of the `abi` field in use by the syscall tables. |
| 54 | "-E", | 41 | /// |
| 55 | // -dM is cleaner, but -dD preserves iteration order. | 42 | /// Since c. 2012, all new Linux architectures use the same numbers for their syscalls. |
| 56 | "-dD", | 43 | /// Before kernel 6.11, the source of truth for this list was the arch-specific `uapi` headers. |
| 57 | // No need for line-markers. | 44 | /// The 6.11 release converted this into a unified table with the same format as the older archs. |
| 58 | "-P", | 45 | /// For these targets, syscalls are enabled/disabled based on the `abi` field. |
| 59 | "-nostdinc", | 46 | /// These fields are sourced from the respective `arch/{arch}/kernel/Makefile.syscalls` |
| 60 | // Using -I=[dir] includes the zig linux headers, which we don't want. | 47 | /// files in the kernel source tree. |
| 61 | "-Itools/include", | 48 | /// Architecture-specific syscalls between [244...259] are also enabled by adding the arch name as an abi. |
| 62 | "-Itools/include/uapi", | 49 | const Abi = enum { |
| 63 | // Output the syscall in a format we can easily recognize. | 50 | /// Syscalls common to two or more sub-targets. |
| 64 | "-D __SYSCALL(nr, nm)=zigsyscall nm nr", | 51 | /// Often used for single targets in lieu of a nil value. |
| | 52 | common, |
| | 53 | /// Syscalls using 64-bit types on 32-bit targets. |
| | 54 | @"32", |
| | 55 | /// 64-bit native syscalls. |
| | 56 | @"64", |
| | 57 | /// 32-bit time syscalls. |
| | 58 | time32, |
| | 59 | /// Supports the older renameat syscall along with renameat2. |
| | 60 | renameat, |
| | 61 | /// Supports the fstatat64 syscall. |
| | 62 | stat64, |
| | 63 | /// Supports the {get,set}rlimit syscalls. |
| | 64 | rlimit, |
| | 65 | /// Implements `memfd_secret` and friends. |
| | 66 | memfd_secret, |
| | 67 | // Architecture-specific syscalls. |
| | 68 | x32, |
| | 69 | eabi, |
| | 70 | nospu, |
| | 71 | arc, |
| | 72 | csky, |
| | 73 | nios2, |
| | 74 | or1k, |
| | 75 | riscv, |
| 65 | }; | 76 | }; |
| 66 | | 77 | |
| 67 | const ProcessPreprocessedFileFn = *const fn (bytes: []const u8, writer: anytype) anyerror!void; | 78 | const __X32_SYSCALL_BIT: u32 = 0x40000000; |
| 68 | const ProcessTableBasedArchFileFn = *const fn ( | 79 | const __NR_Linux_O32: u32 = 4000; |
| 69 | bytes: []const u8, | 80 | const __NR_Linux_N64: u32 = 5000; |
| 70 | filters: Filters, | 81 | const __NR_Linux_N32: u32 = 6000; |
| 71 | writer: anytype, | 82 | |
| 72 | optional_writer: anytype, | 83 | const Arch = struct { |
| 73 | ) anyerror!void; | 84 | /// Name for the generated enum variable. |
| 74 | | 85 | @"var": []const u8, |
| 75 | const FlowControl = enum { | 86 | /// Location of the table if this arch doesn't use the generic one. |
| 76 | @"break", | 87 | table: union(enum) { generic: void, specific: []const u8 }, |
| 77 | @"continue", | 88 | /// List of abi features to filter on. |
| 78 | none, | 89 | /// An empty list implies the abi field is a constant value, thus skipping validation. |
| 79 | }; | 90 | abi: []const Abi = &.{}, |
| 80 | | 91 | /// Some architectures need special handling: |
| 81 | const AbiCheckParams = struct { abi: []const u8, flow: FlowControl }; | 92 | /// - x32 system calls must have their number OR'ed with |
| 82 | | 93 | /// `__X32_SYSCALL_BIT` to distinguish them against the regular x86_64 calls. |
| 83 | const Filters = struct { | 94 | /// - Mips systems calls are offset by a set number based on the ABI. |
| 84 | abiCheckParams: ?AbiCheckParams, | 95 | /// |
| 85 | fixedName: ?*const fn (name: []const u8) []const u8, | 96 | /// Because the `__X32_SYSCALL_BIT` mask is so large, we can turn the OR into a |
| 86 | isReservedNameOld: ?*const fn (name: []const u8) bool, | 97 | /// normal addition and apply a base offset for all targets, defaulting to 0. |
| 87 | }; | 98 | offset: u32 = 0, |
| 88 | | 99 | header: ?[]const u8 = null, |
| 89 | fn abiCheck(abi: []const u8, params: *const AbiCheckParams) FlowControl { | 100 | footer: ?[]const u8 = null, |
| 90 | if (mem.eql(u8, abi, params.abi)) return params.flow; | 101 | |
| 91 | return .none; | 102 | fn get(self: Arch, line: []const u8) ?struct { []const u8, u32 } { |
| 92 | } | 103 | var iter = mem.tokenizeAny(u8, line, " \t"); |
| 93 | | 104 | const num_str = iter.next() orelse @panic("Bad field"); |
| 94 | fn fixedName(name: []const u8) []const u8 { | 105 | const abi = iter.next() orelse @panic("Bad field"); |
| 95 | return if (stdlib_renames.get(name)) |fixed| fixed else name; | 106 | const name = iter.next() orelse @panic("Bad field"); |
| 96 | } | 107 | |
| 97 | | 108 | // Filter out syscalls that aren't actually syscalls. |
| 98 | const ArchInfo = union(enum) { | 109 | if (isReserved(name)) return null; |
| 99 | table: struct { | 110 | // Check abi field matches |
| 100 | name: []const u8, | 111 | const abi_match: bool = if (self.abi.len == 0) true else blk: { |
| 101 | enum_name: []const u8, | 112 | for (self.abi) |a| |
| 102 | file_path: []const u8, | 113 | if (mem.eql(u8, @tagName(a), abi)) break :blk true; |
| 103 | header: ?[]const u8, | 114 | break :blk false; |
| 104 | extra_values: ?[]const u8, | 115 | }; |
| 105 | process_file: ProcessTableBasedArchFileFn, | 116 | if (!abi_match) return null; |
| 106 | filters: Filters, | 117 | |
| 107 | additional_enum: ?[]const u8, | 118 | var num = std.fmt.parseInt(u32, num_str, 10) catch @panic("Bad syscall number"); |
| 108 | }, | 119 | num += self.offset; |
| 109 | preprocessor: struct { | 120 | |
| 110 | name: []const u8, | 121 | return .{ name, num }; |
| 111 | enum_name: []const u8, | 122 | } |
| 112 | file_path: []const u8, | | |
| 113 | child_options: struct { | | |
| 114 | comptime additional_args: ?[]const []const u8 = null, | | |
| 115 | target: []const u8, | | |
| 116 | | | |
| 117 | pub inline fn getArgs(self: *const @This(), zig_exe: []const u8, file_path: []const u8) []const []const u8 { | | |
| 118 | const additional_args: []const []const u8 = self.additional_args orelse &.{}; | | |
| 119 | return .{ zig_exe, "cc" } ++ additional_args ++ .{ "-target", self.target } ++ default_args ++ .{file_path}; | | |
| 120 | } | | |
| 121 | }, | | |
| 122 | header: ?[]const u8, | | |
| 123 | extra_values: ?[]const u8, | | |
| 124 | process_file: ProcessPreprocessedFileFn, | | |
| 125 | additional_enum: ?[]const u8, | | |
| 126 | }, | | |
| 127 | }; | 123 | }; |
| 128 | | 124 | |
| 129 | const arch_infos = [_]ArchInfo{ | 125 | const architectures: []const Arch = &.{ |
| 130 | .{ | 126 | .{ .@"var" = "X86", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_32.tbl" } }, |
| 131 | // These architectures have their syscall definitions generated from a TSV | 127 | .{ .@"var" = "X64", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_64.tbl" }, .abi = &.{ .common, .@"64" } }, |
| 132 | // file, processed via scripts/syscallhdr.sh. | 128 | .{ .@"var" = "X32", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_64.tbl" }, .abi = &.{ .common, .x32 }, .offset = __X32_SYSCALL_BIT }, |
| 133 | .table = .{ | | |
| 134 | .name = "x86", | | |
| 135 | .enum_name = "X86", | | |
| 136 | .file_path = "arch/x86/entry/syscalls/syscall_32.tbl", | | |
| 137 | .process_file = &processTableBasedArch, | | |
| 138 | .filters = .{ | | |
| 139 | .abiCheckParams = null, | | |
| 140 | .fixedName = &fixedName, | | |
| 141 | .isReservedNameOld = null, | | |
| 142 | }, | | |
| 143 | .header = null, | | |
| 144 | .extra_values = null, | | |
| 145 | .additional_enum = null, | | |
| 146 | }, | | |
| 147 | }, | | |
| 148 | .{ | | |
| 149 | .table = .{ | | |
| 150 | .name = "x64", | | |
| 151 | .enum_name = "X64", | | |
| 152 | .file_path = "arch/x86/entry/syscalls/syscall_64.tbl", | | |
| 153 | .process_file = &processTableBasedArch, | | |
| 154 | .filters = .{ | | |
| 155 | // The x32 abi syscalls are always at the end. | | |
| 156 | .abiCheckParams = .{ .abi = "x32", .flow = .@"break" }, | | |
| 157 | .fixedName = &fixedName, | | |
| 158 | .isReservedNameOld = null, | | |
| 159 | }, | | |
| 160 | .header = null, | | |
| 161 | .extra_values = null, | | |
| 162 | .additional_enum = null, | | |
| 163 | }, | | |
| 164 | }, | | |
| 165 | .{ | | |
| 166 | .table = .{ | | |
| 167 | .name = "x32", | | |
| 168 | .enum_name = "X32", | | |
| 169 | .file_path = "arch/x86/entry/syscalls/syscall_64.tbl", | | |
| 170 | .process_file = &processTableBasedArch, | | |
| 171 | .filters = .{ | | |
| 172 | .abiCheckParams = .{ .abi = "64", .flow = .@"continue" }, | | |
| 173 | .fixedName = &fixedName, | | |
| 174 | .isReservedNameOld = null, | | |
| 175 | }, | | |
| 176 | .header = null, | | |
| 177 | .extra_values = null, | | |
| 178 | .additional_enum = null, | | |
| 179 | }, | | |
| 180 | }, | | |
| 181 | .{ | | |
| 182 | .table = .{ | | |
| 183 | .name = "arm", | | |
| 184 | .enum_name = "Arm", | | |
| 185 | .file_path = "arch/arm/tools/syscall.tbl", | | |
| 186 | .process_file = &processTableBasedArch, | | |
| 187 | .filters = .{ | | |
| 188 | .abiCheckParams = .{ .abi = "oabi", .flow = .@"continue" }, | | |
| 189 | .fixedName = &fixedName, | | |
| 190 | .isReservedNameOld = null, | | |
| 191 | }, | | |
| 192 | .header = " const arm_base = 0x0f0000;\n\n", | | |
| 193 | // TODO: maybe extract these from arch/arm/include/uapi/asm/unistd.h | | |
| 194 | .extra_values = | | |
| 195 | \\ | | |
| 196 | \\ breakpoint = arm_base + 1, | | |
| 197 | \\ cacheflush = arm_base + 2, | | |
| 198 | \\ usr26 = arm_base + 3, | | |
| 199 | \\ usr32 = arm_base + 4, | | |
| 200 | \\ set_tls = arm_base + 5, | | |
| 201 | \\ get_tls = arm_base + 6, | | |
| 202 | \\ | | |
| 203 | , | | |
| 204 | .additional_enum = null, | | |
| 205 | }, | | |
| 206 | }, | | |
| 207 | .{ | | |
| 208 | .table = .{ | | |
| 209 | .name = "sparc", | | |
| 210 | .enum_name = "Sparc", | | |
| 211 | .file_path = "arch/sparc/kernel/syscalls/syscall.tbl", | | |
| 212 | .process_file = &processTableBasedArch, | | |
| 213 | .filters = .{ | | |
| 214 | .abiCheckParams = .{ .abi = "64", .flow = .@"continue" }, | | |
| 215 | .fixedName = &fixedName, | | |
| 216 | .isReservedNameOld = null, | | |
| 217 | }, | | |
| 218 | .header = null, | | |
| 219 | .extra_values = null, | | |
| 220 | .additional_enum = null, | | |
| 221 | }, | | |
| 222 | }, | | |
| 223 | .{ | 129 | .{ |
| 224 | .table = .{ | 130 | .@"var" = "Arm", |
| 225 | .name = "sparc64", | 131 | .table = .{ .specific = "arch/arm/tools/syscall.tbl" }, |
| 226 | .enum_name = "Sparc64", | 132 | .abi = &.{ .common, .eabi }, |
| 227 | .file_path = "arch/sparc/kernel/syscalls/syscall.tbl", | 133 | // These values haven't been brought over from `arch/arm/include/uapi/asm/unistd.h`, |
| 228 | .process_file = &processTableBasedArch, | 134 | // so we are forced to add them ourselves. |
| 229 | .filters = .{ | 135 | .header = " const arm_base = 0x0f0000;\n\n", |
| 230 | .abiCheckParams = .{ .abi = "32", .flow = .@"continue" }, | 136 | .footer = |
| 231 | .fixedName = &fixedName, | 137 | \\ |
| 232 | .isReservedNameOld = null, | 138 | \\ breakpoint = arm_base + 1, |
| 233 | }, | 139 | \\ cacheflush = arm_base + 2, |
| 234 | .header = null, | 140 | \\ usr26 = arm_base + 3, |
| 235 | .extra_values = null, | 141 | \\ usr32 = arm_base + 4, |
| 236 | .additional_enum = null, | 142 | \\ set_tls = arm_base + 5, |
| 237 | }, | 143 | \\ get_tls = arm_base + 6, |
| 238 | }, | 144 | \\ |
| 239 | .{ | 145 | , |
| 240 | .table = .{ | | |
| 241 | .name = "m68k", | | |
| 242 | .enum_name = "M68k", | | |
| 243 | .file_path = "arch/m68k/kernel/syscalls/syscall.tbl", | | |
| 244 | .process_file = &processTableBasedArch, | | |
| 245 | .filters = .{ | | |
| 246 | // abi is always common | | |
| 247 | .abiCheckParams = null, | | |
| 248 | .fixedName = &fixedName, | | |
| 249 | .isReservedNameOld = null, | | |
| 250 | }, | | |
| 251 | .header = null, | | |
| 252 | .extra_values = null, | | |
| 253 | .additional_enum = null, | | |
| 254 | }, | | |
| 255 | }, | | |
| 256 | .{ | | |
| 257 | .table = .{ | | |
| 258 | .name = "mips_o32", | | |
| 259 | .enum_name = "MipsO32", | | |
| 260 | .file_path = "arch/mips/kernel/syscalls/syscall_o32.tbl", | | |
| 261 | .process_file = &processMipsBasedArch, | | |
| 262 | .filters = .{ | | |
| 263 | // abi is always o32 | | |
| 264 | .abiCheckParams = null, | | |
| 265 | .fixedName = &fixedName, | | |
| 266 | .isReservedNameOld = &isReservedNameOld, | | |
| 267 | }, | | |
| 268 | .header = " const linux_base = 4000;\n\n", | | |
| 269 | .extra_values = null, | | |
| 270 | .additional_enum = null, | | |
| 271 | }, | | |
| 272 | }, | | |
| 273 | .{ | | |
| 274 | .table = .{ | | |
| 275 | .name = "mips_n64", | | |
| 276 | .enum_name = "MipsN64", | | |
| 277 | .file_path = "arch/mips/kernel/syscalls/syscall_n64.tbl", | | |
| 278 | .process_file = &processMipsBasedArch, | | |
| 279 | .filters = .{ | | |
| 280 | // abi is always n64 | | |
| 281 | .abiCheckParams = null, | | |
| 282 | .fixedName = &fixedName, | | |
| 283 | .isReservedNameOld = &isReservedNameOld, | | |
| 284 | }, | | |
| 285 | .header = " const linux_base = 5000;\n\n", | | |
| 286 | .extra_values = null, | | |
| 287 | .additional_enum = null, | | |
| 288 | }, | | |
| 289 | }, | | |
| 290 | .{ | | |
| 291 | .table = .{ | | |
| 292 | .name = "mips_n32", | | |
| 293 | .enum_name = "MipsN32", | | |
| 294 | .file_path = "arch/mips/kernel/syscalls/syscall_n32.tbl", | | |
| 295 | .process_file = &processMipsBasedArch, | | |
| 296 | .filters = .{ | | |
| 297 | // abi is always n32 | | |
| 298 | .abiCheckParams = null, | | |
| 299 | .fixedName = &fixedName, | | |
| 300 | .isReservedNameOld = &isReservedNameOld, | | |
| 301 | }, | | |
| 302 | .header = " const linux_base = 6000;\n\n", | | |
| 303 | .extra_values = null, | | |
| 304 | .additional_enum = null, | | |
| 305 | }, | | |
| 306 | }, | | |
| 307 | .{ | | |
| 308 | .table = .{ | | |
| 309 | .name = "powerpc", | | |
| 310 | .enum_name = "PowerPC", | | |
| 311 | .file_path = "arch/powerpc/kernel/syscalls/syscall.tbl", | | |
| 312 | .process_file = &processPowerPcBasedArch, | | |
| 313 | .filters = .{ | | |
| 314 | .abiCheckParams = null, | | |
| 315 | .fixedName = null, | | |
| 316 | .isReservedNameOld = null, | | |
| 317 | }, | | |
| 318 | .header = null, | | |
| 319 | .extra_values = null, | | |
| 320 | .additional_enum = "PowerPC64", | | |
| 321 | }, | | |
| 322 | }, | | |
| 323 | .{ | | |
| 324 | .table = .{ | | |
| 325 | .name = "s390x", | | |
| 326 | .enum_name = "S390x", | | |
| 327 | .file_path = "arch/s390/kernel/syscalls/syscall.tbl", | | |
| 328 | .process_file = &processTableBasedArch, | | |
| 329 | .filters = .{ | | |
| 330 | // 32-bit s390 support in linux is deprecated | | |
| 331 | .abiCheckParams = .{ .abi = "32", .flow = .@"continue" }, | | |
| 332 | .fixedName = &fixedName, | | |
| 333 | .isReservedNameOld = null, | | |
| 334 | }, | | |
| 335 | .header = null, | | |
| 336 | .extra_values = null, | | |
| 337 | .additional_enum = null, | | |
| 338 | }, | | |
| 339 | }, | | |
| 340 | .{ | | |
| 341 | .table = .{ | | |
| 342 | .name = "xtensa", | | |
| 343 | .enum_name = "Xtensa", | | |
| 344 | .file_path = "arch/xtensa/kernel/syscalls/syscall.tbl", | | |
| 345 | .process_file = &processTableBasedArch, | | |
| 346 | .filters = .{ | | |
| 347 | // abi is always common | | |
| 348 | .abiCheckParams = null, | | |
| 349 | .fixedName = fixedName, | | |
| 350 | .isReservedNameOld = &isReservedNameOld, | | |
| 351 | }, | | |
| 352 | .header = null, | | |
| 353 | .extra_values = null, | | |
| 354 | .additional_enum = null, | | |
| 355 | }, | | |
| 356 | }, | | |
| 357 | .{ | | |
| 358 | .preprocessor = .{ | | |
| 359 | .name = "arm64", | | |
| 360 | .enum_name = "Arm64", | | |
| 361 | .file_path = "arch/arm64/include/uapi/asm/unistd.h", | | |
| 362 | .child_options = .{ | | |
| 363 | .additional_args = null, | | |
| 364 | .target = "aarch64-freestanding-none", | | |
| 365 | }, | | |
| 366 | .process_file = &processPreprocessedFile, | | |
| 367 | .header = null, | | |
| 368 | .extra_values = null, | | |
| 369 | .additional_enum = null, | | |
| 370 | }, | | |
| 371 | }, | | |
| 372 | .{ | | |
| 373 | .preprocessor = .{ | | |
| 374 | .name = "riscv32", | | |
| 375 | .enum_name = "RiscV32", | | |
| 376 | .file_path = "arch/riscv/include/uapi/asm/unistd.h", | | |
| 377 | .child_options = .{ | | |
| 378 | .additional_args = null, | | |
| 379 | .target = "riscv32-freestanding-none", | | |
| 380 | }, | | |
| 381 | .process_file = &processPreprocessedFile, | | |
| 382 | .header = null, | | |
| 383 | .extra_values = null, | | |
| 384 | .additional_enum = null, | | |
| 385 | }, | | |
| 386 | }, | | |
| 387 | .{ | | |
| 388 | .preprocessor = .{ | | |
| 389 | .name = "riscv64", | | |
| 390 | .enum_name = "RiscV64", | | |
| 391 | .file_path = "arch/riscv/include/uapi/asm/unistd.h", | | |
| 392 | .child_options = .{ | | |
| 393 | .additional_args = null, | | |
| 394 | .target = "riscv64-freestanding-none", | | |
| 395 | }, | | |
| 396 | .process_file = &processPreprocessedFile, | | |
| 397 | .header = null, | | |
| 398 | .extra_values = null, | | |
| 399 | .additional_enum = null, | | |
| 400 | }, | | |
| 401 | }, | | |
| 402 | .{ | | |
| 403 | .preprocessor = .{ | | |
| 404 | .name = "loongarch", | | |
| 405 | .enum_name = "LoongArch64", | | |
| 406 | .file_path = "arch/loongarch/include/uapi/asm/unistd.h", | | |
| 407 | .child_options = .{ | | |
| 408 | .additional_args = null, | | |
| 409 | .target = "loongarch64-freestanding-none", | | |
| 410 | }, | | |
| 411 | .process_file = &processPreprocessedFile, | | |
| 412 | .header = null, | | |
| 413 | .extra_values = null, | | |
| 414 | .additional_enum = null, | | |
| 415 | }, | | |
| 416 | }, | | |
| 417 | .{ | | |
| 418 | .preprocessor = .{ | | |
| 419 | .name = "arc", | | |
| 420 | .enum_name = "Arc", | | |
| 421 | .file_path = "arch/arc/include/uapi/asm/unistd.h", | | |
| 422 | .child_options = .{ | | |
| 423 | .additional_args = null, | | |
| 424 | .target = "arc-freestanding-none", | | |
| 425 | }, | | |
| 426 | .process_file = &processPreprocessedFile, | | |
| 427 | .header = null, | | |
| 428 | .extra_values = null, | | |
| 429 | .additional_enum = null, | | |
| 430 | }, | | |
| 431 | }, | | |
| 432 | .{ | | |
| 433 | .preprocessor = .{ | | |
| 434 | .name = "csky", | | |
| 435 | .enum_name = "CSky", | | |
| 436 | .file_path = "arch/csky/include/uapi/asm/unistd.h", | | |
| 437 | .child_options = .{ | | |
| 438 | .additional_args = null, | | |
| 439 | .target = "csky-freestanding-none", | | |
| 440 | }, | | |
| 441 | .process_file = &processPreprocessedFile, | | |
| 442 | .header = null, | | |
| 443 | .extra_values = null, | | |
| 444 | .additional_enum = null, | | |
| 445 | }, | | |
| 446 | }, | | |
| 447 | .{ | | |
| 448 | .preprocessor = .{ | | |
| 449 | .name = "hexagon", | | |
| 450 | .enum_name = "Hexagon", | | |
| 451 | .file_path = "arch/hexagon/include/uapi/asm/unistd.h", | | |
| 452 | .child_options = .{ | | |
| 453 | .additional_args = null, | | |
| 454 | .target = "hexagon-freestanding-none", | | |
| 455 | }, | | |
| 456 | .process_file = &processPreprocessedFile, | | |
| 457 | .header = null, | | |
| 458 | .extra_values = null, | | |
| 459 | .additional_enum = null, | | |
| 460 | }, | | |
| 461 | }, | 146 | }, |
| | 147 | .{ .@"var" = "Sparc", .table = .{ .specific = "arch/sparc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"32" } }, |
| | 148 | .{ .@"var" = "Sparc64", .table = .{ .specific = "arch/sparc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"64" } }, |
| | 149 | .{ .@"var" = "M68k", .table = .{ .specific = "arch/m68k/kernel/syscalls/syscall.tbl" } }, |
| | 150 | // For Mips, the abi for these tables is always o32/n64/n32. |
| | 151 | .{ .@"var" = "MipsO32", .table = .{ .specific = "arch/mips/kernel/syscalls/syscall_o32.tbl" }, .offset = __NR_Linux_O32 }, |
| | 152 | .{ .@"var" = "MipsN64", .table = .{ .specific = "arch/mips/kernel/syscalls/syscall_n64.tbl" }, .offset = __NR_Linux_N64 }, |
| | 153 | .{ .@"var" = "MipsN32", .table = .{ .specific = "arch/mips/kernel/syscalls/syscall_n32.tbl" }, .offset = __NR_Linux_N32 }, |
| | 154 | .{ .@"var" = "PowerPC", .table = .{ .specific = "arch/powerpc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"32", .nospu } }, |
| | 155 | .{ .@"var" = "PowerPC64", .table = .{ .specific = "arch/powerpc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"64", .nospu } }, |
| | 156 | .{ .@"var" = "S390x", .table = .{ .specific = "arch/s390/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"64" } }, |
| | 157 | .{ .@"var" = "Xtensa", .table = .{ .specific = "arch/xtensa/kernel/syscalls/syscall.tbl" } }, |
| | 158 | .{ .@"var" = "Arm64", .table = .generic, .abi = &.{ .common, .@"64", .renameat, .rlimit, .memfd_secret } }, |
| | 159 | .{ .@"var" = "RiscV32", .table = .generic, .abi = &.{ .common, .@"32", .riscv, .memfd_secret } }, |
| | 160 | .{ .@"var" = "RiscV64", .table = .generic, .abi = &.{ .common, .@"64", .riscv, .rlimit, .memfd_secret } }, |
| | 161 | .{ .@"var" = "LoongArch64", .table = .generic, .abi = &.{ .common, .@"64" } }, |
| | 162 | .{ .@"var" = "Arc", .table = .generic, .abi = &.{ .common, .@"32", .arc, .time32, .renameat, .stat64, .rlimit } }, |
| | 163 | .{ .@"var" = "CSky", .table = .generic, .abi = &.{ .common, .@"32", .csky, .time32, .stat64, .rlimit } }, |
| | 164 | .{ .@"var" = "Hexagon", .table = .generic, .abi = &.{ .common, .@"32", .time32, .stat64, .rlimit, .renameat } }, |
| | 165 | .{ .@"var" = "OpenRisc", .table = .generic, .abi = &.{ .common, .@"32", .or1k, .time32, .stat64, .rlimit, .renameat } }, |
| | 166 | // .{ .@"var" = "Nios2", .table = .generic, .abi = &.{ .common, .@"32", .nios2, .time32, .stat64, .rlimit, .renameat } }, |
| | 167 | // .{ .@"var" = "Parisc", .table = .{ .specific = "arch/parisc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"32" } }, |
| | 168 | // .{ .@"var" = "Parisc64", .table = .{ .specific = "arch/parisc/kernel/syscalls/syscall.tbl" }, .abi = &.{ .common, .@"64" } }, |
| | 169 | // .{ .@"var" = "Sh", .table = .{ .specific = "arch/sh/kernel/syscalls/syscall.tbl" } }, |
| | 170 | // .{ .@"var" = "Microblaze", .table = .{ .specific = "arch/microblaze/kernel/syscalls/syscall.tbl" } }, |
| 462 | }; | 171 | }; |
| 463 | | 172 | |
| 464 | fn processPreprocessedFile( | | |
| 465 | bytes: []const u8, | | |
| 466 | writer: anytype, | | |
| 467 | ) !void { | | |
| 468 | var lines = mem.tokenizeScalar(u8, bytes, '\n'); | | |
| 469 | while (lines.next()) |line| { | | |
| 470 | var fields = mem.tokenizeAny(u8, line, " "); | | |
| 471 | const prefix = fields.next() orelse return error.Incomplete; | | |
| 472 | | | |
| 473 | if (!mem.eql(u8, prefix, "zigsyscall")) continue; | | |
| 474 | | | |
| 475 | const sys_name = fields.next() orelse return error.Incomplete; | | |
| 476 | const value = fields.rest(); | | |
| 477 | const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..]; | | |
| 478 | const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name; | | |
| 479 | | | |
| 480 | try writer.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), value }); | | |
| 481 | } | | |
| 482 | } | | |
| 483 | | | |
| 484 | fn processTableBasedArch( | | |
| 485 | bytes: []const u8, | | |
| 486 | filters: Filters, | | |
| 487 | writer: anytype, | | |
| 488 | optional_writer: anytype, | | |
| 489 | ) !void { | | |
| 490 | _ = optional_writer; | | |
| 491 | | | |
| 492 | var lines = mem.tokenizeScalar(u8, bytes, '\n'); | | |
| 493 | while (lines.next()) |line| { | | |
| 494 | if (line[0] == '#') continue; | | |
| 495 | | | |
| 496 | var fields = mem.tokenizeAny(u8, line, " \t"); | | |
| 497 | const number = fields.next() orelse return error.Incomplete; | | |
| 498 | | | |
| 499 | const abi = fields.next() orelse return error.Incomplete; | | |
| 500 | if (filters.abiCheckParams) |*params| { | | |
| 501 | switch (abiCheck(abi, params)) { | | |
| 502 | .none => {}, | | |
| 503 | .@"break" => break, | | |
| 504 | .@"continue" => continue, | | |
| 505 | } | | |
| 506 | } | | |
| 507 | const name = fields.next() orelse return error.Incomplete; | | |
| 508 | if (filters.isReservedNameOld) |isReservedNameOldFn| { | | |
| 509 | if (isReservedNameOldFn(name)) continue; | | |
| 510 | } | | |
| 511 | const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name; | | |
| 512 | | | |
| 513 | try writer.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 514 | } | | |
| 515 | } | | |
| 516 | | | |
| 517 | fn processMipsBasedArch( | | |
| 518 | bytes: []const u8, | | |
| 519 | filters: Filters, | | |
| 520 | writer: anytype, | | |
| 521 | optional_writer: anytype, | | |
| 522 | ) !void { | | |
| 523 | _ = optional_writer; | | |
| 524 | | | |
| 525 | var lines = mem.tokenizeScalar(u8, bytes, '\n'); | | |
| 526 | while (lines.next()) |line| { | | |
| 527 | if (line[0] == '#') continue; | | |
| 528 | | | |
| 529 | var fields = mem.tokenizeAny(u8, line, " \t"); | | |
| 530 | const number = fields.next() orelse return error.Incomplete; | | |
| 531 | | | |
| 532 | const abi = fields.next() orelse return error.Incomplete; | | |
| 533 | if (filters.abiCheckParams) |*params| { | | |
| 534 | switch (abiCheck(abi, params)) { | | |
| 535 | .none => {}, | | |
| 536 | .@"break" => break, | | |
| 537 | .@"continue" => continue, | | |
| 538 | } | | |
| 539 | } | | |
| 540 | const name = fields.next() orelse return error.Incomplete; | | |
| 541 | if (filters.isReservedNameOld) |isReservedNameOldFn| { | | |
| 542 | if (isReservedNameOldFn(name)) continue; | | |
| 543 | } | | |
| 544 | const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name; | | |
| 545 | | | |
| 546 | try writer.print(" {f} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 547 | } | | |
| 548 | } | | |
| 549 | | | |
| 550 | fn processPowerPcBasedArch( | | |
| 551 | bytes: []const u8, | | |
| 552 | filters: Filters, | | |
| 553 | writer: anytype, | | |
| 554 | optional_writer: anytype, | | |
| 555 | ) !void { | | |
| 556 | _ = filters; | | |
| 557 | var lines = mem.tokenizeScalar(u8, bytes, '\n'); | | |
| 558 | | | |
| 559 | while (lines.next()) |line| { | | |
| 560 | if (line[0] == '#') continue; | | |
| 561 | | | |
| 562 | var fields = mem.tokenizeAny(u8, line, " \t"); | | |
| 563 | const number = fields.next() orelse return error.Incomplete; | | |
| 564 | const abi = fields.next() orelse return error.Incomplete; | | |
| 565 | const name = fields.next() orelse return error.Incomplete; | | |
| 566 | const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; | | |
| 567 | | | |
| 568 | if (mem.eql(u8, abi, "spu")) { | | |
| 569 | continue; | | |
| 570 | } else if (mem.eql(u8, abi, "32")) { | | |
| 571 | try writer.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 572 | } else if (mem.eql(u8, abi, "64")) { | | |
| 573 | try optional_writer.?.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 574 | } else { // common/nospu | | |
| 575 | try writer.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 576 | try optional_writer.?.print(" {f} = {s},\n", .{ zig.fmtId(fixed_name), number }); | | |
| 577 | } | | |
| 578 | } | | |
| 579 | } | | |
| 580 | | | |
| 581 | fn generateSyscallsFromTable( | | |
| 582 | allocator: std.mem.Allocator, | | |
| 583 | buf: []u8, | | |
| 584 | linux_dir: std.fs.Dir, | | |
| 585 | writer: anytype, | | |
| 586 | _arch_info: *const ArchInfo, | | |
| 587 | ) !void { | | |
| 588 | std.debug.assert(_arch_info.* == .table); | | |
| 589 | | | |
| 590 | const arch_info = _arch_info.table; | | |
| 591 | | | |
| 592 | const table = try linux_dir.readFile(arch_info.file_path, buf); | | |
| 593 | | | |
| 594 | var optional_array_list: ?std.array_list.Managed(u8) = if (arch_info.additional_enum) |_| std.array_list.Managed(u8).init(allocator) else null; | | |
| 595 | const optional_writer = if (optional_array_list) |_| optional_array_list.?.writer() else null; | | |
| 596 | | | |
| 597 | try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name}); | | |
| 598 | | | |
| 599 | if (arch_info.header) |header| { | | |
| 600 | try writer.writeAll(header); | | |
| 601 | } | | |
| 602 | | | |
| 603 | try arch_info.process_file(table, arch_info.filters, writer, optional_writer); | | |
| 604 | | | |
| 605 | if (arch_info.extra_values) |extra_values| { | | |
| 606 | try writer.writeAll(extra_values); | | |
| 607 | } | | |
| 608 | try writer.writeAll("};"); | | |
| 609 | | | |
| 610 | if (arch_info.additional_enum) |additional_enum| { | | |
| 611 | try writer.writeAll("\n\n"); | | |
| 612 | try writer.print("pub const {s} = enum(usize) {{\n", .{additional_enum}); | | |
| 613 | try writer.writeAll(optional_array_list.?.items); | | |
| 614 | try writer.writeAll("};"); | | |
| 615 | } | | |
| 616 | } | | |
| 617 | | | |
| 618 | fn generateSyscallsFromPreprocessor( | | |
| 619 | allocator: std.mem.Allocator, | | |
| 620 | linux_dir: std.fs.Dir, | | |
| 621 | linux_path: []const u8, | | |
| 622 | zig_exe: []const u8, | | |
| 623 | writer: anytype, | | |
| 624 | _arch_info: *const ArchInfo, | | |
| 625 | ) !void { | | |
| 626 | std.debug.assert(_arch_info.* == .preprocessor); | | |
| 627 | | | |
| 628 | const arch_info = _arch_info.preprocessor; | | |
| 629 | | | |
| 630 | const child_result = try std.process.Child.run(.{ | | |
| 631 | .allocator = allocator, | | |
| 632 | .argv = arch_info.child_options.getArgs(zig_exe, arch_info.file_path), | | |
| 633 | .cwd = linux_path, | | |
| 634 | .cwd_dir = linux_dir, | | |
| 635 | }); | | |
| 636 | if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr}); | | |
| 637 | | | |
| 638 | const defines = switch (child_result.term) { | | |
| 639 | .Exited => |code| if (code == 0) child_result.stdout else { | | |
| 640 | std.debug.print("zig cc exited with code {d}\n", .{code}); | | |
| 641 | std.process.exit(1); | | |
| 642 | }, | | |
| 643 | else => { | | |
| 644 | std.debug.print("zig cc crashed\n", .{}); | | |
| 645 | std.process.exit(1); | | |
| 646 | }, | | |
| 647 | }; | | |
| 648 | | | |
| 649 | try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name}); | | |
| 650 | if (arch_info.header) |header| { | | |
| 651 | try writer.writeAll(header); | | |
| 652 | } | | |
| 653 | | | |
| 654 | try arch_info.process_file(defines, writer); | | |
| 655 | | | |
| 656 | if (arch_info.extra_values) |extra_values| { | | |
| 657 | try writer.writeAll(extra_values); | | |
| 658 | } | | |
| 659 | | | |
| 660 | try writer.writeAll("};"); | | |
| 661 | } | | |
| 662 | | | |
| 663 | pub fn main() !void { | 173 | pub fn main() !void { |
| 664 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 174 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 665 | defer arena.deinit(); | 175 | defer arena.deinit(); |
| 666 | const allocator = arena.allocator(); | 176 | const gpa = arena.allocator(); |
| 667 | | 177 | |
| 668 | const args = try std.process.argsAlloc(allocator); | 178 | const args = try std.process.argsAlloc(gpa); |
| 669 | if (args.len < 3 or mem.eql(u8, args[1], "--help")) { | 179 | if (args.len < 2 or mem.eql(u8, args[1], "--help")) { |
| 670 | usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); | 180 | usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); |
| 671 | std.process.exit(1); | 181 | std.process.exit(1); |
| 672 | } | 182 | } |
| 673 | const zig_exe = args[1]; | 183 | const linux_path = args[1]; |
| 674 | const linux_path = args[2]; | | |
| 675 | | 184 | |
| 676 | var stdout_buffer: [2000]u8 = undefined; | 185 | var stdout_buffer: [2048]u8 = undefined; |
| 677 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); | 186 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); |
| 678 | const writer = &stdout_writer.interface; | 187 | const stdout = &stdout_writer.interface; |
| 679 | | 188 | |
| 680 | var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); | 189 | var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); |
| 681 | defer linux_dir.close(); | 190 | defer linux_dir.close(); |
| 682 | | 191 | |
| 683 | try writer.writeAll( | 192 | // As of 6.11, the largest table is 24195 bytes. |
| 684 | \\// This file is automatically generated. | 193 | // 32k should be enough for now. |
| | 194 | const buf = try gpa.alloc(u8, 1 << 15); |
| | 195 | defer gpa.free(buf); |
| | 196 | |
| | 197 | // Fetch the kernel version from the Makefile variables. |
| | 198 | const version = blk: { |
| | 199 | const head = try linux_dir.readFile("Makefile", buf[0..128]); |
| | 200 | var lines = mem.tokenizeScalar(u8, head, '\n'); |
| | 201 | _ = lines.next(); // Skip SPDX identifier |
| | 202 | |
| | 203 | var ver = mem.zeroes(std.SemanticVersion); |
| | 204 | inline for (.{ "major", "minor", "patch" }, .{ "VERSION", "PATCHLEVEL", "SUBLEVEL" }) |field, make_var| { |
| | 205 | const line = lines.next() orelse @panic("Bad line"); |
| | 206 | const offset = (make_var ++ " = ").len; |
| | 207 | @field(ver, field) = try std.fmt.parseInt(usize, line[offset..], 10); |
| | 208 | } |
| | 209 | |
| | 210 | break :blk ver; |
| | 211 | }; |
| | 212 | |
| | 213 | try Io.Writer.print(stdout, |
| | 214 | \\// This file is automatically generated, DO NOT edit it manually. |
| 685 | \\// See tools/generate_linux_syscalls.zig for more info. | 215 | \\// See tools/generate_linux_syscalls.zig for more info. |
| | 216 | \\// This list current as of kernel: {f} |
| 686 | \\ | 217 | \\ |
| 687 | \\ | 218 | \\ |
| 688 | ); | 219 | , .{version}); |
| 689 | | 220 | |
| 690 | // As of 5.17.1, the largest table is 23467 bytes. | 221 | for (architectures, 0..) |arch, i| { |
| 691 | // 32k should be enough for now. | 222 | const table = try linux_dir.readFile(switch (arch.table) { |
| 692 | const buf = try allocator.alloc(u8, 1 << 15); | 223 | .generic => "scripts/syscall.tbl", |
| 693 | defer allocator.free(buf); | 224 | .specific => |f| f, |
| 694 | | 225 | }, buf); |
| 695 | inline for (arch_infos, 0..) |arch_info, i| { | 226 | |
| 696 | switch (arch_info) { | 227 | try Io.Writer.print(stdout, "pub const {s} = enum(usize) {{\n", .{arch.@"var"}); |
| 697 | .table => try generateSyscallsFromTable( | 228 | if (arch.header) |h| |
| 698 | allocator, | 229 | try Io.Writer.writeAll(stdout, h); |
| 699 | buf, | 230 | |
| 700 | linux_dir, | 231 | var lines = mem.tokenizeScalar(u8, table, '\n'); |
| 701 | writer, | 232 | while (lines.next()) |line| { |
| 702 | &arch_info, | 233 | if (line[0] == '#') continue; |
| 703 | ), | 234 | if (arch.get(line)) |res| { |
| 704 | .preprocessor => try generateSyscallsFromPreprocessor( | 235 | const name, const num = res; |
| 705 | allocator, | 236 | const final_name = stdlib_renames.get(name) orelse name; |
| 706 | linux_dir, | 237 | try Io.Writer.print(stdout, " {f} = {d},\n", .{ std.zig.fmtId(final_name), num }); |
| 707 | linux_path, | 238 | } |
| 708 | zig_exe, | | |
| 709 | writer, | | |
| 710 | &arch_info, | | |
| 711 | ), | | |
| 712 | } | | |
| 713 | if (i < arch_infos.len - 1) { | | |
| 714 | try writer.writeAll("\n\n"); | | |
| 715 | } else { | | |
| 716 | try writer.writeAll("\n"); | | |
| 717 | } | 239 | } |
| | 240 | |
| | 241 | if (arch.footer) |f| |
| | 242 | try Io.Writer.writeAll(stdout, f); |
| | 243 | try Io.Writer.writeAll(stdout, "};\n"); |
| | 244 | if (i != architectures.len - 1) |
| | 245 | try Io.Writer.writeByte(stdout, '\n'); |
| 718 | } | 246 | } |
| 719 | | 247 | |
| 720 | try writer.flush(); | 248 | try Io.Writer.flush(stdout); |
| 721 | } | 249 | } |
| 722 | | 250 | |
| 723 | fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { | 251 | fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { |