about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2013-11-19 13:39:56 +0000
committerJoseph Myers <joseph@codesourcery.com>2013-11-19 13:39:56 +0000
commit73c1ce4fdbdf117b4d91b6e894686228155bd702 (patch)
treea996f6b56852c7fc2a39dec8d5a41456365990cf
parentb5449b12962da665c29f0a1c85f705de4bc4abf2 (diff)
downloadglibc-73c1ce4fdbdf117b4d91b6e894686228155bd702.tar.gz
glibc-73c1ce4fdbdf117b4d91b6e894686228155bd702.tar.xz
glibc-73c1ce4fdbdf117b4d91b6e894686228155bd702.zip
Make powerpc-nofpu floating-point state thread-local (bug 15483).
-rw-r--r--ChangeLog67
-rw-r--r--NEWS17
-rw-r--r--math/Makefile3
-rw-r--r--math/test-fenv-tls.c208
-rw-r--r--sysdeps/powerpc/nofpu/Versions5
-rw-r--r--sysdeps/powerpc/nofpu/fclrexcpt.c3
-rw-r--r--sysdeps/powerpc/nofpu/fedisblxcpt.c6
-rw-r--r--sysdeps/powerpc/nofpu/feenablxcpt.c9
-rw-r--r--sysdeps/powerpc/nofpu/fegetenv.c10
-rw-r--r--sysdeps/powerpc/nofpu/fegetexcept.c2
-rw-r--r--sysdeps/powerpc/nofpu/fegetround.c2
-rw-r--r--sysdeps/powerpc/nofpu/fesetenv.c10
-rw-r--r--sysdeps/powerpc/nofpu/fesetround.c3
-rw-r--r--sysdeps/powerpc/nofpu/feupdateenv.c7
-rw-r--r--sysdeps/powerpc/nofpu/fgetexcptflg.c2
-rw-r--r--sysdeps/powerpc/nofpu/fraiseexcpt.c5
-rw-r--r--sysdeps/powerpc/nofpu/fsetexcptflg.c4
-rw-r--r--sysdeps/powerpc/nofpu/ftestexcept.c2
-rw-r--r--sysdeps/powerpc/nofpu/get-rounding-mode.h2
-rw-r--r--sysdeps/powerpc/nofpu/sim-full.c39
-rw-r--r--sysdeps/powerpc/nofpu/soft-supp.h37
-rw-r--r--sysdeps/powerpc/soft-fp/sfp-machine.h23
22 files changed, 390 insertions, 76 deletions
diff --git a/ChangeLog b/ChangeLog
index 9568016864..0f032197aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,70 @@
+2013-11-19  Joseph Myers  <joseph@codesourcery.com>
+
+	[BZ #15483]
+	* sysdeps/powerpc/nofpu/sim-full.c (__sim_exceptions): Change to
+	thread-local __sim_exceptions_thread and global
+	__sim_exceptions_global.
+	(__sim_disabled_exceptions): Change to thread-local
+	__sim_disabled_exceptions_thread and global
+	__sim_disabled_exceptions_global.
+	(__sim_round_mode): Change to thread-local __sim_round_mode_thread
+	and global __sim_round_mode_global.
+	(__simulate_exceptions): Use thread-local floating-point state and
+	set global state from it as needed.
+	* sysdeps/powerpc/nofpu/Versions (GLIBC_PRIVATE): Add
+	__sim_exceptions_thread, __sim_disabled_exceptions_thread and
+	__sim_round_mode_thread.
+	* sysdeps/powerpc/nofpu/soft-supp.h: Include <shlib-compat.h>.
+	(__sim_exceptions): Change to thread-local __sim_exceptions_thread
+	and global __sim_exceptions_global.
+	(__sim_disabled_exceptions): Change to thread-local
+	__sim_disabled_exceptions_thread and global
+	__sim_disabled_exceptions_global.
+	(__sim_round_mode): Change to thread-local __sim_round_mode_thread
+	and global __sim_round_mode_global.
+	[SIM_GLOBAL_COMPAT] (SIM_COMPAT_SYMBOL): New macro.
+	(SIM_SET_GLOBAL): Likewise.
+	* sysdeps/powerpc/soft-fp/sfp-machine.h
+	[!(__NO_FPRS__ && !_SOFT_FLOAT)] (FP_ROUNDMODE): Use
+	__sim_round_mode_thread.
+	[!(__NO_FPRS__ && !_SOFT_FLOAT)] (FP_TRAPPING_EXCEPTIONS): Use
+	__sim_disabled_exceptions_thread.
+	(__sim_exceptions): Change to __sim_exceptions_thread.
+	(__sim_disabled_exceptions): Change to
+	__sim_disabled_exceptions_thread.
+	(__sim_round_mode): Change to __sim_round_mode_thread.
+	* sysdeps/powerpc/nofpu/fclrexcpt.c (__feclearexcept): Use
+	thread-local floating-point state and set global state from it as
+	needed.
+	* sysdeps/powerpc/nofpu/fedisblxcpt.c (fedisableexcept): Likewise.
+	* sysdeps/powerpc/nofpu/feenablxcpt.c: Include "soft-supp.h".
+	(__sim_disabled_exceptions): Remove extern declaration.
+	(feenableexcept): Use thread-local floating-point state and set
+	global state from it as needed.
+	* sysdeps/powerpc/nofpu/fegetenv.c (__sim_exceptions): Remove
+	extern declaration.
+	(__sim_disabled_exceptions): Likewise.
+	(__sim_round_mode): Likewise.
+	(__fegetenv): Use thread-local floating-point state.
+	* sysdeps/powerpc/nofpu/fegetexcept.c (fegetexcept): Likewise.
+	* sysdeps/powerpc/nofpu/fegetround.c (fegetround): Likewise.
+	* sysdeps/powerpc/nofpu/fesetenv.c (__fesetenv): Use thread-local
+	floating-point state and set global state from it as needed.
+	* sysdeps/powerpc/nofpu/fesetround.c (fesetround): Likewise.
+	* sysdeps/powerpc/nofpu/feupdateenv.c (__feupdateenv): Likewise.
+	* sysdeps/powerpc/nofpu/fgetexcptflg.c (__fegetexceptflag):
+	Likewise.
+	* sysdeps/powerpc/nofpu/fraiseexcpt.c (__feraiseexcept): Likewise.
+	* sysdeps/powerpc/nofpu/fsetexcptflg.c (__fesetexceptflag):
+	Likewise.
+	sysdeps/powerpc/nofpu/ftestexcept.c (fetestexcept): Likewise.
+	* sysdeps/powerpc/nofpu/get-rounding-mode.h (get_rounding_mode):
+	Use __sim_round_mode_thread.
+	* math/test-fenv-tls.c: New file.
+	* math/Makefile (tests): Add test-fenv-tls.
+	($(objpfx)test-fenv-tls): Depend on
+	$(common-objpfx)nptl/libpthread.so.
+
 2013-11-19  Andreas Schwab  <schwab@suse.de>
 
 	* locale/programs/locale.c (show_info): Decode wordarray elements.
diff --git a/NEWS b/NEWS
index 26fdfd47c8..0fdc535dd4 100644
--- a/NEWS
+++ b/NEWS
@@ -11,14 +11,15 @@ Version 2.19
 
   156, 387, 431, 832, 2801, 7003, 9954, 10253, 10278, 11087, 13028, 13982,
   13985, 14029, 14143, 14155, 14547, 14699, 14752, 14876, 14910, 15048,
-  15218, 15277, 15308, 15362, 15374, 15400, 15427, 15522, 15531, 15532,
-  15608, 15609, 15610, 15632, 15640, 15670, 15672, 15680, 15681, 15723,
-  15734, 15735, 15736, 15748, 15749, 15754, 15760, 15763, 15764, 15797,
-  15799, 15825, 15844, 15847, 15849, 15855, 15856, 15857, 15859, 15867,
-  15886, 15887, 15890, 15892, 15893, 15895, 15897, 15905, 15909, 15917,
-  15919, 15921, 15923, 15939, 15948, 15963, 15966, 15985, 15988, 15997,
-  16032, 16034, 16036, 16037, 16041, 16055, 16071, 16072, 16074, 16078,
-  16103, 16112, 16143, 16144, 16146, 16150, 16151, 16153, 16167, 16172.
+  15218, 15277, 15308, 15362, 15374, 15400, 15427, 15483, 15522, 15531,
+  15532, 15608, 15609, 15610, 15632, 15640, 15670, 15672, 15680, 15681,
+  15723, 15734, 15735, 15736, 15748, 15749, 15754, 15760, 15763, 15764,
+  15797, 15799, 15825, 15844, 15847, 15849, 15855, 15856, 15857, 15859,
+  15867, 15886, 15887, 15890, 15892, 15893, 15895, 15897, 15905, 15909,
+  15917, 15919, 15921, 15923, 15939, 15948, 15963, 15966, 15985, 15988,
+  15997, 16032, 16034, 16036, 16037, 16041, 16055, 16071, 16072, 16074,
+  16078, 16103, 16112, 16143, 16144, 16146, 16150, 16151, 16153, 16167,
+  16172.
 
 * CVE-2012-4412 The strcoll implementation caches indices and rules for
   large collation sequences to optimize multiple passes.  This cache
diff --git a/math/Makefile b/math/Makefile
index a9bd49baee..fcccab2051 100644
--- a/math/Makefile
+++ b/math/Makefile
@@ -90,7 +90,7 @@ tests = test-matherr test-fenv atest-exp atest-sincos atest-exp2 basic-test \
 	test-misc test-fpucw test-fpucw-ieee tst-definitions test-tgmath \
 	test-tgmath-ret bug-nextafter bug-nexttoward bug-tgmath1 \
 	test-tgmath-int test-tgmath2 test-powl tst-CMPLX tst-CMPLX2 test-snan \
-	$(tests-static)
+	test-fenv-tls $(tests-static)
 tests-static = test-fpucw-static test-fpucw-ieee-static
 # We do the `long double' tests only if this data type is available and
 # distinct from `double'.
@@ -232,3 +232,4 @@ gmp-objs = $(patsubst %,$(common-objpfx)stdlib/%.o,\
 $(objpfx)atest-exp: $(gmp-objs)
 $(objpfx)atest-sincos: $(gmp-objs)
 $(objpfx)atest-exp2: $(gmp-objs)
+$(objpfx)test-fenv-tls: $(common-objpfx)nptl/libpthread.so
diff --git a/math/test-fenv-tls.c b/math/test-fenv-tls.c
new file mode 100644
index 0000000000..879c9f9518
--- /dev/null
+++ b/math/test-fenv-tls.c
@@ -0,0 +1,208 @@
+/* Test floating-point environment is thread-local.
+   Copyright (C) 2013 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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <fenv.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#define TEST_ONE_RM(RM)						\
+  do								\
+    {								\
+      if (fesetround (RM) == 0)					\
+	{							\
+	  rm = fegetround ();					\
+	  if (rm != RM)						\
+	    {							\
+	      printf ("expected " #RM ", got %d\n", rm);	\
+	      ret = 1;						\
+	    }							\
+	}							\
+    }								\
+  while (0)
+
+static void *
+test_round (void *arg)
+{
+  intptr_t ret = 0;
+  for (int i = 0; i < 10000; i++)
+    {
+      int rm;
+#ifdef FE_DOWNWARD
+      TEST_ONE_RM (FE_DOWNWARD);
+#endif
+#ifdef FE_TONEAREST
+      TEST_ONE_RM (FE_TONEAREST);
+#endif
+#ifdef FE_TOWARDZERO
+      TEST_ONE_RM (FE_TOWARDZERO);
+#endif
+#ifdef FE_UPWARD
+      TEST_ONE_RM (FE_UPWARD);
+#endif
+    }
+  return (void *) ret;
+}
+
+#define TEST_ONE_RAISE(EX)				\
+  do							\
+    {							\
+      if (feraiseexcept (EX) == 0)			\
+	if (fetestexcept (EX) != EX)			\
+	  {						\
+	    printf (#EX " not raised\n");		\
+	    ret = 1;					\
+	  }						\
+      if (feclearexcept (FE_ALL_EXCEPT) == 0)		\
+	if (fetestexcept (FE_ALL_EXCEPT) != 0)		\
+	  {						\
+	    printf ("exceptions not all cleared\n");	\
+	    ret = 1;					\
+	  }						\
+    }							\
+  while (0)
+
+static void *
+test_raise (void *arg)
+{
+  intptr_t ret = 0;
+  for (int i = 0; i < 10000; i++)
+    {
+#ifdef FE_DIVBYZERO
+      TEST_ONE_RAISE (FE_DIVBYZERO);
+#endif
+#ifdef FE_INEXACT
+      TEST_ONE_RAISE (FE_INEXACT);
+#endif
+#ifdef FE_INVALID
+      TEST_ONE_RAISE (FE_INVALID);
+#endif
+#ifdef FE_OVERFLOW
+      TEST_ONE_RAISE (FE_OVERFLOW);
+#endif
+#ifdef UNDERFLOW
+      TEST_ONE_RAISE (FE_UNDERFLOW);
+#endif
+    }
+  return (void *) ret;
+}
+
+#define TEST_ONE_ENABLE(EX)				\
+  do							\
+    {							\
+      if (feenableexcept (EX) != -1)			\
+	if (fegetexcept () != EX)			\
+	  {						\
+	    printf (#EX " not enabled\n");		\
+	    ret = 1;					\
+	  }						\
+      if (fedisableexcept (EX) != -1)			\
+	if (fegetexcept () != 0)			\
+	  {						\
+	    printf ("exceptions not all disabled\n");	\
+	    ret = 1;					\
+	  }						\
+    }							\
+  while (0)
+
+static void *
+test_enable (void *arg)
+{
+  intptr_t ret = 0;
+  for (int i = 0; i < 10000; i++)
+    {
+#ifdef FE_DIVBYZERO
+      TEST_ONE_ENABLE (FE_DIVBYZERO);
+#endif
+#ifdef FE_INEXACT
+      TEST_ONE_ENABLE (FE_INEXACT);
+#endif
+#ifdef FE_INVALID
+      TEST_ONE_ENABLE (FE_INVALID);
+#endif
+#ifdef FE_OVERFLOW
+      TEST_ONE_ENABLE (FE_OVERFLOW);
+#endif
+#ifdef UNDERFLOW
+      TEST_ONE_ENABLE (FE_UNDERFLOW);
+#endif
+    }
+  return (void *) ret;
+}
+
+static int
+do_test (void)
+{
+  int ret = 0;
+  void *vret;
+  pthread_t thread_id;
+  int pret;
+
+  pret = pthread_create (&thread_id, NULL, test_round, NULL);
+  if (pret != 0)
+    {
+      printf ("pthread_create failed: %d\n", pret);
+      return 1;
+    }
+  vret = test_round (NULL);
+  ret |= (intptr_t) vret;
+  pret = pthread_join (thread_id, &vret);
+  if (pret != 0)
+    {
+      printf ("pthread_join failed: %d\n", pret);
+      return 1;
+    }
+  ret |= (intptr_t) vret;
+
+  pret = pthread_create (&thread_id, NULL, test_raise, NULL);
+  if (pret != 0)
+    {
+      printf ("pthread_create failed: %d\n", pret);
+      return 1;
+    }
+  vret = test_raise (NULL);
+  ret |= (intptr_t) vret;
+  pret = pthread_join (thread_id, &vret);
+  if (pret != 0)
+    {
+      printf ("pthread_join failed: %d\n", pret);
+      return 1;
+    }
+  ret |= (intptr_t) vret;
+
+  pret = pthread_create (&thread_id, NULL, test_enable, NULL);
+  if (pret != 0)
+    {
+      printf ("pthread_create failed: %d\n", pret);
+      return 1;
+    }
+  vret = test_enable (NULL);
+  ret |= (intptr_t) vret;
+  pret = pthread_join (thread_id, &vret);
+  if (pret != 0)
+    {
+      printf ("pthread_join failed: %d\n", pret);
+      return 1;
+    }
+  ret |= (intptr_t) vret;
+
+  return ret;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/powerpc/nofpu/Versions b/sysdeps/powerpc/nofpu/Versions
index 1a29319d5a..8ba6021e9e 100644
--- a/sysdeps/powerpc/nofpu/Versions
+++ b/sysdeps/powerpc/nofpu/Versions
@@ -17,4 +17,9 @@ libc {
     __gtdf2; __gtsf2;
     __ltdf2; __ltsf2;
   }
+  GLIBC_PRIVATE {
+    __sim_exceptions_thread;
+    __sim_disabled_exceptions_thread;
+    __sim_round_mode_thread;
+  }
 }
diff --git a/sysdeps/powerpc/nofpu/fclrexcpt.c b/sysdeps/powerpc/nofpu/fclrexcpt.c
index fabda0ab98..da0b61a894 100644
--- a/sysdeps/powerpc/nofpu/fclrexcpt.c
+++ b/sysdeps/powerpc/nofpu/fclrexcpt.c
@@ -23,7 +23,8 @@
 int
 __feclearexcept (int x)
 {
-  __sim_exceptions &= ~x;
+  __sim_exceptions_thread &= ~x;
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
   return 0;
 }
 
diff --git a/sysdeps/powerpc/nofpu/fedisblxcpt.c b/sysdeps/powerpc/nofpu/fedisblxcpt.c
index e06c8f7676..00490fd6ed 100644
--- a/sysdeps/powerpc/nofpu/fedisblxcpt.c
+++ b/sysdeps/powerpc/nofpu/fedisblxcpt.c
@@ -24,9 +24,11 @@
 int
 fedisableexcept (int x)
 {
-  int old_exceptions = ~__sim_disabled_exceptions & FE_ALL_EXCEPT;
+  int old_exceptions = ~__sim_disabled_exceptions_thread & FE_ALL_EXCEPT;
 
-  __sim_disabled_exceptions |= x;
+  __sim_disabled_exceptions_thread |= x;
+  SIM_SET_GLOBAL (__sim_disabled_exceptions_global,
+		  __sim_disabled_exceptions_thread);
 
   return old_exceptions;
 }
diff --git a/sysdeps/powerpc/nofpu/feenablxcpt.c b/sysdeps/powerpc/nofpu/feenablxcpt.c
index 93249abf6c..09eb823b8b 100644
--- a/sysdeps/powerpc/nofpu/feenablxcpt.c
+++ b/sysdeps/powerpc/nofpu/feenablxcpt.c
@@ -17,16 +17,17 @@
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include "soft-supp.h"
 #include <fenv.h>
 
-extern int __sim_disabled_exceptions;
-
 int
 feenableexcept (int exceptions)
 {
-  int old_exceptions = ~__sim_disabled_exceptions & FE_ALL_EXCEPT;
+  int old_exceptions = ~__sim_disabled_exceptions_thread & FE_ALL_EXCEPT;
 
-  __sim_disabled_exceptions &= ~exceptions;
+  __sim_disabled_exceptions_thread &= ~exceptions;
+  SIM_SET_GLOBAL (__sim_disabled_exceptions_global,
+		  __sim_disabled_exceptions_thread);
 
   return old_exceptions;
 }
diff --git a/sysdeps/powerpc/nofpu/fegetenv.c b/sysdeps/powerpc/nofpu/fegetenv.c
index 51bcef30a1..351e5526c7 100644
--- a/sysdeps/powerpc/nofpu/fegetenv.c
+++ b/sysdeps/powerpc/nofpu/fegetenv.c
@@ -20,18 +20,14 @@
 #include "soft-fp.h"
 #include "soft-supp.h"
 
-extern int __sim_exceptions;
-extern int __sim_disabled_exceptions;
-extern int __sim_round_mode;
-
 int
 __fegetenv (fenv_t *envp)
 {
   fenv_union_t u;
 
-  u.l[0] = __sim_exceptions;
-  u.l[0] |= __sim_round_mode;
-  u.l[1] = __sim_disabled_exceptions;
+  u.l[0] = __sim_exceptions_thread;
+  u.l[0] |= __sim_round_mode_thread;
+  u.l[1] = __sim_disabled_exceptions_thread;
 
   *envp = u.fenv;
 
diff --git a/sysdeps/powerpc/nofpu/fegetexcept.c b/sysdeps/powerpc/nofpu/fegetexcept.c
index ea39a82b73..d907555fb6 100644
--- a/sysdeps/powerpc/nofpu/fegetexcept.c
+++ b/sysdeps/powerpc/nofpu/fegetexcept.c
@@ -23,5 +23,5 @@
 int
 fegetexcept (void)
 {
-  return (__sim_disabled_exceptions ^ FE_ALL_EXCEPT) & FE_ALL_EXCEPT;
+  return (__sim_disabled_exceptions_thread ^ FE_ALL_EXCEPT) & FE_ALL_EXCEPT;
 }
diff --git a/sysdeps/powerpc/nofpu/fegetround.c b/sysdeps/powerpc/nofpu/fegetround.c
index c232ae3794..016602fac6 100644
--- a/sysdeps/powerpc/nofpu/fegetround.c
+++ b/sysdeps/powerpc/nofpu/fegetround.c
@@ -24,5 +24,5 @@
 int
 fegetround (void)
 {
-  return __sim_round_mode;
+  return __sim_round_mode_thread;
 }
diff --git a/sysdeps/powerpc/nofpu/fesetenv.c b/sysdeps/powerpc/nofpu/fesetenv.c
index 3f35909b6d..fa84169836 100644
--- a/sysdeps/powerpc/nofpu/fesetenv.c
+++ b/sysdeps/powerpc/nofpu/fesetenv.c
@@ -26,9 +26,13 @@ __fesetenv (const fenv_t *envp)
   fenv_union_t u;
 
   u.fenv = *envp;
-  __sim_exceptions = u.l[0] & FE_ALL_EXCEPT;
-  __sim_round_mode = u.l[0] & 0x3;
-  __sim_disabled_exceptions = u.l[1];
+  __sim_exceptions_thread = u.l[0] & FE_ALL_EXCEPT;
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
+  __sim_round_mode_thread = u.l[0] & 0x3;
+  SIM_SET_GLOBAL (__sim_round_mode_global, __sim_round_mode_thread);
+  __sim_disabled_exceptions_thread = u.l[1];
+  SIM_SET_GLOBAL (__sim_disabled_exceptions_global,
+		  __sim_disabled_exceptions_thread);
   return 0;
 }
 
diff --git a/sysdeps/powerpc/nofpu/fesetround.c b/sysdeps/powerpc/nofpu/fesetround.c
index 028c1300cc..ab0d52f237 100644
--- a/sysdeps/powerpc/nofpu/fesetround.c
+++ b/sysdeps/powerpc/nofpu/fesetround.c
@@ -26,7 +26,8 @@ fesetround (int round)
   if ((unsigned int) round > FE_DOWNWARD)
     return 1;
 
-  __sim_round_mode = round;
+  __sim_round_mode_thread = round;
+  SIM_SET_GLOBAL (__sim_round_mode_global, __sim_round_mode_thread);
 
   return 0;
 }
diff --git a/sysdeps/powerpc/nofpu/feupdateenv.c b/sysdeps/powerpc/nofpu/feupdateenv.c
index 163f673102..8a26cb86d1 100644
--- a/sysdeps/powerpc/nofpu/feupdateenv.c
+++ b/sysdeps/powerpc/nofpu/feupdateenv.c
@@ -28,14 +28,15 @@ __feupdateenv (const fenv_t *envp)
   int saved_exceptions;
 
   /* Save currently set exceptions.  */
-  saved_exceptions = __sim_exceptions;
+  saved_exceptions = __sim_exceptions_thread;
 
   /* Set environment.  */
   fesetenv (envp);
 
   /* Raise old exceptions.  */
-  __sim_exceptions |= saved_exceptions;
-  if (saved_exceptions & ~__sim_disabled_exceptions)
+  __sim_exceptions_thread |= saved_exceptions;
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
+  if (saved_exceptions & ~__sim_disabled_exceptions_thread)
     raise (SIGFPE);
 
   return 0;
diff --git a/sysdeps/powerpc/nofpu/fgetexcptflg.c b/sysdeps/powerpc/nofpu/fgetexcptflg.c
index 2373fa4002..b7fd90d71e 100644
--- a/sysdeps/powerpc/nofpu/fgetexcptflg.c
+++ b/sysdeps/powerpc/nofpu/fgetexcptflg.c
@@ -23,7 +23,7 @@
 int
 __fegetexceptflag (fexcept_t *flagp, int excepts)
 {
-  *flagp = (fexcept_t) __sim_exceptions  & excepts & FE_ALL_EXCEPT;
+  *flagp = (fexcept_t) __sim_exceptions_thread & excepts & FE_ALL_EXCEPT;
 
   return 0;
 }
diff --git a/sysdeps/powerpc/nofpu/fraiseexcpt.c b/sysdeps/powerpc/nofpu/fraiseexcpt.c
index cd142b60be..215a70b4bf 100644
--- a/sysdeps/powerpc/nofpu/fraiseexcpt.c
+++ b/sysdeps/powerpc/nofpu/fraiseexcpt.c
@@ -25,8 +25,9 @@
 int
 __feraiseexcept (int x)
 {
-  __sim_exceptions |= x;
-  if (x & ~__sim_disabled_exceptions)
+  __sim_exceptions_thread |= x;
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
+  if (x & ~__sim_disabled_exceptions_thread)
     raise (SIGFPE);
   return 0;
 }
diff --git a/sysdeps/powerpc/nofpu/fsetexcptflg.c b/sysdeps/powerpc/nofpu/fsetexcptflg.c
index 3dc368fdda..ee2aa81a4e 100644
--- a/sysdeps/powerpc/nofpu/fsetexcptflg.c
+++ b/sysdeps/powerpc/nofpu/fsetexcptflg.c
@@ -24,7 +24,9 @@ int
 __fesetexceptflag(const fexcept_t *flagp, int excepts)
 {
   /* Ignore exceptions not listed in 'excepts'.  */
-  __sim_exceptions = (__sim_exceptions & ~excepts) | (*flagp & excepts);
+  __sim_exceptions_thread
+    = (__sim_exceptions_thread & ~excepts) | (*flagp & excepts);
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
 
   return 0;
 }
diff --git a/sysdeps/powerpc/nofpu/ftestexcept.c b/sysdeps/powerpc/nofpu/ftestexcept.c
index f5d01e881c..42e861da33 100644
--- a/sysdeps/powerpc/nofpu/ftestexcept.c
+++ b/sysdeps/powerpc/nofpu/ftestexcept.c
@@ -23,6 +23,6 @@
 int
 fetestexcept (int x)
 {
-  return __sim_exceptions & x;
+  return __sim_exceptions_thread & x;
 }
 libm_hidden_def (fetestexcept)
diff --git a/sysdeps/powerpc/nofpu/get-rounding-mode.h b/sysdeps/powerpc/nofpu/get-rounding-mode.h
index 20eb81030f..6d327f57c7 100644
--- a/sysdeps/powerpc/nofpu/get-rounding-mode.h
+++ b/sysdeps/powerpc/nofpu/get-rounding-mode.h
@@ -29,7 +29,7 @@
 static inline int
 get_rounding_mode (void)
 {
-  return __sim_round_mode;
+  return __sim_round_mode_thread;
 }
 
 #endif /* get-rounding-mode.h */
diff --git a/sysdeps/powerpc/nofpu/sim-full.c b/sysdeps/powerpc/nofpu/sim-full.c
index e16703323d..fb09d1bc9d 100644
--- a/sysdeps/powerpc/nofpu/sim-full.c
+++ b/sysdeps/powerpc/nofpu/sim-full.c
@@ -21,26 +21,37 @@
 #include "soft-fp.h"
 #include "soft-supp.h"
 
-/* FIXME: these variables should be thread specific (see bugzilla bug
-   15483) and ideally preserved across signal handlers, like hardware
-   FP status words, but the latter is quite difficult to accomplish in
-   userland.  */
-
-/* Global to store sticky exceptions.  */
-int __sim_exceptions __attribute__ ((nocommon));
-libc_hidden_data_def (__sim_exceptions);
+/* Thread-local to store sticky exceptions.  */
+__thread int __sim_exceptions_thread __attribute__ ((nocommon));
+libc_hidden_data_def (__sim_exceptions_thread);
 
 /* By default, no exceptions should trap.  */
-int __sim_disabled_exceptions = 0xffffffff;
-libc_hidden_data_def (__sim_disabled_exceptions);
+__thread int __sim_disabled_exceptions_thread = 0xffffffff;
+libc_hidden_data_def (__sim_disabled_exceptions_thread);
+
+__thread int __sim_round_mode_thread __attribute__ ((nocommon));
+libc_hidden_data_def (__sim_round_mode_thread);
+
+#if SIM_GLOBAL_COMPAT
+int __sim_exceptions_global __attribute__ ((nocommon));
+libc_hidden_data_def (__sim_exceptions_global);
+SIM_COMPAT_SYMBOL (__sim_exceptions_global, __sim_exceptions);
+
+int __sim_disabled_exceptions_global = 0xffffffff;
+libc_hidden_data_def (__sim_disabled_exceptions_global);
+SIM_COMPAT_SYMBOL (__sim_disabled_exceptions_global,
+		   __sim_disabled_exceptions);
 
-int __sim_round_mode __attribute__ ((nocommon));
-libc_hidden_data_def (__sim_round_mode);
+int __sim_round_mode_global __attribute__ ((nocommon));
+libc_hidden_data_def (__sim_round_mode_global);
+SIM_COMPAT_SYMBOL (__sim_round_mode_global, __sim_round_mode);
+#endif
 
 void
 __simulate_exceptions (int x)
 {
-  __sim_exceptions |= x;
-  if (x & ~__sim_disabled_exceptions)
+  __sim_exceptions_thread |= x;
+  SIM_SET_GLOBAL (__sim_exceptions_global, __sim_exceptions_thread);
+  if (x & ~__sim_disabled_exceptions_thread)
     raise (SIGFPE);
 }
diff --git a/sysdeps/powerpc/nofpu/soft-supp.h b/sysdeps/powerpc/nofpu/soft-supp.h
index 18b4550e3b..0a0614aa6a 100644
--- a/sysdeps/powerpc/nofpu/soft-supp.h
+++ b/sysdeps/powerpc/nofpu/soft-supp.h
@@ -33,16 +33,31 @@ typedef union
 
 #endif
 
-/* FIXME: these variables should be thread specific (see bugzilla bug
-   15483) and ideally preserved across signal handlers, like hardware
-   FP status words, but the latter is quite difficult to accomplish in
-   userland.  */
-
-extern int __sim_exceptions;
-libc_hidden_proto (__sim_exceptions);
-extern int __sim_disabled_exceptions;
-libc_hidden_proto (__sim_disabled_exceptions);
-extern int __sim_round_mode;
-libc_hidden_proto (__sim_round_mode);
+extern __thread int __sim_exceptions_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_exceptions_thread, tls_model ("initial-exec"));
+extern __thread int __sim_disabled_exceptions_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_disabled_exceptions_thread,
+		       tls_model ("initial-exec"));
+extern __thread int __sim_round_mode_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_round_mode_thread, tls_model ("initial-exec"));
+
+/* These variables were formerly global, so there are compat symbols
+   for global versions as well.  */
+
+#include <shlib-compat.h>
+#define SIM_GLOBAL_COMPAT SHLIB_COMPAT (libc, GLIBC_2_3_2, GLIBC_2_19)
+#if SIM_GLOBAL_COMPAT
+extern int __sim_exceptions_global;
+libc_hidden_proto (__sim_exceptions_global);
+extern int __sim_disabled_exceptions_global ;
+libc_hidden_proto (__sim_disabled_exceptions_global);
+extern int __sim_round_mode_global;
+libc_hidden_proto (__sim_round_mode_global);
+# define SIM_COMPAT_SYMBOL(GLOBAL_NAME, NAME) \
+  compat_symbol (libc, GLOBAL_NAME, NAME, GLIBC_2_3_2)
+# define SIM_SET_GLOBAL(GLOBAL_VAR, THREAD_VAR) ((GLOBAL_VAR) = (THREAD_VAR))
+#else
+# define SIM_SET_GLOBAL(GLOBAL_VAR, THREAD_VAR) ((void) 0)
+#endif
 
 extern void __simulate_exceptions (int x) attribute_hidden;
