authorgravatar for dev@sgregoratto.meStephen Gregoratto <dev@sgregoratto.me> 2025-08-05 21:16:46+10:00
committergravatar for dev@sgregoratto.meStephen Gregoratto <dev@sgregoratto.me> 2025-08-14 10:19:31+10:00
logc5f10a3f7dc9705ffcc7868c3362fa77c7803585
tree27db0b4ef075261f6e87bc81abf78a1cdbee4997
parent27d6614f81dfddcbd05b2a0b4afb8db83eaafb0b

Rewrite `generate_linux_syscalls` to be completely table based

Changes by Arnd Bergmann have migrated all supported architectures to use a table for their syscall lists. This removes the need to use the C pre-processor and simplifies the logic considerably. All currently supported architectures have been added, with the ones Zig doesn't support being commented out. Speaking of; OpenRisc has been enabled for generation.

1 files changed, 196 insertions(+), 668 deletions(-)

tools/generate_linux_syscalls.zig+196-668
...@@ -2,12 +2,18 @@...@@ -2,12 +2,18 @@
2//!2//!
3//! This tool extracts the Linux syscall numbers from the Linux source tree3//! 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.
513
6const std = @import("std");14const std = @import("std");
15const Io = std.Io;
7const mem = std.mem;16const mem = std.mem;
8const fmt = std.fmt;
9const zig = std.zig;
10const fs = std.fs;
1117
12const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{18const 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.
30const stdlib_renames_new = std.StaticStringMap([]const u8).initComptime(.{
31 .{ "newuname", "uname" },
32 .{ "umount", "umount2" },
33});31});
3432
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.34fn isReserved(name: []const u8) bool {
37fn 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
47fn 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}
5239
53const 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",49const 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};
6677
67const ProcessPreprocessedFileFn = *const fn (bytes: []const u8, writer: anytype) anyerror!void;78const __X32_SYSCALL_BIT: u32 = 0x40000000;
68const ProcessTableBasedArchFileFn = *const fn (79const __NR_Linux_O32: u32 = 4000;
69 bytes: []const u8,80const __NR_Linux_N64: u32 = 5000;
70 filters: Filters,81const __NR_Linux_N32: u32 = 6000;
71 writer: anytype,82
72 optional_writer: anytype,83const Arch = struct {
73) anyerror!void;84 /// Name for the generated enum variable.
7485 @"var": []const u8,
75const 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 = &.{},
8091 /// Some architectures need special handling:
81const AbiCheckParams = struct { abi: []const u8, flow: FlowControl };92 /// - x32 system calls must have their number OR'ed with
8293 /// `__X32_SYSCALL_BIT` to distinguish them against the regular x86_64 calls.
83const 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,
8899 header: ?[]const u8 = null,
89fn 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");
93104 const num_str = iter.next() orelse @panic("Bad field");
94fn 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
97108 // Filter out syscalls that aren't actually syscalls.
98const 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};
128124
129const arch_infos = [_]ArchInfo{125const 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 TSV127 .{ .@"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};
463172
464fn 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
484fn 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
517fn 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
550fn 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
581fn 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
618fn 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
663pub fn main() !void {173pub 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();
667177
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];
675184
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;
679188
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();
682191
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});
689220
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,
694225 }, 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 }
719247
720 try writer.flush();248 try Io.Writer.flush(stdout);
721}249}
722250
723fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {251fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {