diff --git a/lib/c.zig b/lib/c.zig index 0534013d3bc3..50f1d74e7d4d 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 diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 29466ce8379f..a69038fae4ac 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, }; } diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index e19f473e44e1..c9c867090d0b 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 @@ -1171,12 +1171,25 @@ const LinuxThreadImpl = struct { [len] "r" (self.mapped.len), : "memory" ), + .riscv32 => asm volatile ( + \\ li a7, 215 # SYS_munmap + \\ mv a0, %[ptr] + \\ mv a1, %[len] + \\ ecall + \\ li a7, 93 # SYS_exit + \\ mv a0, zero + \\ ecall + : + : [ptr] "r" (@intFromPtr(self.mapped.ptr)), + [len] "r" (self.mapped.len), + : "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 : @@ -1196,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 : diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 31321d62fd03..be8b0307692d 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/Dir.zig b/lib/std/fs/Dir.zig index 2743f3ed7d97..34967301e115 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 de113b2aad71..36e7999bf79d 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; @@ -731,7 +782,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 +873,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, @@ -1010,34 +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); - - switch (posix.errno(rcx)) { + var stx = std.mem.zeroes(linux.Statx); + + // 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 => {}, - // 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; - }, + .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), } @@ -1731,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; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 12083bd5da4a..4f3db110b261 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, @@ -474,7 +476,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 +1213,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 +1372,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 { @@ -1836,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)); @@ -1844,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)); @@ -1852,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)); @@ -1860,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); @@ -1868,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 { @@ -2198,40 +2217,40 @@ 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 syscall7( - .fadvise64, + return syscall6( + .fadvise64_64, @as(usize, @bitCast(@as(isize, fd))), - 0, + advice, offset_halves[0], offset_halves[1], length_halves[0], length_halves[1], - advice, ); - } else if (comptime builtin.cpu.arch.isARM()) { - // ARM reorders the arguments + } 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); - return syscall6( - .fadvise64_64, + return syscall7( + .fadvise64, @as(usize, @bitCast(@as(isize, fd))), - advice, + 0, offset_halves[0], offset_halves[1], length_halves[0], length_halves[1], + advice, ); - } 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 +2265,11 @@ 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, + if (@hasField(SYS, "fadvise64_64")) .fadvise64_64 else .fadvise64, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(offset)), @as(usize, @bitCast(len)), @@ -6295,12 +6317,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, }; @@ -7338,6 +7361,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 000000000000..219eb5cc191e --- /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 6c590348f4b9..fc0893d5c154 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, diff --git a/lib/std/os/linux/start_pie.zig b/lib/std/os/linux/start_pie.zig index 19b6c332f714..5c35262bc8d7 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 diff --git a/lib/std/os/linux/syscalls.zig b/lib/std/os/linux/syscalls.zig index 55f0cebe5694..1ede5ac62016 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) { diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 0f1349af2028..b3c02803f301 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -79,11 +79,11 @@ 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, } + 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 => {}, diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 2611e9a05c51..f6aef8aa91a2 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] : diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 955987b0360d..e04efbbcc061 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 7db9c3f8058a..11b01a72660e 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); diff --git a/lib/std/start.zig b/lib/std/start.zig index aeefbaffc056..3ec161312802 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 diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index 7022aee1e144..d86d47ba592d 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 }, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 77a6382eb5e1..5e5c05c1cdb1 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); } diff --git a/src/musl.zig b/src/musl.zig index 96caf3d1932d..a7fbfef84d8e 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, diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index 4d45593d45ac..47727587edff 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 }, diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig index a356cbdd0397..9da0737c1e3d 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,79 @@ 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 RiscV64 = enum(usize) { - \\ pub const arch_specific_syscall = 244; - \\ - \\ - ); + try writer.writeAll("pub const RiscV32 = enum(usize) {\n"); + + const child_args = [_][]const u8{ + zig_exe, + "cc", + "-target", + "riscv32-linux-gnuilp32", + "-E", + "-dD", + "-P", + "-nostdinc", + "-Iinclude", + "-Iinclude/uapi", + "-Iarch/riscv/include/uapi", + "-D __SYSCALL(nr, nm)=zigsyscall nm nr", + "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'); + while (lines.next()) |line| { + var fields = mem.tokenizeAny(u8, line, " "); + const prefix = fields.next() orelse return error.Incomplete; + + if (!mem.eql(u8, prefix, "zigsyscall")) continue; + + 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 RiscV64 = enum(usize) {\n"); const child_args = [_][]const u8{ zig_exe, @@ -315,6 +385,8 @@ pub fn main() !void { "-nostdinc", "-Iinclude", "-Iinclude/uapi", + "-Iarch/riscv/include/uapi", + "-D __SYSCALL(nr, nm)=zigsyscall nm nr", "arch/riscv/include/uapi/asm/unistd.h", }; @@ -338,29 +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, - \\}; - \\ - ); + try writer.writeAll("};\n\n"); } { try writer.writeAll(