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.