From 11badbfe8ef5678ffb1d404cc42db036a899d844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 14:28:29 +0200 Subject: [PATCH 01/23] musl: Fix needsCrtiCrtn() to return false for riscv32 too. --- src/musl.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/musl.zig b/src/musl.zig index 96caf3d193..a7fbfef84d 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -301,6 +301,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre pub fn needsCrtiCrtn(target: std.Target) bool { // zig fmt: off return switch (target.cpu.arch) { + .riscv32, .riscv64, .wasm32, .wasm64 => return false, else => true, From 6ec7757914540b7f64fa5c8a2ea3cc241a1a05d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 14:40:45 +0200 Subject: [PATCH 02/23] link.Elf: Define __global_pointer$ for riscv32 too. See: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc --- src/link/Elf.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 77a6382eb5..5e5c05c1cd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3150,7 +3150,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { } } - if (self.getTarget().cpu.arch == .riscv64 and self.isEffectivelyDynLib()) { + if (self.getTarget().cpu.arch.isRISCV() and self.isEffectivelyDynLib()) { self.global_pointer_index = try linker_defined.addGlobal("__global_pointer$", self); } From c31409baa9b6ecb837a2d4a7d01b3b0b7a0bcaa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 22:32:08 +0200 Subject: [PATCH 03/23] std.Target.Abi: Handle a few more GNU ABIs in isGnu(). Importantly, this ensures that the compiler understands that these ABIs need glibc. --- lib/std/Target.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 29466ce837..a69038fae4 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -720,7 +720,16 @@ pub const Abi = enum { pub inline fn isGnu(abi: Abi) bool { return switch (abi) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + .gnu, + .gnuabin32, + .gnuabi64, + .gnueabi, + .gnueabihf, + .gnuf32, + .gnusf, + .gnux32, + .gnuilp32, + => true, else => false, }; } From e74c36896297885528423fe1eded116f1df0befd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 23:29:51 +0200 Subject: [PATCH 04/23] std.zig.target: Set the minimum glibc for riscv32 to 2.33. Relevant release notes: * https://sourceware.org/legacy-ml/libc-announce/2018/msg00000.html * https://sourceware.org/pipermail/libc-announce/2021/000030.html Note that the supported ISAs/ABIs are explicitly listed in each, and only 2.33 actually supports the 32-bit ones. --- lib/std/zig/target.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index 7022aee1e1..d86d47ba59 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -56,7 +56,7 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi }, .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf }, .{ .arch = .powerpc, .os = .linux, .abi = .musl }, - .{ .arch = .riscv32, .os = .linux, .abi = .gnuilp32, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } }, + .{ .arch = .riscv32, .os = .linux, .abi = .gnuilp32, .glibc_min = .{ .major = 2, .minor = 33, .patch = 0 } }, .{ .arch = .riscv32, .os = .linux, .abi = .musl }, .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } }, .{ .arch = .riscv64, .os = .linux, .abi = .musl }, From 8b176ab303724054f0fcef21c3d849c377b54421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 16:14:22 +0200 Subject: [PATCH 05/23] start: Implement _start() for riscv32. --- lib/std/start.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index aeefbaffc0..3ec1613128 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -272,7 +272,7 @@ fn _start() callconv(.Naked) noreturn { \\ bstrins.d $sp, $zero, 3, 0 \\ b %[posixCallMainAndExit] , - .riscv64 => + .riscv32, .riscv64 => \\ li s0, 0 \\ li ra, 0 \\ mv a0, sp From b958225e687c3b044d52a5adcb58dfb413ee141a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 14:52:31 +0200 Subject: [PATCH 06/23] c: Implement clone() for riscv32-linux. --- lib/c.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/c.zig b/lib/c.zig index 0534013d3b..50f1d74e7d 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -319,6 +319,40 @@ fn clone() callconv(.Naked) void { \\3: bx r5 ); }, + .riscv32 => { + // __clone(func, stack, flags, arg, ptid, tls, ctid) + // a0, a1, a2, a3, a4, a5, a6 + + // syscall(SYS_clone, flags, stack, ptid, tls, ctid) + // a7 a0, a1, a2, a3, a4 + asm volatile ( + \\ # Save func and arg to stack + \\ addi a1, a1, -8 + \\ sw a0, 0(a1) + \\ sw a3, 4(a1) + \\ + \\ # Call SYS_clone + \\ mv a0, a2 + \\ mv a2, a4 + \\ mv a3, a5 + \\ mv a4, a6 + \\ li a7, 220 # SYS_clone + \\ ecall + \\ + \\ beqz a0, 1f + \\ # Parent + \\ ret + \\ + \\ # Child + \\1: lw a1, 0(sp) + \\ lw a0, 4(sp) + \\ jalr a1 + \\ + \\ # Exit + \\ li a7, 93 # SYS_exit + \\ ecall + ); + }, .riscv64 => { // __clone(func, stack, flags, arg, ptid, tls, ctid) // a0, a1, a2, a3, a4, a5, a6 From b83d10214bf723c21a912e809fb39f4407dae927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 14:54:03 +0200 Subject: [PATCH 07/23] std.Thread: Implement LinuxThreadImpl.ThreadCompletion.freeAndExit() for riscv32. --- lib/std/Thread.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index e19f473e44..2c724b3b75 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -1171,6 +1171,19 @@ const LinuxThreadImpl = struct { [len] "r" (self.mapped.len), : "memory" ), + .riscv32 => asm volatile ( + \\ li a7, 215 + \\ mv a0, %[ptr] + \\ mv a1, %[len] + \\ ecall + \\ li a7, 93 + \\ mv a0, zero + \\ ecall + : + : [ptr] "r" (@intFromPtr(self.mapped.ptr)), + [len] "r" (self.mapped.len), + : "memory" + ), .riscv64 => asm volatile ( \\ li a7, 215 \\ mv a0, %[ptr] From a59ab5ffa5aacbc2fb372c45c23868bcd92a6385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 20 Jul 2024 05:34:25 +0200 Subject: [PATCH 08/23] std.Thread: Add some syscall comments to LinuxThreadImpl.ThreadCompletion.freeAndExit(). --- lib/std/Thread.zig | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 2c724b3b75..c9c867090d 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -1082,11 +1082,11 @@ const LinuxThreadImpl = struct { fn freeAndExit(self: *ThreadCompletion) noreturn { switch (target.cpu.arch) { .x86 => asm volatile ( - \\ movl $91, %%eax + \\ movl $91, %%eax # SYS_munmap \\ movl %[ptr], %%ebx \\ movl %[len], %%ecx \\ int $128 - \\ movl $1, %%eax + \\ movl $1, %%eax # SYS_exit \\ movl $0, %%ebx \\ int $128 : @@ -1095,9 +1095,9 @@ const LinuxThreadImpl = struct { : "memory" ), .x86_64 => asm volatile ( - \\ movq $11, %%rax + \\ movq $11, %%rax # SYS_munmap \\ syscall - \\ movq $60, %%rax + \\ movq $60, %%rax # SYS_exit \\ movq $1, %%rdi \\ syscall : @@ -1105,11 +1105,11 @@ const LinuxThreadImpl = struct { [len] "{rsi}" (self.mapped.len), ), .arm, .armeb, .thumb, .thumbeb => asm volatile ( - \\ mov r7, #91 + \\ mov r7, #91 // SYS_munmap \\ mov r0, %[ptr] \\ mov r1, %[len] \\ svc 0 - \\ mov r7, #1 + \\ mov r7, #1 // SYS_exit \\ mov r0, #0 \\ svc 0 : @@ -1118,11 +1118,11 @@ const LinuxThreadImpl = struct { : "memory" ), .aarch64, .aarch64_be => asm volatile ( - \\ mov x8, #215 + \\ mov x8, #215 // SYS_munmap \\ mov x0, %[ptr] \\ mov x1, %[len] \\ svc 0 - \\ mov x8, #93 + \\ mov x8, #93 // SYS_exit \\ mov x0, #0 \\ svc 0 : @@ -1132,11 +1132,11 @@ const LinuxThreadImpl = struct { ), .mips, .mipsel => asm volatile ( \\ move $sp, $25 - \\ li $2, 4091 + \\ li $2, 4091 # SYS_munmap \\ move $4, %[ptr] \\ move $5, %[len] \\ syscall - \\ li $2, 4001 + \\ li $2, 4001 # SYS_exit \\ li $4, 0 \\ syscall : @@ -1145,11 +1145,11 @@ const LinuxThreadImpl = struct { : "memory" ), .mips64, .mips64el => asm volatile ( - \\ li $2, 4091 + \\ li $2, 4091 # SYS_munmap \\ move $4, %[ptr] \\ move $5, %[len] \\ syscall - \\ li $2, 4001 + \\ li $2, 4001 # SYS_exit \\ li $4, 0 \\ syscall : @@ -1158,11 +1158,11 @@ const LinuxThreadImpl = struct { : "memory" ), .powerpc, .powerpcle, .powerpc64, .powerpc64le => asm volatile ( - \\ li 0, 91 + \\ li 0, 91 # SYS_munmap \\ mr %[ptr], 3 \\ mr %[len], 4 \\ sc - \\ li 0, 1 + \\ li 0, 1 # SYS_exit \\ li 3, 0 \\ sc \\ blr @@ -1172,11 +1172,11 @@ const LinuxThreadImpl = struct { : "memory" ), .riscv32 => asm volatile ( - \\ li a7, 215 + \\ li a7, 215 # SYS_munmap \\ mv a0, %[ptr] \\ mv a1, %[len] \\ ecall - \\ li a7, 93 + \\ li a7, 93 # SYS_exit \\ mv a0, zero \\ ecall : @@ -1185,11 +1185,11 @@ const LinuxThreadImpl = struct { : "memory" ), .riscv64 => asm volatile ( - \\ li a7, 215 + \\ li a7, 215 # SYS_munmap \\ mv a0, %[ptr] \\ mv a1, %[len] \\ ecall - \\ li a7, 93 + \\ li a7, 93 # SYS_exit \\ mv a0, zero \\ ecall : @@ -1209,14 +1209,14 @@ const LinuxThreadImpl = struct { \\ ba 1b \\ restore \\ 2: - \\ mov 73, %%g1 + \\ mov 73, %%g1 # SYS_munmap \\ mov %[ptr], %%o0 \\ mov %[len], %%o1 \\ # Flush register window contents to prevent background \\ # memory access before unmapping the stack. \\ flushw \\ t 0x6d - \\ mov 1, %%g1 + \\ mov 1, %%g1 # SYS_exit \\ mov 1, %%o0 \\ t 0x6d : From e0b9ebf322341fd17183ab9f0621e9ed985467c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 21:55:55 +0200 Subject: [PATCH 09/23] gen_stubs: Add riscv32 handling in a few more places. --- tools/gen_stubs.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index 4d45593d45..47727587ed 100644 --- a/tools/gen_stubs.zig +++ b/tools/gen_stubs.zig @@ -2,13 +2,14 @@ //! ./gen_stubs /path/to/musl/build-all >libc.S //! //! The directory 'build-all' is expected to contain these subdirectories: -//! arm x86 mips mips64 powerpc powerpc64 riscv64 x86_64 +//! arm x86 mips mips64 powerpc powerpc64 riscv32 riscv64 x86_64 //! //! ...each with 'lib/libc.so' inside of them. //! //! When building the resulting libc.S file, these defines are required: //! * `-DPTR64`: when the architecture is 64-bit //! * One of the following, corresponding to the CPU architecture: +//! - `-DARCH_riscv32` //! - `-DARCH_riscv64` //! - `-DARCH_mips` //! - `-DARCH_mips64` @@ -68,7 +69,8 @@ const MultiSym = struct { } fn is32Only(ms: MultiSym) bool { - return ms.present[archIndex(.riscv64)] == false and + return ms.present[archIndex(.riscv32)] == true and + ms.present[archIndex(.riscv64)] == false and ms.present[archIndex(.mips)] == true and ms.present[archIndex(.mips64)] == false and ms.present[archIndex(.x86)] == true and @@ -110,6 +112,7 @@ const MultiSym = struct { fn isPtrSize(ms: MultiSym) bool { const map = .{ + .{ .riscv32, 4 }, .{ .riscv64, 8 }, .{ .mips, 4 }, .{ .mips64, 8 }, @@ -132,6 +135,7 @@ const MultiSym = struct { fn isPtr2Size(ms: MultiSym) bool { const map = .{ + .{ .riscv32, 8 }, .{ .riscv64, 16 }, .{ .mips, 8 }, .{ .mips64, 16 }, @@ -154,6 +158,7 @@ const MultiSym = struct { fn isWeak64(ms: MultiSym) bool { const map = .{ + .{ .riscv32, 1 }, .{ .riscv64, 2 }, .{ .mips, 1 }, .{ .mips64, 2 }, From 0460248900023a7ac1b60e5872ba466ad8cfab5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 15:28:56 +0200 Subject: [PATCH 10/23] generate_linux_syscalls: Handle riscv_hwprobe. --- tools/generate_linux_syscalls.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index a356cbdd03..88580eb77f 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -358,6 +358,7 @@ pub fn main() !void { try writer.writeAll( \\ \\ riscv_flush_icache = arch_specific_syscall + 15, + \\ riscv_hwprobe = arch_specific_syscall + 14, \\}; \\ ); From 290609eff57a9605ff33d3c1662331c45fb80079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 15:29:33 +0200 Subject: [PATCH 11/23] generate_linux_syscalls: Add riscv32 support. --- tools/generate_linux_syscalls.zig | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 88580eb77f..7f4f9ca42f 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -296,6 +296,73 @@ pub fn main() !void { try writer.writeAll("};\n\n"); } + { + try writer.writeAll( + \\pub const RiscV32 = enum(usize) { + \\ pub const arch_specific_syscall = 244; + \\ + \\ + ); + + const child_args = [_][]const u8{ + zig_exe, + "cc", + "-target", + "riscv32-linux-gnuilp32", + "-E", + "-dD", + "-P", + "-nostdinc", + "-Iinclude", + "-Iinclude/uapi", + "arch/riscv/include/uapi/asm/unistd.h", + }; + + const child_result = try std.process.Child.run(.{ + .allocator = allocator, + .argv = &child_args, + .cwd = linux_path, + .cwd_dir = linux_dir, + }); + if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr}); + + const defines = switch (child_result.term) { + .Exited => |code| if (code == 0) child_result.stdout else { + std.debug.print("zig cc exited with code {d}\n", .{code}); + std.process.exit(1); + }, + else => { + std.debug.print("zig cc crashed\n", .{}); + std.process.exit(1); + }, + }; + + var lines = mem.tokenizeScalar(u8, defines, '\n'); + loop: while (lines.next()) |line| { + var fields = mem.tokenizeAny(u8, line, " \t"); + const cmd = fields.next() orelse return error.Incomplete; + if (!mem.eql(u8, cmd, "#define")) continue; + const define = fields.next() orelse return error.Incomplete; + const number = fields.next() orelse continue; + + if (!std.ascii.isDigit(number[0])) continue; + if (!mem.startsWith(u8, define, "__NR")) continue; + const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); + if (mem.eql(u8, name, "arch_specific_syscall")) continue; + if (mem.eql(u8, name, "syscalls")) break :loop; + + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + + try writer.writeAll( + \\ + \\ riscv_flush_icache = arch_specific_syscall + 15, + \\ riscv_hwprobe = arch_specific_syscall + 14, + \\}; + \\ + ); + } { try writer.writeAll( \\pub const RiscV64 = enum(usize) { From f494a47ca5ac89fcc159d5db6c70763946193630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 16:09:02 +0200 Subject: [PATCH 12/23] generate_linux_syscalls: Add some missing include paths for riscv. --- tools/generate_linux_syscalls.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 7f4f9ca42f..7aedaa6647 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -315,6 +315,7 @@ pub fn main() !void { "-nostdinc", "-Iinclude", "-Iinclude/uapi", + "-Iarch/riscv/include/uapi", "arch/riscv/include/uapi/asm/unistd.h", }; @@ -382,6 +383,7 @@ pub fn main() !void { "-nostdinc", "-Iinclude", "-Iinclude/uapi", + "-Iarch/riscv/include/uapi", "arch/riscv/include/uapi/asm/unistd.h", }; From 7e74276661fd76ce7d23b97e151aa9a6e367aea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 18:33:22 +0200 Subject: [PATCH 13/23] generate_linux_syscalls: Rework generation strategy for newer kernel ports. If we're going to abuse the preprocessor, we may as well go all the way and have it generate a convenient format for us. This achieves two things: 1. We no longer need hacks for the arch-specific syscalls. 2. We now generate the correct syscall names for 32-bit platforms. The latter is because we now resolve __SC_3264, etc. --- tools/generate_linux_syscalls.zig | 144 ++++++++++++++---------------- 1 file changed, 69 insertions(+), 75 deletions(-) diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index 7aedaa6647..9da0737c1e 100644 --- a/tools/generate_linux_syscalls.zig +++ b/tools/generate_linux_syscalls.zig @@ -10,7 +10,12 @@ const zig = std.zig; const fs = std.fs; const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ + // Remove underscore prefix. + .{ "_llseek", "llseek" }, + .{ "_newselect", "newselect" }, + .{ "_sysctl", "sysctl" }, // Most 64-bit archs. + .{ "newfstat", "fstat64" }, .{ "newfstatat", "fstatat64" }, // POWER. .{ "sync_file_range2", "sync_file_range" }, @@ -19,6 +24,24 @@ const stdlib_renames = std.StaticStringMap([]const u8).initComptime(.{ .{ "arm_fadvise64_64", "fadvise64_64" }, }); +// Only for newer architectures where we use the C preprocessor. +const stdlib_renames_new = std.StaticStringMap([]const u8).initComptime(.{ + .{ "newuname", "uname" }, + .{ "umount", "umount2" }, +}); + +// We use this to deal with the fact that multiple syscalls can be mapped to sys_ni_syscall. +// Thankfully it's only 2 well-known syscalls in newer kernel ports at the moment. +fn getOverridenNameNew(value: []const u8) ?[]const u8 { + if (mem.eql(u8, value, "18")) { + return "sys_lookup_dcookie"; + } else if (mem.eql(u8, value, "42")) { + return "sys_nfsservctl"; + } else { + return null; + } +} + pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); @@ -60,8 +83,9 @@ pub fn main() !void { // abi is always i386 _ = fields.next() orelse return error.Incomplete; const name = fields.next() orelse return error.Incomplete; + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = {s},\n", .{ zig.fmtId(name), number }); + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); } try writer.writeAll("};\n\n"); @@ -80,8 +104,8 @@ pub fn main() !void { // The x32 abi syscalls are always at the end. if (mem.eql(u8, abi, "x32")) break; const name = fields.next() orelse return error.Incomplete; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); } @@ -105,8 +129,8 @@ pub fn main() !void { const abi = fields.next() orelse return error.Incomplete; if (mem.eql(u8, abi, "oabi")) continue; const name = fields.next() orelse return error.Incomplete; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); } @@ -136,8 +160,9 @@ pub fn main() !void { const abi = fields.next() orelse return error.Incomplete; if (mem.eql(u8, abi, "32")) continue; const name = fields.next() orelse return error.Incomplete; + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = {s},\n", .{ zig.fmtId(name), number }); + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); } try writer.writeAll("};\n\n"); @@ -161,8 +186,9 @@ pub fn main() !void { _ = fields.next() orelse return error.Incomplete; const name = fields.next() orelse return error.Incomplete; if (mem.startsWith(u8, name, "unused")) continue; + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = Linux + {s},\n", .{ zig.fmtId(name), number }); + try writer.print(" {p} = Linux + {s},\n", .{ zig.fmtId(fixed_name), number }); } try writer.writeAll("};\n\n"); @@ -232,11 +258,6 @@ pub fn main() !void { // Newer architectures (starting with aarch64 c. 2012) now use the same C // header file for their syscall numbers. Arch-specific headers are used to // define pre-proc. vars that add additional (usually obsolete) syscalls. - // - // TODO: - // - It would be better to use libclang/translate-c directly to extract the definitions. - // - The `-dD` option only does minimal pre-processing and doesn't resolve addition, - // so arch specific syscalls are dealt with manually. { try writer.writeAll("pub const Arm64 = enum(usize) {\n"); @@ -254,6 +275,8 @@ pub fn main() !void { // Using -I=[dir] includes the zig linux headers, which we don't want. "-Iinclude", "-Iinclude/uapi", + // Output the syscall in a format we can easily recognize. + "-D __SYSCALL(nr, nm)=zigsyscall nm nr", "arch/arm64/include/uapi/asm/unistd.h", }; @@ -277,32 +300,24 @@ pub fn main() !void { }; var lines = mem.tokenizeScalar(u8, defines, '\n'); - loop: while (lines.next()) |line| { - var fields = mem.tokenizeAny(u8, line, " \t"); - const cmd = fields.next() orelse return error.Incomplete; - if (!mem.eql(u8, cmd, "#define")) continue; - const define = fields.next() orelse return error.Incomplete; - const number = fields.next() orelse continue; + while (lines.next()) |line| { + var fields = mem.tokenizeAny(u8, line, " "); + const prefix = fields.next() orelse return error.Incomplete; - if (!std.ascii.isDigit(number[0])) continue; - if (!mem.startsWith(u8, define, "__NR")) continue; - const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); - if (mem.eql(u8, name, "arch_specific_syscall")) continue; - if (mem.eql(u8, name, "syscalls")) break :loop; + if (!mem.eql(u8, prefix, "zigsyscall")) continue; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); + const sys_name = fields.next() orelse return error.Incomplete; + const value = fields.rest(); + const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..]; + const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name; + + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value }); } try writer.writeAll("};\n\n"); } { - try writer.writeAll( - \\pub const RiscV32 = enum(usize) { - \\ pub const arch_specific_syscall = 244; - \\ - \\ - ); + try writer.writeAll("pub const RiscV32 = enum(usize) {\n"); const child_args = [_][]const u8{ zig_exe, @@ -316,6 +331,7 @@ pub fn main() !void { "-Iinclude", "-Iinclude/uapi", "-Iarch/riscv/include/uapi", + "-D __SYSCALL(nr, nm)=zigsyscall nm nr", "arch/riscv/include/uapi/asm/unistd.h", }; @@ -339,38 +355,24 @@ pub fn main() !void { }; var lines = mem.tokenizeScalar(u8, defines, '\n'); - loop: while (lines.next()) |line| { - var fields = mem.tokenizeAny(u8, line, " \t"); - const cmd = fields.next() orelse return error.Incomplete; - if (!mem.eql(u8, cmd, "#define")) continue; - const define = fields.next() orelse return error.Incomplete; - const number = fields.next() orelse continue; + while (lines.next()) |line| { + var fields = mem.tokenizeAny(u8, line, " "); + const prefix = fields.next() orelse return error.Incomplete; - if (!std.ascii.isDigit(number[0])) continue; - if (!mem.startsWith(u8, define, "__NR")) continue; - const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); - if (mem.eql(u8, name, "arch_specific_syscall")) continue; - if (mem.eql(u8, name, "syscalls")) break :loop; + if (!mem.eql(u8, prefix, "zigsyscall")) continue; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); + const sys_name = fields.next() orelse return error.Incomplete; + const value = fields.rest(); + const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..]; + const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name; + + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value }); } - try writer.writeAll( - \\ - \\ riscv_flush_icache = arch_specific_syscall + 15, - \\ riscv_hwprobe = arch_specific_syscall + 14, - \\}; - \\ - ); + try writer.writeAll("};\n\n"); } { - try writer.writeAll( - \\pub const RiscV64 = enum(usize) { - \\ pub const arch_specific_syscall = 244; - \\ - \\ - ); + try writer.writeAll("pub const RiscV64 = enum(usize) {\n"); const child_args = [_][]const u8{ zig_exe, @@ -384,6 +386,7 @@ pub fn main() !void { "-Iinclude", "-Iinclude/uapi", "-Iarch/riscv/include/uapi", + "-D __SYSCALL(nr, nm)=zigsyscall nm nr", "arch/riscv/include/uapi/asm/unistd.h", }; @@ -407,30 +410,21 @@ pub fn main() !void { }; var lines = mem.tokenizeScalar(u8, defines, '\n'); - loop: while (lines.next()) |line| { - var fields = mem.tokenizeAny(u8, line, " \t"); - const cmd = fields.next() orelse return error.Incomplete; - if (!mem.eql(u8, cmd, "#define")) continue; - const define = fields.next() orelse return error.Incomplete; - const number = fields.next() orelse continue; + while (lines.next()) |line| { + var fields = mem.tokenizeAny(u8, line, " "); + const prefix = fields.next() orelse return error.Incomplete; - if (!std.ascii.isDigit(number[0])) continue; - if (!mem.startsWith(u8, define, "__NR")) continue; - const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); - if (mem.eql(u8, name, "arch_specific_syscall")) continue; - if (mem.eql(u8, name, "syscalls")) break :loop; + if (!mem.eql(u8, prefix, "zigsyscall")) continue; - const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; - try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), number }); + const sys_name = fields.next() orelse return error.Incomplete; + const value = fields.rest(); + const name = (getOverridenNameNew(value) orelse sys_name)["sys_".len..]; + const fixed_name = if (stdlib_renames_new.get(name)) |f| f else if (stdlib_renames.get(name)) |f| f else name; + + try writer.print(" {p} = {s},\n", .{ zig.fmtId(fixed_name), value }); } - try writer.writeAll( - \\ - \\ riscv_flush_icache = arch_specific_syscall + 15, - \\ riscv_hwprobe = arch_specific_syscall + 14, - \\}; - \\ - ); + try writer.writeAll("};\n\n"); } { try writer.writeAll( From 4e7c3cca916d0dbfc4baa7c21f6d5ab6b8d84b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 16:07:38 +0200 Subject: [PATCH 14/23] std.os.linux.syscalls: Regenerate based on Linux v6.7. loongarch64 syscalls not updated because it seems like that kernel port hasn't been working for a year or so: In file included from arch/loongarch/include/uapi/asm/unistd.h:5: include/uapi/asm-generic/unistd.h:2:10: fatal error: 'asm/bitsperlong.h' file not found That file is just missing from the tree. :shrug: --- lib/std/os/linux/syscalls.zig | 370 +++++++++++++++++++++++++++++++--- 1 file changed, 337 insertions(+), 33 deletions(-) diff --git a/lib/std/os/linux/syscalls.zig b/lib/std/os/linux/syscalls.zig index 55f0cebe56..1ede5ac620 100644 --- a/lib/std/os/linux/syscalls.zig +++ b/lib/std/os/linux/syscalls.zig @@ -142,16 +142,16 @@ pub const X86 = enum(usize) { afs_syscall = 137, setfsuid = 138, setfsgid = 139, - _llseek = 140, + llseek = 140, getdents = 141, - _newselect = 142, + newselect = 142, flock = 143, msync = 144, readv = 145, writev = 146, getsid = 147, fdatasync = 148, - _sysctl = 149, + sysctl = 149, mlock = 150, munlock = 151, mlockall = 152, @@ -607,7 +607,7 @@ pub const X64 = enum(usize) { vhangup = 153, modify_ldt = 154, pivot_root = 155, - _sysctl = 156, + sysctl = 156, prctl = 157, arch_prctl = 158, adjtimex = 159, @@ -927,16 +927,16 @@ pub const Arm = enum(usize) { personality = 136, setfsuid = 138, setfsgid = 139, - _llseek = 140, + llseek = 140, getdents = 141, - _newselect = 142, + newselect = 142, flock = 143, msync = 144, readv = 145, writev = 146, getsid = 147, fdatasync = 148, - _sysctl = 149, + sysctl = 149, mlock = 150, munlock = 151, mlockall = 152, @@ -1454,12 +1454,12 @@ pub const Sparc64 = enum(usize) { afs_syscall = 227, setfsuid = 228, setfsgid = 229, - _newselect = 230, + newselect = 230, splice = 232, stime = 233, statfs64 = 234, fstatfs64 = 235, - _llseek = 236, + llseek = 236, mlock = 237, munlock = 238, mlockall = 239, @@ -1474,7 +1474,7 @@ pub const Sparc64 = enum(usize) { sched_rr_get_interval = 248, nanosleep = 249, mremap = 250, - _sysctl = 251, + sysctl = 251, getsid = 252, fdatasync = 253, nfsservctl = 254, @@ -1771,9 +1771,9 @@ pub const Mips = enum(usize) { afs_syscall = Linux + 137, setfsuid = Linux + 138, setfsgid = Linux + 139, - _llseek = Linux + 140, + llseek = Linux + 140, getdents = Linux + 141, - _newselect = Linux + 142, + newselect = Linux + 142, flock = Linux + 143, msync = Linux + 144, readv = Linux + 145, @@ -1783,7 +1783,7 @@ pub const Mips = enum(usize) { sysmips = Linux + 149, getsid = Linux + 151, fdatasync = Linux + 152, - _sysctl = Linux + 153, + sysctl = Linux + 153, mlock = Linux + 154, munlock = Linux + 155, mlockall = Linux + 156, @@ -2087,7 +2087,7 @@ pub const Mips64 = enum(usize) { writev = Linux + 19, access = Linux + 20, pipe = Linux + 21, - _newselect = Linux + 22, + newselect = Linux + 22, sched_yield = Linux + 23, mremap = Linux + 24, msync = Linux + 25, @@ -2217,7 +2217,7 @@ pub const Mips64 = enum(usize) { munlockall = Linux + 149, vhangup = Linux + 150, pivot_root = Linux + 151, - _sysctl = Linux + 152, + sysctl = Linux + 152, prctl = Linux + 153, adjtimex = Linux + 154, setrlimit = Linux + 155, @@ -2568,16 +2568,16 @@ pub const PowerPC = enum(usize) { afs_syscall = 137, setfsuid = 138, setfsgid = 139, - _llseek = 140, + llseek = 140, getdents = 141, - _newselect = 142, + newselect = 142, flock = 143, msync = 144, readv = 145, writev = 146, getsid = 147, fdatasync = 148, - _sysctl = 149, + sysctl = 149, mlock = 150, munlock = 151, mlockall = 152, @@ -3008,16 +3008,16 @@ pub const PowerPC64 = enum(usize) { afs_syscall = 137, setfsuid = 138, setfsgid = 139, - _llseek = 140, + llseek = 140, getdents = 141, - _newselect = 142, + newselect = 142, flock = 143, msync = 144, readv = 145, writev = 146, getsid = 147, fdatasync = 148, - _sysctl = 149, + sysctl = 149, mlock = 150, munlock = 151, mlockall = 152, @@ -3351,7 +3351,7 @@ pub const Arm64 = enum(usize) { pwrite64 = 68, preadv = 69, pwritev = 70, - sendfile = 71, + sendfile64 = 71, pselect6 = 72, ppoll = 73, signalfd4 = 74, @@ -3359,8 +3359,8 @@ pub const Arm64 = enum(usize) { splice = 76, tee = 77, readlinkat = 78, - fstatat = 79, - fstat = 80, + fstatat64 = 79, + fstat64 = 80, sync = 81, fsync = 82, fdatasync = 83, @@ -3503,7 +3503,7 @@ pub const Arm64 = enum(usize) { clone = 220, execve = 221, mmap = 222, - fadvise64 = 223, + fadvise64_64 = 223, swapon = 224, swapoff = 225, mprotect = 226, @@ -3594,9 +3594,313 @@ pub const Arm64 = enum(usize) { futex_requeue = 456, }; -pub const RiscV64 = enum(usize) { - pub const arch_specific_syscall = 244; +pub const RiscV32 = enum(usize) { + io_setup = 0, + io_destroy = 1, + io_submit = 2, + io_cancel = 3, + setxattr = 5, + lsetxattr = 6, + fsetxattr = 7, + getxattr = 8, + lgetxattr = 9, + fgetxattr = 10, + listxattr = 11, + llistxattr = 12, + flistxattr = 13, + removexattr = 14, + lremovexattr = 15, + fremovexattr = 16, + getcwd = 17, + lookup_dcookie = 18, + eventfd2 = 19, + epoll_create1 = 20, + epoll_ctl = 21, + epoll_pwait = 22, + dup = 23, + dup3 = 24, + fcntl64 = 25, + inotify_init1 = 26, + inotify_add_watch = 27, + inotify_rm_watch = 28, + ioctl = 29, + ioprio_set = 30, + ioprio_get = 31, + flock = 32, + mknodat = 33, + mkdirat = 34, + unlinkat = 35, + symlinkat = 36, + linkat = 37, + umount2 = 39, + mount = 40, + pivot_root = 41, + nfsservctl = 42, + statfs64 = 43, + fstatfs64 = 44, + truncate64 = 45, + ftruncate64 = 46, + fallocate = 47, + faccessat = 48, + chdir = 49, + fchdir = 50, + chroot = 51, + fchmod = 52, + fchmodat = 53, + fchownat = 54, + fchown = 55, + openat = 56, + close = 57, + vhangup = 58, + pipe2 = 59, + quotactl = 60, + getdents64 = 61, + llseek = 62, + read = 63, + write = 64, + readv = 65, + writev = 66, + pread64 = 67, + pwrite64 = 68, + preadv = 69, + pwritev = 70, + sendfile64 = 71, + signalfd4 = 74, + vmsplice = 75, + splice = 76, + tee = 77, + readlinkat = 78, + sync = 81, + fsync = 82, + fdatasync = 83, + sync_file_range = 84, + timerfd_create = 85, + acct = 89, + capget = 90, + capset = 91, + personality = 92, + exit = 93, + exit_group = 94, + waitid = 95, + set_tid_address = 96, + unshare = 97, + set_robust_list = 99, + get_robust_list = 100, + getitimer = 102, + setitimer = 103, + kexec_load = 104, + init_module = 105, + delete_module = 106, + timer_create = 107, + timer_getoverrun = 109, + timer_delete = 111, + syslog = 116, + ptrace = 117, + sched_setparam = 118, + sched_setscheduler = 119, + sched_getscheduler = 120, + sched_getparam = 121, + sched_setaffinity = 122, + sched_getaffinity = 123, + sched_yield = 124, + sched_get_priority_max = 125, + sched_get_priority_min = 126, + restart_syscall = 128, + kill = 129, + tkill = 130, + tgkill = 131, + sigaltstack = 132, + rt_sigsuspend = 133, + rt_sigaction = 134, + rt_sigprocmask = 135, + rt_sigpending = 136, + rt_sigqueueinfo = 138, + rt_sigreturn = 139, + setpriority = 140, + getpriority = 141, + reboot = 142, + setregid = 143, + setgid = 144, + setreuid = 145, + setuid = 146, + setresuid = 147, + getresuid = 148, + setresgid = 149, + getresgid = 150, + setfsuid = 151, + setfsgid = 152, + times = 153, + setpgid = 154, + getpgid = 155, + getsid = 156, + setsid = 157, + getgroups = 158, + setgroups = 159, + uname = 160, + sethostname = 161, + setdomainname = 162, + getrusage = 165, + umask = 166, + prctl = 167, + getcpu = 168, + getpid = 172, + getppid = 173, + getuid = 174, + geteuid = 175, + getgid = 176, + getegid = 177, + gettid = 178, + sysinfo = 179, + mq_open = 180, + mq_unlink = 181, + mq_notify = 184, + mq_getsetattr = 185, + msgget = 186, + msgctl = 187, + msgrcv = 188, + msgsnd = 189, + semget = 190, + semctl = 191, + semop = 193, + shmget = 194, + shmctl = 195, + shmat = 196, + shmdt = 197, + socket = 198, + socketpair = 199, + bind = 200, + listen = 201, + accept = 202, + connect = 203, + getsockname = 204, + getpeername = 205, + sendto = 206, + recvfrom = 207, + setsockopt = 208, + getsockopt = 209, + shutdown = 210, + sendmsg = 211, + recvmsg = 212, + readahead = 213, + brk = 214, + munmap = 215, + mremap = 216, + add_key = 217, + request_key = 218, + keyctl = 219, + clone = 220, + execve = 221, + mmap2 = 222, + fadvise64_64 = 223, + swapon = 224, + swapoff = 225, + mprotect = 226, + msync = 227, + mlock = 228, + munlock = 229, + mlockall = 230, + munlockall = 231, + mincore = 232, + madvise = 233, + remap_file_pages = 234, + mbind = 235, + get_mempolicy = 236, + set_mempolicy = 237, + migrate_pages = 238, + move_pages = 239, + rt_tgsigqueueinfo = 240, + perf_event_open = 241, + accept4 = 242, + prlimit64 = 261, + fanotify_init = 262, + fanotify_mark = 263, + name_to_handle_at = 264, + open_by_handle_at = 265, + syncfs = 267, + setns = 268, + sendmmsg = 269, + process_vm_readv = 270, + process_vm_writev = 271, + kcmp = 272, + finit_module = 273, + sched_setattr = 274, + sched_getattr = 275, + renameat2 = 276, + seccomp = 277, + getrandom = 278, + memfd_create = 279, + bpf = 280, + execveat = 281, + userfaultfd = 282, + membarrier = 283, + mlock2 = 284, + copy_file_range = 285, + preadv2 = 286, + pwritev2 = 287, + pkey_mprotect = 288, + pkey_alloc = 289, + pkey_free = 290, + statx = 291, + rseq = 293, + kexec_file_load = 294, + clock_gettime = 403, + clock_settime = 404, + clock_adjtime = 405, + clock_getres = 406, + clock_nanosleep = 407, + timer_gettime = 408, + timer_settime = 409, + timerfd_gettime = 410, + timerfd_settime = 411, + utimensat = 412, + pselect6 = 413, + ppoll = 414, + io_pgetevents = 416, + recvmmsg = 417, + mq_timedsend = 418, + mq_timedreceive = 419, + semtimedop = 420, + rt_sigtimedwait = 421, + futex = 422, + sched_rr_get_interval = 423, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + memfd_secret = 447, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, + cachestat = 451, + fchmodat2 = 452, + map_shadow_stack = 453, + futex_wake = 454, + futex_wait = 455, + futex_requeue = 456, + riscv_flush_icache = (244 + 15), + riscv_hwprobe = (244 + 14), +}; +pub const RiscV64 = enum(usize) { io_setup = 0, io_destroy = 1, io_submit = 2, @@ -3667,7 +3971,7 @@ pub const RiscV64 = enum(usize) { pwrite64 = 68, preadv = 69, pwritev = 70, - sendfile = 71, + sendfile64 = 71, pselect6 = 72, ppoll = 73, signalfd4 = 74, @@ -3675,8 +3979,8 @@ pub const RiscV64 = enum(usize) { splice = 76, tee = 77, readlinkat = 78, - fstatat = 79, - fstat = 80, + fstatat64 = 79, + fstat64 = 80, sync = 81, fsync = 82, fdatasync = 83, @@ -3819,7 +4123,7 @@ pub const RiscV64 = enum(usize) { clone = 220, execve = 221, mmap = 222, - fadvise64 = 223, + fadvise64_64 = 223, swapon = 224, swapoff = 225, mprotect = 226, @@ -3908,8 +4212,8 @@ pub const RiscV64 = enum(usize) { futex_wake = 454, futex_wait = 455, futex_requeue = 456, - - riscv_flush_icache = arch_specific_syscall + 15, + riscv_flush_icache = (244 + 15), + riscv_hwprobe = (244 + 14), }; pub const LoongArch64 = enum(usize) { From 2d1ee678eb2a9d6713e5b7758c7620ccfe1eb1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 19:20:02 +0200 Subject: [PATCH 15/23] std.os.linux: Some adjustments after syscall generation strategy changes. --- lib/std/os/linux.zig | 62 +++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 12083bd5da..fe07c23d45 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -474,7 +474,7 @@ pub fn dup2(old: i32, new: i32) usize { } else { if (old == new) { if (std.debug.runtime_safety) { - const rc = syscall2(.fcntl, @as(usize, @bitCast(@as(isize, old))), F.GETFD); + const rc = fcntl(F.GETFD, @as(fd_t, old), 0); if (@as(isize, @bitCast(rc)) < 0) return rc; } return @as(usize, @intCast(old)); @@ -1211,7 +1211,7 @@ pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize { // NOTE: The offset parameter splitting is independent from the target // endianness. return syscall5( - ._llseek, + .llseek, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @truncate(offset >> 32)), @as(usize, @truncate(offset)), @@ -1370,7 +1370,11 @@ pub fn waitid(id_type: P, id: i32, infop: *siginfo_t, flags: u32) usize { } pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize { - return syscall3(.fcntl, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, cmd))), arg); + if (@hasField(SYS, "fcntl64")) { + return syscall3(.fcntl64, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, cmd))), arg); + } else { + return syscall3(.fcntl, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, cmd))), arg); + } } pub fn flock(fd: fd_t, operation: i32) usize { @@ -2198,8 +2202,24 @@ pub fn process_vm_writev(pid: pid_t, local: []const iovec_const, remote: []const } pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { - if (comptime builtin.cpu.arch.isMIPS()) { - // MIPS requires a 7 argument syscall + if (comptime native_arch.isARM() or native_arch.isPPC()) { + // These architectures reorder the arguments so that a register is not skipped to align the + // register number that `offset` is passed in. + + const offset_halves = splitValue64(offset); + const length_halves = splitValue64(len); + + return syscall6( + .fadvise64_64, + @as(usize, @bitCast(@as(isize, fd))), + advice, + offset_halves[0], + offset_halves[1], + length_halves[0], + length_halves[1], + ); + } else if (comptime native_arch == .mips or native_arch == .mipsel) { + // MIPS O32 does not deal with the register alignment issue, so pass a dummy value. const offset_halves = splitValue64(offset); const length_halves = splitValue64(len); @@ -2214,24 +2234,8 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { length_halves[1], advice, ); - } else if (comptime builtin.cpu.arch.isARM()) { - // ARM reorders the arguments - - const offset_halves = splitValue64(offset); - const length_halves = splitValue64(len); - - return syscall6( - .fadvise64_64, - @as(usize, @bitCast(@as(isize, fd))), - advice, - offset_halves[0], - offset_halves[1], - length_halves[0], - length_halves[1], - ); - } else if (@hasField(SYS, "fadvise64_64") and usize_bits != 64) { - // The extra usize check is needed to avoid SPARC64 because it provides both - // fadvise64 and fadvise64_64 but the latter behaves differently than other platforms. + } else if (comptime usize_bits < 64) { + // Other 32-bit architectures do not require register alignment. const offset_halves = splitValue64(offset); const length_halves = splitValue64(len); @@ -2246,8 +2250,18 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { advice, ); } else { + // On 64-bit architectures, fadvise64_64 and fadvise64 are the same. Generally, older ports + // call it fadvise64 (x86, PowerPC, etc), while newer ports call it fadvise64_64 (RISC-V, + // LoongArch, etc). SPARC is the odd one out because it has both. return syscall4( - .fadvise64, + // 64-bit SPARC (apparently?) has a broken fadvise64_64, so use its fadvise64 instead. + // TODO: I can't make sense of this. They go to the same code in the kernel, and there + // is no special-casing for SPARC in glibc and musl. I suspect a QEMU bug, which is + // really not our responsibility. + if (@hasField(SYS, "fadvise64_64") and native_arch != .sparc64) + .fadvise64_64 + else + .fadvise64, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(offset)), @as(usize, @bitCast(len)), From 7532a8a584730e9f5ca1237cb015eb1455c46588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 16:11:52 +0200 Subject: [PATCH 16/23] std.os.linux: Add riscv32 support. --- lib/std/os/linux.zig | 31 ++++-- lib/std/os/linux/riscv32.zig | 197 +++++++++++++++++++++++++++++++++++ lib/std/os/linux/riscv64.zig | 10 +- 3 files changed, 227 insertions(+), 11 deletions(-) create mode 100644 lib/std/os/linux/riscv32.zig diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index fe07c23d45..b65d530473 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -39,6 +39,7 @@ const arch_bits = switch (native_arch) { .x86_64 => @import("linux/x86_64.zig"), .aarch64, .aarch64_be => @import("linux/arm64.zig"), .arm, .armeb, .thumb, .thumbeb => @import("linux/arm-eabi.zig"), + .riscv32 => @import("linux/riscv32.zig"), .riscv64 => @import("linux/riscv64.zig"), .sparc64 => @import("linux/sparc64.zig"), .mips, .mipsel => @import("linux/mips.zig"), @@ -104,6 +105,7 @@ pub const SYS = switch (@import("builtin").cpu.arch) { .x86_64 => syscalls.X64, .aarch64, .aarch64_be => syscalls.Arm64, .arm, .armeb, .thumb, .thumbeb => syscalls.Arm, + .riscv32 => syscalls.RiscV32, .riscv64 => syscalls.RiscV64, .sparc64 => syscalls.Sparc64, .mips, .mipsel => syscalls.Mips, @@ -163,7 +165,7 @@ pub const MAP = switch (native_arch) { UNINITIALIZED: bool = false, _: u5 = 0, }, - .riscv64 => packed struct(u32) { + .riscv32, .riscv64 => packed struct(u32) { TYPE: MAP_TYPE, FIXED: bool = false, ANONYMOUS: bool = false, @@ -268,7 +270,7 @@ pub const O = switch (native_arch) { TMPFILE: bool = false, _: u9 = 0, }, - .x86, .riscv64 => packed struct(u32) { + .x86, .riscv32, .riscv64 => packed struct(u32) { ACCMODE: ACCMODE = .RDONLY, _2: u4 = 0, CREAT: bool = false, @@ -1840,7 +1842,11 @@ pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flag } pub fn fstat(fd: i32, stat_buf: *Stat) usize { - if (@hasField(SYS, "fstat64")) { + if (native_arch == .riscv32) { + // riscv32 has made the interesting decision to not implement some of + // the older stat syscalls, including this one. + @compileError("No fstat syscall on this architecture."); + } else if (@hasField(SYS, "fstat64")) { return syscall2(.fstat64, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); } else { return syscall2(.fstat, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); @@ -1848,7 +1854,11 @@ pub fn fstat(fd: i32, stat_buf: *Stat) usize { } pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize { - if (@hasField(SYS, "stat64")) { + if (native_arch == .riscv32) { + // riscv32 has made the interesting decision to not implement some of + // the older stat syscalls, including this one. + @compileError("No stat syscall on this architecture."); + } else if (@hasField(SYS, "stat64")) { return syscall2(.stat64, @intFromPtr(pathname), @intFromPtr(statbuf)); } else { return syscall2(.stat, @intFromPtr(pathname), @intFromPtr(statbuf)); @@ -1856,7 +1866,11 @@ pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize { } pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize { - if (@hasField(SYS, "lstat64")) { + if (native_arch == .riscv32) { + // riscv32 has made the interesting decision to not implement some of + // the older stat syscalls, including this one. + @compileError("No lstat syscall on this architecture."); + } else if (@hasField(SYS, "lstat64")) { return syscall2(.lstat64, @intFromPtr(pathname), @intFromPtr(statbuf)); } else { return syscall2(.lstat, @intFromPtr(pathname), @intFromPtr(statbuf)); @@ -1864,7 +1878,11 @@ pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize { } pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize { - if (@hasField(SYS, "fstatat64")) { + if (native_arch == .riscv32) { + // riscv32 has made the interesting decision to not implement some of + // the older stat syscalls, including this one. + @compileError("No fstatat syscall on this architecture."); + } else if (@hasField(SYS, "fstatat64")) { return syscall4(.fstatat64, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); } else { return syscall4(.fstatat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); @@ -7352,6 +7370,7 @@ pub const AUDIT = struct { .x86_64 => .X86_64, .aarch64 => .AARCH64, .arm, .thumb => .ARM, + .riscv32 => .RISCV32, .riscv64 => .RISCV64, .sparc64 => .SPARC64, .mips => .MIPS, diff --git a/lib/std/os/linux/riscv32.zig b/lib/std/os/linux/riscv32.zig new file mode 100644 index 0000000000..219eb5cc19 --- /dev/null +++ b/lib/std/os/linux/riscv32.zig @@ -0,0 +1,197 @@ +const std = @import("../../std.zig"); +const iovec = std.posix.iovec; +const iovec_const = std.posix.iovec_const; +const linux = std.os.linux; +const SYS = linux.SYS; +const uid_t = std.os.linux.uid_t; +const gid_t = std.os.linux.gid_t; +const pid_t = std.os.linux.pid_t; +const sockaddr = linux.sockaddr; +const socklen_t = linux.socklen_t; +const timespec = std.os.linux.timespec; + +pub fn syscall0(number: SYS) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + : "memory" + ); +} + +pub fn syscall1(number: SYS, arg1: usize) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + : "memory" + ); +} + +pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + [arg2] "{x11}" (arg2), + : "memory" + ); +} + +pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + [arg2] "{x11}" (arg2), + [arg3] "{x12}" (arg3), + : "memory" + ); +} + +pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + [arg2] "{x11}" (arg2), + [arg3] "{x12}" (arg3), + [arg4] "{x13}" (arg4), + : "memory" + ); +} + +pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + [arg2] "{x11}" (arg2), + [arg3] "{x12}" (arg3), + [arg4] "{x13}" (arg4), + [arg5] "{x14}" (arg5), + : "memory" + ); +} + +pub fn syscall6( + number: SYS, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { + return asm volatile ("ecall" + : [ret] "={x10}" (-> usize), + : [number] "{x17}" (@intFromEnum(number)), + [arg1] "{x10}" (arg1), + [arg2] "{x11}" (arg2), + [arg3] "{x12}" (arg3), + [arg4] "{x13}" (arg4), + [arg5] "{x14}" (arg5), + [arg6] "{x15}" (arg6), + : "memory" + ); +} + +const CloneFn = *const fn (arg: usize) callconv(.C) u8; + +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +pub const restore = restore_rt; + +pub fn restore_rt() callconv(.Naked) noreturn { + asm volatile ( + \\ ecall + : + : [number] "{x17}" (@intFromEnum(SYS.rt_sigreturn)), + : "memory" + ); +} + +pub const F = struct { + pub const DUPFD = 0; + pub const GETFD = 1; + pub const SETFD = 2; + pub const GETFL = 3; + pub const SETFL = 4; + pub const GETLK = 5; + pub const SETLK = 6; + pub const SETLKW = 7; + pub const SETOWN = 8; + pub const GETOWN = 9; + pub const SETSIG = 10; + pub const GETSIG = 11; + + pub const RDLCK = 0; + pub const WRLCK = 1; + pub const UNLCK = 2; + + pub const SETOWN_EX = 15; + pub const GETOWN_EX = 16; + + pub const GETOWNER_UIDS = 17; +}; + +pub const blksize_t = i32; +pub const nlink_t = u32; +pub const time_t = i64; +pub const mode_t = u32; +pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; + +pub const timeval = extern struct { + sec: time_t, + usec: i64, +}; + +pub const Flock = extern struct { + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, + __unused: [4]u8, +}; + +pub const msghdr = extern struct { + name: ?*sockaddr, + namelen: socklen_t, + iov: [*]iovec, + iovlen: i32, + __pad1: i32 = 0, + control: ?*anyopaque, + controllen: socklen_t, + __pad2: socklen_t = 0, + flags: i32, +}; + +pub const msghdr_const = extern struct { + name: ?*const sockaddr, + namelen: socklen_t, + iov: [*]const iovec_const, + iovlen: i32, + __pad1: i32 = 0, + control: ?*const anyopaque, + controllen: socklen_t, + __pad2: socklen_t = 0, + flags: i32, +}; + +/// No `Stat` structure on this platform, only `Statx`. +pub const Stat = void; + +pub const Elf_Symndx = u32; + +pub const MMAP2_UNIT = 4096; + +pub const VDSO = struct {}; + +/// TODO +pub const ucontext_t = void; + +/// TODO +pub const getcontext = {}; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 6c590348f4..fc0893d5c1 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -136,12 +136,12 @@ pub const F = struct { pub const blksize_t = i32; pub const nlink_t = u32; -pub const time_t = isize; +pub const time_t = i64; pub const mode_t = u32; -pub const off_t = isize; -pub const ino_t = usize; -pub const dev_t = usize; -pub const blkcnt_t = isize; +pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; pub const timeval = extern struct { sec: time_t, From cafce8c7670e90969b1bedae0efab3df583b11dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 25 Jun 2024 21:57:12 +0200 Subject: [PATCH 17/23] std.os.linux.test: Partially skip statx() test on riscv32. No fstatat(), so there's no point doing the rest of it. --- lib/std/os/linux/test.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 0f1349af20..6e70dbcac5 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -84,6 +84,8 @@ test "statx" { else => unreachable, } + if (builtin.cpu.arch == .riscv32) return error.SkipZigTest; // No fstatat, so the rest of the test is meaningless. + var stat_buf: linux.Stat = undefined; switch (linux.E.init(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) { .SUCCESS => {}, From 890433e292da449e79c5a8c2efb91f65555da775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Wed, 26 Jun 2024 02:48:03 +0200 Subject: [PATCH 18/23] std.os.linux: Define timespec as kernel_timespec (64-bit) for riscv32. This is kind of a hack because the timespec in UAPI headers is actually still 32-bit while __kernel_timespec is 64-bit. But, importantly, all the syscalls take __kernel_timespec from the get-go (because riscv32 support is so recent). Defining our timespec this way will allow all the syscall wrappers in std.os.linux to do the right thing for riscv32. For other 32-bit architectures, we have to use the 64-bit time syscalls explicitly to solve the Y2038 problem. --- lib/std/os/linux.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b65d530473..e89f2796fc 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -6327,12 +6327,13 @@ pub const POSIX_FADV = switch (native_arch) { }; /// The timespec struct used by the kernel. -pub const kernel_timespec = if (@sizeOf(usize) >= 8) timespec else extern struct { +pub const kernel_timespec = extern struct { sec: i64, nsec: i64, }; -pub const timespec = extern struct { +// https://github.com/ziglang/zig/issues/4726#issuecomment-2190337877 +pub const timespec = if (!builtin.link_libc and native_arch == .riscv32) kernel_timespec else extern struct { sec: isize, nsec: isize, }; From 43410cdfe91548408083327e428404b192ced524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 23 Jun 2024 19:23:17 +0200 Subject: [PATCH 19/23] std.os.linux: Remove the sparc64 workaround in fadvise(). This does not seem to be needed anymore, and it's unclear if it was ever truly needed or if it was just there to deal with a QEMU/strace bug. See: https://github.com/ziglang/zig/pull/8301#issuecomment-2184995749 --- lib/std/os/linux.zig | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index e89f2796fc..93338ff4cf 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -2272,14 +2272,7 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { // call it fadvise64 (x86, PowerPC, etc), while newer ports call it fadvise64_64 (RISC-V, // LoongArch, etc). SPARC is the odd one out because it has both. return syscall4( - // 64-bit SPARC (apparently?) has a broken fadvise64_64, so use its fadvise64 instead. - // TODO: I can't make sense of this. They go to the same code in the kernel, and there - // is no special-casing for SPARC in glibc and musl. I suspect a QEMU bug, which is - // really not our responsibility. - if (@hasField(SYS, "fadvise64_64") and native_arch != .sparc64) - .fadvise64_64 - else - .fadvise64, + if (@hasField(SYS, "fadvise64_64")) .fadvise64_64 else .fadvise64, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(offset)), @as(usize, @bitCast(len)), From aeb3abc7e649386f518638727c4d8a79dc00fc60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 15:01:44 +0200 Subject: [PATCH 20/23] std.os.linux.start_pie: Handle riscv32 in getDynamicSymbol(). --- lib/std/os/linux/start_pie.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index 19b6c332f7..5c35262bc8 100644 --- a/lib/std/os/linux/start_pie.zig +++ b/lib/std/os/linux/start_pie.zig @@ -26,7 +26,7 @@ const R_RELATIVE = switch (builtin.cpu.arch) { .hexagon => R_HEXAGON_RELATIVE, .loongarch32, .loongarch64 => R_LARCH_RELATIVE, .m68k => R_68K_RELATIVE, - .riscv64 => R_RISCV_RELATIVE, + .riscv32, .riscv64 => R_RISCV_RELATIVE, .s390x => R_390_RELATIVE, else => @compileError("Missing R_RELATIVE definition for this target"), }; @@ -111,7 +111,7 @@ fn getDynamicSymbol() [*]elf.Dyn { \\ lea (%[ret], %%pc), %[ret] : [ret] "=r" (-> [*]elf.Dyn), ), - .riscv64 => asm volatile ( + .riscv32, .riscv64 => asm volatile ( \\ .weak _DYNAMIC \\ .hidden _DYNAMIC \\ lla %[ret], _DYNAMIC From 6eb9cb6f285d31c673aa9c175b5848f5a476fb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 22 Jun 2024 15:02:09 +0200 Subject: [PATCH 21/23] std.os.linux.tls: Handle riscv32 in setThreadPointer(). --- lib/std/os/linux/tls.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 2611e9a05c..f6aef8aa91 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -170,7 +170,7 @@ pub fn setThreadPointer(addr: usize) void { const rc = @call(.always_inline, linux.syscall1, .{ .set_tls, addr }); assert(rc == 0); }, - .riscv64 => { + .riscv32, .riscv64 => { asm volatile ( \\ mv tp, %[addr] : From 4e5068c35cef5e1b3366aa05de60023b9fcd98e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 21 Jul 2024 11:03:53 +0200 Subject: [PATCH 22/23] std: Stop supporting Linux/glibc versions older than declared in std.Target. --- lib/std/debug.zig | 3 ++- lib/std/fs/File.zig | 21 +-------------------- lib/std/os/linux.zig | 19 ++++++++----------- lib/std/os/linux/test.zig | 2 -- lib/std/posix.zig | 36 ++++-------------------------------- lib/std/posix/test.zig | 6 +----- 6 files changed, 16 insertions(+), 71 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 31321d62fd..be8b030769 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -747,7 +747,8 @@ pub const StackIterator = struct { .SUCCESS => return bytes_read == buf.len, .FAULT => return false, .INVAL, .PERM, .SRCH => unreachable, // own pid is always valid - .NOMEM, .NOSYS => {}, + .NOMEM => {}, + .NOSYS => {}, // QEMU is known not to implement this syscall. else => unreachable, // unexpected } var path_buf: [ diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index de113b2aad..05a3b4028e 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -731,7 +731,7 @@ pub const Metadata = struct { /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 /// On Windows, this cannot return null - /// On Linux, this returns null if the filesystem does not support creation times, or if the kernel is older than 4.11 + /// On Linux, this returns null if the filesystem does not support creation times /// On Unices, this returns null if the filesystem or OS does not support creation times /// On MacOS, this returns the ctime if the filesystem does not support creation times; this is insanity, and yet another reason to hate on Apple pub fn created(self: Self) ?i128 { @@ -822,7 +822,6 @@ pub const MetadataUnix = struct { }; /// `MetadataUnix`, but using Linux's `statx` syscall. -/// On Linux versions below 4.11, `statx` will be filled with data from stat. pub const MetadataLinux = struct { statx: std.os.linux.Statx, @@ -1017,24 +1016,6 @@ pub fn metadata(self: File) MetadataError!Metadata { switch (posix.errno(rcx)) { .SUCCESS => {}, - // NOSYS happens when `statx` is unsupported, which is the case on kernel versions before 4.11 - // Here, we call `fstat` and fill `stx` with the data we need - .NOSYS => { - const st = try posix.fstat(self.handle); - - stx.mode = @as(u16, @intCast(st.mode)); - - // Hacky conversion from timespec to statx_timestamp - stx.atime = std.mem.zeroes(l.statx_timestamp); - stx.atime.sec = st.atim.sec; - stx.atime.nsec = @as(u32, @intCast(st.atim.nsec)); // Guaranteed to succeed (nsec is always below 10^9) - - stx.mtime = std.mem.zeroes(l.statx_timestamp); - stx.mtime.sec = st.mtim.sec; - stx.mtime.nsec = @as(u32, @intCast(st.mtim.nsec)); - - stx.mask = l.STATX_BASIC_STATS | l.STATX_MTIME; - }, .BADF => unreachable, .FAULT => unreachable, .NOMEM => return error.SystemResources, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 93338ff4cf..4f3db110b2 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1890,17 +1890,14 @@ pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usi } pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: u32, statx_buf: *Statx) usize { - if (@hasField(SYS, "statx")) { - return syscall5( - .statx, - @as(usize, @bitCast(@as(isize, dirfd))), - @intFromPtr(path), - flags, - mask, - @intFromPtr(statx_buf), - ); - } - return @as(usize, @bitCast(-@as(isize, @intFromEnum(E.NOSYS)))); + return syscall5( + .statx, + @as(usize, @bitCast(@as(isize, dirfd))), + @intFromPtr(path), + flags, + mask, + @intFromPtr(statx_buf), + ); } pub fn listxattr(path: [*:0]const u8, list: [*]u8, size: usize) usize { diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 6e70dbcac5..b3c02803f3 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -79,8 +79,6 @@ test "statx" { var statx_buf: linux.Statx = undefined; switch (linux.E.init(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { .SUCCESS => {}, - // The statx syscall was only introduced in linux 4.11 - .NOSYS => return error.SkipZigTest, else => unreachable, } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 955987b036..e04efbbcc0 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -615,7 +615,6 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { .INVAL => unreachable, .FAULT => unreachable, .INTR => continue, - .NOSYS => return getRandomBytesDevURandom(buf), else => return unexpectedErrno(err), } } @@ -4534,7 +4533,6 @@ pub const FanotifyInitError = error{ ProcessFdQuotaExceeded, SystemFdQuotaExceeded, SystemResources, - OperationNotSupported, PermissionDenied, } || UnexpectedError; @@ -4546,7 +4544,6 @@ pub fn fanotify_init(flags: std.os.linux.fanotify.InitFlags, event_f_flags: u32) .MFILE => return error.ProcessFdQuotaExceeded, .NFILE => return error.SystemFdQuotaExceeded, .NOMEM => return error.SystemResources, - .NOSYS => return error.OperationNotSupported, .PERM => return error.PermissionDenied, else => |err| return unexpectedErrno(err), } @@ -4559,7 +4556,6 @@ pub const FanotifyMarkError = error{ FileNotFound, SystemResources, UserMarkQuotaExceeded, - NotImplemented, NotDir, OperationNotSupported, PermissionDenied, @@ -4600,7 +4596,6 @@ pub fn fanotify_markZ( .NOENT => return error.FileNotFound, .NOMEM => return error.SystemResources, .NOSPC => return error.UserMarkQuotaExceeded, - .NOSYS => return error.NotImplemented, .NOTDIR => return error.NotDir, .OPNOTSUPP => return error.OperationNotSupported, .PERM => return error.PermissionDenied, @@ -6183,13 +6178,6 @@ pub fn sendfile( switch (native_os) { .linux => sf: { - // sendfile() first appeared in Linux 2.2, glibc 2.1. - const call_sf = comptime if (builtin.link_libc) - std.c.versionCheck(.{ .major = 2, .minor = 1, .patch = 0 }) - else - builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2, .patch = 0 }) != .lt; - if (!call_sf) break :sf; - if (headers.len != 0) { const amt = try writev(out_fd, headers); total_written += amt; @@ -6223,14 +6211,14 @@ pub fn sendfile( .OVERFLOW => unreachable, // We avoid passing too large of a `count`. .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket - .INVAL, .NOSYS => { + .INVAL => { // EINVAL could be any of the following situations: // * Descriptor is not valid or locked // * an mmap(2)-like operation is not available for in_fd // * count is negative // * out_fd has the APPEND flag set // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write - // manually, the same as ENOSYS. + // manually. break :sf; }, .AGAIN => return error.WouldBlock, @@ -6456,21 +6444,15 @@ pub const CopyFileRangeError = error{ /// `flags` has different meanings per operating system; refer to the respective man pages. /// /// These systems support in-kernel data copying: -/// * Linux 4.5 (cross-filesystem 5.3) +/// * Linux (cross-filesystem from version 5.3) /// * FreeBSD 13.0 /// /// Other systems fall back to calling `pread` / `pwrite`. /// /// Maximum offsets on Linux and FreeBSD are `maxInt(i64)`. pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize { - const global = struct { - var has_copy_file_range = true; - }; - if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) or - ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5, .patch = 0 }) orelse false and - std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) and - @atomicLoad(bool, &global.has_copy_file_range, .monotonic))) + (comptime builtin.os.tag == .linux and std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }))) { var off_in_copy: i64 = @bitCast(off_in); var off_out_copy: i64 = @bitCast(off_out); @@ -6504,10 +6486,6 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len .PERM => return error.PermissionDenied, .TXTBSY => return error.SwapFile, .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback - .NOSYS => { - @atomicStore(bool, &global.has_copy_file_range, false, .monotonic); - break; - }, else => |err| return unexpectedErrno(err), } } @@ -6775,10 +6753,6 @@ pub const MemFdCreateError = error{ OutOfMemory, /// Either the name provided exceeded `NAME_MAX`, or invalid flags were passed. NameTooLong, - - /// memfd_create is available in Linux 3.17 and later. This error is returned - /// for older kernel versions. - SystemOutdated, } || UnexpectedError; pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { @@ -6795,7 +6769,6 @@ pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { .NFILE => return error.SystemFdQuotaExceeded, .MFILE => return error.ProcessFdQuotaExceeded, .NOMEM => return error.OutOfMemory, - .NOSYS => return error.SystemOutdated, else => |err| return unexpectedErrno(err), } }, @@ -6915,7 +6888,6 @@ pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t { .NOMEM => return error.SystemResources, .MFILE => return error.ProcessResources, .NODEV => return error.InodeMountFail, - .NOSYS => return error.SystemOutdated, else => |err| return unexpectedErrno(err), } } diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 7db9c3f805..11b01a7266 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -580,11 +580,7 @@ test "memfd_create" { else => return error.SkipZigTest, } - const fd = posix.memfd_create("test", 0) catch |err| switch (err) { - // Related: https://github.com/ziglang/zig/issues/4019 - error.SystemOutdated => return error.SkipZigTest, - else => |e| return e, - }; + const fd = try posix.memfd_create("test", 0); defer posix.close(fd); try expect((try posix.write(fd, "test")) == 4); try posix.lseek_SET(fd, 0); From 13945548fcd4fe3aab0879b670db1fe8131d8b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 21 Jul 2024 12:45:47 +0200 Subject: [PATCH 23/23] std.fs: Rework to always use statx() instead of fstat()/fstatat() on Linux. statx() is strictly superior to stat() and friends. We can do this because the standard library declares Linux 4.19 to be the minimum version supported in std.Target. This is also necessary on riscv32 where there is only statx(). While here, I improved std.fs.File.metadata() to gather as much information as possible when calling statx() since that is the expectation from this particular API. --- lib/std/fs/Dir.zig | 29 ++++++++++++++-- lib/std/fs/File.zig | 83 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 2743f3ed7d..34967301e1 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -334,7 +334,6 @@ pub const Iterator = switch (native_os) { first_iter: bool, const Self = @This(); - const linux = std.os.linux; pub const Error = IteratorError; @@ -2690,8 +2689,33 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true }); return Stat.fromWasi(st); } + if (native_os == .linux) { + const sub_path_c = try posix.toPosixPath(sub_path); + var stx = std.mem.zeroes(linux.Statx); + + const rc = linux.statx( + self.fd, + &sub_path_c, + linux.AT.NO_AUTOMOUNT, + linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + &stx, + ); + + return switch (linux.E.init(rc)) { + .SUCCESS => Stat.fromLinux(stx), + .ACCES => error.AccessDenied, + .BADF => unreachable, + .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => error.SymLinkLoop, + .NAMETOOLONG => unreachable, // Handled by posix.toPosixPath() above. + .NOENT, .NOTDIR => error.FileNotFound, + .NOMEM => error.SystemResources, + else => |err| posix.unexpectedErrno(err), + }; + } const st = try posix.fstatat(self.fd, sub_path, 0); - return Stat.fromSystem(st); + return Stat.fromPosix(st); } pub const ChmodError = File.ChmodError; @@ -2751,6 +2775,7 @@ const path = fs.path; const fs = std.fs; const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const linux = std.os.linux; const windows = std.os.windows; const native_os = builtin.os.tag; const have_flock = @TypeOf(posix.system.flock) != void; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 05a3b4028e..36e7999bf7 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -342,7 +342,7 @@ pub fn seekTo(self: File, offset: u64) SeekError!void { return posix.lseek_SET(self.handle, offset); } -pub const GetSeekPosError = posix.SeekError || posix.FStatError; +pub const GetSeekPosError = posix.SeekError || StatError; /// TODO: integrate with async I/O pub fn getPos(self: File) GetSeekPosError!u64 { @@ -357,7 +357,7 @@ pub fn getEndPos(self: File) GetSeekPosError!u64 { return (try self.stat()).size; } -pub const ModeError = posix.FStatError; +pub const ModeError = StatError; /// TODO: integrate with async I/O pub fn mode(self: File) ModeError!Mode { @@ -392,7 +392,7 @@ pub const Stat = struct { /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01. ctime: i128, - pub fn fromSystem(st: posix.Stat) Stat { + pub fn fromPosix(st: posix.Stat) Stat { const atime = st.atime(); const mtime = st.mtime(); const ctime = st.ctime(); @@ -426,6 +426,31 @@ pub const Stat = struct { }; } + pub fn fromLinux(stx: linux.Statx) Stat { + const atime = stx.atime; + const mtime = stx.mtime; + const ctime = stx.ctime; + + return .{ + .inode = stx.ino, + .size = stx.size, + .mode = stx.mode, + .kind = switch (stx.mode & linux.S.IFMT) { + linux.S.IFDIR => .directory, + linux.S.IFCHR => .character_device, + linux.S.IFBLK => .block_device, + linux.S.IFREG => .file, + linux.S.IFIFO => .named_pipe, + linux.S.IFLNK => .sym_link, + linux.S.IFSOCK => .unix_domain_socket, + else => .unknown, + }, + .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, + .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, + .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, + }; + } + pub fn fromWasi(st: std.os.wasi.filestat_t) Stat { return .{ .inode = st.ino, @@ -502,8 +527,34 @@ pub fn stat(self: File) StatError!Stat { return Stat.fromWasi(st); } + if (builtin.os.tag == .linux) { + var stx = std.mem.zeroes(linux.Statx); + + const rc = linux.statx( + self.handle, + "", + linux.AT.EMPTY_PATH, + linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + &stx, + ); + + return switch (linux.E.init(rc)) { + .SUCCESS => Stat.fromLinux(stx), + .ACCES => unreachable, + .BADF => unreachable, + .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => unreachable, + .NAMETOOLONG => unreachable, + .NOENT => unreachable, + .NOMEM => error.SystemResources, + .NOTDIR => unreachable, + else => |err| posix.unexpectedErrno(err), + }; + } + const st = try posix.fstat(self.handle); - return Stat.fromSystem(st); + return Stat.fromPosix(st); } pub const ChmodError = posix.FChmodError; @@ -1009,16 +1060,29 @@ pub fn metadata(self: File) MetadataError!Metadata { }; }, .linux => blk: { - const l = std.os.linux; - var stx = std.mem.zeroes(l.Statx); - const rcx = l.statx(self.handle, "\x00", l.AT.EMPTY_PATH, l.STATX_TYPE | - l.STATX_MODE | l.STATX_ATIME | l.STATX_MTIME | l.STATX_BTIME, &stx); + var stx = std.mem.zeroes(linux.Statx); - switch (posix.errno(rcx)) { + // We are gathering information for Metadata, which is meant to contain all the + // native OS information about the file, so use all known flags. + const rc = linux.statx( + self.handle, + "", + linux.AT.EMPTY_PATH, + linux.STATX_BASIC_STATS | linux.STATX_BTIME, + &stx, + ); + + switch (posix.errno(rc)) { .SUCCESS => {}, + .ACCES => unreachable, .BADF => unreachable, .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => unreachable, + .NAMETOOLONG => unreachable, + .NOENT => unreachable, .NOMEM => return error.SystemResources, + .NOTDIR => unreachable, else => |err| return posix.unexpectedErrno(err), } @@ -1712,6 +1776,7 @@ const posix = std.posix; const io = std.io; const math = std.math; const assert = std.debug.assert; +const linux = std.os.linux; const windows = std.os.windows; const Os = std.builtin.Os; const maxInt = std.math.maxInt;