diff --git a/src/Enclave.edl b/src/Enclave.edl index ef7617fb..50b9f63b 100644 --- a/src/Enclave.edl +++ b/src/Enclave.edl @@ -13,7 +13,11 @@ enclave { /* * Initialize the LibOS according to the specified attributes. * - * @retval On success, return 0; otherwise, return -1. + * @retval On success, return 0; otherwise, return -errno. + * + * The possible values of errno are + * EEXIST - The LibOS has already been initialized. + * EINVAL - The value of an argument are invalid. */ public int occlum_ecall_init([in, string] const char* log_level, [in, string] const char* instance_dir); @@ -23,7 +27,13 @@ enclave { * * @retval On success, return the thread ID of the * newly-created process (pid == tid for a new process). On error, - * return -1. + * return -errno. + * + * The possible values of errno are + * EAGAIN - The LibOS is not initialized. + * EINVAL - The value of an argument are invalid. + * ENOMEM - Not enough memory to create the new process. + * EACCES - The path of the executable is not accessible. */ public int occlum_ecall_new_process( [in, string] const char* executable_path, @@ -33,10 +43,22 @@ enclave { /* * Execute the LibOS thread specified by the TID. * - * This API is synchronous: it returns until the LibOS thread exits. + * This API is synchronous: it returns until the LibOS thread + * terminates. * - * @retval On success, return the exit status of the thread. On error, - * return -1. + * @retval On success, return the exit status of the thread, whose + * value is always greater than or equal to zero. To unify the two + * cases of terminating normally and being killed by a signal, the + * exit status is encoded in the way described in wait(2) man page. + * This means if the thread exits normally (e.g., returning from the + * main page or call exit(2) explictly), then the exit status is + * can be gained by WEXITSTATUS(status). If the thread is killed by a + * signal, then the signal number is WTERMSIG(status). + * + * The possible values of errno are + * EAGAIN - The LibOS is not initialized. + * EINVAL - The value of an argument are invalid. + * ESRCH - Cannot find the LibOS thread. */ public int occlum_ecall_exec_thread(int libos_tid, int host_tid); }; diff --git a/src/libos/src/entry.rs b/src/libos/src/entry.rs index 7546ab1d..a6a0cdf1 100644 --- a/src/libos/src/entry.rs +++ b/src/libos/src/entry.rs @@ -19,10 +19,17 @@ lazy_static! { static ref HAS_INIT: AtomicBool = AtomicBool::new(false); } +macro_rules! ecall_errno { + ($errno:expr) => {{ + let errno: Errno = $errno; + -(errno as i32) + }}; +} + #[no_mangle] pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *const c_char) -> i32 { if HAS_INIT.load(Ordering::SeqCst) == true { - return EXIT_STATUS_INTERNAL_ERROR; + return ecall_errno!(EEXIST); } assert!(!instance_dir.is_null()); @@ -31,7 +38,7 @@ pub extern "C" fn occlum_ecall_init(log_level: *const c_char, instance_dir: *con let input_log_level = match parse_log_level(log_level) { Err(e) => { eprintln!("invalid log level: {}", e.backtrace()); - return EXIT_STATUS_INTERNAL_ERROR; + return ecall_errno!(EINVAL); } Ok(log_level) => log_level, }; @@ -75,14 +82,14 @@ pub extern "C" fn occlum_ecall_new_process( host_stdio_fds: *const HostStdioFds, ) -> i32 { if HAS_INIT.load(Ordering::SeqCst) == false { - return EXIT_STATUS_INTERNAL_ERROR; + return ecall_errno!(EAGAIN); } let (path, args, host_stdio_fds) = match parse_arguments(path_buf, argv, host_stdio_fds) { Ok(path_and_args_and_host_stdio_fds) => path_and_args_and_host_stdio_fds, Err(e) => { eprintln!("invalid arguments for LibOS: {}", e.backtrace()); - return EXIT_STATUS_INTERNAL_ERROR; + return ecall_errno!(e.errno()); } }; @@ -93,18 +100,18 @@ pub extern "C" fn occlum_ecall_new_process( Ok(pid_t) => pid_t as i32, Err(e) => { eprintln!("failed to boot up LibOS: {}", e.backtrace()); - EXIT_STATUS_INTERNAL_ERROR + ecall_errno!(e.errno()) } } }) }) - .unwrap_or(EXIT_STATUS_INTERNAL_ERROR) + .unwrap_or(ecall_errno!(EFAULT)) } #[no_mangle] pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 { if HAS_INIT.load(Ordering::SeqCst) == false { - return EXIT_STATUS_INTERNAL_ERROR; + return ecall_errno!(EAGAIN); } let _ = unsafe { backtrace::enable_backtrace(&ENCLAVE_PATH, PrintFormat::Short) }; @@ -114,19 +121,14 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32 Ok(exit_status) => exit_status, Err(e) => { eprintln!("failed to execute a process: {}", e.backtrace()); - EXIT_STATUS_INTERNAL_ERROR + ecall_errno!(e.errno()) } } }) }) - .unwrap_or(EXIT_STATUS_INTERNAL_ERROR) + .unwrap_or(ecall_errno!(EFAULT)) } -// Use -128 as a special value to indicate internal error from libos, not from -// user programs. The LibOS ensures that an user program can only return a -// value between 0 and 255 (inclusive). -const EXIT_STATUS_INTERNAL_ERROR: i32 = -128; - fn parse_log_level(level_chars: *const c_char) -> Result { const DEFAULT_LEVEL: LevelFilter = LevelFilter::Off; @@ -249,7 +251,7 @@ fn validate_program_path(target_path: &PathBuf) -> Result<()> { .iter() .any(|valid_path_prefix| target_path.starts_with(valid_path_prefix)); if !is_valid_entry_point { - return_errno!(EINVAL, "program path is NOT a valid entry point"); + return_errno!(EACCES, "program path is NOT a valid entry point"); } Ok(()) } diff --git a/src/libos/src/process/task/exec.rs b/src/libos/src/process/task/exec.rs index ec2bfd89..617dd755 100644 --- a/src/libos/src/process/task/exec.rs +++ b/src/libos/src/process/task/exec.rs @@ -28,7 +28,7 @@ fn dequeue(libos_tid: pid_t) -> Result { .lock() .unwrap() .remove(&libos_tid) - .ok_or_else(|| errno!(EAGAIN, "the given TID does not match any pending thread")) + .ok_or_else(|| errno!(ESRCH, "the given TID does not match any pending thread")) } /// Execute the specified LibOS thread in the current host thread. diff --git a/src/pal/include/errno2str.h b/src/pal/include/errno2str.h new file mode 100644 index 00000000..4861e525 --- /dev/null +++ b/src/pal/include/errno2str.h @@ -0,0 +1,14 @@ +#ifndef __ERRNO2STR_H__ +#define __ERRNO2STR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +const char* errno2str(int errno_); + +#ifdef __cplusplus +} +#endif + +#endif /* __ERRNO2STR_H__ */ diff --git a/src/pal/src/errno2str.c b/src/pal/src/errno2str.c new file mode 100644 index 00000000..5865e7dd --- /dev/null +++ b/src/pal/src/errno2str.c @@ -0,0 +1,21 @@ +#include +#include "errno2str.h" + +const char* errno2str(int errno_) { + switch(errno_) { + case EPERM: return "EPERM"; + case ENOENT: return "ENOENT"; + case ESRCH: return "ESRCH"; + case ENOEXEC: return "ENOEXEC"; + case EBADF: return "EBADF"; + case ECHILD: return "ECHILD"; + case EAGAIN: return "EAGAIN"; + case ENOMEM: return "ENOMEM"; + case EACCES: return "EACCES"; + case EFAULT: return "EFAULT"; + case EBUSY: return "EBUSY"; + case EINVAL: return "EINVAL"; + case ENOSYS: return "ENOSYS"; + default: return "unknown"; + } +} diff --git a/src/pal/src/pal_api.c b/src/pal/src/pal_api.c index 855b3abd..cb28a189 100644 --- a/src/pal/src/pal_api.c +++ b/src/pal/src/pal_api.c @@ -4,6 +4,7 @@ #include "pal_error.h" #include "pal_log.h" #include "pal_syscall.h" +#include "errno2str.h" int occlum_pal_init(const struct occlum_pal_attr* attr) { errno = 0; @@ -27,21 +28,18 @@ int occlum_pal_init(const struct occlum_pal_attr* attr) { if (pal_init_enclave(attr->instance_dir) < 0) { return -1; } - - // Invoke a do-nothing ECall for two purposes: - // 1) Test the enclave can work; - // 2) Initialize the global data structures inside the enclave (which is - // automatically done by Intel SGX SDK). eid = pal_get_enclave_id(); - int ret; - sgx_status_t ecall_status = occlum_ecall_init(eid, &ret, attr->log_level, attr->instance_dir); + + int ecall_ret = 0; + sgx_status_t ecall_status = occlum_ecall_init(eid, &ecall_ret, attr->log_level, attr->instance_dir); if (ecall_status != SGX_SUCCESS) { const char* sgx_err = pal_get_sgx_error_msg(ecall_status); PAL_ERROR("Failed to do ECall: %s", sgx_err); return -1; } - if (ret < 0) { - errno = EINVAL; + if (ecall_ret < 0) { + errno = -ecall_ret; + PAL_ERROR("occlum_ecall_init returns %s", errno2str(errno)); return -1; } return 0; @@ -65,25 +63,34 @@ int occlum_pal_exec(const char* cmd_path, return -1; } - int libos_tid = -1; - sgx_status_t ecall_status = occlum_ecall_new_process(eid, &libos_tid, cmd_path, cmd_args, io_fds); + int ecall_ret = 0; // libos_tid + sgx_status_t ecall_status = occlum_ecall_new_process(eid, &ecall_ret, cmd_path, cmd_args, io_fds); if (ecall_status != SGX_SUCCESS) { const char* sgx_err = pal_get_sgx_error_msg(ecall_status); PAL_ERROR("Failed to do ECall: %s", sgx_err); return -1; } - if (libos_tid < 0) { + if (ecall_ret < 0) { + errno = -ecall_ret; + PAL_ERROR("occlum_ecall_new_process returns %s", errno2str(errno)); return -1; } + int libos_tid = ecall_ret; int host_tid = gettid(); - ecall_status = occlum_ecall_exec_thread(eid, exit_status, libos_tid, host_tid); + ecall_status = occlum_ecall_exec_thread(eid, &ecall_ret, libos_tid, host_tid); if (ecall_status != SGX_SUCCESS) { const char* sgx_err = pal_get_sgx_error_msg(ecall_status); PAL_ERROR("Failed to do ECall: %s", sgx_err); return -1; } + if (ecall_ret < 0) { + errno = -ecall_ret; + PAL_ERROR("occlum_ecall_exec_thread returns %s", errno2str(errno)); + return -1; + } + *exit_status = ecall_ret; return 0; }