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

Reply via email to