diff --git a/manual/signal.texi b/manual/signal.texi
index 33e6646975..08f0edb306 100644
--- a/manual/signal.texi
+++ b/manual/signal.texi
@@ -830,8 +830,8 @@ termination status of a child process (@pxref{Process Completion}) or it
 may come from a signal handler in the same process.
 
 @deftypefun {char *} strsignal (int @var{signum})
-@standards{GNU, string.h}
-@safety{@prelim{}@mtunsafe{@mtasurace{:strsignal} @mtslocale{}}@asunsafe{@asuinit{} @ascuintl{} @asucorrupt{} @ascuheap{}}@acunsafe{@acuinit{} @acucorrupt{} @acsmem{}}}
+@standards{POSIX-1.2008, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 @c strsignal @mtasurace:strsignal @mtslocale @asuinit @ascuintl @asucorrupt @ascuheap @acucorrupt @acsmem
 @c   uses a static buffer if tsd key creation fails
 @c  [once] init
@@ -852,7 +852,7 @@ rewritten on subsequent calls, you should save a copy of it if you need
 to reference it later.
 
 @pindex string.h
-This function is a GNU extension, declared in the header file
+This function is declared in the header file
 @file{string.h}.
 @end deftypefun
 
diff --git a/nptl/descr.h b/nptl/descr.h
index e1c7db5473..ee8f4b0e37 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -34,6 +34,7 @@
 #include <unwind.h>
 #include <bits/types/res_state.h>
 #include <kernel-features.h>
+#include <tls_internal-struct.h>
 
 #ifndef TCB_ALIGNMENT
 # define TCB_ALIGNMENT	sizeof (double)
@@ -116,7 +117,6 @@ struct priority_protection_data
   unsigned int priomap[];
 };
 
