Bug 59364

Summary: thread_local link error
Product: gcc Reporter: Conrad S <conradsand.arma>
Component: c++Assignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED DUPLICATE    
Severity: major CC: daniel.kruegler, hubicka, jakub, jakub, jason, jason, oleg, victor.dyachenko
Priority: P3    
Version: 4.8.2   
Target Milestone: ---   
Host: Target:
Build: Known to work: 4.9.0
Known to fail: 4.8.2 Last reconfirmed:
Bug Depends on:    
Bug Blocks: 59994    
Attachments: reduced test case

Description Conrad S 2013-12-02 02:30:48 UTC
Short description:

Declaring a variable as extern thread_local in a C++11 program leads to "undefined reference to `TLS init function for ..." during linking.

Long description:

Consider three files: rng.hpp, a.cpp and b.cpp, listed below.
Compile using:
g++ a.cpp -c -o a.o -std=c++11
g++ b.cpp -c -o b.o -std=c++11
g++ a.o b.o -o prog -std=c++11

b.o: In function `TLS wrapper function for rng_instance':
b.cpp:(.text._ZTW12rng_instance[_ZTW12rng_instance]+0x5): undefined reference to `TLS init function for rng_instance'

Things only work if thread_local is removed.

gcc version 4.8.2 20131017 (Red Hat 4.8.2-1) on Fedora 19 (x86-64).

---
rng.hpp:

#include <random>

class rng {
  public:
  std::mt19937_64 engine;
  std::uniform_real_distribution<double> distr;
  double get_val() { return distr(engine); }
  };

---
a.cpp:

#include "rng.hpp"
thread_local rng rng_instance;

---
b.cpp:

#include <iostream>
#include "rng.hpp"

extern thread_local rng rng_instance;

int main(int argc, char** argv)
  {
  std::cout << "val: " << rng_instance.get_val() << std::endl;
  return 0;
  }
---
Comment 1 Tom De Caluwé 2013-12-10 05:52:36 UTC
This seems to be a duplicate of bug #55800. The workaround mentioned in the comments fixes the problem.
Comment 2 Conrad S 2013-12-10 06:45:39 UTC
I wouldn't call the method presented in the comments to bug #55800 as a workaround.

Quote:
"at least adding .globl  _ZTWN3xyz3blaE _ZTWN3xyz3blaE = __tls_init manually at the end of the assembly seems to make the code do what it is supposed to"

Editing the assembly is not a workaround -- it's a one off hack.
Comment 3 Conrad S 2013-12-10 07:06:01 UTC
Reduced test case.  Compile with:

g++ a.cpp -c -o a.o -std=c++11
g++ b.cpp -c -o b.o -std=c++11
g++ a.o b.o -o prog -std=c++11

file foo.hpp:

class foo
  {
  public:
  
  inline  foo() {}
  inline ~foo() {}
  
  inline double bar() { return 123.456; }
  };


file a.cpp:

#include "foo.hpp"
thread_local foo foo_instance;


file b.cpp:

#include "foo.hpp"
extern thread_local foo foo_instance;

int main(int argc, char** argv)
  {
  double bar = foo_instance.bar();
  return 0;
  }
Comment 4 Conrad S 2013-12-10 07:10:49 UTC
Created attachment 31406 [details]
reduced test case

Attached reduced_test_case.tar.gz

No need for pre-processed input, as it doesn't include any system headers.
Comment 5 Richard Biener 2014-01-30 15:20:21 UTC
Works in 4.9.
Comment 6 Conrad S 2014-01-30 15:34:58 UTC
Any chance of a backport of this fix to gcc 4.8.3 ?
Comment 7 Jakub Jelinek 2014-01-30 15:46:34 UTC
I thought I've commented on this, perhaps in other PR, but can't find it right now.  The link error disappeared with r199577, which certainly doesn't seem to be backportable nor a change with the intent to fix this, and furthermore, I'd say that for such a trivial (sure, not in C++ terminology) ctor we'd better not to require dynamic initialization of the TLS var.
Comment 8 Jason Merrill 2014-01-31 15:48:25 UTC
Duplicate, yes.

*** This bug has been marked as a duplicate of bug 55800 ***