From 1e20a62126e66d0306a6db01b3a2ffd6b946b9b6 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Tue, 27 Jul 2021 08:59:34 +0900 Subject: [PATCH] WASI,libc: enable tests. Signed-off-by: Takeshi Yoneda --- lib/std/Thread.zig | 2 +- lib/std/c.zig | 2 ++ lib/std/c/wasi.zig | 60 ++++++++++++++++++++++++++++++++++++++ lib/std/fs.zig | 55 ++++++++++++++++++++--------------- lib/std/fs/file.zig | 37 +++++++++++------------ lib/std/fs/wasi.zig | 2 +- lib/std/io/c_writer.zig | 2 +- lib/std/os.zig | 47 ++++++++++++++++-------------- lib/std/os/bits/wasi.zig | 63 +++++++++++++++++++++++++++++++++++----- lib/std/process.zig | 8 ++--- lib/std/start.zig | 9 +++++- lib/std/testing.zig | 4 ++- src/stage1/codegen.cpp | 4 ++- test/cases.zig | 2 +- test/stage2/darwin.zig | 2 +- test/tests.zig | 8 +++++ 16 files changed, 221 insertions(+), 86 deletions(-) create mode 100644 lib/std/c/wasi.zig diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 58a409c64eea..9553cd429e86 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -24,7 +24,7 @@ pub const Condition = @import("Thread/Condition.zig"); pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint"); -pub const use_pthreads = target.os.tag != .windows and std.builtin.link_libc; +pub const use_pthreads = target.os.tag != .windows and std.Target.current.os.tag != .wasi and std.builtin.link_libc; const Thread = @This(); const Impl = if (target.os.tag == .windows) diff --git a/lib/std/c.zig b/lib/std/c.zig index a3fe814a7115..8ded463e9c23 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -31,6 +31,7 @@ pub usingnamespace switch (std.Target.current.os.tag) { .fuchsia => @import("c/fuchsia.zig"), .minix => @import("c/minix.zig"), .emscripten => @import("c/emscripten.zig"), + .wasi => @import("c/wasi.zig"), else => struct {}, }; @@ -77,6 +78,7 @@ pub extern "c" fn fread(noalias ptr: [*]u8, size_of_type: usize, item_count: usi pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn abort() noreturn; + pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn _exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: fd_t) c_int; diff --git a/lib/std/c/wasi.zig b/lib/std/c/wasi.zig new file mode 100644 index 000000000000..0edc77de3780 --- /dev/null +++ b/lib/std/c/wasi.zig @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +usingnamespace @import("../os/bits.zig"); + +extern threadlocal var errno: c_int; + +pub fn _errno() *c_int { + return &errno; +} + +pub const pid_t = c_int; +pub const uid_t = u32; +pub const gid_t = u32; +pub const off_t = i64; + +pub const libc_stat = extern struct { + dev: i32, + ino: ino_t, + nlink: u64, + + mode: mode_t, + uid: uid_t, + gid: gid_t, + __pad0: isize, + rdev: i32, + size: off_t, + blksize: i32, + blocks: i64, + + atimesec: time_t, + atimensec: isize, + mtimesec: time_t, + mtimensec: isize, + ctimesec: time_t, + ctimensec: isize, + + pub fn atime(self: @This()) timespec { + return timespec{ + .tv_sec = self.atimesec, + .tv_nsec = self.atimensec, + }; + } + + pub fn mtime(self: @This()) timespec { + return timespec{ + .tv_sec = self.mtimesec, + .tv_nsec = self.mtimensec, + }; + } + + pub fn ctime(self: @This()) timespec { + return timespec{ + .tv_sec = self.ctimesec, + .tv_nsec = self.ctimensec, + }; + } +}; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 4ecdc3803ba0..eec4733e9546 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -647,6 +647,9 @@ pub const Dir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { + // We intentinally use fd_readdir even when linked with libc, + // since its implementation is exactly the same as below, + // and we avoid the code complexity here. const w = os.wasi; start_over: while (true) { if (self.index >= self.end_index) { @@ -770,7 +773,7 @@ pub const Dir = struct { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openFileW(path_w.span(), flags); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return self.openFileWasi(sub_path, flags); } const path_c = try os.toPosixPath(sub_path); @@ -846,14 +849,16 @@ pub const Dir = struct { try os.openatZ(self.fd, sub_path, os_flags, 0); errdefer os.close(fd); - if (!has_flock_open_flags and flags.lock != .None) { - // TODO: integrate async I/O - const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); - try os.flock(fd, switch (flags.lock) { - .None => unreachable, - .Shared => os.LOCK_SH | lock_nonblocking, - .Exclusive => os.LOCK_EX | lock_nonblocking, - }); + if (builtin.target.os.tag != .wasi) { + if (!has_flock_open_flags and flags.lock != .None) { + // TODO: integrate async I/O + const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); + try os.flock(fd, switch (flags.lock) { + .None => unreachable, + .Shared => os.LOCK_SH | lock_nonblocking, + .Exclusive => os.LOCK_EX | lock_nonblocking, + }); + } } if (has_flock_open_flags and flags.lock_nonblocking) { @@ -926,7 +931,7 @@ pub const Dir = struct { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.createFileW(path_w.span(), flags); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return self.createFileWasi(sub_path, flags); } const path_c = try os.toPosixPath(sub_path); @@ -996,14 +1001,16 @@ pub const Dir = struct { try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode); errdefer os.close(fd); - if (!has_flock_open_flags and flags.lock != .None) { - // TODO: integrate async I/O - const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); - try os.flock(fd, switch (flags.lock) { - .None => unreachable, - .Shared => os.LOCK_SH | lock_nonblocking, - .Exclusive => os.LOCK_EX | lock_nonblocking, - }); + if (builtin.target.os.tag != .wasi) { + if (!has_flock_open_flags and flags.lock != .None and builtin.target.os.tag != .wasi) { + // TODO: integrate async I/O + const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); + try os.flock(fd, switch (flags.lock) { + .None => unreachable, + .Shared => os.LOCK_SH | lock_nonblocking, + .Exclusive => os.LOCK_EX | lock_nonblocking, + }); + } } if (has_flock_open_flags and flags.lock_nonblocking) { @@ -1284,7 +1291,7 @@ pub const Dir = struct { if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openDirW(sub_path_w.span().ptr, args); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return self.openDirWasi(sub_path, args); } else { const sub_path_c = try os.toPosixPath(sub_path); @@ -1431,7 +1438,7 @@ pub const Dir = struct { if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.deleteFileW(sub_path_w.span()); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) { error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR else => |e| return e, @@ -1494,7 +1501,7 @@ pub const Dir = struct { if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.deleteDirW(sub_path_w.span()); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) { error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR else => |e| return e, @@ -1553,7 +1560,7 @@ pub const Dir = struct { sym_link_path: []const u8, flags: SymLinkFlags, ) !void { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return self.symLinkWasi(target_path, sym_link_path, flags); } if (builtin.os.tag == .windows) { @@ -1606,7 +1613,7 @@ pub const Dir = struct { /// The return value is a slice of `buffer`, from index `0`. /// Asserts that the path parameter has no null bytes. pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return self.readLinkWasi(sub_path, buffer); } if (builtin.os.tag == .windows) { @@ -2025,7 +2032,7 @@ pub const Dir = struct { pub fn cwd() Dir { if (builtin.os.tag == .windows) { return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead"); } else { return Dir{ .fd = os.AT_FDCWD }; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index a4300e7376ed..147ecc760861 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -326,26 +326,23 @@ pub const File = struct { .inode = st.ino, .size = @bitCast(u64, st.size), .mode = st.mode, - .kind = switch (builtin.os.tag) { - .wasi => switch (st.filetype) { - os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice, - os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice, - os.FILETYPE_DIRECTORY => Kind.Directory, - os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink, - os.FILETYPE_REGULAR_FILE => Kind.File, - os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket, - else => Kind.Unknown, - }, - else => switch (st.mode & os.S_IFMT) { - os.S_IFBLK => Kind.BlockDevice, - os.S_IFCHR => Kind.CharacterDevice, - os.S_IFDIR => Kind.Directory, - os.S_IFIFO => Kind.NamedPipe, - os.S_IFLNK => Kind.SymLink, - os.S_IFREG => Kind.File, - os.S_IFSOCK => Kind.UnixDomainSocket, - else => Kind.Unknown, - }, + .kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { + os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice, + os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice, + os.FILETYPE_DIRECTORY => Kind.Directory, + os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink, + os.FILETYPE_REGULAR_FILE => Kind.File, + os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket, + else => Kind.Unknown, + } else switch (st.mode & os.S_IFMT) { + os.S_IFBLK => Kind.BlockDevice, + os.S_IFCHR => Kind.CharacterDevice, + os.S_IFDIR => Kind.Directory, + os.S_IFIFO => Kind.NamedPipe, + os.S_IFLNK => Kind.SymLink, + os.S_IFREG => Kind.File, + os.S_IFSOCK => Kind.UnixDomainSocket, + else => Kind.Unknown, }, .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index c41c9a7614ba..b56854bb9ebc 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -169,7 +169,7 @@ pub const PreopenList = struct { }; test "extracting WASI preopens" { - if (std.builtin.os.tag != .wasi) return error.SkipZigTest; + if (std.builtin.os.tag != .wasi or @import("builtin").link_libc) return error.SkipZigTest; var preopens = PreopenList.init(std.testing.allocator); defer preopens.deinit(); diff --git a/lib/std/io/c_writer.zig b/lib/std/io/c_writer.zig index 26ae1fde010a..9f551b08fcda 100644 --- a/lib/std/io/c_writer.zig +++ b/lib/std/io/c_writer.zig @@ -35,7 +35,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u } test { - if (!builtin.link_libc) return error.SkipZigTest; + if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest; const filename = "tmp_io_test_file.txt"; const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; diff --git a/lib/std/os.zig b/lib/std/os.zig index 4fd0998b0d21..7ba247419a38 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -404,7 +404,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { const first = iov[0]; return read(fd, first.iov_base[0..first.iov_len]); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var nread: usize = undefined; switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) { wasi.ESUCCESS => return nread, @@ -461,7 +461,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { if (builtin.os.tag == .windows) { return windows.ReadFile(fd, buf, offset, std.io.default_mode); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const iovs = [1]iovec{iovec{ .iov_base = buf.ptr, .iov_len = buf.len, @@ -556,7 +556,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { else => return windows.unexpectedStatus(rc), } } - if (std.Target.current.os.tag == .wasi) { + if (std.Target.current.os.tag == .wasi and !builtin.link_libc) { switch (wasi.fd_filestat_set_size(fd, length)) { wasi.ESUCCESS => return, wasi.EINTR => unreachable, @@ -617,7 +617,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { const first = iov[0]; return pread(fd, first.iov_base[0..first.iov_len], offset); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var nread: usize = undefined; switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) { wasi.ESUCCESS => return nread, @@ -795,7 +795,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { const first = iov[0]; return write(fd, first.iov_base[0..first.iov_len]); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var nwritten: usize = undefined; switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) { wasi.ESUCCESS => return nwritten, @@ -867,7 +867,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { if (std.Target.current.os.tag == .windows) { return windows.WriteFile(fd, bytes, offset, std.io.default_mode); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { const ciovs = [1]iovec_const{iovec_const{ .iov_base = bytes.ptr, .iov_len = bytes.len, @@ -968,7 +968,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz const first = iov[0]; return pwrite(fd, first.iov_base[0..first.iov_len], offset); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var nwritten: usize = undefined; switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) { wasi.ESUCCESS => return nwritten, @@ -1627,7 +1627,7 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return symlinkatWasi(target_path, newdirfd, sym_link_path); } if (builtin.os.tag == .windows) { @@ -1856,7 +1856,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo if (builtin.os.tag == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); return unlinkatW(dirfd, file_path_w.span(), flags); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return unlinkatWasi(dirfd, file_path, flags); } else { const file_path_c = try toPosixPath(file_path); @@ -2023,7 +2023,7 @@ pub fn renameat( const old_path_w = try windows.sliceToPrefixedFileW(old_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path); return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path); } else { const old_path_c = try toPosixPath(old_path); @@ -2159,7 +2159,7 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v if (builtin.os.tag == .windows) { const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return mkdiratWasi(dir_fd, sub_dir_path, mode); } else { const sub_dir_path_c = try toPosixPath(sub_dir_path); @@ -2519,7 +2519,7 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 /// The return value is a slice of `out_buffer` from index 0. /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return readlinkatWasi(dirfd, file_path, out_buffer); } if (builtin.os.tag == .windows) { @@ -3481,7 +3481,7 @@ pub const FStatError = error{ /// Return information about a file descriptor. pub fn fstat(fd: fd_t) FStatError!Stat { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var stat: wasi.filestat_t = undefined; switch (wasi.fd_filestat_get(fd, &stat)) { wasi.ESUCCESS => return Stat.fromFilestat(stat), @@ -3519,7 +3519,7 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink /// which is relative to `dirfd` handle. /// See also `fstatatZ` and `fstatatWasi`. pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return fstatatWasi(dirfd, pathname, flags); } else if (builtin.os.tag == .windows) { @compileError("fstatat is not yet implemented on Windows"); @@ -4123,7 +4123,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_BEGIN(fd, offset); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var new_offset: wasi.filesize_t = undefined; switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) { wasi.ESUCCESS => return, @@ -4171,7 +4171,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT(fd, offset); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var new_offset: wasi.filesize_t = undefined; switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) { wasi.ESUCCESS => return, @@ -4218,7 +4218,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_END(fd, offset); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var new_offset: wasi.filesize_t = undefined; switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) { wasi.ESUCCESS => return, @@ -4265,7 +4265,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { if (builtin.os.tag == .windows) { return windows.SetFilePointerEx_CURRENT_get(fd); } - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { var new_offset: wasi.filesize_t = undefined; switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) { wasi.ESUCCESS => return new_offset, @@ -4665,7 +4665,7 @@ pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; /// TODO: change this to return the timespec as a return value /// TODO: look into making clk_id an enum pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { - if (std.Target.current.os.tag == .wasi) { + if (std.Target.current.os.tag == .wasi and !builtin.link_libc) { var ts: timestamp_t = undefined; switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { 0 => { @@ -4706,7 +4706,7 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { } pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { - if (std.Target.current.os.tag == .wasi) { + if (std.Target.current.os.tag == .wasi and !builtin.link_libc) { var ts: timestamp_t = undefined; switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { 0 => res.* = .{ @@ -4834,7 +4834,7 @@ pub const FutimensError = error{ } || UnexpectedError; pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { // TODO WASI encodes `wasi.fstflags` to signify magic values // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore // this here, but we should really handle it somehow. @@ -5581,7 +5581,10 @@ var has_copy_file_range_syscall = std.atomic.Atomic(bool).init(true); /// /// Maximum offsets on Linux are `math.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 call_cfr = comptime if (builtin.link_libc) + const call_cfr = comptime if (std.Target.current.os.tag == .wasi) + // WASI-libc doesn't have copy_file_range. + false + else if (builtin.link_libc) std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok else std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse true; diff --git a/lib/std/os/bits/wasi.zig b/lib/std/os/bits/wasi.zig index 8b2f5c3351a5..0f89ec98897f 100644 --- a/lib/std/os/bits/wasi.zig +++ b/lib/std/os/bits/wasi.zig @@ -4,11 +4,13 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. // Convenience types and consts used by std.os module +const builtin = @import("builtin"); + pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; -pub const mode_t = u0; +pub const mode_t = u32; pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc @@ -71,7 +73,8 @@ pub const kernel_stat = struct { } }; -pub const AT_REMOVEDIR: u32 = 1; // there's no AT_REMOVEDIR in WASI, but we simulate here to match other OSes +pub const AT_REMOVEDIR: u32 = 0x4; +pub const AT_FDCWD: fd_t = -2; // As defined in the wasi_snapshot_preview1 spec file: // https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx @@ -111,6 +114,7 @@ pub const EADDRINUSE: errno_t = 3; pub const EADDRNOTAVAIL: errno_t = 4; pub const EAFNOSUPPORT: errno_t = 5; pub const EAGAIN: errno_t = 6; +pub const EWOULDBLOCK = EAGAIN; pub const EALREADY: errno_t = 7; pub const EBADF: errno_t = 8; pub const EBADMSG: errno_t = 9; @@ -163,6 +167,7 @@ pub const ENOTEMPTY: errno_t = 55; pub const ENOTRECOVERABLE: errno_t = 56; pub const ENOTSOCK: errno_t = 57; pub const ENOTSUP: errno_t = 58; +pub const EOPNOTSUPP = ENOTSUP; pub const ENOTTY: errno_t = 59; pub const ENXIO: errno_t = 60; pub const EOVERFLOW: errno_t = 61; @@ -204,7 +209,7 @@ pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; pub const exitcode_t = u32; -pub const fd_t = u32; +pub const fd_t = if (builtin.link_libc) c_int else u32; pub const fdflags_t = u16; pub const FDFLAG_APPEND: fdflags_t = 0x0001; @@ -271,11 +276,33 @@ pub const linkcount_t = u64; pub const lookupflags_t = u32; pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; -pub const oflags_t = u16; -pub const O_CREAT: oflags_t = 0x0001; -pub const O_DIRECTORY: oflags_t = 0x0002; -pub const O_EXCL: oflags_t = 0x0004; -pub const O_TRUNC: oflags_t = 0x0008; +pub usingnamespace if (builtin.link_libc) struct { + pub const O_ACCMODE = (O_EXEC | O_RDWR | O_SEARCH); + pub const O_APPEND = 1 << 0; // = __WASI_FDFLAGS_APPEND + pub const O_CLOEXEC = (0); + pub const O_CREAT = ((1 << 0) << 12); // = __WASI_OFLAGS_CREAT << 12 + pub const O_DIRECTORY = ((1 << 1) << 12); // = __WASI_OFLAGS_DIRECTORY << 12 + pub const O_DSYNC = (1 << 1); // = __WASI_FDFLAGS_DSYNC + pub const O_EXCL = ((1 << 2) << 12); // = __WASI_OFLAGS_EXCL << 12 + pub const O_EXEC = (0x02000000); + pub const O_NOCTTY = (0); + pub const O_NOFOLLOW = (0x01000000); + pub const O_NONBLOCK = (1 << 2); // = __WASI_FDFLAGS_NONBLOCK + pub const O_RDONLY = (0x04000000); + pub const O_RDWR = (O_RDONLY | O_WRONLY); + pub const O_RSYNC = (1 << 3); // = __WASI_FDFLAGS_RSYNC + pub const O_SEARCH = (0x08000000); + pub const O_SYNC = (1 << 4); // = __WASI_FDFLAGS_SYNC + pub const O_TRUNC = ((1 << 3) << 12); // = __WASI_OFLAGS_TRUNC << 12 + pub const O_TTY_INIT = (0); + pub const O_WRONLY = (0x10000000); +} else struct { + pub const oflags_t = u16; + pub const O_CREAT: oflags_t = 0x0001; + pub const O_DIRECTORY: oflags_t = 0x0002; + pub const O_EXCL: oflags_t = 0x0004; + pub const O_TRUNC: oflags_t = 0x0008; +}; pub const preopentype_t = u8; pub const PREOPENTYPE_DIR: preopentype_t = 0; @@ -437,3 +464,23 @@ pub const whence_t = u8; pub const WHENCE_SET: whence_t = 0; pub const WHENCE_CUR: whence_t = 1; pub const WHENCE_END: whence_t = 2; + +pub const S_IEXEC = S_IXUSR; +pub const S_IFBLK = 0x6000; +pub const S_IFCHR = 0x2000; +pub const S_IFDIR = 0x4000; +pub const S_IFIFO = 0xc000; +pub const S_IFLNK = 0xa000; +pub const S_IFMT = S_IFBLK | S_IFCHR | S_IFDIR | S_IFIFO | S_IFLNK | S_IFREG | S_IFSOCK; +pub const S_IFREG = 0x8000; +// There's no concept of UNIX domain socket but we define this value here in order to line with other OSes. +pub const S_IFSOCK = 0x1; + +pub const SEEK_SET = 0x0; // = __WASI_WHENCE_SET +pub const SEEK_CUR = 0x1; // = __WASI_WHENCE_CUR +pub const SEEK_END = 0x2; // = __WASI_WHENCE_END + +pub const LOCK_SH = 0x1; +pub const LOCK_EX = 0x2; +pub const LOCK_NB = 0x4; +pub const LOCK_UN = 0x8; diff --git a/lib/std/process.zig b/lib/std/process.zig index 4e875e7ebd5d..34b12fd8f830 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -88,7 +88,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { try result.putMove(key, value); } return result; - } else if (builtin.os.tag == .wasi) { + } else if (builtin.os.tag == .wasi and !builtin.link_libc) { var environ_count: usize = undefined; var environ_buf_size: usize = undefined; @@ -450,7 +450,7 @@ pub const ArgIteratorWindows = struct { pub const ArgIterator = struct { const InnerType = switch (builtin.os.tag) { .windows => ArgIteratorWindows, - .wasi => ArgIteratorWasi, + .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, else => ArgIteratorPosix, }; @@ -469,7 +469,7 @@ pub const ArgIterator = struct { /// You must deinitialize iterator's internal buffers by calling `deinit` when done. pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator { - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { return ArgIterator{ .inner = try InnerType.init(allocator) }; } @@ -507,7 +507,7 @@ pub const ArgIterator = struct { /// was created with `initWithAllocator` function. pub fn deinit(self: *ArgIterator) void { // Unless we're targeting WASI, this is a no-op. - if (builtin.os.tag == .wasi) { + if (builtin.os.tag == .wasi and !builtin.link_libc) { self.inner.deinit(); } } diff --git a/lib/std/start.zig b/lib/std/start.zig index 1a290a90db06..b8e26869f010 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -46,7 +46,9 @@ comptime { } } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { if (builtin.link_libc and @hasDecl(root, "main")) { - if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { + if (native_arch.isWasm()) { + @export(mainWithoutEnv, .{ .name = "main" }); + } else if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { @export(main, .{ .name = "main" }); } } else if (native_os == .windows) { @@ -420,6 +422,11 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp }); } +fn mainWithoutEnv(c_argc: i32, c_argv: [*][*:0]u8) callconv(.C) usize { + std.os.argv = c_argv[0..@intCast(usize, c_argc)]; + return @call(.{ .modifier = .always_inline }, callMain, .{}); +} + // General error message for a malformed return type const bad_main_ret = "expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'"; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index a370c21c291b..fbf089a97ef2 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -4,6 +4,8 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std.zig"); +const builtin = @import("builtin"); + const math = std.math; const print = std.debug.print; @@ -326,7 +328,7 @@ pub const TmpDir = struct { }; fn getCwdOrWasiPreopen() std.fs.Dir { - if (std.builtin.os.tag == .wasi) { + if (std.builtin.os.tag == .wasi and !builtin.link_libc) { var preopens = std.fs.wasi.PreopenList.init(allocator); defer preopens.deinit(); preopens.populate() catch diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 562327d50032..2ea230404f74 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -582,7 +582,9 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { bool want_ssp_attrs = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease && - g->link_libc; + g->link_libc && + // WASI-libc does not support stack-protector yet. + !target_is_wasm(g->zig_target); if (want_ssp_attrs) { addLLVMFnAttr(llvm_fn, "sspstrong"); addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4"); diff --git a/test/cases.zig b/test/cases.zig index f235992f718e..39d6128e8c4b 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.exe("hello world with updates", linux_x64); case.addError("", &[_][]const u8{ - ":93:9: error: struct 'tmp.tmp' has no member named 'main'", + ":95:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type diff --git a/test/stage2/darwin.zig b/test/stage2/darwin.zig index d40fcbc5acaf..3f05d6c19817 100644 --- a/test/stage2/darwin.zig +++ b/test/stage2/darwin.zig @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", target); case.addError("", &[_][]const u8{ - ":93:9: error: struct 'tmp.tmp' has no member named 'main'", + ":95:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type diff --git a/test/tests.zig b/test/tests.zig index 0b736792b942..d83701ff6a04 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -57,6 +57,14 @@ const test_targets = blk: { .link_libc = false, .single_threaded = true, }, + TestTarget{ + .target = .{ + .cpu_arch = .wasm32, + .os_tag = .wasi, + }, + .link_libc = true, + .single_threaded = true, + }, TestTarget{ .target = .{