| diff -pruN glibc-2.17-c758a686/nptl/sysdeps/pthread/unwind-forcedunwind.c glibc-2.17-c758a686/nptl/sysdeps/pthread/unwind-forcedunwind.c |
| |
| |
| @@ -45,8 +45,10 @@ pthread_cancel_init (void) |
| |
| if (__builtin_expect (libgcc_s_handle != NULL, 1)) |
| { |
| - /* Force gcc to reload all values. */ |
| - asm volatile ("" ::: "memory"); |
| + /* Order reads so as to prevent speculation of loads |
| + of libgcc_s_{resume,personality,forcedunwind,getcfa} |
| + to points prior to the write barrier. */ |
| + atomic_read_barrier (); |
| return; |
| } |
| |
| @@ -72,9 +74,14 @@ pthread_cancel_init (void) |
| libgcc_s_forcedunwind = forcedunwind; |
| PTR_MANGLE (getcfa); |
| libgcc_s_getcfa = getcfa; |
| - /* Make sure libgcc_s_handle is written last. Otherwise, |
| - pthread_cancel_init might return early even when the pointer the |
| - caller is interested in is not initialized yet. */ |
| + /* At the point at which any thread writes the handle |
| + to libgcc_s_handle, the initialization is complete. |
| + The writing of libgcc_s_handle is atomic. All other |
| + threads reading libgcc_s_handle do so atomically. Any |
| + thread that does not execute this function must issue |
| + a read barrier to ensure that all of the above has |
| + actually completed and that the values of the |
| + function pointers are correct. */ |
| atomic_write_barrier (); |
| libgcc_s_handle = handle; |
| } |
| @@ -91,13 +98,19 @@ __unwind_freeres (void) |
| } |
| } |
| |
| -void |
| -_Unwind_Resume (struct _Unwind_Exception *exc) |
| +static __always_inline void |
| +_maybe_pthread_cancel_init (void) |
| { |
| if (__builtin_expect (libgcc_s_handle == NULL, 0)) |
| pthread_cancel_init (); |
| else |
| atomic_read_barrier (); |
| +} |
| + |
| +void |
| +_Unwind_Resume (struct _Unwind_Exception *exc) |
| +{ |
| + _maybe_pthread_cancel_init (); |
| |
| void (*resume) (struct _Unwind_Exception *exc) = libgcc_s_resume; |
| PTR_DEMANGLE (resume); |
| @@ -108,10 +123,7 @@ __gcc_personality_v0 (int version, _Unwi |
| struct _Unwind_Exception *ue_header, |
| struct _Unwind_Context *context) |
| { |
| - if (__builtin_expect (libgcc_s_handle == NULL, 0)) |
| - pthread_cancel_init (); |
| - else |
| - atomic_read_barrier (); |
| + _maybe_pthread_cancel_init (); |
| |
| _Unwind_Reason_Code (*personality) |
| (int, _Unwind_Action, _Unwind_Exception_Class, struct _Unwind_Exception *, |
| @@ -122,10 +136,7 @@ _Unwind_Reason_Code |
| _Unwind_ForcedUnwind (struct _Unwind_Exception *exc, _Unwind_Stop_Fn stop, |
| void *stop_argument) |
| { |
| - if (__builtin_expect (libgcc_s_handle == NULL, 0)) |
| - pthread_cancel_init (); |
| - else |
| - atomic_read_barrier (); |
| + _maybe_pthread_cancel_init (); |
| |
| _Unwind_Reason_Code (*forcedunwind) |
| (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *) |
| @@ -135,10 +148,7 @@ _Unwind_ForcedUnwind (struct _Unwind_Exc |
| _Unwind_Word |
| _Unwind_GetCFA (struct _Unwind_Context *context) |
| { |
| - if (__builtin_expect (libgcc_s_handle == NULL, 0)) |
| - pthread_cancel_init (); |
| - else |
| - atomic_read_barrier (); |
| + _maybe_pthread_cancel_init (); |
| |
| _Unwind_Word (*getcfa) (struct _Unwind_Context *) = libgcc_s_getcfa; |
| PTR_DEMANGLE (getcfa); |
| diff -pruN glibc-2.17-c758a686/sysdeps/gnu/unwind-resume.c glibc-2.17-c758a686/sysdeps/gnu/unwind-resume.c |
| |
| |
| @@ -20,8 +20,11 @@ |
| #include <dlfcn.h> |
| #include <stdio.h> |
| #include <unwind.h> |
| +#include <pthreadP.h> |
| +#include <sysdep.h> |
| #include <gnu/lib-names.h> |
| |
| +static void *libgcc_s_handle; |
| static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); |
| static _Unwind_Reason_Code (*libgcc_s_personality) |
| (int, _Unwind_Action, _Unwind_Exception_Class, struct _Unwind_Exception *, |
| @@ -42,13 +45,32 @@ init (void) |
| |
| libgcc_s_resume = resume; |
| libgcc_s_personality = personality; |
| + atomic_write_barrier (); |
| + /* At the point at which any thread writes the handle |
| + to libgcc_s_handle, the initialization is complete. |
| + The writing of libgcc_s_handle is atomic. All other |
| + threads reading libgcc_s_handle do so atomically. Any |
| + thread that does not execute this function must issue |
| + a read barrier to ensure that all of the above has |
| + actually completed and that the values of the |
| + function pointers are correct. */ |
| + libgcc_s_handle = handle; |
| } |
| |
| +static __always_inline void |
| +_maybe_init (void) |
| +{ |
| + if (__builtin_expect (libgcc_s_handle == NULL, 0)) |
| + init (); |
| + else |
| + atomic_read_barrier (); |
| +} |
| + |
| + |
| void |
| _Unwind_Resume (struct _Unwind_Exception *exc) |
| { |
| - if (__builtin_expect (libgcc_s_resume == NULL, 0)) |
| - init (); |
| + _maybe_init (); |
| libgcc_s_resume (exc); |
| } |
| |
| @@ -58,8 +80,7 @@ __gcc_personality_v0 (int version, _Unwi |
| struct _Unwind_Exception *ue_header, |
| struct _Unwind_Context *context) |
| { |
| - if (__builtin_expect (libgcc_s_personality == NULL, 0)) |
| - init (); |
| + _maybe_init (); |
| return libgcc_s_personality (version, actions, exception_class, |
| ue_header, context); |
| } |