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@PLTand 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.