I’ve recently run into a problem with an executable that links together some object files built with C++17 and others built with C++20. Both the C++17 and the C++20 translation units contain an inline function declaring this variable:
[[clang::no_destroy]] static const std::string empty;
With C++17, the string()
constructor isn’t constexpr. The variable symbol and a guard symbol are emitted into .bss
, and the variable is initialized on its first use.
With C++20, the string()
constructor is constexpr. There is no guard symbol, and the variable symbol is emitted into .rodata
.
The static linker deduplicates the variable symbols, picking the one in .rodata
arbitrarily, and combines it with the dynamic initialization code (inlined into a caller). When this dynamic initialization code runs, it segfaults writing to .rodata
.
I wondered to what extent mixing object files of different C++ language dialects was supported, where libc++ users are concerned. In general, I believe it’s an ODR violation to mix object files where an inlined constructor is sometimes declared constexpr and sometimes not.
If [[clang::no_destroy]]
were removed, the variable would require dynamic initialization to register the destructor call, avoiding the problem.
I have a workaround, https://r.android.com/3010579, which wraps the variable in a class whose constructor isn’t constexpr.