[PATCH v2 5/6] Support loading libunwind instead of libgcc_s
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu Mar 12 16:11:04 GMT 2026
On 11/03/26 09:03, Sam James wrote:
> 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.
Fair enough, I will make it explicit on the commit message.
>
>>
>> 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,
More information about the Libc-alpha
mailing list