Merge pull request #20389 from alexrp/riscv32

Some `riscv32-linux` porting work
This commit is contained in:
Andrew Kelley 2024-07-29 16:13:35 -07:00 committed by GitHub
commit 38e0f049c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 920 additions and 229 deletions

View file

@ -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

View file

@ -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,
};
}

View file

@ -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"
),
.riscv64 => asm volatile (
\\ li a7, 215
.riscv32 => asm volatile (
\\ li a7, 215 # SYS_munmap
\\ mv a0, %[ptr]
\\ mv a1, %[len]
\\ ecall
\\ li a7, 93
\\ 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 # SYS_munmap
\\ mv a0, %[ptr]
\\ mv a1, %[len]
\\ ecall
\\ 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
:

View file

@ -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: [

View file

@ -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;

View file

@ -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);
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 => {},
// 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;

View file

@ -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,8 +2217,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 +2249,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 +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,

View file

@ -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 = {};

View file

@ -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,

View file

@ -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

View file

@ -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) {

View file

@ -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 => {},

View file

@ -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]
:

View file

@ -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),
}
}

View file

@ -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);

View file

@ -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

View file

@ -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 },

View file

@ -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);
}

View file

@ -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,

View file

@ -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 },

View file

@ -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,44 +300,38 @@ 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",
"riscv64-linux-gnu",
"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",
};
@ -338,29 +355,76 @@ 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("pub const RiscV64 = enum(usize) {\n");
const child_args = [_][]const u8{
zig_exe,
"cc",
"-target",
"riscv64-linux-gnu",
"-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(