glibc functions perform stack unwinding mainly in two places, pthread_exit/pthread_cancel, and backtrace-family functions.
Here is a C++ program that calls pthread_exit. Shall the destructors ~A and ~B be called?
#include <pthread.h>
#include <stdio.h>
void foo() { pthread_exit(NULL); }
void bar() {
struct A { ~A() { puts("~A"); } } a;
foo();
}
void *qux(void *arg) {
struct B { ~B() { puts("~B"); } } b;
bar();
return nullptr;
}
int main() {
pthread_t t;
pthread_create(&t, nullptr, qux, nullptr);
pthread_join(t, nullptr);
}POSIX doesn't have the requirement, but glibc and FreeBSD made a decision to call destructors.
In glibc, pthread_exit calls dlopen("libgcc_s.so.1", RTLD_NOW | __RTLD_DLOPEN) and then uses dlsym to retrieve definitions like _Unwind_ForcedUnwind and _Unwind_GetIP.
If libgcc_s.so.1 is unavailable, glibc exits with an error message "libgcc_s.so.1 must be installed for pthread_exit to work".
If libgcc_s.so.1 is available, __pthread_unwind will invoke _Unwind_ForcedUnwind to call destructors and then invoke __libc_unwind_longjmp to go back to the saved point in pthread_create.
Second, functions backtrace, backtrace_symbols, and backtrace_symbols_fd declared in <execinfo.h> perform stack unwinding.
When an executable is linked against libunwind.so or libunwind.a from llvm-project, there is an alternative implementation of _Unwind_* functions.
If libgcc_s.so.1:_Unwind_Backtrace calls external _Unwind_* helper functions, these calls will be resolved to libunwind.so.
However, this cross-walking can cause issues if the _Unwind_Context created by libgcc is accessed by libunwind, as the two have different layouts of _Unwind_Context.
ChromeOS has an issue related to this problem in glibc on AArch32.
libgcc's _Unwind_Backtrace calls _Unwind_SetGR (inline function in gcc/ginclude/unwind-arm-common.h), which calls _Unwind_VRS_Set in libunwind.
Gentoo has compiled a list of packages not building with musl due to the absence of backtrace.
backtrace is a scope creep for the C library and needs the .eh_frame unwind table, so I don't recommend it.
An alternative option is to use libbacktrace. Otherwise, you can simply utilize _Unwind_Backtrace provided by either libunwind or libgcc.