[m-rev.] for review: Don't use __builtin_setjmp, __builtin_longjmp on AArch64.
Peter Wang
novalazy at gmail.com
Thu May 26 14:23:31 AEST 2022
On Wed, 25 May 2022 18:52:22 +1000 Julien Fischer <jfischer at opturion.com> wrote:
>
> Hi Peter,
>
> On Wed, 25 May 2022, Peter Wang wrote:
>
> > There is an issue on Linux/aarch64/glibc where a commit performed on a
> > thread using the gcc __builtin_longjmp function causes an assertion
> > failure in pthread_create() if the Mercury runtime is dynamically
> > linked:
> >
> > pthread_create.c:430: start_thread: Assertion `freesize < pd->stackblock_size' failed.
> >
> > or a crash if the Mercury runtime is statically linked:
> >
> > *** Mercury runtime: caught segmentation violation ***
> > cause: address not mapped to object
> >
> > Tested using gcc version 6.3.0 20170516 (Debian 6.3.0-18)
>
> Are you in a position to test it with other versions?
Yes. The problem also occurs with gcc 8.3.0, but not gcc 10.2.
I have updated the patch accordingly.
> > diff --git a/runtime/mercury.h b/runtime/mercury.h
> > index 699dc9712..748e38906 100644
> > --- a/runtime/mercury.h
> > +++ b/runtime/mercury.h
>
> ...
>
> > @@ -75,12 +75,20 @@
> > // The jmp_buf type used by MR_builtin_setjmp()
> > // to save the stack context when implementing commits.
> >
> > -#ifdef MR_GNUC
> > - // For GCC, we use `__builtin_setjmp' and `__builtin_longjmp'.
> > - // These are documented (in gcc/builtins.c in the GCC source code)
> > - // as taking for their parameter a pointer to an array of five words.
> > +#if (MR_GNUC > 2 || (MR_GNUC == 2 && __GNUC_MINOR__ >= 8))
>
> Perhaps just test if MR_GNUC is > 2 here, since we don't support GCC
> versions that old anyway.
Leaving it for now.
> > + // There is some problem with using __builtin_setjmp and __builtin_longjmp on
> > + // Linux/glibc/aarch64. When a thread commits using __builtin_longjmp, either
> > + // we get an assertion failure in pthread_create or a segmentation fault.
> > + #if !defined(__aarch64__) && !defined(MR_DARWIN_SETJMP_WORKAROUND)
>
> Aside: we can probably get rid of MR_DARWIN_SETJMP_WORKAROUND as it only
> applied to the i*86 Darwin target and that's no longer a thing. (Also,
> the bug in GCC that was being worked around has long since been fixed.)
Feel free to change it afterwards.
> > diff --git a/tests/hard_coded/thread_commit.m b/tests/hard_coded/thread_commit.m
> > new file mode 100644
> > index 000000000..86e46bbc9
> > --- /dev/null
> > +++ b/tests/hard_coded/thread_commit.m
> > @@ -0,0 +1,99 @@
> > +%---------------------------------------------------------------------------%
> > +% vim: ts=4 sw=4 et ft=mercury
> > +%---------------------------------------------------------------------------%
> > +
> > +% This program checks if performing a commit on a thread causes any issues.
>
> There should be a comment here describing what the two expected outputs
> are for.
Done.
>
> That looks fine otherwise; I assume I should apply this one to the
> release branch as well?
Yes, thanks.
The updated patch follows (for review).
Peter
----
Don't use __builtin_setjmp, __builtin_longjmp on AArch64 with older GCCs.
There is an issue on Linux/aarch64 with older versions of gcc where a
commit performed on a thread using the gcc __builtin_longjmp function
causes an assertion failure in pthread_create() if the Mercury runtime
is dynamically linked:
pthread_create.c:430: start_thread: Assertion `freesize < pd->stackblock_size' failed.
or a crash if the Mercury runtime is statically linked:
*** Mercury runtime: caught segmentation violation ***
cause: address not mapped to object
The gcc versions tested are:
BAD - gcc version 6.3.0 20170516 (Debian 6.3.0-18) from Debian 9
BAD - gcc version 8.3.0 (Debian 8.3.0-2) from Debian 10
OK - gcc version 10.2.1 20210110 (Debian 10.2.1-6) from Debian 11
on Debian with glibc.
runtime/mercury.h:
Define MR_USE_GCC_BUILTIN_SETJMP_LONGJMP if we decide to use gcc's
__builtin_setjmp and __builtin_longjmp functions instead of the
standard functions.
Use standard setjmp/longjmp on aarch64 if gcc version is < 10.
Update the comment for MR_builtin_jmp_buf to refer to the GCC manual
instead of the GCC source code. Use an array of 'intptr_t's instead
of 'void *'s, to match the manual.
tests/hard_coded/Mmakefile
tests/hard_coded/thread_commit.m:
tests/hard_coded/thread_commit.exp:
tests/hard_coded/thread_commit.exp2:
Add test case.
diff --git a/runtime/mercury.h b/runtime/mercury.h
index 699dc9712..235df9777 100644
--- a/runtime/mercury.h
+++ b/runtime/mercury.h
@@ -1,7 +1,7 @@
// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 1999-2006, 2011 The University of Melbourne.
-// Copyright (C) 2014, 2015-2016, 2018 The Mercury team.
+// Copyright (C) 2014, 2015-2016, 2018, 2022 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// mercury.h - This file defines the macros, types, etc. that
@@ -75,12 +75,25 @@
// The jmp_buf type used by MR_builtin_setjmp()
// to save the stack context when implementing commits.
-#ifdef MR_GNUC
- // For GCC, we use `__builtin_setjmp' and `__builtin_longjmp'.
- // These are documented (in gcc/builtins.c in the GCC source code)
- // as taking for their parameter a pointer to an array of five words.
+#if (MR_GNUC > 2 || (MR_GNUC == 2 && __GNUC_MINOR__ >= 8))
+ // There is an issue with using __builtin_setjmp and __builtin_longjmp on
+ // Linux/aarch64. When a thread commits using __builtin_longjmp, we get an
+ // assertion failure or a segmentation fault. The problem appears to have
+ // been fixed in some version between gcc 8.3.0 and gcc 10.2.
+ #if defined(__aarch64__) && (MR_GNUC < 10)
+ // Don't define MR_USE_GCC_BUILTIN_SETJMP_LONGJMP.
+ #elif defined(MR_DARWIN_SETJMP_WORKAROUND)
+ // Don't define MR_USE_GCC_BUILTIN_SETJMP_LONGJMP.
+ #else
+ #define MR_USE_GCC_BUILTIN_SETJMP_LONGJMP
+ #endif
+#endif
- typedef void *MR_builtin_jmp_buf[5];
+#ifdef MR_USE_GCC_BUILTIN_SETJMP_LONGJMP
+ // The jump buffer for GCC `__builtin_setjmp' and `__builtin_longjmp'
+ // functions is documented to be an array of five intptr_t's
+ // (GCC manual section 6.5 Nonlocal gotos).
+ typedef intptr_t MR_builtin_jmp_buf[5];
#else
// Otherwise we use the standard jmp_buf type.
typedef jmp_buf MR_builtin_jmp_buf;
@@ -141,14 +154,11 @@ extern MR_Word mercury__private_builtin__dummy_var;
// 2. The call to __builtin_longjmp() must not be in the same
// function as the call to __builtin_setjmp().
-#if (MR_GNUC > 2 || (MR_GNUC == 2 && __GNUC_MINOR__ >= 8))
- #ifndef MR_DARWIN_SETJMP_WORKAROUND
- #define MR_builtin_setjmp(buf) __builtin_setjmp((buf))
- #define MR_builtin_longjmp(buf, val) __builtin_longjmp((buf), (val))
- #endif
-#endif
-#ifndef MR_builtin_setjmp
- #define MR_builtin_setjmp(buf) setjmp((buf))
+#ifdef MR_USE_GCC_BUILTIN_SETJMP_LONGJMP
+ #define MR_builtin_setjmp(buf) __builtin_setjmp((buf))
+ #define MR_builtin_longjmp(buf, val) __builtin_longjmp((buf), (val))
+#else
+ #define MR_builtin_setjmp(buf) setjmp((buf))
#define MR_builtin_longjmp(buf, val) longjmp((buf), (val))
#endif
[test case changes omitted]
More information about the reviews
mailing list