authorgravatar for 69125922+ippsav@users.noreply.github.comippsav <69125922+ippsav@users.noreply.github.com> 2024-08-05 18:43:47+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-05 10:43:47-07:00
log724804a4e026eea8cd42804b44bb0449d4ab3f3c
tree9d9ad4a4d7c6b80eae342117340025fce429e361
parentfab5df4028f686a69330982b41ee880b5bdd4e04
signaturebadge-check Signed by PGP key B5690EEEBB952194

Rewrite `generate_linux_syscalls.zig` (#20895)

refactors the syscall generation tool aiming to reduce code duplication for both the table based arches and the ones generated using a preprocessor.

1 files changed, 588 insertions(+), 651 deletions(-)

tools/generate_linux_syscalls.zig+588-651
...@@ -48,100 +48,132 @@ fn isReservedNameOld(name: []const u8) bool {...@@ -48,100 +48,132 @@ fn isReservedNameOld(name: []const u8) bool {
48 std.mem.startsWith(u8, name, "unused");48 std.mem.startsWith(u8, name, "unused");
49}49}
5050
51pub fn main() !void {51const default_args: []const []const u8 = &.{
52 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);52 "-E",
53 defer arena.deinit();53 // -dM is cleaner, but -dD preserves iteration order.
54 const allocator = arena.allocator();54 "-dD",
5555 // No need for line-markers.
56 const args = try std.process.argsAlloc(allocator);56 "-P",
57 if (args.len < 3 or mem.eql(u8, args[1], "--help"))57 "-nostdinc",
58 usageAndExit(std.io.getStdErr(), args[0], 1);58 // Using -I=[dir] includes the zig linux headers, which we don't want.
59 const zig_exe = args[1];59 "-Itools/include",
60 const linux_path = args[2];60 "-Itools/include/uapi",
6161 // Output the syscall in a format we can easily recognize.
62 var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer());62 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
63 const writer = buf_out.writer();63};
6464
65 // As of 5.17.1, the largest table is 23467 bytes.65const ProcessPreprocessedFileFn = *const fn (bytes: []const u8, writer: anytype) anyerror!void;
66 // 32k should be enough for now.66const ProcessTableBasedArchFileFn = *const fn (
67 const buf = try allocator.alloc(u8, 1 << 15);67 bytes: []const u8,
68 const linux_dir = try std.fs.openDirAbsolute(linux_path, .{});68 filters: Filters,
6969 writer: anytype,
70 try writer.writeAll(70 optional_writer: anytype,
71 \\// This file is automatically generated.71) anyerror!void;
72 \\// See tools/generate_linux_syscalls.zig for more info.72
73 \\73const FlowControl = enum {
74 \\74 @"break",
75 );75 @"continue",
7676 none,
77 // These architectures have their syscall definitions generated from a TSV77};
78 // file, processed via scripts/syscallhdr.sh.78
79 {79const AbiCheckParams = struct { abi: []const u8, flow: FlowControl };
80 try writer.writeAll("pub const X86 = enum(usize) {\n");80
8181const Filters = struct {
82 const table = try linux_dir.readFile("arch/x86/entry/syscalls/syscall_32.tbl", buf);82 abiCheckParams: ?AbiCheckParams,
83 var lines = mem.tokenizeScalar(u8, table, '\n');83 fixedName: ?*const fn (name: []const u8) []const u8,
84 while (lines.next()) |line| {84 isReservedNameOld: ?*const fn (name: []const u8) bool,
85 if (line[0] == '#') continue;85};
8686
87 var fields = mem.tokenizeAny(u8, line, " \t");87fn abiCheck(abi: []const u8, params: *const AbiCheckParams) FlowControl {
88 const number = fields.next() orelse return error.Incomplete;88 if (mem.eql(u8, abi, params.abi)) return params.flow;
89 // abi is always i38689 return .none;
90 _ = fields.next() orelse return error.Incomplete;90}
91 const name = fields.next() orelse return error.Incomplete;
92 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
93
94 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
95 }
96
97 try writer.writeAll("};\n\n");
98 }
99 {
100 try writer.writeAll("pub const X64 = enum(usize) {\n");
101
102 const table = try linux_dir.readFile("arch/x86/entry/syscalls/syscall_64.tbl", buf);
103 var lines = mem.tokenizeScalar(u8, table, '\n');
104 while (lines.next()) |line| {
105 if (line[0] == '#') continue;
106
107 var fields = mem.tokenizeAny(u8, line, " \t");
108 const number = fields.next() orelse return error.Incomplete;
109 const abi = fields.next() orelse return error.Incomplete;
110 // The x32 abi syscalls are always at the end.
111 if (mem.eql(u8, abi, "x32")) break;
112 const name = fields.next() orelse return error.Incomplete;
113 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
114
115 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
116 }
117
118 try writer.writeAll("};\n\n");
119 }
120 {
121 try writer.writeAll(
122 \\pub const Arm = enum(usize) {
123 \\ const arm_base = 0x0f0000;
124 \\
125 \\
126 );
127
128 const table = try linux_dir.readFile("arch/arm/tools/syscall.tbl", buf);
129 var lines = mem.tokenizeScalar(u8, table, '\n');
130 while (lines.next()) |line| {
131 if (line[0] == '#') continue;
132
133 var fields = mem.tokenizeAny(u8, line, " \t");
134 const number = fields.next() orelse return error.Incomplete;
135 const abi = fields.next() orelse return error.Incomplete;
136 if (mem.eql(u8, abi, "oabi")) continue;
137 const name = fields.next() orelse return error.Incomplete;
138 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
13991
140 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });92fn fixedName(name: []const u8) []const u8 {
141 }93 return if (stdlib_renames.get(name)) |fixed| fixed else name;
94}
14295
143 // TODO: maybe extract these from arch/arm/include/uapi/asm/unistd.h96const ArchInfo = union(enum) {
144 try writer.writeAll(97 table: struct {
98 name: []const u8,
99 enum_name: []const u8,
100 file_path: []const u8,
101 header: ?[]const u8,
102 extra_values: ?[]const u8,
103 process_file: ProcessTableBasedArchFileFn,
104 filters: Filters,
105 additional_enum: ?[]const u8,
106 },
107 preprocessor: struct {
108 name: []const u8,
109 enum_name: []const u8,
110 file_path: []const u8,
111 child_options: struct {
112 comptime additional_args: ?[]const []const u8 = null,
113 target: []const u8,
114
115 pub inline fn getArgs(self: *const @This(), zig_exe: []const u8, file_path: []const u8) []const []const u8 {
116 const additional_args: []const []const u8 = self.additional_args orelse &.{};
117 return .{ zig_exe, "cc" } ++ additional_args ++ .{ "-target", self.target } ++ default_args ++ .{file_path};
118 }
119 },
120 header: ?[]const u8,
121 extra_values: ?[]const u8,
122 process_file: ProcessPreprocessedFileFn,
123 additional_enum: ?[]const u8,
124 },
125};
126
127const arch_infos = [_]ArchInfo{
128 .{
129 // These architectures have their syscall definitions generated from a TSV
130 // file, processed via scripts/syscallhdr.sh.
131 .table = .{
132 .name = "x86",
133 .enum_name = "X86",
134 .file_path = "arch/x86/entry/syscalls/syscall_32.tbl",
135 .process_file = &processTableBasedArch,
136 .filters = .{
137 .abiCheckParams = null,
138 .fixedName = &fixedName,
139 .isReservedNameOld = null,
140 },
141 .header = null,
142 .extra_values = null,
143 .additional_enum = null,
144 },
145 },
146 .{
147 .table = .{
148 .name = "x64",
149 .enum_name = "X64",
150 .file_path = "arch/x86/entry/syscalls/syscall_64.tbl",
151 .process_file = &processTableBasedArch,
152 .filters = .{
153 // The x32 abi syscalls are always at the end.
154 .abiCheckParams = .{ .abi = "x32", .flow = .@"break" },
155 .fixedName = &fixedName,
156 .isReservedNameOld = null,
157 },
158 .header = null,
159 .extra_values = null,
160 .additional_enum = null,
161 },
162 },
163 .{
164 .table = .{
165 .name = "arm",
166 .enum_name = "Arm",
167 .file_path = "arch/arm/tools/syscall.tbl",
168 .process_file = &processTableBasedArch,
169 .filters = .{
170 .abiCheckParams = .{ .abi = "oabi", .flow = .@"continue" },
171 .fixedName = &fixedName,
172 .isReservedNameOld = null,
173 },
174 .header = " const arm_base = 0x0f0000;\n\n",
175 // TODO: maybe extract these from arch/arm/include/uapi/asm/unistd.h
176 .extra_values =
145 \\177 \\
146 \\ breakpoint = arm_base + 1,178 \\ breakpoint = arm_base + 1,
147 \\ cacheflush = arm_base + 2,179 \\ cacheflush = arm_base + 2,
...@@ -149,609 +181,514 @@ pub fn main() !void {...@@ -149,609 +181,514 @@ pub fn main() !void {
149 \\ usr32 = arm_base + 4,181 \\ usr32 = arm_base + 4,
150 \\ set_tls = arm_base + 5,182 \\ set_tls = arm_base + 5,
151 \\ get_tls = arm_base + 6,183 \\ get_tls = arm_base + 6,
152 \\};
153 \\
154 \\184 \\
155 );185 ,
156 }186 .additional_enum = null,
157 {187 },
158 try writer.writeAll("pub const Sparc = enum(usize) {\n");188 },
159 const table = try linux_dir.readFile("arch/sparc/kernel/syscalls/syscall.tbl", buf);189 .{
160 var lines = mem.tokenizeScalar(u8, table, '\n');190 .table = .{
161 while (lines.next()) |line| {191 .name = "sparc",
162 if (line[0] == '#') continue;192 .enum_name = "Sparc",
163193 .file_path = "arch/sparc/kernel/syscalls/syscall.tbl",
164 var fields = mem.tokenizeAny(u8, line, " \t");194 .process_file = &processTableBasedArch,
165 const number = fields.next() orelse return error.Incomplete;195 .filters = .{
166 const abi = fields.next() orelse return error.Incomplete;196 .abiCheckParams = .{ .abi = "64", .flow = .@"continue" },
167 if (mem.eql(u8, abi, "64")) continue;197 .fixedName = &fixedName,
168 const name = fields.next() orelse return error.Incomplete;198 .isReservedNameOld = null,
169 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;199 },
170200 .header = null,
171 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });201 .extra_values = null,
172 }202 .additional_enum = null,
173203 },
174 try writer.writeAll("};\n\n");204 },
175 }205 .{
176 {206 .table = .{
177 try writer.writeAll("pub const Sparc64 = enum(usize) {\n");207 .name = "sparc64",
178 const table = try linux_dir.readFile("arch/sparc/kernel/syscalls/syscall.tbl", buf);208 .enum_name = "Sparc64",
179 var lines = mem.tokenizeScalar(u8, table, '\n');209 .file_path = "arch/sparc/kernel/syscalls/syscall.tbl",
180 while (lines.next()) |line| {210 .process_file = &processTableBasedArch,
181 if (line[0] == '#') continue;211 .filters = .{
182212 .abiCheckParams = .{ .abi = "32", .flow = .@"continue" },
183 var fields = mem.tokenizeAny(u8, line, " \t");213 .fixedName = &fixedName,
184 const number = fields.next() orelse return error.Incomplete;214 .isReservedNameOld = null,
185 const abi = fields.next() orelse return error.Incomplete;215 },
186 if (mem.eql(u8, abi, "32")) continue;216 .header = null,
187 const name = fields.next() orelse return error.Incomplete;217 .extra_values = null,
188 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;218 .additional_enum = null,
189219 },
190 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });220 },
191 }221 .{
192222 .table = .{
193 try writer.writeAll("};\n\n");223 .name = "m68k",
224 .enum_name = "M68k",
225 .file_path = "arch/m68k/kernel/syscalls/syscall.tbl",
226 .process_file = &processTableBasedArch,
227 .filters = .{
228 // abi is always common
229 .abiCheckParams = null,
230 .fixedName = &fixedName,
231 .isReservedNameOld = null,
232 },
233 .header = null,
234 .extra_values = null,
235 .additional_enum = null,
236 },
237 },
238 .{
239 .table = .{
240 .name = "mips_o32",
241 .enum_name = "MipsO32",
242 .file_path = "arch/mips/kernel/syscalls/syscall_o32.tbl",
243 .process_file = &processMipsBasedArch,
244 .filters = .{
245 // abi is always o32
246 .abiCheckParams = null,
247 .fixedName = &fixedName,
248 .isReservedNameOld = &isReservedNameOld,
249 },
250 .header = " const linux_base = 4000;\n\n",
251 .extra_values = null,
252 .additional_enum = null,
253 },
254 },
255 .{
256 .table = .{
257 .name = "mips_n64",
258 .enum_name = "MipsN64",
259 .file_path = "arch/mips/kernel/syscalls/syscall_n64.tbl",
260 .process_file = &processMipsBasedArch,
261 .filters = .{
262 // abi is always n64
263 .abiCheckParams = null,
264 .fixedName = &fixedName,
265 .isReservedNameOld = &isReservedNameOld,
266 },
267 .header = " const linux_base = 5000;\n\n",
268 .extra_values = null,
269 .additional_enum = null,
270 },
271 },
272 .{
273 .table = .{
274 .name = "mips_n32",
275 .enum_name = "MipsN32",
276 .file_path = "arch/mips/kernel/syscalls/syscall_n32.tbl",
277 .process_file = &processMipsBasedArch,
278 .filters = .{
279 // abi is always n32
280 .abiCheckParams = null,
281 .fixedName = &fixedName,
282 .isReservedNameOld = &isReservedNameOld,
283 },
284 .header = " const linux_base = 6000;\n\n",
285 .extra_values = null,
286 .additional_enum = null,
287 },
288 },
289 .{
290 .table = .{
291 .name = "powerpc",
292 .enum_name = "PowerPC",
293 .file_path = "arch/powerpc/kernel/syscalls/syscall.tbl",
294 .process_file = &processPowerPcBasedArch,
295 .filters = .{
296 .abiCheckParams = null,
297 .fixedName = null,
298 .isReservedNameOld = null,
299 },
300 .header = null,
301 .extra_values = null,
302 .additional_enum = "PowerPC64",
303 },
304 },
305 .{
306 .table = .{
307 .name = "s390x",
308 .enum_name = "S390x",
309 .file_path = "arch/s390/kernel/syscalls/syscall.tbl",
310 .process_file = &processTableBasedArch,
311 .filters = .{
312 // 32-bit s390 support in linux is deprecated
313 .abiCheckParams = .{ .abi = "32", .flow = .@"continue" },
314 .fixedName = &fixedName,
315 .isReservedNameOld = null,
316 },
317 .header = null,
318 .extra_values = null,
319 .additional_enum = null,
320 },
321 },
322 .{
323 .table = .{
324 .name = "xtensa",
325 .enum_name = "Xtensa",
326 .file_path = "arch/xtensa/kernel/syscalls/syscall.tbl",
327 .process_file = &processTableBasedArch,
328 .filters = .{
329 // abi is always common
330 .abiCheckParams = null,
331 .fixedName = fixedName,
332 .isReservedNameOld = &isReservedNameOld,
333 },
334 .header = null,
335 .extra_values = null,
336 .additional_enum = null,
337 },
338 },
339 .{
340 .preprocessor = .{
341 .name = "arm64",
342 .enum_name = "Arm64",
343 .file_path = "arch/arm64/include/uapi/asm/unistd.h",
344 .child_options = .{
345 .additional_args = null,
346 .target = "aarch64-freestanding-none",
347 },
348 .process_file = &processPreprocessedFile,
349 .header = null,
350 .extra_values = null,
351 .additional_enum = null,
352 },
353 },
354 .{
355 .preprocessor = .{
356 .name = "riscv32",
357 .enum_name = "RiscV32",
358 .file_path = "arch/riscv/include/uapi/asm/unistd.h",
359 .child_options = .{
360 .additional_args = null,
361 .target = "riscv32-freestanding-none",
362 },
363 .process_file = &processPreprocessedFile,
364 .header = null,
365 .extra_values = null,
366 .additional_enum = null,
367 },
368 },
369 .{
370 .preprocessor = .{
371 .name = "riscv64",
372 .enum_name = "RiscV64",
373 .file_path = "arch/riscv/include/uapi/asm/unistd.h",
374 .child_options = .{
375 .additional_args = null,
376 .target = "riscv64-freestanding-none",
377 },
378 .process_file = &processPreprocessedFile,
379 .header = null,
380 .extra_values = null,
381 .additional_enum = null,
382 },
383 },
384 .{
385 .preprocessor = .{
386 .name = "loongarch",
387 .enum_name = "LoongArch64",
388 .file_path = "arch/loongarch/include/uapi/asm/unistd.h",
389 .child_options = .{
390 .additional_args = null,
391 .target = "loongarch64-freestanding-none",
392 },
393 .process_file = &processPreprocessedFile,
394 .header = null,
395 .extra_values = null,
396 .additional_enum = null,
397 },
398 },
399 .{
400 .preprocessor = .{
401 .name = "arc",
402 .enum_name = "Arc",
403 .file_path = "arch/arc/include/uapi/asm/unistd.h",
404 .child_options = .{
405 .additional_args = null,
406 .target = "arc-freestanding-none",
407 },
408 .process_file = &processPreprocessedFile,
409 .header = null,
410 .extra_values = null,
411 .additional_enum = null,
412 },
413 },
414 .{
415 .preprocessor = .{
416 .name = "csky",
417 .enum_name = "CSky",
418 .file_path = "arch/csky/include/uapi/asm/unistd.h",
419 .child_options = .{
420 .additional_args = null,
421 .target = "csky-freestanding-none",
422 },
423 .process_file = &processPreprocessedFile,
424 .header = null,
425 .extra_values = null,
426 .additional_enum = null,
427 },
428 },
429 .{
430 .preprocessor = .{
431 .name = "hexagon",
432 .enum_name = "Hexagon",
433 .file_path = "arch/hexagon/include/uapi/asm/unistd.h",
434 .child_options = .{
435 .additional_args = null,
436 .target = "hexagon-freestanding-none",
437 },
438 .process_file = &processPreprocessedFile,
439 .header = null,
440 .extra_values = null,
441 .additional_enum = null,
442 },
443 },
444};
445
446fn processPreprocessedFile(
447 bytes: []const u8,
448 writer: anytype,
449) !void {
450 var lines = mem.tokenizeScalar(u8, bytes, '\n');
451 while (lines.next()) |line| {
452 var fields = mem.tokenizeAny(u8, line, " ");
453 const prefix = fields.next() orelse return error.Incomplete;
454
455 if (!mem.eql(u8, prefix, "zigsyscall")) continue;
456
457 const sys_name = fields.next() orelse return error.Incomplete;
458 const value = fields.rest();
459 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
460 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
461
462 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });
194 }463 }
195 {464}
196 try writer.writeAll("pub const M68k = enum(usize) {\n");
197
198 const table = try linux_dir.readFile("arch/m68k/kernel/syscalls/syscall.tbl", buf);
199 var lines = mem.tokenizeScalar(u8, table, '\n');
200 while (lines.next()) |line| {
201 if (line[0] == '#') continue;
202
203 var fields = mem.tokenizeAny(u8, line, " \t");
204 const number = fields.next() orelse return error.Incomplete;
205 // abi is always common
206 _ = fields.next() orelse return error.Incomplete;
207 const name = fields.next() orelse return error.Incomplete;
208 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
209465
210 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });466fn processTableBasedArch(
467 bytes: []const u8,
468 filters: Filters,
469 writer: anytype,
470 optional_writer: anytype,
471) !void {
472 _ = optional_writer;
473
474 var lines = mem.tokenizeScalar(u8, bytes, '\n');
475 while (lines.next()) |line| {
476 if (line[0] == '#') continue;
477
478 var fields = mem.tokenizeAny(u8, line, " \t");
479 const number = fields.next() orelse return error.Incomplete;
480
481 const abi = fields.next() orelse return error.Incomplete;
482 if (filters.abiCheckParams) |*params| {
483 switch (abiCheck(abi, params)) {
484 .none => {},
485 .@"break" => break,
486 .@"continue" => continue,
487 }
211 }488 }
212489 const name = fields.next() orelse return error.Incomplete;
213 try writer.writeAll("};\n\n");490 if (filters.isReservedNameOld) |isReservedNameOldFn| {
214 }491 if (isReservedNameOldFn(name)) continue;
215 {
216 try writer.writeAll(
217 \\pub const MipsO32 = enum(usize) {
218 \\ const linux_base = 4000;
219 \\
220 \\
221 );
222
223 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_o32.tbl", buf);
224 var lines = mem.tokenizeScalar(u8, table, '\n');
225 while (lines.next()) |line| {
226 if (line[0] == '#') continue;
227
228 var fields = mem.tokenizeAny(u8, line, " \t");
229 const number = fields.next() orelse return error.Incomplete;
230 // abi is always o32
231 _ = fields.next() orelse return error.Incomplete;
232 const name = fields.next() orelse return error.Incomplete;
233 if (isReservedNameOld(name)) continue;
234 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
235
236 try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number });
237 }492 }
493 const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name;
238494
239 try writer.writeAll("};\n\n");495 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
240 }496 }
241 {497}
242 try writer.writeAll(
243 \\pub const MipsN64 = enum(usize) {
244 \\ const linux_base = 5000;
245 \\
246 \\
247 );
248
249 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n64.tbl", buf);
250 var lines = mem.tokenizeScalar(u8, table, '\n');
251 while (lines.next()) |line| {
252 if (line[0] == '#') continue;
253
254 var fields = mem.tokenizeAny(u8, line, " \t");
255 const number = fields.next() orelse return error.Incomplete;
256 // abi is always n64
257 _ = fields.next() orelse return error.Incomplete;
258 const name = fields.next() orelse return error.Incomplete;
259 if (isReservedNameOld(name)) continue;
260 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
261
262 try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number });
263 }
264
265 try writer.writeAll("};\n\n");
266 }
267 {
268 try writer.writeAll(
269 \\pub const MipsN32 = enum(usize) {
270 \\ const linux_base = 6000;
271 \\
272 \\
273 );
274
275 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n32.tbl", buf);
276 var lines = mem.tokenizeScalar(u8, table, '\n');
277 while (lines.next()) |line| {
278 if (line[0] == '#') continue;
279
280 var fields = mem.tokenizeAny(u8, line, " \t");
281 const number = fields.next() orelse return error.Incomplete;
282 // abi is always n32
283 _ = fields.next() orelse return error.Incomplete;
284 const name = fields.next() orelse return error.Incomplete;
285 if (isReservedNameOld(name)) continue;
286 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
287
288 try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number });
289 }
290498
291 try writer.writeAll("};\n\n");499fn processMipsBasedArch(
292 }500 bytes: []const u8,
293 {501 filters: Filters,
294 try writer.writeAll("pub const PowerPC = enum(usize) {\n");502 writer: anytype,
295503 optional_writer: anytype,
296 const table = try linux_dir.readFile("arch/powerpc/kernel/syscalls/syscall.tbl", buf);504) !void {
297 var list_64 = std.ArrayList(u8).init(allocator);505 _ = optional_writer;
298 var lines = mem.tokenizeScalar(u8, table, '\n');506
299 while (lines.next()) |line| {507 var lines = mem.tokenizeScalar(u8, bytes, '\n');
300 if (line[0] == '#') continue;508 while (lines.next()) |line| {
301509 if (line[0] == '#') continue;
302 var fields = mem.tokenizeAny(u8, line, " \t");510
303 const number = fields.next() orelse return error.Incomplete;511 var fields = mem.tokenizeAny(u8, line, " \t");
304 const abi = fields.next() orelse return error.Incomplete;512 const number = fields.next() orelse return error.Incomplete;
305 const name = fields.next() orelse return error.Incomplete;513
306 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;514 const abi = fields.next() orelse return error.Incomplete;
307515 if (filters.abiCheckParams) |*params| {
308 if (mem.eql(u8, abi, "spu")) {516 switch (abiCheck(abi, params)) {
309 continue;517 .none => {},
310 } else if (mem.eql(u8, abi, "32")) {518 .@"break" => break,
311 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });519 .@"continue" => continue,
312 } else if (mem.eql(u8, abi, "64")) {
313 try list_64.writer().print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
314 } else { // common/nospu
315 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
316 try list_64.writer().print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
317 }520 }
318 }521 }
319522 const name = fields.next() orelse return error.Incomplete;
320 try writer.writeAll(523 if (filters.isReservedNameOld) |isReservedNameOldFn| {
321 \\};524 if (isReservedNameOldFn(name)) continue;
322 \\
323 \\pub const PowerPC64 = enum(usize) {
324 \\
325 );
326 try writer.writeAll(list_64.items);
327 try writer.writeAll("};\n\n");
328 }
329 {
330 try writer.writeAll("pub const S390x = enum(usize) {\n");
331
332 const table = try linux_dir.readFile("arch/s390/kernel/syscalls/syscall.tbl", buf);
333 var lines = mem.tokenizeScalar(u8, table, '\n');
334 while (lines.next()) |line| {
335 if (line[0] == '#') continue;
336
337 var fields = mem.tokenizeAny(u8, line, " \t");
338 const number = fields.next() orelse return error.Incomplete;
339 const abi = fields.next() orelse return error.Incomplete;
340 if (mem.eql(u8, abi, "32")) continue; // 32-bit s390 support in linux is deprecated
341 const name = fields.next() orelse return error.Incomplete;
342 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
343
344 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
345 }525 }
526 const fixed_name = if (filters.fixedName) |fixedNameFn| fixedNameFn(name) else name;
346527
347 try writer.writeAll("};\n\n");528 try writer.print(" {p} = linux_base + {s},\n", .{ zig.fmtId(fixed_name), number });
348 }529 }
349 {530}
350 try writer.writeAll("pub const Xtensa = enum(usize) {\n");
351
352 const table = try linux_dir.readFile("arch/xtensa/kernel/syscalls/syscall.tbl", buf);
353 var lines = mem.tokenizeScalar(u8, table, '\n');
354 while (lines.next()) |line| {
355 if (line[0] == '#') continue;
356
357 var fields = mem.tokenizeAny(u8, line, " \t");
358 const number = fields.next() orelse return error.Incomplete;
359 // abi is always common
360 _ = fields.next() orelse return error.Incomplete;
361 const name = fields.next() orelse return error.Incomplete;
362 if (isReservedNameOld(name)) continue;
363 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
364531
532fn processPowerPcBasedArch(
533 bytes: []const u8,
534 filters: Filters,
535 writer: anytype,
536 optional_writer: anytype,
537) !void {
538 _ = filters;
539 var lines = mem.tokenizeScalar(u8, bytes, '\n');
540
541 while (lines.next()) |line| {
542 if (line[0] == '#') continue;
543
544 var fields = mem.tokenizeAny(u8, line, " \t");
545 const number = fields.next() orelse return error.Incomplete;
546 const abi = fields.next() orelse return error.Incomplete;
547 const name = fields.next() orelse return error.Incomplete;
548 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
549
550 if (mem.eql(u8, abi, "spu")) {
551 continue;
552 } else if (mem.eql(u8, abi, "32")) {
553 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
554 } else if (mem.eql(u8, abi, "64")) {
555 try optional_writer.?.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
556 } else { // common/nospu
365 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });557 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
558 try optional_writer.?.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number });
366 }559 }
367
368 try writer.writeAll("};\n\n");
369 }560 }
561}
370562
371 // Newer architectures (starting with aarch64 c. 2012) now use the same C563fn generateSyscallsFromTable(
372 // header file for their syscall numbers. Arch-specific headers are used to564 allocator: std.mem.Allocator,
373 // define pre-proc. vars that add additional (usually obsolete) syscalls.565 buf: []u8,
374 {566 linux_dir: std.fs.Dir,
375 try writer.writeAll("pub const Arm64 = enum(usize) {\n");567 writer: anytype,
376568 _arch_info: *const ArchInfo,
377 const child_args = [_][]const u8{569) !void {
378 zig_exe,570 std.debug.assert(_arch_info.* == .table);
379 "cc",
380 "-target",
381 "aarch64-linux-gnu",
382 "-E",
383 // -dM is cleaner, but -dD preserves iteration order.
384 "-dD",
385 // No need for line-markers.
386 "-P",
387 "-nostdinc",
388 // Using -I=[dir] includes the zig linux headers, which we don't want.
389 "-Itools/include",
390 "-Itools/include/uapi",
391 // Output the syscall in a format we can easily recognize.
392 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
393 "arch/arm64/include/uapi/asm/unistd.h",
394 };
395
396 const child_result = try std.process.Child.run(.{
397 .allocator = allocator,
398 .argv = &child_args,
399 .cwd = linux_path,
400 .cwd_dir = linux_dir,
401 });
402 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
403
404 const defines = switch (child_result.term) {
405 .Exited => |code| if (code == 0) child_result.stdout else {
406 std.debug.print("zig cc exited with code {d}\n", .{code});
407 std.process.exit(1);
408 },
409 else => {
410 std.debug.print("zig cc crashed\n", .{});
411 std.process.exit(1);
412 },
413 };
414571
415 var lines = mem.tokenizeScalar(u8, defines, '\n');572 const arch_info = _arch_info.table;
416 while (lines.next()) |line| {
417 var fields = mem.tokenizeAny(u8, line, " ");
418 const prefix = fields.next() orelse return error.Incomplete;
419573
420 if (!mem.eql(u8, prefix, "zigsyscall")) continue;574 const table = try linux_dir.readFile(arch_info.file_path, buf);
421575
422 const sys_name = fields.next() orelse return error.Incomplete;576 var optional_array_list: ?std.ArrayList(u8) = if (arch_info.additional_enum) |_| std.ArrayList(u8).init(allocator) else null;
423 const value = fields.rest();577 const optional_writer = if (optional_array_list) |_| optional_array_list.?.writer() else null;
424 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
425 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
426578
427 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });579 try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name});
428 }
429580
430 try writer.writeAll("};\n\n");581 if (arch_info.header) |header| {
582 try writer.writeAll(header);
431 }583 }
432 {
433 try writer.writeAll("pub const RiscV32 = enum(usize) {\n");
434
435 const child_args = [_][]const u8{
436 zig_exe,
437 "cc",
438 "-target",
439 "riscv32-linux-gnuilp32",
440 "-E",
441 "-dD",
442 "-P",
443 "-nostdinc",
444 "-Itools/include",
445 "-Itools/include/uapi",
446 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
447 "arch/riscv/include/uapi/asm/unistd.h",
448 };
449
450 const child_result = try std.process.Child.run(.{
451 .allocator = allocator,
452 .argv = &child_args,
453 .cwd = linux_path,
454 .cwd_dir = linux_dir,
455 });
456 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
457
458 const defines = switch (child_result.term) {
459 .Exited => |code| if (code == 0) child_result.stdout else {
460 std.debug.print("zig cc exited with code {d}\n", .{code});
461 std.process.exit(1);
462 },
463 else => {
464 std.debug.print("zig cc crashed\n", .{});
465 std.process.exit(1);
466 },
467 };
468584
469 var lines = mem.tokenizeScalar(u8, defines, '\n');585 try arch_info.process_file(table, arch_info.filters, writer, optional_writer);
470 while (lines.next()) |line| {
471 var fields = mem.tokenizeAny(u8, line, " ");
472 const prefix = fields.next() orelse return error.Incomplete;
473586
474 if (!mem.eql(u8, prefix, "zigsyscall")) continue;587 if (arch_info.extra_values) |extra_values| {
475588 try writer.writeAll(extra_values);
476 const sys_name = fields.next() orelse return error.Incomplete;
477 const value = fields.rest();
478 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
479 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
480
481 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });
482 }
483
484 try writer.writeAll("};\n\n");
485 }589 }
486 {
487 try writer.writeAll("pub const RiscV64 = enum(usize) {\n");
488
489 const child_args = [_][]const u8{
490 zig_exe,
491 "cc",
492 "-target",
493 "riscv64-linux-gnu",
494 "-E",
495 "-dD",
496 "-P",
497 "-nostdinc",
498 "-Itools/include",
499 "-Itools/include/uapi",
500 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
501 "arch/riscv/include/uapi/asm/unistd.h",
502 };
503
504 const child_result = try std.process.Child.run(.{
505 .allocator = allocator,
506 .argv = &child_args,
507 .cwd = linux_path,
508 .cwd_dir = linux_dir,
509 });
510 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
511
512 const defines = switch (child_result.term) {
513 .Exited => |code| if (code == 0) child_result.stdout else {
514 std.debug.print("zig cc exited with code {d}\n", .{code});
515 std.process.exit(1);
516 },
517 else => {
518 std.debug.print("zig cc crashed\n", .{});
519 std.process.exit(1);
520 },
521 };
522
523 var lines = mem.tokenizeScalar(u8, defines, '\n');
524 while (lines.next()) |line| {
525 var fields = mem.tokenizeAny(u8, line, " ");
526 const prefix = fields.next() orelse return error.Incomplete;
527
528 if (!mem.eql(u8, prefix, "zigsyscall")) continue;
529590
530 const sys_name = fields.next() orelse return error.Incomplete;591 try writer.writeAll("};\n\n");
531 const value = fields.rest();
532 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
533 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
534
535 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });
536 }
537592
593 if (arch_info.additional_enum) |additional_enum| {
594 try writer.print("pub const {s} = enum(usize) {{\n", .{additional_enum});
595 try writer.writeAll(optional_array_list.?.items);
538 try writer.writeAll("};\n\n");596 try writer.writeAll("};\n\n");
539 }597 }
540 {598}
541 try writer.writeAll("pub const LoongArch64 = enum(usize) {\n");
542
543 const child_args = [_][]const u8{
544 zig_exe,
545 "cc",
546 "-target",
547 "loongarch64-linux-gnu",
548 "-E",
549 "-dD",
550 "-P",
551 "-nostdinc",
552 "-Itools/include",
553 "-Itools/include/uapi",
554 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
555 "arch/loongarch/include/uapi/asm/unistd.h",
556 };
557
558 const child_result = try std.process.Child.run(.{
559 .allocator = allocator,
560 .argv = &child_args,
561 .cwd = linux_path,
562 .cwd_dir = linux_dir,
563 });
564 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
565
566 const defines = switch (child_result.term) {
567 .Exited => |code| if (code == 0) child_result.stdout else {
568 std.debug.print("zig cc exited with code {d}\n", .{code});
569 std.process.exit(1);
570 },
571 else => {
572 std.debug.print("zig cc crashed\n", .{});
573 std.process.exit(1);
574 },
575 };
576
577 var lines = mem.tokenizeScalar(u8, defines, '\n');
578 while (lines.next()) |line| {
579 var fields = mem.tokenizeAny(u8, line, " ");
580 const prefix = fields.next() orelse return error.Incomplete;
581
582 if (!mem.eql(u8, prefix, "zigsyscall")) continue;
583
584 const sys_name = fields.next() orelse return error.Incomplete;
585 const value = fields.rest();
586 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
587 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
588
589 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });
590 }
591599
592 try writer.writeAll("};\n\n");600fn generateSyscallsFromPreprocessor(
601 allocator: std.mem.Allocator,
602 linux_dir: std.fs.Dir,
603 linux_path: []const u8,
604 zig_exe: []const u8,
605 writer: anytype,
606 _arch_info: *const ArchInfo,
607) !void {
608 std.debug.assert(_arch_info.* == .preprocessor);
609
610 const arch_info = _arch_info.preprocessor;
611
612 const child_result = try std.process.Child.run(.{
613 .allocator = allocator,
614 .argv = arch_info.child_options.getArgs(zig_exe, arch_info.file_path),
615 .cwd = linux_path,
616 .cwd_dir = linux_dir,
617 });
618 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
619
620 const defines = switch (child_result.term) {
621 .Exited => |code| if (code == 0) child_result.stdout else {
622 std.debug.print("zig cc exited with code {d}\n", .{code});
623 std.process.exit(1);
624 },
625 else => {
626 std.debug.print("zig cc crashed\n", .{});
627 std.process.exit(1);
628 },
629 };
630
631 try writer.print("pub const {s} = enum(usize) {{\n", .{arch_info.enum_name});
632 if (arch_info.header) |header| {
633 try writer.writeAll(header);
593 }634 }
594 {
595 try writer.writeAll("pub const Arc = enum(usize) {\n");
596
597 const child_args = [_][]const u8{
598 zig_exe,
599 "cc",
600 "-target",
601 "arc-freestanding-none",
602 "-E",
603 "-dD",
604 "-P",
605 "-nostdinc",
606 "-Itools/include",
607 "-Itools/include/uapi",
608 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
609 "arch/arc/include/uapi/asm/unistd.h",
610 };
611
612 const child_result = try std.process.Child.run(.{
613 .allocator = allocator,
614 .argv = &child_args,
615 .cwd = linux_path,
616 .cwd_dir = linux_dir,
617 });
618 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
619
620 const defines = switch (child_result.term) {
621 .Exited => |code| if (code == 0) child_result.stdout else {
622 std.debug.print("zig cc exited with code {d}\n", .{code});
623 std.process.exit(1);
624 },
625 else => {
626 std.debug.print("zig cc crashed\n", .{});
627 std.process.exit(1);
628 },
629 };
630
631 var lines = mem.tokenizeScalar(u8, defines, '\n');
632 while (lines.next()) |line| {
633 var fields = mem.tokenizeAny(u8, line, " ");
634 const prefix = fields.next() orelse return error.Incomplete;
635635
636 if (!mem.eql(u8, prefix, "zigsyscall")) continue;636 try arch_info.process_file(defines, writer);
637
638 const sys_name = fields.next() orelse return error.Incomplete;
639 const value = fields.rest();
640 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
641 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
642
643 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });
644 }
645637
646 try writer.writeAll("};\n\n");638 if (arch_info.extra_values) |extra_values| {
639 try writer.writeAll(extra_values);
647 }640 }
648 {
649 try writer.writeAll("pub const CSky = enum(usize) {\n");
650
651 const child_args = [_][]const u8{
652 zig_exe,
653 "cc",
654 "-target",
655 "csky-freestanding-none",
656 "-E",
657 "-dD",
658 "-P",
659 "-nostdinc",
660 "-Itools/include",
661 "-Itools/include/uapi",
662 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
663 "arch/csky/include/uapi/asm/unistd.h",
664 };
665
666 const child_result = try std.process.Child.run(.{
667 .allocator = allocator,
668 .argv = &child_args,
669 .cwd = linux_path,
670 .cwd_dir = linux_dir,
671 });
672 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
673
674 const defines = switch (child_result.term) {
675 .Exited => |code| if (code == 0) child_result.stdout else {
676 std.debug.print("zig cc exited with code {d}\n", .{code});
677 std.process.exit(1);
678 },
679 else => {
680 std.debug.print("zig cc crashed\n", .{});
681 std.process.exit(1);
682 },
683 };
684641
685 var lines = mem.tokenizeScalar(u8, defines, '\n');642 try writer.writeAll("};\n\n");
686 while (lines.next()) |line| {643}
687 var fields = mem.tokenizeAny(u8, line, " ");
688 const prefix = fields.next() orelse return error.Incomplete;
689
690 if (!mem.eql(u8, prefix, "zigsyscall")) continue;
691
692 const sys_name = fields.next() orelse return error.Incomplete;
693 const value = fields.rest();
694 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];
695 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;
696644
697 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });645pub fn main() !void {
698 }646 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
647 defer arena.deinit();
648 const allocator = arena.allocator();
699649
700 try writer.writeAll("};\n\n");650 const args = try std.process.argsAlloc(allocator);
701 }651 if (args.len < 3 or mem.eql(u8, args[1], "--help"))
702 {652 usageAndExit(std.io.getStdErr(), args[0], 1);
703 try writer.writeAll("pub const Hexagon = enum(usize) {\n");653 const zig_exe = args[1];
704654 const linux_path = args[2];
705 const child_args = [_][]const u8{
706 zig_exe,
707 "cc",
708 "-target",
709 "hexagon-freestanding-none",
710 "-E",
711 "-dD",
712 "-P",
713 "-nostdinc",
714 "-Itools/include",
715 "-Itools/include/uapi",
716 "-D __SYSCALL(nr, nm)=zigsyscall nm nr",
717 "arch/hexagon/include/uapi/asm/unistd.h",
718 };
719
720 const child_result = try std.process.Child.run(.{
721 .allocator = allocator,
722 .argv = &child_args,
723 .cwd = linux_path,
724 .cwd_dir = linux_dir,
725 });
726 if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr});
727
728 const defines = switch (child_result.term) {
729 .Exited => |code| if (code == 0) child_result.stdout else {
730 std.debug.print("zig cc exited with code {d}\n", .{code});
731 std.process.exit(1);
732 },
733 else => {
734 std.debug.print("zig cc crashed\n", .{});
735 std.process.exit(1);
736 },
737 };
738655
739 var lines = mem.tokenizeScalar(u8, defines, '\n');656 var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer());
740 while (lines.next()) |line| {657 const writer = buf_out.writer();
741 var fields = mem.tokenizeAny(u8, line, " ");
742 const prefix = fields.next() orelse return error.Incomplete;
743658
744 if (!mem.eql(u8, prefix, "zigsyscall")) continue;659 var linux_dir = try std.fs.cwd().openDir(linux_path, .{});
660 defer linux_dir.close();
745661
746 const sys_name = fields.next() orelse return error.Incomplete;662 try writer.writeAll(
747 const value = fields.rest();663 \\// This file is automatically generated.
748 const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..];664 \\// See tools/generate_linux_syscalls.zig for more info.
749 const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name;665 \\
666 \\
667 );
750668
751 try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value });669 // As of 5.17.1, the largest table is 23467 bytes.
670 // 32k should be enough for now.
671 const buf = try allocator.alloc(u8, 1 << 15);
672 defer allocator.free(buf);
673
674 inline for (arch_infos) |arch_info| {
675 switch (arch_info) {
676 .table => try generateSyscallsFromTable(
677 allocator,
678 buf,
679 linux_dir,
680 writer,
681 &arch_info,
682 ),
683 .preprocessor => try generateSyscallsFromPreprocessor(
684 allocator,
685 linux_dir,
686 linux_path,
687 zig_exe,
688 writer,
689 &arch_info,
690 ),
752 }691 }
753
754 try writer.writeAll("};\n\n");
755 }692 }
756693
757 try buf_out.flush();694 try buf_out.flush();