| ... | ... | @@ -2,12 +2,18 @@ |
| 2 | 2 | //! |
| 3 | 3 | //! This tool extracts the Linux syscall numbers from the Linux source tree |
| 4 | 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 | 14 | const std = @import("std"); |
| 15 | const Io = std.Io; |
| 7 | 16 | const mem = std.mem; |
| 8 | | const fmt = std.fmt; |
| 9 | | const zig = std.zig; |
| 10 | | const fs = std.fs; |
| 11 | 17 | |
| 12 | 18 | const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ |
| 13 | 19 | // Remove underscore prefix. |
| ... | ... | @@ -22,702 +28,224 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ |
| 22 | 28 | // ARM EABI/Thumb. |
| 23 | 29 | .{ "arm_sync_file_range", "sync_file_range" }, |
| 24 | 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. |
| 36 | | // Thankfully it's only 2 well-known syscalls in newer kernel ports at the moment. |
| 37 | | fn getOverridenNameNew(value: []const u8) ?[]const u8 { |
| 38 | | if (mem.eql(u8, value, "18")) { |
| 39 | | return "sys_lookup_dcookie"; |
| 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"); |
| 33 | /// Filter syscalls that aren't actually syscalls. |
| 34 | fn isReserved(name: []const u8) bool { |
| 35 | return mem.startsWith(u8, name, "available") or |
| 36 | mem.startsWith(u8, name, "reserved") or |
| 37 | mem.startsWith(u8, name, "unused"); |
| 51 | 38 | } |
| 52 | 39 | |
| 53 | | const default_args: []const []const u8 = &.{ |
| 54 | | "-E", |
| 55 | | // -dM is cleaner, but -dD preserves iteration order. |
| 56 | | "-dD", |
| 57 | | // No need for line-markers. |
| 58 | | "-P", |
| 59 | | "-nostdinc", |
| 60 | | // Using -I=[dir] includes the zig linux headers, which we don't want. |
| 61 | | "-Itools/include", |
| 62 | | "-Itools/include/uapi", |
| 63 | | // Output the syscall in a format we can easily recognize. |
| 64 | | "-D __SYSCALL(nr, nm)=zigsyscall nm nr", |
| 40 | /// Values of the `abi` field in use by the syscall tables. |
| 41 | /// |
| 42 | /// Since c. 2012, all new Linux architectures use the same numbers for their syscalls. |
| 43 | /// Before kernel 6.11, the source of truth for this list was the arch-specific `uapi` headers. |
| 44 | /// The 6.11 release converted this into a unified table with the same format as the older archs. |
| 45 | /// For these targets, syscalls are enabled/disabled based on the `abi` field. |
| 46 | /// These fields are sourced from the respective `arch/{arch}/kernel/Makefile.syscalls` |
| 47 | /// files in the kernel source tree. |
| 48 | /// Architecture-specific syscalls between [244...259] are also enabled by adding the arch name as an abi. |
| 49 | const Abi = enum { |
| 50 | /// Syscalls common to two or more sub-targets. |
| 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; |
| 68 | | const ProcessTableBasedArchFileFn = *const fn ( |
| 69 | | bytes: []const u8, |
| 70 | | filters: Filters, |
| 71 | | writer: anytype, |
| 72 | | optional_writer: anytype, |
| 73 | | ) anyerror!void; |
| 74 | | |
| 75 | | const FlowControl = enum { |
| 76 | | @"break", |
| 77 | | @"continue", |
| 78 | | none, |
| 79 | | }; |
| 80 | | |
| 81 | | const AbiCheckParams = struct { abi: []const u8, flow: FlowControl }; |
| 82 | | |
| 83 | | const Filters = struct { |
| 84 | | abiCheckParams: ?AbiCheckParams, |
| 85 | | fixedName: ?*const fn (name: []const u8) []const u8, |
| 86 | | isReservedNameOld: ?*const fn (name: []const u8) bool, |
| 87 | | }; |
| 88 | | |
| 89 | | fn abiCheck(abi: []const u8, params: *const AbiCheckParams) FlowControl { |
| 90 | | if (mem.eql(u8, abi, params.abi)) return params.flow; |
| 91 | | return .none; |
| 92 | | } |
| 93 | | |
| 94 | | fn fixedName(name: []const u8) []const u8 { |
| 95 | | return if (stdlib_renames.get(name)) |fixed| fixed else name; |
| 96 | | } |
| 97 | | |
| 98 | | const ArchInfo = union(enum) { |
| 99 | | table: struct { |
| 100 | | name: []const u8, |
| 101 | | enum_name: []const u8, |
| 102 | | file_path: []const u8, |
| 103 | | header: ?[]const u8, |
| 104 | | extra_values: ?[]const u8, |
| 105 | | process_file: ProcessTableBasedArchFileFn, |
| 106 | | filters: Filters, |
| 107 | | additional_enum: ?[]const u8, |
| 108 | | }, |
| 109 | | preprocessor: struct { |
| 110 | | name: []const u8, |
| 111 | | enum_name: []const u8, |
| 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 | | }, |
| 78 | const __X32_SYSCALL_BIT: u32 = 0x40000000; |
| 79 | const __NR_Linux_O32: u32 = 4000; |
| 80 | const __NR_Linux_N64: u32 = 5000; |
| 81 | const __NR_Linux_N32: u32 = 6000; |
| 82 | |
| 83 | const Arch = struct { |
| 84 | /// Name for the generated enum variable. |
| 85 | @"var": []const u8, |
| 86 | /// Location of the table if this arch doesn't use the generic one. |
| 87 | table: union(enum) { generic: void, specific: []const u8 }, |
| 88 | /// List of abi features to filter on. |
| 89 | /// An empty list implies the abi field is a constant value, thus skipping validation. |
| 90 | abi: []const Abi = &.{}, |
| 91 | /// Some architectures need special handling: |
| 92 | /// - x32 system calls must have their number OR'ed with |
| 93 | /// `__X32_SYSCALL_BIT` to distinguish them against the regular x86_64 calls. |
| 94 | /// - Mips systems calls are offset by a set number based on the ABI. |
| 95 | /// |
| 96 | /// Because the `__X32_SYSCALL_BIT` mask is so large, we can turn the OR into a |
| 97 | /// normal addition and apply a base offset for all targets, defaulting to 0. |
| 98 | offset: u32 = 0, |
| 99 | header: ?[]const u8 = null, |
| 100 | footer: ?[]const u8 = null, |
| 101 | |
| 102 | fn get(self: Arch, line: []const u8) ?struct { []const u8, u32 } { |
| 103 | var iter = mem.tokenizeAny(u8, line, " \t"); |
| 104 | const num_str = iter.next() orelse @panic("Bad field"); |
| 105 | const abi = iter.next() orelse @panic("Bad field"); |
| 106 | const name = iter.next() orelse @panic("Bad field"); |
| 107 | |
| 108 | // Filter out syscalls that aren't actually syscalls. |
| 109 | if (isReserved(name)) return null; |
| 110 | // Check abi field matches |
| 111 | const abi_match: bool = if (self.abi.len == 0) true else blk: { |
| 112 | for (self.abi) |a| |
| 113 | if (mem.eql(u8, @tagName(a), abi)) break :blk true; |
| 114 | break :blk false; |
| 115 | }; |
| 116 | if (!abi_match) return null; |
| 117 | |
| 118 | var num = std.fmt.parseInt(u32, num_str, 10) catch @panic("Bad syscall number"); |
| 119 | num += self.offset; |
| 120 | |
| 121 | return .{ name, num }; |
| 122 | } |
| 127 | 123 | }; |
| 128 | 124 | |
| 129 | | const arch_infos = [_]ArchInfo{ |
| 130 | | .{ |
| 131 | | // These architectures have their syscall definitions generated from a TSV |
| 132 | | // file, processed via scripts/syscallhdr.sh. |
| 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 | | }, |
| 125 | const architectures: []const Arch = &.{ |
| 126 | .{ .@"var" = "X86", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_32.tbl" } }, |
| 127 | .{ .@"var" = "X64", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_64.tbl" }, .abi = &.{ .common, .@"64" } }, |
| 128 | .{ .@"var" = "X32", .table = .{ .specific = "arch/x86/entry/syscalls/syscall_64.tbl" }, .abi = &.{ .common, .x32 }, .offset = __X32_SYSCALL_BIT }, |
| 223 | 129 | .{ |
| 224 | | .table = .{ |
| 225 | | .name = "sparc64", |
| 226 | | .enum_name = "Sparc64", |
| 227 | | .file_path = "arch/sparc/kernel/syscalls/syscall.tbl", |
| 228 | | .process_file = &processTableBasedArch, |
| 229 | | .filters = .{ |
| 230 | | .abiCheckParams = .{ .abi = "32", .flow = .@"continue" }, |
| 231 | | .fixedName = &fixedName, |
| 232 | | .isReservedNameOld = null, |
| 233 | | }, |
| 234 | | .header = null, |
| 235 | | .extra_values = null, |
| 236 | | .additional_enum = null, |
| 237 | | }, |
| 238 | | }, |
| 239 | | .{ |
| 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 | | }, |
| 130 | .@"var" = "Arm", |
| 131 | .table = .{ .specific = "arch/arm/tools/syscall.tbl" }, |
| 132 | .abi = &.{ .common, .eabi }, |
| 133 | // These values haven't been brought over from `arch/arm/include/uapi/asm/unistd.h`, |
| 134 | // so we are forced to add them ourselves. |
| 135 | .header = " const arm_base = 0x0f0000;\n\n", |
| 136 | .footer = |
| 137 | \\ |
| 138 | \\ breakpoint = arm_base + 1, |
| 139 | \\ cacheflush = arm_base + 2, |
| 140 | \\ usr26 = arm_base + 3, |
| 141 | \\ usr32 = arm_base + 4, |
| 142 | \\ set_tls = arm_base + 5, |
| 143 | \\ get_tls = arm_base + 6, |
| 144 | \\ |
| 145 | , |
| 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 | 173 | pub fn main() !void { |
| 664 | 174 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 665 | 175 | defer arena.deinit(); |
| 666 | | const allocator = arena.allocator(); |
| 176 | const gpa = arena.allocator(); |
| 667 | 177 | |
| 668 | | const args = try std.process.argsAlloc(allocator); |
| 669 | | if (args.len < 3 or mem.eql(u8, args[1], "--help")) { |
| 178 | const args = try std.process.argsAlloc(gpa); |
| 179 | if (args.len < 2 or mem.eql(u8, args[1], "--help")) { |
| 670 | 180 | usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2); |
| 671 | 181 | std.process.exit(1); |
| 672 | 182 | } |
| 673 | | const zig_exe = args[1]; |
| 674 | | const linux_path = args[2]; |
| 183 | const linux_path = args[1]; |
| 675 | 184 | |
| 676 | | var stdout_buffer: [2000]u8 = undefined; |
| 185 | var stdout_buffer: [2048]u8 = undefined; |
| 677 | 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 | 189 | var linux_dir = try std.fs.cwd().openDir(linux_path, .{}); |
| 681 | 190 | defer linux_dir.close(); |
| 682 | 191 | |
| 683 | | try writer.writeAll( |
| 684 | | \\// This file is automatically generated. |
| 192 | // As of 6.11, the largest table is 24195 bytes. |
| 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 | 215 | \\// See tools/generate_linux_syscalls.zig for more info. |
| 216 | \\// This list current as of kernel: {f} |
| 686 | 217 | \\ |
| 687 | 218 | \\ |
| 688 | | ); |
| 689 | | |
| 690 | | // As of 5.17.1, the largest table is 23467 bytes. |
| 691 | | // 32k should be enough for now. |
| 692 | | const buf = try allocator.alloc(u8, 1 << 15); |
| 693 | | defer allocator.free(buf); |
| 694 | | |
| 695 | | inline for (arch_infos, 0..) |arch_info, i| { |
| 696 | | switch (arch_info) { |
| 697 | | .table => try generateSyscallsFromTable( |
| 698 | | allocator, |
| 699 | | buf, |
| 700 | | linux_dir, |
| 701 | | writer, |
| 702 | | &arch_info, |
| 703 | | ), |
| 704 | | .preprocessor => try generateSyscallsFromPreprocessor( |
| 705 | | allocator, |
| 706 | | linux_dir, |
| 707 | | linux_path, |
| 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"); |
| 219 | , .{version}); |
| 220 | |
| 221 | for (architectures, 0..) |arch, i| { |
| 222 | const table = try linux_dir.readFile(switch (arch.table) { |
| 223 | .generic => "scripts/syscall.tbl", |
| 224 | .specific => |f| f, |
| 225 | }, buf); |
| 226 | |
| 227 | try Io.Writer.print(stdout, "pub const {s} = enum(usize) {{\n", .{arch.@"var"}); |
| 228 | if (arch.header) |h| |
| 229 | try Io.Writer.writeAll(stdout, h); |
| 230 | |
| 231 | var lines = mem.tokenizeScalar(u8, table, '\n'); |
| 232 | while (lines.next()) |line| { |
| 233 | if (line[0] == '#') continue; |
| 234 | if (arch.get(line)) |res| { |
| 235 | const name, const num = res; |
| 236 | const final_name = stdlib_renames.get(name) orelse name; |
| 237 | try Io.Writer.print(stdout, " {f} = {d},\n", .{ std.zig.fmtId(final_name), num }); |
| 238 | } |
| 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 | 251 | fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void { |