554 lines
19 KiB
C++
Raw Normal View History

2026-06-23 09:54:30 +08:00
#include "il2cpp-config.h"
#include "pal_platform.h"
#if IL2CPP_USES_POSIX_CLASS_LIBRARY_PAL
#include <errno.h>
extern "C"
{
// Items needed by mscorlib
IL2CPP_EXPORT int32_t SystemNative_ConvertErrorPlatformToPal(int32_t platformErrno);
IL2CPP_EXPORT const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t bufferSize);
IL2CPP_EXPORT int32_t SystemNative_ConvertErrorPalToPlatform(int32_t error);
}
/**
* Error codes returned via ConvertErrno.
*
* Only the names (without the PAL_ prefix) are specified by POSIX.
*
* The values chosen below are simply assigned arbitrarily (originally
* in alphabetical order they appear in the spec, but they can't change so
* add new values to the end!).
*
* Also, the values chosen are deliberately outside the range of
* typical UNIX errnos (small numbers), HRESULTs (negative for errors)
* and Win32 errors (0x0000 - 0xFFFF). This isn't required for
* correctness, but may help debug a caller that is interpreting a raw
* int incorrectly.
*
* Wherever the spec says "x may be the same value as y", we do use
* the same value so that callers cannot not take a dependency on
* being able to distinguish between them.
*/
enum Error
{
Error_SUCCESS = 0,
Error_E2BIG = 0x10001, // Argument list too long.
Error_EACCES = 0x10002, // Permission denied.
Error_EADDRINUSE = 0x10003, // Address in use.
Error_EADDRNOTAVAIL = 0x10004, // Address not available.
Error_EAFNOSUPPORT = 0x10005, // Address family not supported.
Error_EAGAIN = 0x10006, // Resource unavailable, try again (same value as EWOULDBLOCK),
Error_EALREADY = 0x10007, // Connection already in progress.
Error_EBADF = 0x10008, // Bad file descriptor.
Error_EBADMSG = 0x10009, // Bad message.
Error_EBUSY = 0x1000A, // Device or resource busy.
Error_ECANCELED = 0x1000B, // Operation canceled.
Error_ECHILD = 0x1000C, // No child processes.
Error_ECONNABORTED = 0x1000D, // Connection aborted.
Error_ECONNREFUSED = 0x1000E, // Connection refused.
Error_ECONNRESET = 0x1000F, // Connection reset.
Error_EDEADLK = 0x10010, // Resource deadlock would occur.
Error_EDESTADDRREQ = 0x10011, // Destination address required.
Error_EDOM = 0x10012, // Mathematics argument out of domain of function.
Error_EDQUOT = 0x10013, // Reserved.
Error_EEXIST = 0x10014, // File exists.
Error_EFAULT = 0x10015, // Bad address.
Error_EFBIG = 0x10016, // File too large.
Error_EHOSTUNREACH = 0x10017, // Host is unreachable.
Error_EIDRM = 0x10018, // Identifier removed.
Error_EILSEQ = 0x10019, // Illegal byte sequence.
Error_EINPROGRESS = 0x1001A, // Operation in progress.
Error_EINTR = 0x1001B, // Interrupted function.
Error_EINVAL = 0x1001C, // Invalid argument.
Error_EIO = 0x1001D, // I/O error.
Error_EISCONN = 0x1001E, // Socket is connected.
Error_EISDIR = 0x1001F, // Is a directory.
Error_ELOOP = 0x10020, // Too many levels of symbolic links.
Error_EMFILE = 0x10021, // File descriptor value too large.
Error_EMLINK = 0x10022, // Too many links.
Error_EMSGSIZE = 0x10023, // Message too large.
Error_EMULTIHOP = 0x10024, // Reserved.
Error_ENAMETOOLONG = 0x10025, // Filename too long.
Error_ENETDOWN = 0x10026, // Network is down.
Error_ENETRESET = 0x10027, // Connection aborted by network.
Error_ENETUNREACH = 0x10028, // Network unreachable.
Error_ENFILE = 0x10029, // Too many files open in system.
Error_ENOBUFS = 0x1002A, // No buffer space available.
Error_ENODEV = 0x1002C, // No such device.
Error_ENOENT = 0x1002D, // No such file or directory.
Error_ENOEXEC = 0x1002E, // Executable file format error.
Error_ENOLCK = 0x1002F, // No locks available.
Error_ENOLINK = 0x10030, // Reserved.
Error_ENOMEM = 0x10031, // Not enough space.
Error_ENOMSG = 0x10032, // No message of the desired type.
Error_ENOPROTOOPT = 0x10033, // Protocol not available.
Error_ENOSPC = 0x10034, // No space left on device.
Error_ENOSYS = 0x10037, // Function not supported.
Error_ENOTCONN = 0x10038, // The socket is not connected.
Error_ENOTDIR = 0x10039, // Not a directory or a symbolic link to a directory.
Error_ENOTEMPTY = 0x1003A, // Directory not empty.
Error_ENOTRECOVERABLE = 0x1003B, // State not recoverable.
Error_ENOTSOCK = 0x1003C, // Not a socket.
Error_ENOTSUP = 0x1003D, // Not supported (same value as EOPNOTSUP).
Error_ENOTTY = 0x1003E, // Inappropriate I/O control operation.
Error_ENXIO = 0x1003F, // No such device or address.
Error_EOVERFLOW = 0x10040, // Value too large to be stored in data type.
Error_EOWNERDEAD = 0x10041, // Previous owner died.
Error_EPERM = 0x10042, // Operation not permitted.
Error_EPIPE = 0x10043, // Broken pipe.
Error_EPROTO = 0x10044, // Protocol error.
Error_EPROTONOSUPPORT = 0x10045, // Protocol not supported.
Error_EPROTOTYPE = 0x10046, // Protocol wrong type for socket.
Error_ERANGE = 0x10047, // Result too large.
Error_EROFS = 0x10048, // Read-only file system.
Error_ESPIPE = 0x10049, // Invalid seek.
Error_ESRCH = 0x1004A, // No such process.
Error_ESTALE = 0x1004B, // Reserved.
Error_ETIMEDOUT = 0x1004D, // Connection timed out.
Error_ETXTBSY = 0x1004E, // Text file busy.
Error_EXDEV = 0x1004F, // Cross-device link.
Error_ESOCKTNOSUPPORT = 0x1005E, // Socket type not supported.
Error_EPFNOSUPPORT = 0x10060, // Protocol family not supported.
Error_ESHUTDOWN = 0x1006C, // Socket shutdown.
Error_EHOSTDOWN = 0x10070, // Host is down.
Error_ENODATA = 0x10071, // No data available.
// POSIX permits these to have the same value and we make them
// always equal so that we cannot introduce a dependency on
// distinguishing between them that would not work on all
// platforms.
Error_EOPNOTSUPP = Error_ENOTSUP, // Operation not supported on socket
Error_EWOULDBLOCK = Error_EAGAIN, // Operation would block
// This one is not part of POSIX, but is a catch-all for the case
// where we cannot convert the raw errno value to something above.
Error_ENONSTANDARD = 0x1FFFF,
};
// Items needed by mscorlib
int32_t SystemNative_ConvertErrorPlatformToPal(int32_t platformErrno)
{
switch (platformErrno)
{
case 0:
return Error_SUCCESS;
case E2BIG:
return Error_E2BIG;
case EACCES:
return Error_EACCES;
case EADDRINUSE:
return Error_EADDRINUSE;
case EADDRNOTAVAIL:
return Error_EADDRNOTAVAIL;
case EAFNOSUPPORT:
return Error_EAFNOSUPPORT;
case EAGAIN:
return Error_EAGAIN;
case EALREADY:
return Error_EALREADY;
case EBADF:
return Error_EBADF;
case EBADMSG:
return Error_EBADMSG;
case EBUSY:
return Error_EBUSY;
case ECANCELED:
return Error_ECANCELED;
case ECHILD:
return Error_ECHILD;
case ECONNABORTED:
return Error_ECONNABORTED;
case ECONNREFUSED:
return Error_ECONNREFUSED;
case ECONNRESET:
return Error_ECONNRESET;
case EDEADLK:
return Error_EDEADLK;
case EDESTADDRREQ:
return Error_EDESTADDRREQ;
case EDOM:
return Error_EDOM;
case EDQUOT:
return Error_EDQUOT;
case EEXIST:
return Error_EEXIST;
case EFAULT:
return Error_EFAULT;
case EFBIG:
return Error_EFBIG;
case EHOSTUNREACH:
return Error_EHOSTUNREACH;
case EIDRM:
return Error_EIDRM;
case EILSEQ:
return Error_EILSEQ;
case EINPROGRESS:
return Error_EINPROGRESS;
case EINTR:
return Error_EINTR;
case EINVAL:
return Error_EINVAL;
case EIO:
return Error_EIO;
case EISCONN:
return Error_EISCONN;
case EISDIR:
return Error_EISDIR;
case ELOOP:
return Error_ELOOP;
case EMFILE:
return Error_EMFILE;
case EMLINK:
return Error_EMLINK;
case EMSGSIZE:
return Error_EMSGSIZE;
case EMULTIHOP:
return Error_EMULTIHOP;
case ENAMETOOLONG:
return Error_ENAMETOOLONG;
case ENETDOWN:
return Error_ENETDOWN;
case ENETRESET:
return Error_ENETRESET;
case ENETUNREACH:
return Error_ENETUNREACH;
case ENFILE:
return Error_ENFILE;
case ENOBUFS:
return Error_ENOBUFS;
case ENODEV:
return Error_ENODEV;
case ENOENT:
return Error_ENOENT;
case ENOEXEC:
return Error_ENOEXEC;
case ENOLCK:
return Error_ENOLCK;
case ENOLINK:
return Error_ENOLINK;
case ENOMEM:
return Error_ENOMEM;
case ENOMSG:
return Error_ENOMSG;
case ENOPROTOOPT:
return Error_ENOPROTOOPT;
case ENOSPC:
return Error_ENOSPC;
case ENOSYS:
return Error_ENOSYS;
case ENOTCONN:
return Error_ENOTCONN;
case ENOTDIR:
return Error_ENOTDIR;
#if ENOTEMPTY != EEXIST // AIX defines this
case ENOTEMPTY:
return Error_ENOTEMPTY;
#endif
#ifdef ENOTRECOVERABLE // not available in NetBSD
case ENOTRECOVERABLE:
return Error_ENOTRECOVERABLE;
#endif
case ENOTSOCK:
return Error_ENOTSOCK;
case ENOTSUP:
return Error_ENOTSUP;
case ENOTTY:
return Error_ENOTTY;
case ENXIO:
return Error_ENXIO;
case EOVERFLOW:
return Error_EOVERFLOW;
#ifdef EOWNERDEAD // not available in NetBSD
case EOWNERDEAD:
return Error_EOWNERDEAD;
#endif
case EPERM:
return Error_EPERM;
case EPIPE:
return Error_EPIPE;
case EPROTO:
return Error_EPROTO;
case EPROTONOSUPPORT:
return Error_EPROTONOSUPPORT;
case EPROTOTYPE:
return Error_EPROTOTYPE;
case ERANGE:
return Error_ERANGE;
case EROFS:
return Error_EROFS;
case ESPIPE:
return Error_ESPIPE;
case ESRCH:
return Error_ESRCH;
case ESTALE:
return Error_ESTALE;
case ETIMEDOUT:
return Error_ETIMEDOUT;
case ETXTBSY:
return Error_ETXTBSY;
case EXDEV:
return Error_EXDEV;
#ifdef ESOCKTNOSUPPORT
case ESOCKTNOSUPPORT:
return Error_ESOCKTNOSUPPORT;
#endif
case EPFNOSUPPORT:
return Error_EPFNOSUPPORT;
case ESHUTDOWN:
return Error_ESHUTDOWN;
case EHOSTDOWN:
return Error_EHOSTDOWN;
case ENODATA:
return Error_ENODATA;
// #if because these will trigger duplicate case label warnings when
// they have the same value, which is permitted by POSIX and common.
#if EOPNOTSUPP != ENOTSUP
case EOPNOTSUPP:
return Error_EOPNOTSUPP;
#endif
#if EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
return Error_EWOULDBLOCK;
#endif
}
return Error_ENONSTANDARD;
}
const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t bufferSize)
{
IL2CPP_ASSERT(buffer != NULL);
IL2CPP_ASSERT(bufferSize > 0);
if (bufferSize < 0)
return NULL;
// Note that we must use strerror_r because plain strerror is not
// thread-safe.
//
// However, there are two versions of strerror_r:
// - GNU: char* strerror_r(int, char*, size_t);
// - POSIX: int strerror_r(int, char*, size_t);
//
// The former may or may not use the supplied buffer, and returns
// the error message string. The latter stores the error message
// string into the supplied buffer and returns an error code.
#if IL2CPP_HAVE_GNU_STRERROR_R
const char* message = strerror_r(platformErrno, buffer, (uint32_t)bufferSize);
IL2CPP_ASSERT(message != NULL);
return message;
#else
int error = strerror_r(platformErrno, buffer, (uint32_t)bufferSize);
if (error == ERANGE)
{
// Buffer is too small to hold the entire message, but has
// still been filled to the extent possible and null-terminated.
return NULL;
}
// The only other valid error codes are 0 for success or EINVAL for
// an unknown error, but in the latter case a reasonable string (e.g
// "Unknown error: 0x123") is returned.
IL2CPP_ASSERT((error == 0 || error == EINVAL) && "invalid error");
return buffer;
#endif
}
int32_t SystemNative_ConvertErrorPalToPlatform(int32_t error)
{
switch (error)
{
case Error_SUCCESS:
return 0;
case Error_E2BIG:
return E2BIG;
case Error_EACCES:
return EACCES;
case Error_EADDRINUSE:
return EADDRINUSE;
case Error_EADDRNOTAVAIL:
return EADDRNOTAVAIL;
case Error_EAFNOSUPPORT:
return EAFNOSUPPORT;
case Error_EAGAIN:
return EAGAIN;
case Error_EALREADY:
return EALREADY;
case Error_EBADF:
return EBADF;
case Error_EBADMSG:
return EBADMSG;
case Error_EBUSY:
return EBUSY;
case Error_ECANCELED:
return ECANCELED;
case Error_ECHILD:
return ECHILD;
case Error_ECONNABORTED:
return ECONNABORTED;
case Error_ECONNREFUSED:
return ECONNREFUSED;
case Error_ECONNRESET:
return ECONNRESET;
case Error_EDEADLK:
return EDEADLK;
case Error_EDESTADDRREQ:
return EDESTADDRREQ;
case Error_EDOM:
return EDOM;
case Error_EDQUOT:
return EDQUOT;
case Error_EEXIST:
return EEXIST;
case Error_EFAULT:
return EFAULT;
case Error_EFBIG:
return EFBIG;
case Error_EHOSTUNREACH:
return EHOSTUNREACH;
case Error_EIDRM:
return EIDRM;
case Error_EILSEQ:
return EILSEQ;
case Error_EINPROGRESS:
return EINPROGRESS;
case Error_EINTR:
return EINTR;
case Error_EINVAL:
return EINVAL;
case Error_EIO:
return EIO;
case Error_EISCONN:
return EISCONN;
case Error_EISDIR:
return EISDIR;
case Error_ELOOP:
return ELOOP;
case Error_EMFILE:
return EMFILE;
case Error_EMLINK:
return EMLINK;
case Error_EMSGSIZE:
return EMSGSIZE;
case Error_EMULTIHOP:
return EMULTIHOP;
case Error_ENAMETOOLONG:
return ENAMETOOLONG;
case Error_ENETDOWN:
return ENETDOWN;
case Error_ENETRESET:
return ENETRESET;
case Error_ENETUNREACH:
return ENETUNREACH;
case Error_ENFILE:
return ENFILE;
case Error_ENOBUFS:
return ENOBUFS;
case Error_ENODEV:
return ENODEV;
case Error_ENOENT:
return ENOENT;
case Error_ENOEXEC:
return ENOEXEC;
case Error_ENOLCK:
return ENOLCK;
case Error_ENOLINK:
return ENOLINK;
case Error_ENOMEM:
return ENOMEM;
case Error_ENOMSG:
return ENOMSG;
case Error_ENOPROTOOPT:
return ENOPROTOOPT;
case Error_ENOSPC:
return ENOSPC;
case Error_ENOSYS:
return ENOSYS;
case Error_ENOTCONN:
return ENOTCONN;
case Error_ENOTDIR:
return ENOTDIR;
case Error_ENOTEMPTY:
return ENOTEMPTY;
#ifdef ENOTRECOVERABLE // not available in NetBSD
case Error_ENOTRECOVERABLE:
return ENOTRECOVERABLE;
#endif
case Error_ENOTSOCK:
return ENOTSOCK;
case Error_ENOTSUP:
return ENOTSUP;
case Error_ENOTTY:
return ENOTTY;
case Error_ENXIO:
return ENXIO;
case Error_EOVERFLOW:
return EOVERFLOW;
#ifdef EOWNERDEAD // not available in NetBSD
case Error_EOWNERDEAD:
return EOWNERDEAD;
#endif
case Error_EPERM:
return EPERM;
case Error_EPIPE:
return EPIPE;
case Error_EPROTO:
return EPROTO;
case Error_EPROTONOSUPPORT:
return EPROTONOSUPPORT;
case Error_EPROTOTYPE:
return EPROTOTYPE;
case Error_ERANGE:
return ERANGE;
case Error_EROFS:
return EROFS;
case Error_ESPIPE:
return ESPIPE;
case Error_ESRCH:
return ESRCH;
case Error_ESTALE:
return ESTALE;
case Error_ETIMEDOUT:
return ETIMEDOUT;
case Error_ETXTBSY:
return ETXTBSY;
case Error_EXDEV:
return EXDEV;
case Error_EPFNOSUPPORT:
return EPFNOSUPPORT;
#ifdef ESOCKTNOSUPPORT
case Error_ESOCKTNOSUPPORT:
return ESOCKTNOSUPPORT;
#endif
case Error_ESHUTDOWN:
return ESHUTDOWN;
case Error_EHOSTDOWN:
return EHOSTDOWN;
case Error_ENODATA:
return ENODATA;
case Error_ENONSTANDARD:
break; // fall through to assert
}
// We should not use this function to round-trip platform -> pal
// -> platform. It's here only to synthesize a platform number
// from the fixed set above. Note that the assert is outside the
// switch rather than in a default case block because not
// having a default will trigger a warning (as error) if there's
// an enum value we haven't handled. Should that trigger, make
// note that there is probably a corresponding missing case in the
// other direction above, but the compiler can't warn in that case
// because the platform values are not part of an enum.
IL2CPP_ASSERT(0 && "Unknown error code");
return -1;
}
#endif