mkxp-z/binding-sandbox/wasi.h
刘皓 b8d785b7e1
Implement growing VM memory in libretro builds
The binding coroutines in libretro builds are constructed on the VM
stack, so reallocating the VM memory would corrupt the memory of any
currently existing coroutines.

I've changed it so that the coroutines are no longer constructed on the
VM stack so that they're unaffected by VM memory reallocations, and
added a "slot" mechanism for storing variables on the VM stack. (Any
Ruby `VALUE`s used by a coroutine have to be stored on the VM stack so
that the Ruby garbage collector doesn't free them while they're being
used, which is why the slot mechanism is necessary.)
2025-05-09 22:49:13 -04:00

234 lines
10 KiB
C++

/*
** wasi.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MKXPZ_SANDBOX_WASI_H
#define MKXPZ_SANDBOX_WASI_H
#include <memory>
#include <string>
#include <vector>
#include "filesystem.h"
#include "types.h"
// Internal utility macros
#define _WASM_CAT(x, y) x##y
#define WASM_CAT(x, y) _WASM_CAT(x, y)
#ifdef MKXPZ_BIG_ENDIAN
#define WASM_ORDER_u8(value) (value)
#define WASM_ORDER_s8(value) (value)
#define WASM_ORDER_u16(value) __builtin_bswap16(value)
#define WASM_ORDER_s16(value) __builtin_bswap16(value)
#define WASM_ORDER_u32(value) __builtin_bswap32(value)
#define WASM_ORDER_s32(value) __builtin_bswap32(value)
#define WASM_ORDER_u64(value) __builtin_bswap64(value)
#define WASM_ORDER_s64(value) __builtin_bswap64(value)
#define WASM_ORDER_f32(value) __builtin_bswap32(value)
#define WASM_ORDER_f64(value) __builtin_bswap64(value)
#else
#define WASM_ORDER_u8(value) (value)
#define WASM_ORDER_s8(value) (value)
#define WASM_ORDER_u16(value) (value)
#define WASM_ORDER_s16(value) (value)
#define WASM_ORDER_u32(value) (value)
#define WASM_ORDER_s32(value) (value)
#define WASM_ORDER_u64(value) (value)
#define WASM_ORDER_s64(value) (value)
#define WASM_ORDER_f32(value) (value)
#define WASM_ORDER_f64(value) (value)
#endif // MKXPZ_BIG_ENDIAN
#define WASM_ORDER_usize(value) WASM_CAT(WASM_ORDER_, usize)(value)
// Memory manipulation macros
#define WASM_GET(type, address) WASM_ORDER_##type(*(type *)WASM_MEM(address)) // Returns the value at the given memory address (address should be `usize`), cast to the given type
#define WASM_SET(type, address, value) do *(type *)WASM_MEM(address) = WASM_ORDER_##type(value); while (0) // Sets the value of the given type to the given value at the given memory address (address should be `usize`)
// WASI error numbers
#define WASI_ESUCCESS 0 // No error occurred. System call completed successfully.
#define WASI_E2BIG 1 // Argument list too long.
#define WASI_EACCES 2 // Permission denied.
#define WASI_EADDRINUSE 3 // Address in use.
#define WASI_EADDRNOTAVAIL 4 // Address not available.
#define WASI_EAFNOSUPPORT 5 // Address family not supported.
#define WASI_EAGAIN 6 // Resource unavailable, or operation would block.
#define WASI_EALREADY 7 // Connection already in progress.
#define WASI_EBADF 8 // Bad file descriptor.
#define WASI_EBADMSG 9 // Bad message.
#define WASI_EBUSY 10 // Device or resource busy.
#define WASI_ECANCELED 11 // Operation canceled.
#define WASI_ECHILD 12 // No child processes.
#define WASI_ECONNABORTED 13 // Connection aborted.
#define WASI_ECONNREFUSED 14 // Connection refused.
#define WASI_ECONNRESET 15 // Connection reset.
#define WASI_EDEADLK 16 // Resource deadlock would occur.
#define WASI_EDESTADDRREQ 17 // Destination address required.
#define WASI_EDOM 18 // Mathematics argument out of domain of function.
#define WASI_EDQUOT 19 // Reserved.
#define WASI_EEXIST 20 // File exists.
#define WASI_EFAULT 21 // Bad address.
#define WASI_EFBIG 22 // File too large.
#define WASI_EHOSTUNREACH 23 // Host is unreachable.
#define WASI_EIDRM 24 // Identifier removed.
#define WASI_EILSEQ 25 // Illegal byte sequence.
#define WASI_EINPROGRESS 26 // Operation in progress.
#define WASI_EINTR 27 // Interrupted function.
#define WASI_EINVAL 28 // Invalid argument.
#define WASI_EIO 29 // I/O error.
#define WASI_EISCONN 30 // Socket is connected.
#define WASI_EISDIR 31 // Is a directory.
#define WASI_ELOOP 32 // Too many levels of symbolic links.
#define WASI_EMFILE 33 // File descriptor value too large.
#define WASI_EMLINK 34 // Too many links.
#define WASI_EMSGSIZE 35 // Message too large.
#define WASI_EMULTIHOP 36 // Reserved.
#define WASI_ENAMETOOLONG 37 // Filename too long.
#define WASI_ENETDOWN 38 // Network is down.
#define WASI_ENETRESET 39 // Connection aborted by network.
#define WASI_ENETUNREACH 40 // Network unreachable.
#define WASI_ENFILE 41 // Too many files open in system.
#define WASI_ENOBUFS 42 // No buffer space available.
#define WASI_ENODEV 43 // No such device.
#define WASI_ENOENT 44 // No such file or directory.
#define WASI_ENOEXEC 45 // Executable file format error.
#define WASI_ENOLCK 46 // No locks available.
#define WASI_ENOLINK 47 // Reserved.
#define WASI_ENOMEM 48 // Not enough space.
#define WASI_ENOMSG 49 // No message of the desired type.
#define WASI_ENOPROTOOPT 50 // Protocol not available.
#define WASI_ENOSPC 51 // No space left on device.
#define WASI_ENOSYS 52 // Function not supported.
#define WASI_ENOTCONN 53 // The socket is not connected.
#define WASI_ENOTDIR 54 // Not a directory or a symbolic link to a directory.
#define WASI_ENOTEMPTY 55 // Directory not empty.
#define WASI_ENOTRECOVERABLE 56 // State not recoverable.
#define WASI_ENOTSOCK 57 // Not a socket.
#define WASI_ENOTSUP 58 // Not supported, or operation not supported on socket.
#define WASI_ENOSTDIO 59 // Inappropriate I/O control operation.
#define WASI_ENXIO 60 // No such device or address.
#define WASI_EOVERFLOW 61 // Value too large to be stored in data type.
#define WASI_EOWNERDEAD 62 // Previous owner died.
#define WASI_EPERM 63 // Operation not permitted.
#define WASI_EPIPE 64 // Broken pipe.
#define WASI_EPROTO 65 // Protocol error.
#define WASI_EPROTONOSUPPORT 66 // Protocol not supported.
#define WASI_EPROTOTYPE 67 // Protocol wrong type for socket.
#define WASI_ERANGE 68 // Result too large.
#define WASI_EROFS 69 // Read-only file system.
#define WASI_ESPIPE 70 // Invalid seek.
#define WASI_ESRCH 71 // No such process.
#define WASI_ESTALE 72 // Reserved.
#define WASI_ETIMEDOUT 73 // Connection timed out.
#define WASI_ETXTBSY 74 // Text file busy.
#define WASI_EXDEV 75 // Cross-device link.
#define WASI_ENOTCAPABLE 76 // Extension: Capabilities insufficient.
// WASI file types
#define WASI_IFUNK 0 // Unknown
#define WASI_IFBLK 1 // Block device
#define WASI_IFCHR 2 // Character device
#define WASI_IFDIR 3 // Directory
#define WASI_IFREG 4 // Regular file
#define WASI_IFSOCKD 5 // Datagram socket
#define WASI_IFSOCKS 6 // Stream socket
#define WASI_IFLNK 7 // Symbolic link
// WASI file flags
#define WASI_APPEND (1 << 0)
#define WASI_DSYNC (1 << 1)
#define WASI_NONBLOCK (1 << 2)
#define WASI_RSYNC (1 << 3)
#define WASI_SYNC (1 << 4)
// WASI rights flags
#define WASI_FD_DATASYNC (1 << 0)
#define WASI_FD_READ (1 << 1)
#define WASI_FD_SEEK (1 << 2)
#define WASI_FD_FDSTAT_SET_FLAGS (1 << 3)
#define WASI_FD_SYNC (1 << 4)
#define WASI_FD_TELL (1 << 5)
#define WASI_FD_WRITE (1 << 6)
#define WASI_FD_ADVISE (1 << 7)
#define WASI_FD_ALLOCATE (1 << 8)
#define WASI_PATH_CREATE_DIRECTORY (1 << 9)
#define WASI_PATH_CREATE_FILE (1 << 10)
#define WASI_PATH_LINK_SOURCE (1 << 11)
#define WASI_PATH_LINK_TARGET (1 << 12)
#define WASI_PATH_OPEN (1 << 13)
#define WASI_FD_READDIR (1 << 14)
#define WASI_PATH_READLINK (1 << 15)
#define WASI_PATH_RENAME_SOURCE (1 << 16)
#define WASI_PATH_RENAME_TARGET (1 << 17)
#define WASI_PATH_FILESTAT_GET (1 << 18)
#define WASI_PATH_FILESTAT_SET_SIZE (1 << 19)
#define WASI_PATH_FILESTAT_SET_TIMES (1 << 20)
#define WASI_FD_FILESTAT_GET (1 << 21)
#define WASI_FD_FILESTAT_SET_SIZE (1 << 22)
#define WASI_FD_FILESTAT_SET_TIMES (1 << 23)
#define WASI_PATH_SYMLINK (1 << 24)
#define WASI_PATH_REMOVE_DIRECTORY (1 << 25)
#define WASI_PATH_UNLINK_FILE (1 << 26)
#define WASI_POLL_FD_READWRITE (1 << 27)
#define WASI_SOCK_SHUTDOWN (1 << 28)
#define WASI_SOCK_ACCEPT (1 << 29)
typedef std::pair<u32, std::string> path_cache_entry_t;
struct fs_dir {
struct fs_dir *root; // Null if this is a preopened directory handle, otherwise a pointer to the handle of the preopened directory that contains this directory.
std::string path; // Path of the directory.
bool writable; // If true, writes made to this directory handle will be routed into the save directory. Otherwise, writes are disallowed.
};
enum wasi_fd_type {
STDIN, // This file descriptor is standard input. The `handle` field is null.
STDOUT, // This file descriptor is standard output. The `handle` field is null.
STDERR, // This file descriptor is standard error. The `handle` field is null.
FS, // This file descriptor is a preopened directory handled by PhysFS. The `handle` field is a `struct fs_dir *`.
FSDIR, // This file descriptor is a directory handled by PhysFS. The `handle` field is a `struct fs_dir *`.
FSFILE, // This file descriptor is a file handled by PhysFS. The `handle` field is a `struct FileSystem::File *`.
VACANT, // Indicates this is a vacant file descriptor that doesn't correspond to a file. The `handle` field is null.
};
struct wasi_file_entry {
wasi_fd_type type;
// The file/directory handle that the file descriptor corresponds to. The exact type of this handle depends on the type of file descriptor.
void *handle;
struct fs_dir *dir_handle();
struct FileSystem::File *file_handle();
};
typedef struct w2c_wasi__snapshot__preview1 {
std::shared_ptr<struct w2c_ruby> ruby;
// WASI file descriptor table. Maps WASI file descriptors (unsigned 32-bit integers) to file handles.
std::vector<wasi_file_entry> fdtable;
// List of vacant WASI file descriptors so that we can reallocate vacant WASI file descriptors in O(1) amortized time.
std::vector<u32> vacant_fds;
w2c_wasi__snapshot__preview1(std::shared_ptr<struct w2c_ruby> ruby);
~w2c_wasi__snapshot__preview1();
u32 allocate_file_descriptor(enum wasi_fd_type type, void *handle = nullptr);
void deallocate_file_descriptor(u32 fd);
} wasi_t;
#endif /* MKXPZ_SANDBOX_WASI_H */