-
 /* Thread descriptor data structure.  */
 struct pthread
 {
@@ -398,6 +398,9 @@ struct pthread
   /* Indicates whether is a C11 thread created by thrd_creat.  */
   bool c11;
 
+  /* Used on strsignal.  */
+  struct tls_internal_t tls_state;
+
   /* This member must be last.  */
   char end_padding[];
 
diff --git a/string/strsignal.c b/string/strsignal.c
index 1551480026..5fbc3d9a60 100644
--- a/string/strsignal.c
+++ b/string/strsignal.c
@@ -16,110 +16,55 @@
    <https://www.gnu.org/licenses/>.  */
 
 #include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <libintl.h>
-#include <libc-lock.h>
-
-static __libc_key_t key;
-
-/* If nonzero the key allocation failed and we should better use a
-   static buffer than fail.  */
-#define BUFFERSIZ	100
-static char local_buf[BUFFERSIZ];
-static char *static_buf;
+#include <stdlib.h>
+#include <array_length.h>
+#include <tls_internal.h>
 
-/* Destructor for the thread-specific data.  */
-static void init (void);
-static void free_key_mem (void *mem);
-static char *getbuffer (void);
+#define RT_STR  "Real-time signal "
+_Static_assert (sizeof (RT_STR) <= strsignal_str_len,
+		RT_STR " > strsignal_str_len");
 
+#define UNK_STR "Unknown signal "
+_Static_assert (sizeof (UNK_STR) <= strsignal_str_len,
+		UNK_STR " > strsignal_str_len");
 
 /* Return a string describing the meaning of the signal number SIGNUM.  */
 char *
 strsignal (int signum)
 {
-  __libc_once_define (static, once);
-  const char *desc;
+  const char *desc = NULL;
+
+  if (signum >= 0 && signum <= NSIG
+      && signum < array_length (__sys_siglist_internal))
+    desc = __sys_siglist_internal[signum];
 
-  /* If we have not yet initialized the buffer do it now.  */
-  __libc_once (once, init);
+  if (desc != NULL)
+    return (char *) desc;
 
-  if (
+  const char* prefix = UNK_STR;
 #ifdef SIGRTMIN
-      (signum >= SIGRTMIN && signum <= SIGRTMAX) ||
-#endif
-      signum < 0 || signum >= NSIG
-      || (desc = __sys_siglist_internal[signum]) == NULL)
+  if (signum >= SIGRTMIN && signum <= SIGRTMAX)
     {
-      char *buffer = getbuffer ();
-      int len;
-#ifdef SIGRTMIN
-      if (signum >= SIGRTMIN && signum <= SIGRTMAX)
-	len = __snprintf (buffer, BUFFERSIZ - 1, _("Real-time signal %d"),
-			  signum - SIGRTMIN);
-      else
-#endif
-	len = __snprintf (buffer, BUFFERSIZ - 1, _("Unknown signal %d"),
-			  signum);
-      if (len >= BUFFERSIZ)
-	buffer = NULL;
-      else
-	buffer[len] = '\0';
-
-      return buffer;
+      prefix = RT_STR;
+      signum -= SIGRTMIN;
     }
+#endif
 
-  return (char *) _(desc);
-}
-
-
-/* Initialize buffer.  */
-static void
-init (void)
-{
-  if (__libc_key_create (&key, free_key_mem))
-    /* Creating the key failed.  This means something really went
-       wrong.  In any case use a static buffer which is better than
-       nothing.  */
-    static_buf = local_buf;
-}
-
-
-/* Free the thread specific data, this is done if a thread terminates.  */
-static void
-free_key_mem (void *mem)
-{
-  free (mem);
-  __libc_setspecific (key, NULL);
-}
-
+  char *buffer = __glibc_tls_internal ()->strsignal_buf;
 
-/* Return the buffer to be used.  */
-static char *
-getbuffer (void)
-{
-  char *result;
+  char *q = __stpcpy (buffer, prefix);
 
-  if (static_buf != NULL)
-    result = static_buf;
-  else
+  if (signum < 0)
     {
-      /* We don't use the static buffer and so we have a key.  Use it
-	 to get the thread-specific buffer.  */
-      result = __libc_getspecific (key);
-      if (result == NULL)
-	{
-	  /* No buffer allocated so far.  */
-	  result = malloc (BUFFERSIZ);
-	  if (result == NULL)
-	    /* No more memory available.  We use the static buffer.  */
-	    result = local_buf;
-	  else
-	    __libc_setspecific (key, result);
-	}
+      *q++ = '-';
+      signum = abs (signum);
     }
+  for (int d = signum; q++, (d /= 10) != 0; )
+    continue;
+  *q = '\0';
+  for (int d = signum; *--q = '0' + d % 10, (d /= 10) != 0; )
+    continue;
 
-  return result;
+  return buffer;
 }
diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile
index bd4f9425ca..9bed5d7fa2 100644
--- a/sysdeps/generic/Makefile
+++ b/sysdeps/generic/Makefile
@@ -16,6 +16,7 @@
 # <https://www.gnu.org/licenses/>.
 
 ifeq ($(subdir),string)
+sysdep_routines += tls_internal
 CFLAGS-wordcopy.c += -Wno-uninitialized
 endif
 
diff --git a/sysdeps/generic/tls_internal-struct.h b/sysdeps/generic/tls_internal-struct.h
new file mode 100644
index 0000000000..dac967b2f3
--- /dev/null
+++ b/sysdeps/generic/tls_internal-struct.h
@@ -0,0 +1,32 @@
+/* Per-thread state.  Generic version.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _TLS_INTERNAL_STRUCT_H
+#define _TLS_INTERNAL_STRUCT_H 1
+
+#include <intprops.h>
+
+enum { strsignal_str_len = 20 };
+
+struct tls_internal_t
+{
+  /* Used on strsignal.c.  */
+  char strsignal_buf[strsignal_str_len + INT_STRLEN_BOUND (int) + 1];
+};
+
+#endif
diff --git a/sysdeps/generic/tls_internal.c b/sysdeps/generic/tls_internal.c
new file mode 100644
index 0000000000..e988a608b2
--- /dev/null
+++ b/sysdeps/generic/tls_internal.c
@@ -0,0 +1,3 @@
+#include <tls_internal.h>
+
+__thread struct tls_internal_t tls_internal;
diff --git a/sysdeps/generic/tls_internal.h b/sysdeps/generic/tls_internal.h
new file mode 100644
index 0000000000..6edc4e574c
--- /dev/null
+++ b/sysdeps/generic/tls_internal.h
@@ -0,0 +1,32 @@
+/* Per-thread state.  Generic version.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _TLS_INTERNAL_H
+#define _TLS_INTERNAL_H 1
+
+#include <tls_internal-struct.h>
+
+extern __thread struct tls_internal_t tls_internal attribute_hidden;
+
+static inline struct tls_internal_t *
+__glibc_tls_internal (void)
+{
+  return &tls_internal;
+}
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/tls_internal.c b/sysdeps/unix/sysv/linux/tls_internal.c
new file mode 100644
index 0000000000..6e25b021ab
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tls_internal.c
@@ -0,0 +1 @@
+/* Empty.  */
diff --git a/sysdeps/unix/sysv/linux/tls_internal.h b/sysdeps/unix/sysv/linux/tls_internal.h
new file mode 100644
index 0000000000..f1d3161e08
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tls_internal.h
@@ -0,0 +1,30 @@
+/* Per-thread state.  Linux version.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _TLS_INTERNAL_H
+#define _TLS_INTERNAL_H 1
+
+#include <nptl/pthreadP.h>
+
+static inline struct tls_internal_t *
+__glibc_tls_internal (void)
+{
+  return &THREAD_SELF->tls_state;
+}
+
+#endif
diff --git a/time/strftime_l.c b/time/strftime_l.c
index a9aae42191..83a62dbb6b 100644
--- a/time/strftime_l.c
+++ b/time/strftime_l.c
@@ -136,15 +136,7 @@ extern char *tzname[];
 # define NULL 0
 #endif
 
-#define TYPE_SIGNED(t) ((t) -1 < 0)
-
-/* Bound on length of the string representing an integer value of type t.
-   Subtract one for the sign bit if t is signed;
-   302 / 1000 is log10 (2) rounded up;
-   add one for integer division truncation;
-   add one more for a minus sign if t is signed.  */
-#define INT_STRLEN_BOUND(t) \
- ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
+#include <intprops.h>
 
 #define TM_YEAR_BASE 1900
 
