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 @@
22//!
33//! This tool extracts the Linux syscall numbers from the Linux source tree
44//! 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
614const std = @import("std");
15const Io = std.Io;
716const mem = std.mem;
8const fmt = std.fmt;
9const zig = std.zig;
10const fs = std.fs;
1117
1218const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{
1319 // Remove underscore prefix.
......@@ -22,702 +28,224 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{
2228 // ARM EABI/Thumb.
2329 .{ "arm_sync_file_range", "sync_file_range" },
2430 .{ "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" },
3331});
3432
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.
37fn 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
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");
33/// Filter syscalls that aren't actually syscalls.
34fn 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");
5138}
5239
53const 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.
49const 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,
6576};
6677
67const ProcessPreprocessedFileFn = *const fn (bytes: []const u8, writer: anytype) anyerror!void;
68const ProcessTableBasedArchFileFn = *const fn (
69 bytes: []const u8,
70 filters: Filters,
71 writer: anytype,
72 optional_writer: anytype,
73) anyerror!void;
74
75const FlowControl = enum {
76 @"break",
77 @"continue",
78 none,
79};
80
81const AbiCheckParams = struct { abi: []const u8, flow: FlowControl };
82
83const Filters = struct {
84 abiCheckParams: ?AbiCheckParams,
85 fixedName: ?*const fn (name: []const u8) []const u8,
86 isReservedNameOld: ?*const fn (name: []const u8) bool,
87};
88
89fn abiCheck(abi: []const u8, params: *const AbiCheckParams) FlowControl {
90 if (mem.eql(u8, abi, params.abi)) return params.flow;
91 return .none;
92}
93
94fn fixedName(name: []const u8) []const u8 {
95 return if (stdlib_renames.get(name)) |fixed| fixed else name;
96}
97
98const 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 },
78const __X32_SYSCALL_BIT: u32 = 0x40000000;
79const __NR_Linux_O32: u32 = 4000;
80const __NR_Linux_N64: u32 = 5000;
81const __NR_Linux_N32: u32 = 6000;
82
83const 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 }
127123};
128124
129const 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 },
125const 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 },
223129 .{
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 ,
461146 },
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" } },
462171};
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
663173pub fn main() !void {
664174 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
665175 defer arena.deinit();
666 const allocator = arena.allocator();
176 const gpa = arena.allocator();
667177
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")) {
670180 usage(std.debug.lockStderrWriter(&.{}), args[0]) catch std.process.exit(2);
671181 std.process.exit(1);
672182 }
673 const zig_exe = args[1];
674 const linux_path = args[2];
183 const linux_path = args[1];
675184
676 var stdout_buffer: [2000]u8 = undefined;
185 var stdout_buffer: [2048]u8 = undefined;
677186 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
678 const writer = &stdout_writer.interface;
187 const stdout = &stdout_writer.interface;
679188
680189 var linux_dir = try std.fs.cwd().openDir(linux_path, .{});
681190 defer linux_dir.close();
682191
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.
685215 \\// See tools/generate_linux_syscalls.zig for more info.
216 \\// This list current as of kernel: {f}
686217 \\
687218 \\
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 }
717239 }
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');
718246 }
719247
720 try writer.flush();
248 try Io.Writer.flush(stdout);
721249}
722250
723251fn usage(w: *std.io.Writer, arg0: []const u8) std.io.Writer.Error!void {