We have a C++20 library that compiles several Boost modules and statically links their object files into our shared library. One of those modules is Boost.Stacktrace. We compile this library, and a test application that uses it, on many platforms: macOS 14 Clang 16 ARM, macOS 15 Clang 17 ARM, and macOS 15 Clang 17 Intel; GCC 8, 9, 11, 13, and 14 across RHEL 8, RHEL9, Ubuntu 22.04, and Ubuntu 24.04; and Windows 10, 11, 2022, and 2025 with MSVC 2022. And then for all of these, we compile separate opt and debug (-g) libraries and test applications.
On the POSIX platforms (so excluding Windows for the duration of this question), we link to libbacktrace, and we build and link to boost.stacktrace.backtrace. On Ubuntu, this is using the GCC-provided libbacktrace. On RHEL and macOS, libbacktrace is not provided, so we compile libbacktrace from source and use that.
And on all of these POSIX platforms except one, it all works great. We get expected, helpful information for stacktraces without error. However, on macOS 15 (only) Clang 17 (only) Intel (only) opt (only), calling boost::stacktrace::frame::source_line() or source_file() or name() segfaults (EXC_BAD_ACCESS (SIGSEGV)), and it segfaults every single time consistently. This does not happen on ARM. It does not happen on Clang 16. It does not happen on macOS 15 Clang 17 Intel debug. Only macOS 15 Clang 17 Intel opt (which makes tracing down the source harder, but I think I have).
Here are the first two frames from the macOS Console crash report. The third frame is our code's call to source_line():
0 libdyld.dylib 0x7ff817a8ef96 _tlv_get_addr + 3 1 libNameOfMySharedLibrary.dylib 0x1096e44db boost::stacktrace::frame::source_line() const + 27 3 [our code] ... Even without the file names and line numbers I'd get on a debug build, this is decisive. _tlv_get_addr is a function specifically used for thread_local storage. This leaves only one possible call stack that can generate this specific condition (manually generated):
0 _tlv_get_addr in libdylib.dylib 1 thread_local in ??? 2 boost::stacktrace::detail::construct_state(...) at boost/stacktrace/detail/libbacktrace_impls.hpp:104 3 boost::stacktrace::frame::source_line() at boost/stacktrace/detail/libbacktrace_impls.hpp:229 4 [our code] ... Links to above sources:
boost/stacktrace/detail/libbacktrace_impls.hpp:104boost/stacktrace/detail/libbacktrace_impls.hpp:229
Here is some more information from the crash report, little of which I understand:
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00007ff914b8b9e0 Exception Codes: 0x0000000000000001, 0x00007ff914b8b9e0 Termination Reason: Namespace SIGNAL, Code 11 Segmentation fault: 11 Terminating Process: exc handler [11903] VM Region Info: 0x7ff914b8b9e0 is not in any region. Bytes after previous region: 1961314785 Bytes before following region: 16938528 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL __LINKEDIT 7ff895bb3000-7ff89fd17000 [161.4M] r--/r-- SM=COW dyld shared cache combined __LINKEDIT ---> GAP OF 0x75e9c000 BYTES unused __TEXT 7ff915bb3000-7ff935a5b000 [510.7M] r-x/r-x SM=COW unused unknown system shared lib __TEXT So, all of that information dumped out there, I don't know who to report this problem to. Is this a:
- Boost bug (I don't think so)?
- macOS bug?
- Clang (LLVM) bug?
- Some third-party OSS C++ library bug?
- Something else entirely?
-g -O3 -fPIC(there are others, like many-Is and-Ds, but those are the only ones I feel could affect this). The only difference between that and the debug build is the-O3, which isn't present on the debug build. All other arguments are present on both. I'll try decreasing to-O2and then-O1and report back.boost/libs/stacktrace/src/backtrace.cppbeing compiled with-O3. I tried changing that to-O2with no affect. However, changing it to-O1fixed the segfault. We can still compile our code with-O3, but Boost'sbacktrace.cppneeds-O1.