[PATCH v2 5/6] Support loading libunwind instead of libgcc_s

Sam James sam@gentoo.org
Wed Mar 11 12:03:06 GMT 2026


Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:

> The 'unwind-link' facility allows glibc to support thread cancellation
> and exit (pthread_cancel, pthread_exiti, backtrace) by dynamically
> loading the  unwind library at runtime, preventing a hard dependency on
> libgcc_s within libc.so.

I assume this is LLVM libunwind, not
https://github.com/libunwind/libunwind? I think it's worth being
specific in the commit message because they differ in behaviour.

>
> When building with libunwind (for clang/LLVM toolchains), two assumptions
> in the existing code break:
>
>   1. The runtime library is libunwind.so instead of libgcc_s.so.
>
>   2. libgcc relies on __gcc_personality_v0 to handle unwinding mechanics.
>      libunwind exposes the standard '_Unwind_*' accessors directly.
>
> This patch adapts `unwind-link` to handle both environments based on
> the HAVE_CC_WITH_LIBUNWIND configuration:
>
>   * The UNWIND_SONAME macro now selects between LIBGCC_S_SO and
>     LIBUNWIND_SO.
>
>   * For libgcc, it continues to resolve `__gcc_personality_v0`.
>
>   * For libunwind, it instead resolves the standard
>     _Unwind_GetLanguageSpecificData, _Unwind_SetGR, _Unwind_SetIP,
>      and _Unwind_GetRegionStart helpers.
>
>    * unwind-resume.c is updated to implement wrappers for these
>      accessors that forward calls to the dynamically loaded function
>      pointers, effectively shimming the unwinder.
>
> Tests and Makefiles are updated to link against `$(libunwind)` where
> appropriate.

Reviewed-by: Sam James <sam@gentoo.org>

