Reproducer
main.cpp:
void do_some_fun(); thread_local double kek; int main() { kek = 42.0; do_some_fun(); }
bug.cpp:
#include <cstdio> extern thread_local __attribute__((weak)) double kek; void do_some_fun() { if (&kek != nullptr) { printf("The address is not nullptr!\n"); } else { printf("The address is nullptr :(\n"); } printf("Actually, the address is %p\n", &kek); printf("And the value is %f\n", kek); }
Commands to reproduce:
clang++ -O3 -c main.cpp -o main.o clang++ -O3 -c bug.cpp -o bug.o clang++ -fuse-ld=lld main.o bug.o -o program
Expected behavior
The address is not nullptr! Actually, the address is 0x7fcb82adb738 And the value is 42.000000
Actual behavior
The address is nullptr :( Actually, the address is 0x7fa226ab5738 And the value is 42.000000
Explanation
Clang generates the following assembly:
movq %fs:0, %rax addq kek@GOTTPOFF(%rip), %rax ; ADDQ sets ZF if the address is NULL leaq .Lstr(%rip), %rax ; "The value is nullptr :(" leaq .Lstr.3(%rip), %rdi ; "The value is not nullptr!" cmoveq %rax, %rdi ; Checks ZF callq puts@PLT
and a R_X86_64_GOTTPOFF relocation.
Then, when LLD discovers location of kek, it tries to relax the relocation to R_X86_64_TPOFF32. The transformation uses LEAQ thus proper ZF is lost and CMOVEQ misbehaves.