https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91757
Bug ID: 91757 Summary: Function block scope thread_local variable with unique global symbol linkage is not unique at run-time. Product: gcc Version: 6.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: maxim.yegorushkin at gmail dot com Target Milestone: --- Consider the following application comprised of an executable and a shared library loaded at run-time. In the application, static thread_local data member a0 resolves to the same address from both the executable and shared library, as expected. Function scope static variable a2 resolves to the same address from both the executable and shared library, as expected. Whereas function block scope thread_local variable a1 incorrectly resolves to different addresses, whereas it must resolve to the same address. $ cat shared.h #pragma once namespace shared { template<class T> struct A { static thread_local unsigned a0; static unsigned* f1() { thread_local unsigned a1 = 0; return &a1; } static unsigned* f2() { static unsigned a2 = 0; return &a2; } }; template<class T> thread_local unsigned A<T>::a0 = 0; unsigned* f0(); unsigned* f1(); unsigned* f2(); } // namespace shared $ cat shared.cc #include "shared.h" unsigned* shared::f0() { return &A<void>::a0; } unsigned* shared::f1() { return A<void>::f1(); } unsigned* shared::f2() { return A<void>::f2(); } $ cat test.cc #include "shared.h" #include <cstdio> #include <dlfcn.h> int main() { using namespace shared; std::printf("main a0: %p\n", &A<void>::a0); std::printf("main a1: %p\n", A<void>::f1()); std::printf("main a2: %p\n", A<void>::f2()); void* so = ::dlopen("./libshared.so", RTLD_NOW | RTLD_GLOBAL); auto pf0 = reinterpret_cast<decltype(&f0)>(::dlsym(so, "_ZN6shared2f0Ev")); auto pf1 = reinterpret_cast<decltype(&f1)>(::dlsym(so, "_ZN6shared2f1Ev")); auto pf2 = reinterpret_cast<decltype(&f2)>(::dlsym(so, "_ZN6shared2f2Ev")); std::printf("shared a0: %p\n", pf0()); std::printf("shared a1: %p\n", pf1()); std::printf("shared a2: %p\n", pf2()); } $ g++ -c -o test.o -pthread -std=gnu++1z -march=native -W{all,extra,error} -g -O2 test.cc $ g++ -c -o shared.o -pthread -std=gnu++1z -march=native -W{all,extra,error} -g -O2 -fPIC shared.cc $ g++ -o test -rdynamic -pthread -g test.o -ldl $ g++ -o libshared.so -shared -pthread -g shared.o $ nm -C --extern-only --dynamic --defined-only test 0000000000600e90 B __bss_start 0000000000600e80 D __data_start 0000000000600e80 W data_start 0000000000600e90 D _edata 0000000000600e98 B _end 0000000000400a7c T _fini 00000000004007a0 T _init 0000000000400a88 R _IO_stdin_used 00000000004009e0 T __libc_csu_fini 00000000004009f0 T __libc_csu_init 0000000000400800 T main 00000000004008c4 T _start 0000000000000004 u shared::A<void>::a0 <--- Unique global object, as expected. 0000000000000000 u shared::A<void>::f1()::a1 <--- Unique global object, as expected. 0000000000600e94 u shared::A<void>::f2()::a2 <--- Unique global object, as expected. $ nm -C --extern-only --dynamic --defined-only libshared.so 0000000000200c78 B __bss_start 0000000000200c78 D _edata 0000000000200c80 B _end 0000000000000908 T _fini 0000000000000758 T _init 0000000000000004 u shared::A<void>::a0 <--- Unique global object, as expected. 00000000000008c0 T shared::f0() 00000000000008e0 T shared::f1() 0000000000000900 T shared::f2() 0000000000000000 u shared::A<void>::f1()::a1 <--- Unique global object, as expected. 0000000000200c7c u shared::A<void>::f2()::a2 <--- Unique global object, as expected. $ ./test main a0: 0x7fcc4c42171c main a1: 0x7fcc4c421718 main a2: 0x600e94 shared a0: 0x7fcc4c42171c shared a1: 0x1b9b330 <---- wrong address, must resolve to 0x7fcc4c421718. shared a2: 0x600e94