The LLVM toolchain's C++ runtime libraries are compiled without -ftls-model=local-exec. This causes TLS accesses in these libraries to use R_ARM_TLS_IE32 (initial-exec) relocations, which require GOT-relative indirection.
Zephyr applications use local-exec TLS. The model mismatch between the application (LE32) and the linked library objects (IE32) can cause runtime crashes.
For example C++ code like this fails:
int i;
std::istringstream iss("123");
iss >> i;
The IE32 relocation for internal errno attempts to load via GOT at an offset that doesn't exist resulting in a null/invalid pointer dereference. That gets caught when running Zephyr with CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU enabled.
Root Cause
The get_runtimes_flags() function in scripts/llvm/CMakeLists.txt sets the compiler flags for building but does not include -ftls-model=local-exec.
For reference the GCC toolchain and Picolibc already use local-exec.
Fix
Adding -ftls-model=local-exec to get_runtimes_flags() in scripts/llvm/CMakeLists.txt fixes the tls-model mismatch.
function(get_runtimes_flags directory flags)
- set(runtimes_flags "${flags} -ffunction-sections -fdata-sections -fno-ident --sysroot ${LLVM_BINARY_DIR}/${directory}" PARENT_SCOPE)
+ set(runtimes_flags "${flags} -ffunction-sections -fdata-sections -fno-ident -ftls-model=local-exec --sysroot ${LLVM_BINARY_DIR}/${directory}" PARENT_SCOPE)
endfunction()
I rebuild this for an armv7m_hard_fpv5_d16 target and verified that the C++ code shown now functions correctly.
The LLVM toolchain's C++ runtime libraries are compiled without -ftls-model=local-exec. This causes TLS accesses in these libraries to use R_ARM_TLS_IE32 (initial-exec) relocations, which require GOT-relative indirection.
Zephyr applications use local-exec TLS. The model mismatch between the application (LE32) and the linked library objects (IE32) can cause runtime crashes.
For example C++ code like this fails:
The IE32 relocation for internal errno attempts to load via GOT at an offset that doesn't exist resulting in a null/invalid pointer dereference. That gets caught when running Zephyr with CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU enabled.
Root Cause
The get_runtimes_flags() function in scripts/llvm/CMakeLists.txt sets the compiler flags for building but does not include -ftls-model=local-exec.
For reference the GCC toolchain and Picolibc already use local-exec.
Fix
Adding -ftls-model=local-exec to get_runtimes_flags() in scripts/llvm/CMakeLists.txt fixes the tls-model mismatch.
I rebuild this for an armv7m_hard_fpv5_d16 target and verified that the C++ code shown now functions correctly.