mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
wasm_interp: support WASI argc/argv, and writing to stdout/stderr
This commit is contained in:
parent
736630b53f
commit
284dc6fa51
4 changed files with 386 additions and 69 deletions
|
@ -145,15 +145,12 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
self.call_export_help(module, arg_type_bytes)
|
self.call_export_help(module, arg_type_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_export_from_cli<'arg, A>(
|
pub fn call_export_from_cli(
|
||||||
&mut self,
|
&mut self,
|
||||||
module: &WasmModule<'a>,
|
module: &WasmModule<'a>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_strings: A,
|
arg_strings: &'a [&'a String],
|
||||||
) -> Result<Option<Value>, String>
|
) -> Result<Option<Value>, String> {
|
||||||
where
|
|
||||||
A: IntoIterator<Item = &'arg str>,
|
|
||||||
{
|
|
||||||
let arg_type_bytes = self.prepare_to_call_export(module, fn_name)?;
|
let arg_type_bytes = self.prepare_to_call_export(module, fn_name)?;
|
||||||
|
|
||||||
for (value_str, type_byte) in arg_strings.into_iter().zip(arg_type_bytes.iter().copied()) {
|
for (value_str, type_byte) in arg_strings.into_iter().zip(arg_type_bytes.iter().copied()) {
|
||||||
|
@ -246,7 +243,6 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
// We just popped the stack frame for the entry function. Terminate the program.
|
// We just popped the stack frame for the entry function. Terminate the program.
|
||||||
Action::Break
|
Action::Break
|
||||||
} else {
|
} else {
|
||||||
dbg!(return_addr, block_depth);
|
|
||||||
self.program_counter = return_addr as usize;
|
self.program_counter = return_addr as usize;
|
||||||
self.outermost_block = block_depth;
|
self.outermost_block = block_depth;
|
||||||
Action::Continue
|
Action::Continue
|
||||||
|
@ -374,9 +370,13 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
if let Some(return_val) = optional_return_val {
|
if let Some(return_val) = optional_return_val {
|
||||||
self.value_stack.push(return_val);
|
self.value_stack.push(return_val);
|
||||||
}
|
}
|
||||||
|
if let Some(debug_string) = self.debug_string.as_mut() {
|
||||||
|
std::write!(debug_string, " {}.{}", import.module, import.name).unwrap();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let return_addr = self.program_counter as u32;
|
let return_addr = self.program_counter as u32;
|
||||||
self.program_counter = module.code.function_offsets[fn_index] as usize;
|
let internal_fn_index = fn_index - n_import_fns;
|
||||||
|
self.program_counter = module.code.function_offsets[internal_fn_index] as usize;
|
||||||
|
|
||||||
let return_block_depth = self.outermost_block;
|
let return_block_depth = self.outermost_block;
|
||||||
self.outermost_block = self.block_loop_addrs.len() as u32;
|
self.outermost_block = self.block_loop_addrs.len() as u32;
|
||||||
|
@ -407,6 +407,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut action = Action::Continue;
|
let mut action = Action::Continue;
|
||||||
|
let mut implicit_return = false;
|
||||||
|
|
||||||
match op_code {
|
match op_code {
|
||||||
UNREACHABLE => {
|
UNREACHABLE => {
|
||||||
|
@ -461,6 +462,7 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
if self.block_loop_addrs.len() == self.outermost_block as usize {
|
if self.block_loop_addrs.len() == self.outermost_block as usize {
|
||||||
// implicit RETURN at end of function
|
// implicit RETURN at end of function
|
||||||
action = self.do_return();
|
action = self.do_return();
|
||||||
|
implicit_return = true;
|
||||||
} else {
|
} else {
|
||||||
self.block_loop_addrs.pop().unwrap();
|
self.block_loop_addrs.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1457,6 +1459,18 @@ impl<'a, I: ImportDispatcher> Instance<'a, I> {
|
||||||
let base = self.call_stack.value_stack_base();
|
let base = self.call_stack.value_stack_base();
|
||||||
let slice = self.value_stack.get_slice(base as usize);
|
let slice = self.value_stack.get_slice(base as usize);
|
||||||
eprintln!("{:#07x} {:17} {:?}", file_offset, debug_string, slice);
|
eprintln!("{:#07x} {:17} {:?}", file_offset, debug_string, slice);
|
||||||
|
if op_code == RETURN || (op_code == END && implicit_return) {
|
||||||
|
let next_code_section_index = module
|
||||||
|
.code
|
||||||
|
.function_offsets
|
||||||
|
.iter()
|
||||||
|
.position(|o| *o as usize > self.program_counter)
|
||||||
|
.unwrap_or(module.code.function_offsets.len());
|
||||||
|
let fn_index = module.import.imports.len() + next_code_section_index - 1;
|
||||||
|
eprintln!("returning to function {}\n", fn_index);
|
||||||
|
} else if op_code == CALL || op_code == CALLINDIRECT {
|
||||||
|
eprintln!("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action
|
action
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub use instance::Instance;
|
||||||
|
|
||||||
use roc_wasm_module::Value;
|
use roc_wasm_module::Value;
|
||||||
use value_stack::ValueStack;
|
use value_stack::ValueStack;
|
||||||
|
use wasi::WasiDispatcher;
|
||||||
|
|
||||||
pub trait ImportDispatcher {
|
pub trait ImportDispatcher {
|
||||||
/// Dispatch a call from WebAssembly to your own code, based on module and function name.
|
/// Dispatch a call from WebAssembly to your own code, based on module and function name.
|
||||||
|
@ -21,11 +22,23 @@ pub trait ImportDispatcher {
|
||||||
) -> Option<Value>;
|
) -> Option<Value>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_IMPORTS: DefaultImportDispatcher = DefaultImportDispatcher {};
|
pub const DEFAULT_IMPORTS: DefaultImportDispatcher = DefaultImportDispatcher {
|
||||||
|
wasi: WasiDispatcher { args: &[] },
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DefaultImportDispatcher {}
|
pub struct DefaultImportDispatcher<'a> {
|
||||||
|
wasi: WasiDispatcher<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ImportDispatcher for DefaultImportDispatcher {
|
impl<'a> DefaultImportDispatcher<'a> {
|
||||||
|
pub fn new(args: &'a [&'a String]) -> Self {
|
||||||
|
DefaultImportDispatcher {
|
||||||
|
wasi: WasiDispatcher { args },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ImportDispatcher for DefaultImportDispatcher<'a> {
|
||||||
fn dispatch(
|
fn dispatch(
|
||||||
&mut self,
|
&mut self,
|
||||||
module_name: &str,
|
module_name: &str,
|
||||||
|
@ -34,7 +47,7 @@ impl ImportDispatcher for DefaultImportDispatcher {
|
||||||
memory: &mut [u8],
|
memory: &mut [u8],
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
if module_name == wasi::MODULE_NAME {
|
if module_name == wasi::MODULE_NAME {
|
||||||
wasi::dispatch(function_name, arguments, memory)
|
self.wasi.dispatch(function_name, arguments, memory)
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"DefaultImportDispatcher does not implement {}.{}",
|
"DefaultImportDispatcher does not implement {}.{}",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use clap::ArgAction;
|
use clap::ArgAction;
|
||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command};
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
@ -6,7 +6,7 @@ use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use roc_wasm_interp::{Instance, DEFAULT_IMPORTS};
|
use roc_wasm_interp::{DefaultImportDispatcher, Instance};
|
||||||
use roc_wasm_module::WasmModule;
|
use roc_wasm_module::WasmModule;
|
||||||
|
|
||||||
pub const FLAG_FUNCTION: &str = "function";
|
pub const FLAG_FUNCTION: &str = "function";
|
||||||
|
@ -61,10 +61,7 @@ fn main() -> io::Result<()> {
|
||||||
let start_fn_name = matches.get_one::<String>(FLAG_FUNCTION).unwrap();
|
let start_fn_name = matches.get_one::<String>(FLAG_FUNCTION).unwrap();
|
||||||
let is_debug_mode = matches.get_flag(FLAG_DEBUG);
|
let is_debug_mode = matches.get_flag(FLAG_DEBUG);
|
||||||
let is_hex_format = matches.get_flag(FLAG_HEX);
|
let is_hex_format = matches.get_flag(FLAG_HEX);
|
||||||
let start_arg_strings = matches
|
let start_arg_strings = matches.get_many::<String>(ARGS_FOR_APP).unwrap_or_default();
|
||||||
.get_many::<String>(ARGS_FOR_APP)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.map(|s| s.as_str());
|
|
||||||
|
|
||||||
// Load the WebAssembly binary file
|
// Load the WebAssembly binary file
|
||||||
|
|
||||||
|
@ -87,15 +84,17 @@ fn main() -> io::Result<()> {
|
||||||
|
|
||||||
// Create an execution instance
|
// Create an execution instance
|
||||||
|
|
||||||
let mut inst = Instance::for_module(&arena, &module, DEFAULT_IMPORTS, is_debug_mode)
|
let args = Vec::from_iter_in(start_arg_strings, &arena);
|
||||||
.unwrap_or_else(|e| {
|
let dispatcher = DefaultImportDispatcher::new(&args);
|
||||||
|
let mut inst =
|
||||||
|
Instance::for_module(&arena, &module, dispatcher, is_debug_mode).unwrap_or_else(|e| {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
process::exit(2);
|
process::exit(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
|
|
||||||
let result = inst.call_export_from_cli(&module, start_fn_name, start_arg_strings);
|
let result = inst.call_export_from_cli(&module, start_fn_name, &args);
|
||||||
|
|
||||||
// Print out return value, if any
|
// Print out return value, if any
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,63 @@
|
||||||
use roc_wasm_module::Value;
|
use roc_wasm_module::Value;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
pub const MODULE_NAME: &str = "wasi_snapshot_preview1";
|
pub const MODULE_NAME: &str = "wasi_snapshot_preview1";
|
||||||
|
|
||||||
pub fn dispatch(function_name: &str, arguments: &[Value], _memory: &mut [u8]) -> Option<Value> {
|
pub struct WasiDispatcher<'a> {
|
||||||
|
pub args: &'a [&'a String],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of WASI syscalls
|
||||||
|
/// References for other engines:
|
||||||
|
/// https://github.com/wasmerio/wasmer/blob/ef8d2f651ed29b4b06fdc2070eb8189922c54d82/lib/wasi/src/syscalls/mod.rs
|
||||||
|
/// https://github.com/wasm3/wasm3/blob/045040a97345e636b8be4f3086e6db59cdcc785f/source/extra/wasi_core.h
|
||||||
|
impl<'a> WasiDispatcher<'a> {
|
||||||
|
pub fn new(args: &'a [&'a String]) -> Self {
|
||||||
|
WasiDispatcher { args }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch(
|
||||||
|
&mut self,
|
||||||
|
function_name: &str,
|
||||||
|
arguments: &[Value],
|
||||||
|
memory: &mut [u8],
|
||||||
|
) -> Option<Value> {
|
||||||
|
let success_code = Some(Value::I32(Errno::Success as i32));
|
||||||
|
|
||||||
match function_name {
|
match function_name {
|
||||||
"args_get" => todo!("WASI {}({:?})", function_name, arguments),
|
"args_get" => {
|
||||||
"args_sizes_get" => todo!("WASI {}({:?})", function_name, arguments),
|
// uint8_t ** argv,
|
||||||
|
let mut ptr_ptr_argv = arguments[0].unwrap_i32() as usize;
|
||||||
|
// uint8_t * argv_buf
|
||||||
|
let mut ptr_argv_buf = arguments[1].unwrap_i32() as usize;
|
||||||
|
|
||||||
|
for arg in self.args {
|
||||||
|
write_u32(memory, ptr_ptr_argv, ptr_argv_buf as u32);
|
||||||
|
let bytes_target = &mut memory[ptr_argv_buf..][..arg.len()];
|
||||||
|
bytes_target.copy_from_slice(arg.as_bytes());
|
||||||
|
memory[ptr_argv_buf + arg.len()] = 0; // C string zero termination
|
||||||
|
ptr_argv_buf += arg.len() + 1;
|
||||||
|
ptr_ptr_argv += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
success_code
|
||||||
|
}
|
||||||
|
"args_sizes_get" => {
|
||||||
|
// (i32, i32) -> i32
|
||||||
|
|
||||||
|
// number of string arguments
|
||||||
|
let ptr_argc = arguments[0].unwrap_i32() as usize;
|
||||||
|
// size of string arguments buffer
|
||||||
|
let ptr_argv_buf_size = arguments[1].unwrap_i32() as usize;
|
||||||
|
|
||||||
|
let argc = self.args.len() as u32;
|
||||||
|
write_u32(memory, ptr_argc, argc);
|
||||||
|
|
||||||
|
let argv_buf_size: u32 = self.args.iter().map(|a| 1 + a.len() as u32).sum();
|
||||||
|
write_u32(memory, ptr_argv_buf_size, argv_buf_size);
|
||||||
|
|
||||||
|
success_code
|
||||||
|
}
|
||||||
"environ_get" => todo!("WASI {}({:?})", function_name, arguments),
|
"environ_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"environ_sizes_get" => todo!("WASI {}({:?})", function_name, arguments),
|
"environ_sizes_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"clock_res_get" => todo!("WASI {}({:?})", function_name, arguments),
|
"clock_res_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
|
@ -30,7 +82,62 @@ pub fn dispatch(function_name: &str, arguments: &[Value], _memory: &mut [u8]) ->
|
||||||
"fd_seek" => todo!("WASI {}({:?})", function_name, arguments),
|
"fd_seek" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"fd_sync" => todo!("WASI {}({:?})", function_name, arguments),
|
"fd_sync" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"fd_tell" => todo!("WASI {}({:?})", function_name, arguments),
|
"fd_tell" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"fd_write" => todo!("WASI {}({:?})", function_name, arguments),
|
"fd_write" => {
|
||||||
|
// file descriptor
|
||||||
|
let fd = arguments[0].unwrap_i32();
|
||||||
|
// Array of IO vectors
|
||||||
|
let ptr_iovs = arguments[1].unwrap_i32() as usize;
|
||||||
|
// Length of array
|
||||||
|
let iovs_len = arguments[2].unwrap_i32();
|
||||||
|
// Out param: number of bytes written
|
||||||
|
let ptr_nwritten = arguments[3].unwrap_i32() as usize;
|
||||||
|
|
||||||
|
let mut write_lock = match fd {
|
||||||
|
1 => Ok(io::stdout().lock()),
|
||||||
|
2 => Err(io::stderr().lock()),
|
||||||
|
_ => return Some(Value::I32(Errno::Inval as i32)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut n_written: i32 = 0;
|
||||||
|
let mut negative_length_count = 0;
|
||||||
|
for _ in 0..iovs_len {
|
||||||
|
// https://man7.org/linux/man-pages/man2/readv.2.html
|
||||||
|
// struct iovec {
|
||||||
|
// void *iov_base; /* Starting address */
|
||||||
|
// size_t iov_len; /* Number of bytes to transfer */
|
||||||
|
// };
|
||||||
|
let iov_base = read_u32(memory, ptr_iovs) as usize;
|
||||||
|
let iov_len = read_i32(memory, ptr_iovs + 4);
|
||||||
|
if iov_len < 0 {
|
||||||
|
// I found negative-length iov's when I implemented this in JS for the web REPL (see wasi.js)
|
||||||
|
// I'm not sure why, but this solution worked, and it's the same WASI libc - there's only one.
|
||||||
|
n_written += iov_len;
|
||||||
|
negative_length_count += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let bytes = &memory[iov_base..][..iov_len as usize];
|
||||||
|
|
||||||
|
match &mut write_lock {
|
||||||
|
Ok(stdout) => {
|
||||||
|
n_written += stdout.write(bytes).unwrap() as i32;
|
||||||
|
}
|
||||||
|
Err(stderr) => {
|
||||||
|
n_written += stderr.write(bytes).unwrap() as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_i32(memory, ptr_nwritten, n_written);
|
||||||
|
if negative_length_count > 0 {
|
||||||
|
// Let's see if we ever get this message. If not, we can remove this negative-length stuff.
|
||||||
|
eprintln!(
|
||||||
|
"WASI DEV INFO: found {} negative-length iovecs.",
|
||||||
|
negative_length_count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
success_code
|
||||||
|
}
|
||||||
"path_create_directory" => todo!("WASI {}({:?})", function_name, arguments),
|
"path_create_directory" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"path_filestat_get" => todo!("WASI {}({:?})", function_name, arguments),
|
"path_filestat_get" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
"path_filestat_set_times" => todo!("WASI {}({:?})", function_name, arguments),
|
"path_filestat_set_times" => todo!("WASI {}({:?})", function_name, arguments),
|
||||||
|
@ -52,3 +159,187 @@ pub fn dispatch(function_name: &str, arguments: &[Value], _memory: &mut [u8]) ->
|
||||||
_ => panic!("Unknown WASI function {}({:?})", function_name, arguments),
|
_ => panic!("Unknown WASI function {}({:?})", function_name, arguments),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32(memory: &mut [u8], addr: usize) -> u32 {
|
||||||
|
let mut bytes = [0; 4];
|
||||||
|
bytes.copy_from_slice(&memory[addr..][..4]);
|
||||||
|
u32::from_le_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_i32(memory: &mut [u8], addr: usize) -> i32 {
|
||||||
|
let mut bytes = [0; 4];
|
||||||
|
bytes.copy_from_slice(&memory[addr..][..4]);
|
||||||
|
i32::from_le_bytes(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u32(memory: &mut [u8], addr: usize, value: u32) {
|
||||||
|
memory[addr..][..4].copy_from_slice(&value.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_i32(memory: &mut [u8], addr: usize, value: i32) {
|
||||||
|
memory[addr..][..4].copy_from_slice(&value.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error codes returned by functions.
|
||||||
|
/// Not all of these error codes are returned by the functions provided by this
|
||||||
|
/// API; some are used in higher-level library layers, and others are provided
|
||||||
|
/// merely for alignment with POSIX.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Errno {
|
||||||
|
/// No error occurred. System call completed successfully.
|
||||||
|
Success,
|
||||||
|
/// Argument list too long.
|
||||||
|
Toobig,
|
||||||
|
/// Permission denied.
|
||||||
|
Access,
|
||||||
|
/// Address in use.
|
||||||
|
Addrinuse,
|
||||||
|
/// Address not available.
|
||||||
|
Addrnotavail,
|
||||||
|
/// Address family not supported.
|
||||||
|
Afnosupport,
|
||||||
|
/// Resource unavailable, or operation would block.
|
||||||
|
Again,
|
||||||
|
/// Connection already in progress.
|
||||||
|
Already,
|
||||||
|
/// Bad file descriptor.
|
||||||
|
Badf,
|
||||||
|
/// Bad message.
|
||||||
|
Badmsg,
|
||||||
|
/// Device or resource busy.
|
||||||
|
Busy,
|
||||||
|
/// Operation canceled.
|
||||||
|
Canceled,
|
||||||
|
/// No child processes.
|
||||||
|
Child,
|
||||||
|
/// Connection aborted.
|
||||||
|
Connaborted,
|
||||||
|
/// Connection refused.
|
||||||
|
Connrefused,
|
||||||
|
/// Connection reset.
|
||||||
|
Connreset,
|
||||||
|
/// Resource deadlock would occur.
|
||||||
|
Deadlk,
|
||||||
|
/// Destination address required.
|
||||||
|
Destaddrreq,
|
||||||
|
/// Mathematics argument out of domain of function.
|
||||||
|
Dom,
|
||||||
|
/// Reserved.
|
||||||
|
Dquot,
|
||||||
|
/// File exists.
|
||||||
|
Exist,
|
||||||
|
/// Bad address.
|
||||||
|
Fault,
|
||||||
|
/// File too large.
|
||||||
|
Fbig,
|
||||||
|
/// Host is unreachable.
|
||||||
|
Hostunreach,
|
||||||
|
/// Identifier removed.
|
||||||
|
Idrm,
|
||||||
|
/// Illegal byte sequence.
|
||||||
|
Ilseq,
|
||||||
|
/// Operation in progress.
|
||||||
|
Inprogress,
|
||||||
|
/// Interrupted function.
|
||||||
|
Intr,
|
||||||
|
/// Invalid argument.
|
||||||
|
Inval,
|
||||||
|
/// I/O error.
|
||||||
|
Io,
|
||||||
|
/// Socket is connected.
|
||||||
|
Isconn,
|
||||||
|
/// Is a directory.
|
||||||
|
Isdir,
|
||||||
|
/// Too many levels of symbolic links.
|
||||||
|
Loop,
|
||||||
|
/// File descriptor value too large.
|
||||||
|
Mfile,
|
||||||
|
/// Too many links.
|
||||||
|
Mlink,
|
||||||
|
/// Message too large.
|
||||||
|
Msgsize,
|
||||||
|
/// Reserved.
|
||||||
|
Multihop,
|
||||||
|
/// Filename too long.
|
||||||
|
Nametoolong,
|
||||||
|
/// Network is down.
|
||||||
|
Netdown,
|
||||||
|
/// Connection aborted by network.
|
||||||
|
Netreset,
|
||||||
|
/// Network unreachable.
|
||||||
|
Netunreach,
|
||||||
|
/// Too many files open in system.
|
||||||
|
Nfile,
|
||||||
|
/// No buffer space available.
|
||||||
|
Nobufs,
|
||||||
|
/// No such device.
|
||||||
|
Nodev,
|
||||||
|
/// No such file or directory.
|
||||||
|
Noent,
|
||||||
|
/// Executable file format error.
|
||||||
|
Noexec,
|
||||||
|
/// No locks available.
|
||||||
|
Nolck,
|
||||||
|
/// Reserved.
|
||||||
|
Nolink,
|
||||||
|
/// Not enough space.
|
||||||
|
Nomem,
|
||||||
|
/// No message of the desired type.
|
||||||
|
Nomsg,
|
||||||
|
/// Protocol not available.
|
||||||
|
Noprotoopt,
|
||||||
|
/// No space left on device.
|
||||||
|
Nospc,
|
||||||
|
/// Function not supported.
|
||||||
|
Nosys,
|
||||||
|
/// The socket is not connected.
|
||||||
|
Notconn,
|
||||||
|
/// Not a directory or a symbolic link to a directory.
|
||||||
|
Notdir,
|
||||||
|
/// Directory not empty.
|
||||||
|
Notempty,
|
||||||
|
/// State not recoverable.
|
||||||
|
Notrecoverable,
|
||||||
|
/// Not a socket.
|
||||||
|
Notsock,
|
||||||
|
/// Not supported, or operation not supported on socket.
|
||||||
|
Notsup,
|
||||||
|
/// Inappropriate I/O control operation.
|
||||||
|
Notty,
|
||||||
|
/// No such device or address.
|
||||||
|
Nxio,
|
||||||
|
/// Value too large to be stored in data type.
|
||||||
|
Overflow,
|
||||||
|
/// Previous owner died.
|
||||||
|
Ownerdead,
|
||||||
|
/// Operation not permitted.
|
||||||
|
Perm,
|
||||||
|
/// Broken pipe.
|
||||||
|
Pipe,
|
||||||
|
/// Protocol error.
|
||||||
|
Proto,
|
||||||
|
/// Protocol not supported.
|
||||||
|
Protonosupport,
|
||||||
|
/// Protocol wrong type for socket.
|
||||||
|
Prototype,
|
||||||
|
/// Result too large.
|
||||||
|
Range,
|
||||||
|
/// Read-only file system.
|
||||||
|
Rofs,
|
||||||
|
/// Invalid seek.
|
||||||
|
Spipe,
|
||||||
|
/// No such process.
|
||||||
|
Srch,
|
||||||
|
/// Reserved.
|
||||||
|
Stale,
|
||||||
|
/// Connection timed out.
|
||||||
|
Timedout,
|
||||||
|
/// Text file busy.
|
||||||
|
Txtbsy,
|
||||||
|
/// Cross-device link.
|
||||||
|
Xdev,
|
||||||
|
/// Extension: Capabilities insufficient.
|
||||||
|
Notcapable,
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue