I'm seeing link errors of the 'asyncsafe-spin' unit tests on two platforms:
* On an older Linux/arm system (with glibc, gcc 4.2.3 arm-none-linux-gnueabi): gcc -std=gnu99 -Wno-error -g -O2 -o test-asyncsafe-spin1 test-asyncsafe-spin1.o libtests.a ../gllib/libgnu.a libtests.a ../gllib/libgnu.a libtests.a libtests.a(asyncsafe-spin.o): In function `do_lock': /admin/testdir1/gltests/asyncsafe-spin.c:157: undefined reference to `__sync_val_compare_and_swap_4' libtests.a(asyncsafe-spin.o): In function `do_unlock': /admin/testdir1/gltests/asyncsafe-spin.c:165: undefined reference to `__sync_val_compare_and_swap_4' collect2: ld returned 1 exit status make[4]: *** [test-asyncsafe-spin1] Error 1 * On OpenBSD 7.4/hppa (with gcc 4.2.1 hppa-unknown-openbsd7.4) gmake[3]: Entering directory '/home/bruno/testdir1/build/gltests' gcc -std=gnu99 -Wno-error -g -O2 -L/home/bruno/lib -o test-asyncsafe-spin1 test-asyncsafe-spin1.o libtests.a ../gllib/libgnu.a libtests.a ../gllib/libgnu.a libtests.a libtests.a(asyncsafe-spin.o): In function `do_lock': ../../gltests/asyncsafe-spin.c:157: undefined reference to `__sync_val_compare_and_swap_4' libtests.a(asyncsafe-spin.o): In function `do_unlock': ../../gltests/asyncsafe-spin.c:165: undefined reference to `__sync_val_compare_and_swap_4' collect2: ld returned 1 exit status gmake[3]: *** [Makefile:3357: test-asyncsafe-spin1] Error 1 The cause is that a use of the compiler built-in __sync_val_compare_and_swap on these systems does not produce inline code with CAS instructions or loops, but rather a call to an external function __sync_val_compare_and_swap_4. And this function is not implemented, neither in libc, nor in libgcc / libgcc_s.so. The code indicates that such link errors had already been observed on Solaris/SPARC, on Android/arm, and with older versions of AIX xlc. A tedious analysis reveals that these link errors will regularly occur on: - arm, with gcc versions >= 4.1, < 4.7 on all systems except NetBSD, with gcc versions >= 4.7, < 5 on FreeBSD, OpenBSD, Android, with gcc versions >= 5 on OpenBSD, Android, - hppa, hppa64, with gcc versions >= 4.1, < 4.7 on all systems, with gcc versions >= 4.7, < 13 on NetBSD, OpenBSD, - i386 (without '-march=i486'), with gcc versions >= 4.1 on all systems except NetBSD, - sparc (32-bit, for certain CPU models), with gcc versions >= 4.1 on all systems except NetBSD, - m68k, with gcc versions >= 4.1, < 4.7 on all systems except NetBSD, - mips, with gcc versions >= 4.1, < 4.3 on all systems. And also possible on new architectures that are not known yet. Therefore a configure-time test is the best approach. gdb uses an autoconf test for this as well. 2024-01-21 Bruno Haible <br...@clisp.org> asyncsafe-spin: Fix link error on various platforms. * m4/atomic-cas.m4: New file. * lib/asyncsafe-spin.c: Test HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41 instead of a condition that considers only __sparc__, __ANDROID__, __ibmxl__. * lib/pthread-spin.c: Likewise. * lib/simple-atomic.c: Likewise. * modules/asyncsafe-spin (Files): Add m4/atomic-cas.m4. (configure.ac): Require gl_ATOMIC_COMPARE_AND_SWAP. * modules/pthread-spin: Likewise. * modules/simple-atomic: Likewise. =============================== m4/atomic-cas.m4 =============================== # atomic-cas.m4 serial 1 dnl Copyright (C) 2024 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. # Determines whether the atomic compare-and-swap operations, officially # supported in GCC >= 4.1 and clang >= 3.0, are actually available. # These primitives are documented in # <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> # without platform restrictions. But they are not actually available # everywhere: # * On some platforms, __sync_bool_compare_and_swap does not expand # to standalone inline code, but to a call to a function # '__sync_bool_compare_and_swap_4' (assuming a 32-bit value). # This is the case on # - arm, for all gcc versions # - hppa, hppa64, for all gcc versions # - i386 (without '-march=i486'), for all gcc versions # - sparc (32-bit, for certain CPU models), for all gcc versions # - m68k, for gcc < 4.7 # - mips, for gcc < 4.3 # This can be seen by compiling this piece of code # ---------------------------------------------------------------- # int cmpxchg (int* value, int comp_val, int new_val) # { # return __sync_val_compare_and_swap (value, comp_val, new_val); # } # ---------------------------------------------------------------- # with option -S, using a (native or cross-) compiler. # * The function __sync_bool_compare_and_swap_4 is meant to be included # in libgcc. And indeed, libgcc contains the source code for this # function on # - arm, for gcc versions >= 4.7, but only for Linux # and (for gcc >= 5) FreeBSD, # - hppa, hppa64, for gcc versions >= 4.7, but only for Linux # and (for gcc >= 13) NetBSD, OpenBSD, hppa64 HP-UX # - i386, never at all # - sparc, never at all # - m68k, for gcc versions >= 4.7, but only for Linux # - mips, never at all # * The NetBSD C library provides this function on # - arm, arm64, # - i386, # - sparc, # - m68k, # - riscv64. # Other C libraries (e.g. glibc, musl libc) do not provide this function. # So, the use of these primitives results in a link error on: # - arm, with gcc versions >= 4.1, < 4.7 on all systems except NetBSD, # with gcc versions >= 4.7, < 5 on FreeBSD, OpenBSD, Android, # with gcc versions >= 5 on OpenBSD, Android, # - hppa, hppa64, with gcc versions >= 4.1, < 4.7 on all systems, # with gcc versions >= 4.7, < 13 on NetBSD, OpenBSD, # - i386 (without '-march=i486'), with gcc versions >= 4.1 # on all systems except NetBSD, # - sparc (32-bit, for certain CPU models), with gcc versions >= 4.1 # on all systems except NetBSD, # - m68k, with gcc versions >= 4.1, < 4.7 on all systems except NetBSD, # - mips, with gcc versions >= 4.1, < 4.3 on all systems. # Additionally, link errors can occur if - such as on glibc systems - the libgcc # functions are distributed through glibc, but the glibc version is older than # the gcc version. AC_DEFUN([gl_ATOMIC_COMPARE_AND_SWAP], [ AC_CACHE_CHECK([for __sync_bool_compare_and_swap], [gl_cv_builtin_sync_bool_compare_and_swap], [AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[int cmpxchg (int* value, int comp_val, int new_val) { return __sync_val_compare_and_swap (value, comp_val, new_val); } ]], [[]]) ], [gl_cv_builtin_sync_bool_compare_and_swap=yes], [gl_cv_builtin_sync_bool_compare_and_swap=no]) ]) if test $gl_cv_builtin_sync_bool_compare_and_swap = yes; then AC_DEFINE([HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41], [1], [Define to 1 if the GCC 4.1 primitives for atomic compare-and-swap can be used.]) fi ]) ================================================================================ diff --git a/lib/asyncsafe-spin.c b/lib/asyncsafe-spin.c index f16fb54b1c..63352184ae 100644 --- a/lib/asyncsafe-spin.c +++ b/lib/asyncsafe-spin.c @@ -134,11 +134,10 @@ do_unlock (asyncsafe_spinlock_t *lock) # endif # elif (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \ - && !(defined __sun && defined __sparc__) && !defined __ANDROID__) \ - || __clang_major__ >= 3) \ - && !defined __ibmxl__ -/* Use GCC built-ins (available in GCC >= 4.1, except on Solaris/SPARC and - Android, and clang >= 3.0). + || __clang_major__ >= 3) \ + && HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41) +/* Use GCC built-ins (available on many platforms with GCC >= 4.1 or + clang >= 3.0). Documentation: <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */ diff --git a/lib/pthread-spin.c b/lib/pthread-spin.c index 32073fbc36..26caa72496 100644 --- a/lib/pthread-spin.c +++ b/lib/pthread-spin.c @@ -163,10 +163,10 @@ pthread_spin_destroy (pthread_spinlock_t *lock) } # elif (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \ - && !defined __ANDROID__) \ - || __clang_major__ >= 3) \ - && !defined __ibmxl__ -/* Use GCC built-ins (available in GCC >= 4.1 and clang >= 3.0). + || __clang_major__ >= 3) \ + && HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41) +/* Use GCC built-ins (available on many platforms with GCC >= 4.1 or + clang >= 3.0). Documentation: <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */ diff --git a/lib/simple-atomic.c b/lib/simple-atomic.c index 61fc602367..656b4bdc19 100644 --- a/lib/simple-atomic.c +++ b/lib/simple-atomic.c @@ -67,11 +67,10 @@ atomic_compare_and_swap_ptr (uintptr_t volatile *vp, require to link with -latomic. */ # if (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \ - && !(defined __sun && defined __sparc__) && !defined __ANDROID__) \ - || __clang_major__ >= 3) \ - && !defined __ibmxl__ -/* Use GCC built-ins (available in GCC >= 4.1, except on Solaris/SPARC and - Android, and clang >= 3.0). + || __clang_major__ >= 3) \ + && HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41) +/* Use GCC built-ins (available on many platforms with GCC >= 4.1 or + clang >= 3.0). Documentation: <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */ diff --git a/modules/asyncsafe-spin b/modules/asyncsafe-spin index fc734928db..ffb2429e65 100644 --- a/modules/asyncsafe-spin +++ b/modules/asyncsafe-spin @@ -4,6 +4,7 @@ Spin locks for communication between threads and signal handlers. Files: lib/asyncsafe-spin.h lib/asyncsafe-spin.c +m4/atomic-cas.m4 Depends-on: signal-h @@ -15,6 +16,7 @@ sparcv8+ configure.ac: AC_REQUIRE([AC_C_INLINE]) AC_CHECK_HEADERS_ONCE([pthread.h]) +AC_REQUIRE([gl_ATOMIC_COMPARE_AND_SWAP]) Makefile.am: lib_SOURCES += asyncsafe-spin.c diff --git a/modules/pthread-spin b/modules/pthread-spin index 35e3aa701e..36b6180c90 100644 --- a/modules/pthread-spin +++ b/modules/pthread-spin @@ -4,6 +4,7 @@ POSIX spin locks. Files: lib/pthread-spin.c m4/pthread-spin.m4 +m4/atomic-cas.m4 Depends-on: pthread-h @@ -15,6 +16,7 @@ gl_PTHREAD_SPIN gl_CONDITIONAL([GL_COND_OBJ_PTHREAD_SPIN], [test $HAVE_PTHREAD_SPIN_INIT = 0 || test $REPLACE_PTHREAD_SPIN_INIT = 1]) gl_PTHREAD_MODULE_INDICATOR([pthread-spin]) +AC_REQUIRE([gl_ATOMIC_COMPARE_AND_SWAP]) Makefile.am: if GL_COND_OBJ_PTHREAD_SPIN diff --git a/modules/simple-atomic b/modules/simple-atomic index 5f3f63d109..8558620580 100644 --- a/modules/simple-atomic +++ b/modules/simple-atomic @@ -4,6 +4,7 @@ Simple atomic operations for multithreading. Files: lib/simple-atomic.h lib/simple-atomic.c +m4/atomic-cas.m4 Depends-on: stdint @@ -11,6 +12,7 @@ sparcv8+ configure.ac: AC_CHECK_HEADERS_ONCE([pthread.h]) +AC_REQUIRE([gl_ATOMIC_COMPARE_AND_SWAP]) Makefile.am: lib_SOURCES += simple-atomic.c