diff --git a/include/errno.h b/include/errno.h new file mode 100644 index 0000000..1bf21d1 --- /dev/null +++ b/include/errno.h @@ -0,0 +1,229 @@ +#ifndef VLIBC_ERRNO_H +#define VLIBC_ERRNO_H + +/* + * vlibc — . + * + * errno is per-thread state. It lives in a slot of the thread control block + * (TCB), addressed relative to the x86_64 FS thread pointer; there is no + * process-global errno object and no compiler-managed TLS (`__thread`) here. + * The errno macro below expands to a dereference of __errno_location(), the + * implementation-reserved accessor that returns the address of the calling + * thread's errno slot. The offset of that slot inside the TCB is the ABI + * constant VLIBC_TCB_ERRNO_OFF, owned by the internal TCB/ABI headers (see + * src/internal/errno.h) — this public header is the single canonical home + * for the errno macro, the __errno_location() declaration, and the E* + * constant table, so internal code includes it instead of keeping a copy. + * + * Levels are cumulative (see include/vlibc/features.h): + * Level 1 (onlyposix): POSIX base — strerror. + * Level 2 (muslmimic): XSI — strerror_r (the int-returning flavor; the + * GNU char*-returning variant is level 3+ and is + * deliberately not provided). + * + * This header includes itself, so the gates below always + * see the configured VLIBC_LEVEL even when the caller included no vlibc + * header first, and for size_t. + */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Return the address of the calling thread's errno slot. + * + * Requires the thread pointer (FS) to be initialized to a TCB whose first + * word is the TCB's own address — the startup todo owns that bootstrap and + * sets it before any code that can touch errno runs. This function itself + * performs no setup and no other TCB access. + * + * The name sits in the implementation-reserved namespace deliberately: it is + * this libc's private errno accessor, not public API. + */ +int * +__errno_location(void); // NOLINT(bugprone-reserved-identifier) + +/* The conventional errno lvalue; resolves to the caller's TCB slot. */ +#define errno (*__errno_location()) + +/* + * Error numbers: the Linux errno ABI (asm-generic/errno-base.h plus + * asm-generic/errno.h, which is the table x86_64 uses), transcribed as + * kernel-ABI facts. These values are fixed and shared with the kernel. + * Values 41 and 58 do not exist in the ABI (EWOULDBLOCK and EDEADLOCK are + * aliases of EAGAIN and EDEADLK); EHWPOISON is the last defined value. + */ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Invalid system call number */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ +#define EDEADLOCK EDEADLK /* File locking deadlock error */ +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ +#define ERFKILL 132 /* Operation not possible due to RF-kill */ +#define EHWPOISON 133 /* Memory page has hardware error */ + +/* + * Return a pointer to the message string for errnum. The string for a known + * value points to immutable static storage and is safe to keep across calls; + * an unknown value yields "Unknown error ", formatted into shared + * static storage whose contents a later call may overwrite (unknown errno + * values are a rare diagnostic path; see strerror.c). Never returns NULL. + * + * No intent attribute: the unknown-value path writes shared storage. + */ +char * +strerror(int errnum); + +#if VLIBC_LEVEL_GE(2) +/* Level 2 (muslmimic): XSI. */ + +/* + * XSI strerror_r: copy the message for errnum into buf, truncated to buflen + * and always NUL-terminated when buflen > 0, and return 0 — including for + * unknown errnum values, whose text is "Unknown error ". When buflen + * is 0 nothing is written and 0 is returned; when buf is NULL and buflen > 0, + * EINVAL is returned. The GNU char*-returning variant is deliberately not + * provided. + * + * No intent attribute: it writes memory. + */ +int +strerror_r(int errnum, char *buf, size_t buflen); +#endif /* VLIBC_LEVEL_GE(2) */ + +#ifdef __cplusplus +} +#endif + +#endif /* VLIBC_ERRNO_H */ diff --git a/src/errno/strerror.c b/src/errno/strerror.c new file mode 100644 index 0000000..5d6dec3 --- /dev/null +++ b/src/errno/strerror.c @@ -0,0 +1,296 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* + * vlibc — strerror / strerror_r (todo 2). + * + * strerror maps an errno value to an informative, descriptive message. The + * messages are this libc's own standard English texts (they need not match + * glibc verbatim). Known values resolve to immutable static string constants; + * unknown values resolve to "Unknown error " with formatted in place. + * + * Thread-safety of the unknown-value path: the formatted string lives in a + * single static buffer, so two threads formatting *unknown* errno values + * concurrently can observe each other's text. This tradeoff is deliberate at + * this stage: unknown errno values are a rare diagnostic path, the library + * has no per-thread storage beyond the errno TCB slot yet, and strerror must + * not depend on malloc (or any other subsystem). The pointer returned for + * known values is unaffected. Once the real TCB layout lands (startup todo), + * this can migrate to a per-thread buffer without changing the interface. + * + * The unknown-value formatter is hand-rolled: strerror is libc base and must + * not pull in stdio/string before those exist. + */ + +/* + * Message table: one entry per defined E* value, indexed directly by the + * errno number. Designated initializers keep name/value pairs provably in + * sync with ; entries for the ABI gaps (41, 58 — values the kernel + * never produces) stay NULL and are treated as unknown. EWOULDBLOCK and + * EDEADLOCK are aliases of EAGAIN and EDEADLK, so they share those entries. + * The array auto-sizes to EHWPOISON + 1 (134) entries. + */ +static const char *const errmsg[] = { + [0] = "Success", + [EPERM] = "Operation not permitted", + [ENOENT] = "No such file or directory", + [ESRCH] = "No such process", + [EINTR] = "Interrupted system call", + [EIO] = "Input/output error", + [ENXIO] = "No such device or address", + [E2BIG] = "Argument list too long", + [ENOEXEC] = "Exec format error", + [EBADF] = "Bad file descriptor", + [ECHILD] = "No child processes", + [EAGAIN] = "Resource temporarily unavailable", + [ENOMEM] = "Cannot allocate memory", + [EACCES] = "Permission denied", + [EFAULT] = "Bad address", + [ENOTBLK] = "Block device required", + [EBUSY] = "Device or resource busy", + [EEXIST] = "File exists", + [EXDEV] = "Invalid cross-device link", + [ENODEV] = "No such device", + [ENOTDIR] = "Not a directory", + [EISDIR] = "Is a directory", + [EINVAL] = "Invalid argument", + [ENFILE] = "Too many open files in system", + [EMFILE] = "Too many open files", + [ENOTTY] = "Inappropriate ioctl for device", + [ETXTBSY] = "Text file busy", + [EFBIG] = "File too large", + [ENOSPC] = "No space left on device", + [ESPIPE] = "Illegal seek", + [EROFS] = "Read-only file system", + [EMLINK] = "Too many links", + [EPIPE] = "Broken pipe", + [EDOM] = "Numerical argument out of domain", + [ERANGE] = "Numerical result out of range", + [EDEADLK] = "Resource deadlock would occur", + [ENAMETOOLONG] = "File name too long", + [ENOLCK] = "No locks available", + [ENOSYS] = "Function not implemented", + [ENOTEMPTY] = "Directory not empty", + [ELOOP] = "Too many levels of symbolic links", + [ENOMSG] = "No message of desired type", + [EIDRM] = "Identifier removed", + [ECHRNG] = "Channel number out of range", + [EL2NSYNC] = "Level 2 not synchronized", + [EL3HLT] = "Level 3 halted", + [EL3RST] = "Level 3 reset", + [ELNRNG] = "Link number out of range", + [EUNATCH] = "Protocol driver not attached", + [ENOCSI] = "No CSI structure available", + [EL2HLT] = "Level 2 halted", + [EBADE] = "Invalid exchange", + [EBADR] = "Invalid request descriptor", + [EXFULL] = "Exchange full", + [ENOANO] = "No anode", + [EBADRQC] = "Invalid request code", + [EBADSLT] = "Invalid slot", + [EBFONT] = "Bad font file format", + [ENOSTR] = "Device not a stream", + [ENODATA] = "No data available", + [ETIME] = "Timer expired", + [ENOSR] = "Out of streams resources", + [ENONET] = "Machine is not on the network", + [ENOPKG] = "Package not installed", + [EREMOTE] = "Object is remote", + [ENOLINK] = "Link has been severed", + [EADV] = "Advertise error", + [ESRMNT] = "Srmount error", + [ECOMM] = "Communication error on send", + [EPROTO] = "Protocol error", + [EMULTIHOP] = "Multihop attempted", + [EDOTDOT] = "RFS specific error", + [EBADMSG] = "Bad message", + [EOVERFLOW] = "Value too large for defined data type", + [ENOTUNIQ] = "Name not unique on network", + [EBADFD] = "File descriptor in bad state", + [EREMCHG] = "Remote address changed", + [ELIBACC] = "Can not access a needed shared library", + [ELIBBAD] = "Accessing a corrupted shared library", + [ELIBSCN] = ".lib section in a.out corrupted", + [ELIBMAX] = "Attempting to link in too many shared libraries", + [ELIBEXEC] = "Cannot exec a shared library directly", + [EILSEQ] = "Invalid or incomplete multibyte or wide character", + [ERESTART] = "Interrupted system call should be restarted", + [ESTRPIPE] = "Streams pipe error", + [EUSERS] = "Too many users", + [ENOTSOCK] = "Socket operation on non-socket", + [EDESTADDRREQ] = "Destination address required", + [EMSGSIZE] = "Message too long", + [EPROTOTYPE] = "Protocol wrong type for socket", + [ENOPROTOOPT] = "Protocol not available", + [EPROTONOSUPPORT] = "Protocol not supported", + [ESOCKTNOSUPPORT] = "Socket type not supported", + [EOPNOTSUPP] = "Operation not supported", + [EPFNOSUPPORT] = "Protocol family not supported", + [EAFNOSUPPORT] = "Address family not supported by protocol", + [EADDRINUSE] = "Address already in use", + [EADDRNOTAVAIL] = "Cannot assign requested address", + [ENETDOWN] = "Network is down", + [ENETUNREACH] = "Network is unreachable", + [ENETRESET] = "Network dropped connection on reset", + [ECONNABORTED] = "Software caused connection abort", + [ECONNRESET] = "Connection reset by peer", + [ENOBUFS] = "No buffer space available", + [EISCONN] = "Transport endpoint is already connected", + [ENOTCONN] = "Transport endpoint is not connected", + [ESHUTDOWN] = "Cannot send after transport endpoint shutdown", + [ETOOMANYREFS] = "Too many references: cannot splice", + [ETIMEDOUT] = "Connection timed out", + [ECONNREFUSED] = "Connection refused", + [EHOSTDOWN] = "Host is down", + [EHOSTUNREACH] = "No route to host", + [EALREADY] = "Operation already in progress", + [EINPROGRESS] = "Operation now in progress", + [ESTALE] = "Stale file handle", + [EUCLEAN] = "Structure needs cleaning", + [ENOTNAM] = "Not a XENIX named type file", + [ENAVAIL] = "No XENIX semaphores available", + [EISNAM] = "Is a named type file", + [EREMOTEIO] = "Remote I/O error", + [EDQUOT] = "Disk quota exceeded", + [ENOMEDIUM] = "No medium found", + [EMEDIUMTYPE] = "Wrong medium type", + [ECANCELED] = "Operation canceled", + [ENOKEY] = "Required key not available", + [EKEYEXPIRED] = "Key has expired", + [EKEYREVOKED] = "Key has been revoked", + [EKEYREJECTED] = "Key was rejected by service", + [EOWNERDEAD] = "Owner died", + [ENOTRECOVERABLE] = "State not recoverable", + [ERFKILL] = "Operation not possible due to RF-kill", + [EHWPOISON] = "Memory page has hardware error", +}; + +/* The table spans 0..EHWPOISON exactly: the ABI gaps stay NULL entries. */ +_Static_assert(sizeof errmsg / sizeof errmsg[0] == EHWPOISON + 1, + "strerror table must cover every E* value 0..EHWPOISON"); + +/* Shared storage for the unknown-value path (see the file-top comment). */ +static char errbuf[32]; + +/* + * Write "Unknown error " into dst, truncating to cap bytes and always + * NUL-terminating when cap > 0. Hand-rolled so this file needs no stdio. + */ +static void +format_unknown(int errnum, char *dst, size_t cap) +{ + static const char prefix[] = "Unknown error "; + char digits[12]; /* enough for "-2147483648" */ + size_t ndigits; + size_t i; + unsigned long mag; + + if (cap == 0) + { + return; + } + + /* Absolute value as unsigned long; INT_MIN negates safely in long. */ + mag = (unsigned long)(errnum < 0 ? -(long)errnum : errnum); + + /* Digits, least significant first. */ + ndigits = 0; + do + { + digits[ndigits] = (char)('0' + (int)(mag % 10)); + ndigits++; + mag /= 10; + } while (mag != 0); + + i = 0; + while (i + 1 < cap && prefix[i] != '\0') + { + dst[i] = prefix[i]; + i++; + } + if (i + 1 < cap && errnum < 0) + { + dst[i] = '-'; + i++; + } + while (i + 1 < cap && ndigits > 0) + { + ndigits--; + dst[i] = digits[ndigits]; + i++; + } + dst[i] = '\0'; +} + +/* + * Return the message for errnum, or NULL when it is unknown (out of table + * range or an ABI gap). + */ +static const char * +lookup(int errnum) +{ + if (errnum >= 0 && errnum < (int)(sizeof errmsg / sizeof errmsg[0])) + { + return errmsg[errnum]; + } + return NULL; +} + +char * +strerror(int errnum) +{ + const char *msg; + + msg = lookup(errnum); + if (msg == NULL) + { + format_unknown(errnum, errbuf, sizeof errbuf); + msg = errbuf; + } + return (char *)msg; +} + +#if VLIBC_LEVEL_GE(2) +/* + * XSI strerror_r: copy the message for errnum into buf (truncated to buflen, + * always NUL-terminated when buflen > 0) and return 0. Unknown errnum values + * produce "Unknown error " with the same success return; buf == NULL + * with buflen > 0 yields EINVAL; buflen == 0 writes nothing and returns 0. + * The GNU char*-returning semantics are deliberately not implemented. + */ +int +strerror_r(int errnum, char *buf, size_t buflen) +{ + const char *msg; + char unknown[32]; + size_t i; + + if (buflen == 0) + { + return 0; + } + if (buf == NULL) + { + return EINVAL; + } + + msg = lookup(errnum); + if (msg == NULL) + { + format_unknown(errnum, unknown, sizeof unknown); + msg = unknown; + } + + i = 0; + while (i + 1 < buflen && msg[i] != '\0') + { + buf[i] = msg[i]; + i++; + } + buf[i] = '\0'; + return 0; +} +#endif /* VLIBC_LEVEL_GE(2) */ diff --git a/src/internal/errno.h b/src/internal/errno.h index 8f90bd2..bc1d651 100644 --- a/src/internal/errno.h +++ b/src/internal/errno.h @@ -8,6 +8,14 @@ * (TCB), addressed relative to the x86_64 FS thread pointer; there is no * process-global errno object and no compiler-managed TLS (`__thread`) here. * + * The public is the single canonical home for the errno macro, the + * __errno_location() declaration, and the E* constant table (1..133, with + * EAGAIN/EWOULDBLOCK and EDEADLK/EDEADLOCK aliases and no values 41/58). + * This header includes it instead of keeping a second copy, so the public + * and internal views of errno can never drift apart. What stays internal- + * only here is the errno TCB-slot offset, an ABI constant the public header + * must not expose. + * * The offset of the errno slot inside the TCB is an ABI constant defined * below and consumed by the authoritative TCB layout (see the startup todo, * which owns the TCB/DTV layout). No other layer may re-derive errno's @@ -23,163 +31,7 @@ */ #define VLIBC_TCB_ERRNO_OFF 8 -/* - * Return the address of the calling thread's errno slot. - * - * Requires the thread pointer (FS) to be initialized to a TCB whose first - * word is the TCB's own address — the startup todo owns that bootstrap and - * sets it before any code that can touch errno runs. This function itself - * performs no setup and no other TCB access. - * - * The name sits in the implementation-reserved namespace deliberately: it is - * this libc's private errno accessor, not public API. - */ -int * -__errno_location(void); // NOLINT(bugprone-reserved-identifier) - -/* The conventional errno lvalue; resolves to the caller's TCB slot. */ -#define errno (*__errno_location()) - -/* - * Error numbers: the Linux errno ABI (asm-generic/errno-base.h plus - * asm-generic/errno.h, which is the table x86_64 uses), transcribed as - * kernel-ABI facts. These values are fixed and shared with the kernel. The - * public is owned by a later todo; this internal copy is what - * internal code and tests compile against. - */ - -#define EPERM 1 /* Operation not permitted */ -#define ENOENT 2 /* No such file or directory */ -#define ESRCH 3 /* No such process */ -#define EINTR 4 /* Interrupted system call */ -#define EIO 5 /* I/O error */ -#define ENXIO 6 /* No such device or address */ -#define E2BIG 7 /* Argument list too long */ -#define ENOEXEC 8 /* Exec format error */ -#define EBADF 9 /* Bad file descriptor */ -#define ECHILD 10 /* No child processes */ -#define EAGAIN 11 /* Try again */ -#define ENOMEM 12 /* Out of memory */ -#define EACCES 13 /* Permission denied */ -#define EFAULT 14 /* Bad address */ -#define ENOTBLK 15 /* Block device required */ -#define EBUSY 16 /* Device or resource busy */ -#define EEXIST 17 /* File exists */ -#define EXDEV 18 /* Cross-device link */ -#define ENODEV 19 /* No such device */ -#define ENOTDIR 20 /* Not a directory */ -#define EISDIR 21 /* Is a directory */ -#define EINVAL 22 /* Invalid argument */ -#define ENFILE 23 /* File table overflow */ -#define EMFILE 24 /* Too many open files */ -#define ENOTTY 25 /* Not a typewriter */ -#define ETXTBSY 26 /* Text file busy */ -#define EFBIG 27 /* File too large */ -#define ENOSPC 28 /* No space left on device */ -#define ESPIPE 29 /* Illegal seek */ -#define EROFS 30 /* Read-only file system */ -#define EMLINK 31 /* Too many links */ -#define EPIPE 32 /* Broken pipe */ -#define EDOM 33 /* Math argument out of domain of func */ -#define ERANGE 34 /* Math result not representable */ -#define EDEADLK 35 /* Resource deadlock would occur */ -#define ENAMETOOLONG 36 /* File name too long */ -#define ENOLCK 37 /* No record locks available */ -#define ENOSYS 38 /* Invalid system call number */ -#define ENOTEMPTY 39 /* Directory not empty */ -#define ELOOP 40 /* Too many symbolic links encountered */ -#define EWOULDBLOCK EAGAIN /* Operation would block */ -#define ENOMSG 42 /* No message of desired type */ -#define EIDRM 43 /* Identifier removed */ -#define ECHRNG 44 /* Channel number out of range */ -#define EL2NSYNC 45 /* Level 2 not synchronized */ -#define EL3HLT 46 /* Level 3 halted */ -#define EL3RST 47 /* Level 3 reset */ -#define ELNRNG 48 /* Link number out of range */ -#define EUNATCH 49 /* Protocol driver not attached */ -#define ENOCSI 50 /* No CSI structure available */ -#define EL2HLT 51 /* Level 2 halted */ -#define EBADE 52 /* Invalid exchange */ -#define EBADR 53 /* Invalid request descriptor */ -#define EXFULL 54 /* Exchange full */ -#define ENOANO 55 /* No anode */ -#define EBADRQC 56 /* Invalid request code */ -#define EBADSLT 57 /* Invalid slot */ -#define EDEADLOCK EDEADLK /* File locking deadlock error */ -#define EBFONT 59 /* Bad font file format */ -#define ENOSTR 60 /* Device not a stream */ -#define ENODATA 61 /* No data available */ -#define ETIME 62 /* Timer expired */ -#define ENOSR 63 /* Out of streams resources */ -#define ENONET 64 /* Machine is not on the network */ -#define ENOPKG 65 /* Package not installed */ -#define EREMOTE 66 /* Object is remote */ -#define ENOLINK 67 /* Link has been severed */ -#define EADV 68 /* Advertise error */ -#define ESRMNT 69 /* Srmount error */ -#define ECOMM 70 /* Communication error on send */ -#define EPROTO 71 /* Protocol error */ -#define EMULTIHOP 72 /* Multihop attempted */ -#define EDOTDOT 73 /* RFS specific error */ -#define EBADMSG 74 /* Not a data message */ -#define EOVERFLOW 75 /* Value too large for defined data type */ -#define ENOTUNIQ 76 /* Name not unique on network */ -#define EBADFD 77 /* File descriptor in bad state */ -#define EREMCHG 78 /* Remote address changed */ -#define ELIBACC 79 /* Can not access a needed shared library */ -#define ELIBBAD 80 /* Accessing a corrupted shared library */ -#define ELIBSCN 81 /* .lib section in a.out corrupted */ -#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ -#define ELIBEXEC 83 /* Cannot exec a shared library directly */ -#define EILSEQ 84 /* Illegal byte sequence */ -#define ERESTART 85 /* Interrupted system call should be restarted */ -#define ESTRPIPE 86 /* Streams pipe error */ -#define EUSERS 87 /* Too many users */ -#define ENOTSOCK 88 /* Socket operation on non-socket */ -#define EDESTADDRREQ 89 /* Destination address required */ -#define EMSGSIZE 90 /* Message too long */ -#define EPROTOTYPE 91 /* Protocol wrong type for socket */ -#define ENOPROTOOPT 92 /* Protocol not available */ -#define EPROTONOSUPPORT 93 /* Protocol not supported */ -#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ -#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ -#define EPFNOSUPPORT 96 /* Protocol family not supported */ -#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ -#define EADDRINUSE 98 /* Address already in use */ -#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ -#define ENETDOWN 100 /* Network is down */ -#define ENETUNREACH 101 /* Network is unreachable */ -#define ENETRESET 102 /* Network dropped connection because of reset */ -#define ECONNABORTED 103 /* Software caused connection abort */ -#define ECONNRESET 104 /* Connection reset by peer */ -#define ENOBUFS 105 /* No buffer space available */ -#define EISCONN 106 /* Transport endpoint is already connected */ -#define ENOTCONN 107 /* Transport endpoint is not connected */ -#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ -#define ETOOMANYREFS 109 /* Too many references: cannot splice */ -#define ETIMEDOUT 110 /* Connection timed out */ -#define ECONNREFUSED 111 /* Connection refused */ -#define EHOSTDOWN 112 /* Host is down */ -#define EHOSTUNREACH 113 /* No route to host */ -#define EALREADY 114 /* Operation already in progress */ -#define EINPROGRESS 115 /* Operation now in progress */ -#define ESTALE 116 /* Stale file handle */ -#define EUCLEAN 117 /* Structure needs cleaning */ -#define ENOTNAM 118 /* Not a XENIX named type file */ -#define ENAVAIL 119 /* No XENIX semaphores available */ -#define EISNAM 120 /* Is a named type file */ -#define EREMOTEIO 121 /* Remote I/O error */ -#define EDQUOT 122 /* Quota exceeded */ -#define ENOMEDIUM 123 /* No medium found */ -#define EMEDIUMTYPE 124 /* Wrong medium type */ -#define ECANCELED 125 /* Operation canceled */ -#define ENOKEY 126 /* Required key not available */ -#define EKEYEXPIRED 127 /* Key has expired */ -#define EKEYREVOKED 128 /* Key has been revoked */ -#define EKEYREJECTED 129 /* Key was rejected by service */ -#define EOWNERDEAD 130 /* Owner died */ -#define ENOTRECOVERABLE 131 /* State not recoverable */ -#define ERFKILL 132 /* Operation not possible due to RF-kill */ -#define EHWPOISON 133 /* Memory page has hardware error */ +/* errno, __errno_location(), and the E* table live in the public header. */ +#include #endif /* VLIBC_INTERNAL_ERRNO_H */ diff --git a/tests/test_strerror.c b/tests/test_strerror.c new file mode 100644 index 0000000..828f13f --- /dev/null +++ b/tests/test_strerror.c @@ -0,0 +1,145 @@ +/* + * vlibc — strerror/strerror_r test (todo 2). + * + * Exercises the public end to end: + * + * 1. strerror(0) is "Success"; known values return the exact message and + * are non-NULL and distinct; aliases (EWOULDBLOCK/EDEADLOCK) resolve to + * their canonical entries; the last table value (EHWPOISON) is covered. + * 2. Unknown errno values never return NULL and never crash: 9999 and the + * ABI-gap value 41 format as "Unknown error ", negatives too. + * 3. strerror_r (XSI): returns 0 and fills buf with the right text; an + * unknown value still returns 0 with "Unknown error "; truncation + * into a tiny buf stays NUL-terminated; buflen 0 writes nothing; a NULL + * buf with buflen > 0 returns EINVAL. + * + * The test deliberately never touches errno itself: under the host libc the + * TCB slot our errno macro addresses is glibc's private TLS state, and + * writing it would corrupt the host (see tests/syscall_test.c). All coverage + * here is through the errnum parameters. + * + * Not part of the library proper; compiled manually for this todo (the + * tests/ + make check wiring is owned by a later todo). + */ + +#include +#include +#include + +static int failures; + +static void +check(int cond, const char *what) +{ + if (!cond) + { + printf("FAIL: %s\n", what); + failures++; + } +} + +/* + * Failure scenario (-f): an unknown errno must yield "Unknown error 9999", + * non-NULL, without crashing. Prints the observed string; exits 0 only when + * the failure behaved exactly as specified. + */ +static int +failure_scenario(void) +{ + const char *s = strerror(9999); + + if (s == NULL) + { + printf("strerror(9999)=NULL\n"); + return 1; + } + printf("strerror(9999)=%s\n", s); + return strcmp(s, "Unknown error 9999") == 0 ? 0 : 1; +} + +int +main(int argc, char **argv) +{ + const char *a; + const char *b; + char buf[64]; + char tiny[8]; + char untouched[4]; + int r; + + if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') + { + return failure_scenario(); + } + + /* 1. Known values: exact text, non-NULL, distinct. */ + check(strcmp(strerror(0), "Success") == 0, "strerror(0) == \"Success\""); + check(strcmp(strerror(2), "No such file or directory") == 0, + "strerror(2) == \"No such file or directory\""); + check(strcmp(strerror(EINVAL), "Invalid argument") == 0, + "strerror(EINVAL) == \"Invalid argument\""); + check(strcmp(strerror(EPERM), "Operation not permitted") == 0, + "strerror(EPERM) == \"Operation not permitted\""); + + a = strerror(EINVAL); + b = strerror(EPERM); + check(a != NULL && b != NULL, "known values non-NULL"); + check(a != b, "known values distinct"); + + /* Aliases resolve to their canonical entries. */ + check(strerror(EWOULDBLOCK) == strerror(EAGAIN), "EWOULDBLOCK == EAGAIN"); + check(strerror(EDEADLOCK) == strerror(EDEADLK), "EDEADLOCK == EDEADLK"); + + /* Table ends: last defined value and ABI gap values. */ + check(strerror(EHWPOISON) != NULL, "EHWPOISON (last value) non-NULL"); + check(strcmp(strerror(41), "Unknown error 41") == 0, "ABI gap 41 unknown"); + check(strcmp(strerror(58), "Unknown error 58") == 0, "ABI gap 58 unknown"); + + /* 2. Unknown values: never NULL, never crash, formatted. */ + a = strerror(9999); + check(a != NULL, "strerror(9999) non-NULL"); + check(strcmp(a, "Unknown error 9999") == 0, "strerror(9999) text"); + + a = strerror(-7); + check(a != NULL, "strerror(-7) non-NULL"); + check(strcmp(a, "Unknown error -7") == 0, "strerror(-7) text"); + + /* 3. strerror_r: XSI int return, exact fill. */ + r = strerror_r(EDOM, buf, sizeof buf); + check(r == 0, "strerror_r(EDOM) returns 0"); + check(strcmp(buf, "Numerical argument out of domain") == 0, "strerror_r(EDOM) fills buf"); + + /* Unknown errno still succeeds and formats into buf (XSI). */ + r = strerror_r(9999, buf, sizeof buf); + check(r == 0, "strerror_r(9999) returns 0 (XSI)"); + check(strcmp(buf, "Unknown error 9999") == 0, "strerror_r(9999) text"); + + /* Truncation into a tiny buf stays NUL-terminated. */ + r = strerror_r(ENAMETOOLONG, tiny, sizeof tiny); + check(r == 0, "strerror_r(ENAMETOOLONG, tiny) returns 0"); + check(tiny[sizeof tiny - 1] == '\0', "tiny buf NUL-terminated"); + check(strcmp(tiny, "File na") == 0, "tiny buf truncated prefix"); + + /* buflen 0: nothing written, still success. */ + untouched[0] = 'X'; + untouched[1] = 'X'; + untouched[2] = 'X'; + untouched[3] = '\0'; + r = strerror_r(EDOM, untouched, 0); + check(r == 0, "strerror_r(EDOM, buf, 0) returns 0"); + check(untouched[0] == 'X', "buflen 0 writes nothing"); + + /* NULL buf with buflen > 0: EINVAL. */ + r = strerror_r(EDOM, NULL, 16); + check(r == EINVAL, "strerror_r(NULL buf) returns EINVAL"); + + if (failures > 0) + { + printf("FAILED\n"); + } + else + { + printf("strerror ok\n"); + } + return failures == 0 ? 0 : 1; +}