> ---
>  elf/Makefile                    |  2 ++
>  misc/unwind-link.c              | 34 ++++++++++++++++++++++++++++++++-
>  nptl/pthread_cancel.c           |  2 +-
>  nptl/pthread_exit.c             |  2 +-
>  shlib-versions                  |  2 ++
>  sysdeps/generic/unwind-link.h   | 13 +++++++++++++
>  sysdeps/generic/unwind-resume.c | 32 +++++++++++++++++++++++++++++++
>  7 files changed, 84 insertions(+), 3 deletions(-)
>
> diff --git a/elf/Makefile b/elf/Makefile
> index bd7ae54632..889df3c12f 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -2763,6 +2763,8 @@ $(objpfx)tst-big-note-lib.so: $(objpfx)tst-big-note-lib.o
>  	$(LINK.o) -shared -o $@ $(LDFLAGS.so) $(dt-relr-ldflag) $<
>  
>  $(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
> +LDLIBS-tst-unwind-ctor += $(libunwind)
> +LDFLAGS-tst-unwind-ctor-lib.so = -Wl,--unresolved-symbols=ignore-all
>  
>  CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0
>  
> diff --git a/misc/unwind-link.c b/misc/unwind-link.c
> index 6019dacf66..c5c1a3ea69 100644
> --- a/misc/unwind-link.c
> +++ b/misc/unwind-link.c
> @@ -37,6 +37,12 @@ static void *global_libgcc_handle;
>     may depend on unwinding.  */
>  __libc_lock_define (static, lock);
>  
> +#ifndef HAVE_CC_WITH_LIBUNWIND
> +# define UNWIND_SONAME LIBGCC_S_SO
> +#else
> +# define UNWIND_SONAME LIBUNWIND_SO
> +#endif
> +
>  struct unwind_link *
>  __libc_unwind_link_get (void)
>  {
> @@ -48,7 +54,7 @@ __libc_unwind_link_get (void)
>    /* Initialize a copy of the data, so that we do not need about
>       unlocking in case the dynamic loader somehow triggers
>       unwinding.  */
> -  void *local_libgcc_handle = __libc_dlopen (LIBGCC_S_SO);
> +  void *local_libgcc_handle = __libc_dlopen (UNWIND_SONAME);
>    if (local_libgcc_handle == NULL)
>      {
>        __libc_lock_unlock (lock);
> @@ -72,8 +78,19 @@ __libc_unwind_link_get (void)
>    local.ptr___frame_state_for
>      = __libc_dlsym (local_libgcc_handle, "__frame_state_for");
>  #endif
> +#ifndef HAVE_CC_WITH_LIBUNWIND
>    local.ptr_personality
>      = __libc_dlsym (local_libgcc_handle, "__gcc_personality_v0");
> +#else
> +  local.ptr__Unwind_GetLanguageSpecificData
> +    = __libc_dlsym (local_libgcc_handle, "_Unwind_GetLanguageSpecificData");
> +  local.ptr__Unwind_SetGR
> +    = __libc_dlsym (local_libgcc_handle, "_Unwind_SetGR");
> +  local.ptr__Unwind_SetIP
> +    = __libc_dlsym (local_libgcc_handle, "_Unwind_SetIP");
> +  local.ptr__Unwind_GetRegionStart
> +    = __libc_dlsym (local_libgcc_handle, "_Unwind_GetRegionStart");
> +#endif
>    UNWIND_LINK_EXTRA_INIT
>  
>    /* If a symbol is missing, libgcc_s has somehow been corrupted.  */
> @@ -84,7 +101,15 @@ __libc_unwind_link_get (void)
>    assert (local.ptr__Unwind_GetIP != NULL);
>  #endif
>    assert (local.ptr__Unwind_Resume != NULL);
> +#ifndef HAVE_CC_WITH_LIBUNWIND
>    assert (local.ptr_personality != NULL);
> +#else
> +  assert (local.ptr__Unwind_GetLanguageSpecificData != NULL);
> +  assert (local.ptr__Unwind_SetGR != NULL);
> +  assert (local.ptr__Unwind_GetIP != NULL);
> +  assert (local.ptr__Unwind_SetIP != NULL);
> +  assert (local.ptr__Unwind_GetRegionStart != NULL);
> +#endif
>  
>    PTR_MANGLE (local.ptr__Unwind_Backtrace);
>    PTR_MANGLE (local.ptr__Unwind_ForcedUnwind);
> @@ -96,7 +121,14 @@ __libc_unwind_link_get (void)
>  #if UNWIND_LINK_FRAME_STATE_FOR
>    PTR_MANGLE (local.ptr___frame_state_for);
>  #endif
> +#ifndef HAVE_CC_WITH_LIBUNWIND
>    PTR_MANGLE (local.ptr_personality);
> +#else
> +  PTR_MANGLE (local.ptr__Unwind_GetLanguageSpecificData);
> +  PTR_MANGLE (local.ptr__Unwind_SetGR);
> +  PTR_MANGLE (local.ptr__Unwind_SetIP);
> +  PTR_MANGLE (local.ptr__Unwind_GetRegionStart);
> +#endif
>  
>    __libc_lock_lock (lock);
>    if (atomic_load_relaxed (&global_libgcc_handle) != NULL)
> diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
> index 5fe31c266c..6757ea2ba4 100644
> --- a/nptl/pthread_cancel.c
> +++ b/nptl/pthread_cancel.c
> @@ -87,7 +87,7 @@ __pthread_cancel (pthread_t th)
>    {
>      struct unwind_link *unwind_link = __libc_unwind_link_get ();
>      if (unwind_link == NULL)
> -      __libc_fatal (LIBGCC_S_SO
> +      __libc_fatal (UNWIND_SONAME
>  		    " must be installed for pthread_cancel to work\n");
>    }
>  #endif
> diff --git a/nptl/pthread_exit.c b/nptl/pthread_exit.c
> index 4c15171992..1ed5ed52c4 100644
> --- a/nptl/pthread_exit.c
> +++ b/nptl/pthread_exit.c
> @@ -28,7 +28,7 @@ __pthread_exit (void *value)
>    {
>      struct unwind_link *unwind_link = __libc_unwind_link_get ();
>      if (unwind_link == NULL)
> -      __libc_fatal (LIBGCC_S_SO
> +      __libc_fatal (UNWIND_SONAME
>                      " must be installed for pthread_exit to work\n");
>    }
>  #endif
> diff --git a/shlib-versions b/shlib-versions
> index b1fd0a5eab..32093228c4 100644
> --- a/shlib-versions
> +++ b/shlib-versions
> @@ -68,3 +68,5 @@ libmvec=1
>  
>  # The malloc debug library
>  libc_malloc_debug=0
> +
> +libunwind=1
> diff --git a/sysdeps/generic/unwind-link.h b/sysdeps/generic/unwind-link.h
> index 1150cda219..ea39e525d2 100644
> --- a/sysdeps/generic/unwind-link.h
> +++ b/sysdeps/generic/unwind-link.h
> @@ -34,6 +34,12 @@ unwind_arch_adjustment (void *prev, void *addr)
>  # include <pointer_guard.h>
>  # include <unwind-resume.h>
>  
> +# ifndef HAVE_CC_WITH_LIBUNWIND
> +#  define UNWIND_SONAME LIBGCC_S_SO
> +# else
> +#  define UNWIND_SONAME LIBUNWIND_SO
> +# endif
> +
>  # if UNWIND_LINK_FRAME_STATE_FOR
>  struct frame_state;
>  # endif
> @@ -50,7 +56,14 @@ struct unwind_link
>  #if UNWIND_LINK_FRAME_STATE_FOR
>    struct frame_state *(*ptr___frame_state_for) (void *, struct frame_state *);
>  #endif
> +#ifndef HAVE_CC_WITH_LIBUNWIND
>    _Unwind_Reason_Code (*ptr_personality) PERSONALITY_PROTO;
> +#else
> +  __typeof (_Unwind_GetLanguageSpecificData) *ptr__Unwind_GetLanguageSpecificData;
> +  __typeof (_Unwind_SetGR) *ptr__Unwind_SetGR;
> +  __typeof (_Unwind_SetIP) *ptr__Unwind_SetIP;
> +  __typeof (_Unwind_GetRegionStart) *ptr__Unwind_GetRegionStart;
> +#endif
>    UNWIND_LINK_EXTRA_FIELDS
>  };
>  
> diff --git a/sysdeps/generic/unwind-resume.c b/sysdeps/generic/unwind-resume.c
> index de176a1f72..18c40501bb 100644
> --- a/sysdeps/generic/unwind-resume.c
> +++ b/sysdeps/generic/unwind-resume.c
> @@ -38,11 +38,43 @@ _Unwind_Resume (struct _Unwind_Exception *exc)
>  }
>  #endif
>  
> +#ifndef HAVE_CC_WITH_LIBUNWIND
>  _Unwind_Reason_Code
>  __gcc_personality_v0 PERSONALITY_PROTO
>  {
>    return UNWIND_LINK_PTR (link (), personality) PERSONALITY_ARGS;
>  }
> +#else
> +void *
> +_Unwind_GetLanguageSpecificData (struct _Unwind_Context *context)
> +{
> +  return UNWIND_LINK_PTR (link (), _Unwind_GetLanguageSpecificData) (context);
> +}
> +
> +void
> +_Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val)
> +{
> +  UNWIND_LINK_PTR (link (), _Unwind_SetGR) (context, index, val);
> +}
> +
> +_Unwind_Ptr
> +_Unwind_GetIP (struct _Unwind_Context *context)
> +{
> +  return UNWIND_LINK_PTR (link (), _Unwind_GetIP) (context);
> +}
> +
> +void
> +_Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val)
> +{
> +  UNWIND_LINK_PTR (link (), _Unwind_SetIP) (context, val);
> +}
> +
> +_Unwind_Ptr
> +_Unwind_GetRegionStart (struct _Unwind_Context *context)
> +{
> +  return UNWIND_LINK_PTR (link (), _Unwind_GetRegionStart) (context);
> +}
> +#endif
>  
>  _Unwind_Reason_Code
>  _Unwind_ForcedUnwind (struct _Unwind_Exception *exc, _Unwind_Stop_Fn stop,
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 418 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20260311/35c49b49/attachment.sig>


More information about the Libc-alpha mailing list