This is the mail archive of the
libc-ports@sources.redhat.com
mailing list for the libc-ports project.
Re: [PATCH] Memory fencing problem in pthread cancellation
- From: Carlos O'Donell <carlos at systemhalted dot org>
- To: "Joseph S. Myers" <joseph at codesourcery dot com>
- Cc: Jeff Law <law at redhat dot com>, Mike Frysinger <vapier at gentoo dot org>, libc-alpha <libc-alpha at sourceware dot org>, libc-ports at sourceware dot org, Marcus Shawcroft <marcus dot shawcroft at linaro dot org>
- Date: Mon, 14 Jan 2013 17:58:44 -0500
- Subject: Re: [PATCH] Memory fencing problem in pthread cancellation
- References: <50F46969.1000305@redhat.com> <50F48281.3070207@systemhalted.org> <Pine.LNX.4.64.1301142218260.19709@digraph.polyomino.org.uk>
On 01/14/2013 05:21 PM, Joseph S. Myers wrote:
> On Mon, 14 Jan 2013, Carlos O'Donell wrote:
>
>> diff --git a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
>> index 58ca9ac..d7ac847 100644
>> --- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
>> +++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
>> @@ -104,6 +104,7 @@ asm (
>> " mov r6, r0\n"
>> " cmp r3, #0\n"
>> " beq 4f\n"
>> + atomic_asm_full_barrier()
>> "5: mov r0, r6\n"
>> " ldmfd sp!, {r4, r5, r6, lr}\n"
>> " " CFI_ADJUST_CFA_OFFSET (-16) "\n"
>> @@ -123,7 +124,7 @@ asm (
>> #else
>> "1: .word _GLOBAL_OFFSET_TABLE_ - 3b - 8\n"
>> #endif
>> -"2: .word libgcc_s_resume(GOTOFF)\n"
>> +"2: .word libgcc_s_handle(GOTOFF)\n"
>> " .size _Unwind_Resume, .-_Unwind_Resume\n"
>> );
>
> That last change doesn't seem right. Note the subsequent "bx r3" if r3
> isn't zero. If you want to test libgcc_s_handle for being zero, you then
> need, separately (after the barrier?) to load the libgcc_s_resume value.
You are correct, we need another entry for libgcc_s_resume, and we need
to load that after the barrier, otherwise we won't call libgcc_s_resume.
Something like this:
diff --git a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
index 58ca9ac..1e03b13 100644
--- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-forcedunwind.c
@@ -104,7 +104,12 @@ asm (
" mov r6, r0\n"
" cmp r3, #0\n"
" beq 4f\n"
-"5: mov r0, r6\n"
+ atomic_asm_full_barrier()
+"5: ldr r4, 6f\n"
+" ldr r5, 7f\n"
+"8: add r4, pc, r4\n"
+" ldr r3, [r4, r5]\n"
+" mov r0, r6\n"
" ldmfd sp!, {r4, r5, r6, lr}\n"
" " CFI_ADJUST_CFA_OFFSET (-16) "\n"
" " CFI_RESTORE (r4) "\n"
@@ -123,7 +128,13 @@ asm (
#else
"1: .word _GLOBAL_OFFSET_TABLE_ - 3b - 8\n"
#endif
-"2: .word libgcc_s_resume(GOTOFF)\n"
+"2: .word libgcc_s_handle(GOTOFF)\n"
+#ifdef __thumb2__
+"6: .word _GLOBAL_OFFSET_TABLE_ - 8b - 4\n"
+#else
+"6: .word _GLOBAL_OFFSET_TABLE_ - 8b - 8\n"
+#endif
+"7: .word libgcc_s_resume(GOTOFF)\n"
" .size _Unwind_Resume, .-_Unwind_Resume\n"
);
---
I was hoping that someone with an ARM cross-build environment
could test this and tell us how it goes? :-)
> (Whatever you do in unwind-forcedunwind.c, presumably much the same change
> should go in unwind-resume.c as well.)
Yes, all the same changes are required in:
./sysdeps/gnu/unwind-resume.c
./ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c
I'd say something like this?
diff --git a/sysdeps/gnu/unwind-resume.c b/sysdeps/gnu/unwind-resume.c
index df845cd..fecfc94 100644
--- a/sysdeps/gnu/unwind-resume.c
+++ b/sysdeps/gnu/unwind-resume.c
@@ -21,6 +21,7 @@
#include <unwind.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 *,
@@ -41,13 +42,25 @@ 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;
}
void
_Unwind_Resume (struct _Unwind_Exception *exc)
{
- if (__builtin_expect (libgcc_s_resume == NULL, 0))
+ if (__builtin_expect (libgcc_s_handle == NULL, 0))
init ();
+ else
+ atomic_read_barrier ();
libgcc_s_resume (exc);
}
@@ -57,8 +70,10 @@ __gcc_personality_v0 (int version, _Unwind_Action actions,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
{
- if (__builtin_expect (libgcc_s_personality == NULL, 0))
+ if (__builtin_expect (libgcc_s_handle == NULL, 0))
init ();
+ else
+ atomic_read_barrier ();
return libgcc_s_personality (version, actions, exception_class,
ue_header, context);
}
---
diff --git a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c
index 0a3ad95..a67190d 100644
--- a/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c
+++ b/ports/sysdeps/unix/sysv/linux/arm/nptl/unwind-resume.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <unwind.h>
+static void *libgcc_s_handle;
static void (*libgcc_s_resume) (struct _Unwind_Exception *exc);
static _Unwind_Reason_Code (*libgcc_s_personality)
(_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *);
@@ -41,6 +42,16 @@ 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;
}
/* It's vitally important that _Unwind_Resume not have a stack frame; the
@@ -67,7 +78,12 @@ asm (
" mov r6, r0\n"
" cmp r3, #0\n"
" beq 4f\n"
-"5: mov r0, r6\n"
+ atomic_asm_full_barrier()
+"5: ldr r4, 6f\n"
+" ldr r5, 7f\n"
+"8: add r4, pc, r4\n"
+" ldr r3, [r4, r5]\n"
+" mov r0, r6\n"
" ldmfd sp!, {r4, r5, r6, lr}\n"
" " CFI_ADJUST_CFA_OFFSET (-16) "\n"
" " CFI_RESTORE (r4) "\n"
@@ -76,7 +92,7 @@ asm (
" " CFI_RESTORE (lr) "\n"
" bx r3\n"
" " CFI_RESTORE_STATE "\n"
-"4: bl init\n"
+"4: bl pthread_cancel_init\n"
" ldr r3, [r4, r5]\n"
" b 5b\n"
" " CFI_ENDPROC "\n"
@@ -86,7 +102,13 @@ asm (
#else
"1: .word _GLOBAL_OFFSET_TABLE_ - 3b - 8\n"
#endif
-"2: .word libgcc_s_resume(GOTOFF)\n"
+"2: .word libgcc_s_handle(GOTOFF)\n"
+#ifdef __thumb2__
+"6: .word _GLOBAL_OFFSET_TABLE_ - 8b - 4\n"
+#else
+"6: .word _GLOBAL_OFFSET_TABLE_ - 8b - 8\n"
+#endif
+"7: .word libgcc_s_resume(GOTOFF)\n"
" .size _Unwind_Resume, .-_Unwind_Resume\n"
);
@@ -95,7 +117,9 @@ __gcc_personality_v0 (_Unwind_State state,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
{
- if (__builtin_expect (libgcc_s_personality == NULL, 0))
+ if (__builtin_expect (libgcc_s_handle == NULL, 0))
init ();
+ else
+ atomic_read_barrier ();
return libgcc_s_personality (state, ue_header, context);
}
---
It looks like the assembly in unwind-resume.c and unwind-forcedunwind.c
are identical and should be shared?
Cheers,
Carlos.