Reduce strerror(3)'s impact on .data.rel.ro.

Test: tests pass
Change-Id: I60b15dfac6ca7dce45f4858ad10b8255e2f1b66d
diff --git a/libc/bionic/strerror.cpp b/libc/bionic/strerror.cpp
index 99692ca..38d8191 100644
--- a/libc/bionic/strerror.cpp
+++ b/libc/bionic/strerror.cpp
@@ -26,11 +26,188 @@
  * SUCH DAMAGE.
  */
 
+// G++ automatically defines _GNU_SOURCE, which then means that <string.h>
+// gives us the GNU variant.
+#undef _GNU_SOURCE
+
+#include <string.h>
+
+#include <errno.h>
+#include <limits.h>
+
+#include <async_safe/log.h>
+
+#include "private/ErrnoRestorer.h"
+
 #include <string.h>
 
 #include "bionic/pthread_internal.h"
 
-extern "C" const char* __strerror_lookup(int);
+static const char* __sys_error_strings[] = {
+    [0] = "Success",
+    [EPERM] = "Operation not permitted",
+    [ENOENT] = "No such file or directory",
+    [ESRCH] = "No such process",
+    [EINTR] = "Interrupted system call",
+    [EIO] = "I/O 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] = "Try again",
+    [ENOMEM] = "Out of memory",
+    [EACCES] = "Permission denied",
+    [EFAULT] = "Bad address",
+    [ENOTBLK] = "Block device required",
+    [EBUSY] = "Device or resource busy",
+    [EEXIST] = "File exists",
+    [EXDEV] = "Cross-device link",
+    [ENODEV] = "No such device",
+    [ENOTDIR] = "Not a directory",
+    [EISDIR] = "Is a directory",
+    [EINVAL] = "Invalid argument",
+    [ENFILE] = "File table overflow",
+    [EMFILE] = "Too many open files",
+    [ENOTTY] = "Not a typewriter",
+    [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] = "Math argument out of domain of func",
+    [ERANGE] = "Math result not representable",
+    [EDEADLK] = "Resource deadlock would occur",
+    [ENAMETOOLONG] = "File name too long",
+    [ENOLCK] = "No record locks available",
+    [ENOSYS] = "Function not implemented",
+    [ENOTEMPTY] = "Directory not empty",
+    [ELOOP] = "Too many symbolic links encountered",
+    [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] = "Not a data 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] = "Illegal byte sequence",
+    [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 on transport endpoint",
+    [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 because of 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 NFS 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] = "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",
+};
+
+static inline const char* __strerror_lookup(int error_number) {
+  if (error_number < 0 || error_number >= static_cast<int>(arraysize(__sys_error_strings))) {
+    return nullptr;
+  }
+  return __sys_error_strings[error_number];
+}
+
+int strerror_r(int error_number, char* buf, size_t buf_len) {
+  ErrnoRestorer errno_restorer;
+  size_t length;
+
+  const char* error_name = __strerror_lookup(error_number);
+  if (error_name != nullptr) {
+    length = strlcpy(buf, error_name, buf_len);
+  } else {
+    length = async_safe_format_buffer(buf, buf_len, "Unknown error %d", error_number);
+  }
+  if (length >= buf_len) {
+    errno_restorer.override(ERANGE);
+    return -1;
+  }
+
+  return 0;
+}
+
+extern "C" char* __gnu_strerror_r(int error_number, char* buf, size_t buf_len) {
+  ErrnoRestorer errno_restorer; // The glibc strerror_r doesn't set errno if it truncates...
+  strerror_r(error_number, buf, buf_len);
+  return buf; // ...and just returns whatever fit.
+}
 
 char* strerror(int error_number) {
   // Just return the original constant in the easy cases.