diff --git a/sysdeps/powerpc/soft-fp/sfp-machine.h b/sysdeps/powerpc/soft-fp/sfp-machine.h
index 0411878071..35a38b0031 100644
--- a/sysdeps/powerpc/soft-fp/sfp-machine.h
+++ b/sysdeps/powerpc/soft-fp/sfp-machine.h
@@ -95,21 +95,18 @@ libc_hidden_proto (__feraiseexcept_soft)
 # define FP_EX_INEXACT         (1 << (31 - 6))
 
 # define FP_HANDLE_EXCEPTIONS  __simulate_exceptions (_fex)
-# define FP_ROUNDMODE          __sim_round_mode
-# define FP_TRAPPING_EXCEPTIONS (~__sim_disabled_exceptions & 0x3e000000)
+# define FP_ROUNDMODE          __sim_round_mode_thread
+# define FP_TRAPPING_EXCEPTIONS \
+  (~__sim_disabled_exceptions_thread & 0x3e000000)
 
 #endif
 
-/* FIXME: these variables should be thread specific (see bugzilla bug
-   15483) and ideally preserved across signal handlers, like hardware
-   FP status words, but the latter is quite difficult to accomplish in
-   userland.  */
-
-extern int __sim_exceptions;
-libc_hidden_proto (__sim_exceptions);
-extern int __sim_disabled_exceptions;
-libc_hidden_proto (__sim_disabled_exceptions);
-extern int __sim_round_mode;
-libc_hidden_proto (__sim_round_mode);
+extern __thread int __sim_exceptions_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_exceptions_thread, tls_model ("initial-exec"));
+extern __thread int __sim_disabled_exceptions_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_disabled_exceptions_thread,
+		       tls_model ("initial-exec"));
+extern __thread int __sim_round_mode_thread attribute_tls_model_ie;
+libc_hidden_tls_proto (__sim_round_mode_thread, tls_model ("initial-exec"));
 
 extern void __simulate_exceptions (int x) attribute_hidden;