Bug 50986 - weak static data members with constant initializers emitted in .rodata, leading to segfault on startup
Summary: weak static data members with constant initializers emitted in .rodata, leadi...
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.6.3
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-11-03 18:40 UTC by Richard Smith
Modified: 2017-12-04 23:21 UTC (History)
7 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Smith 2011-11-03 18:40:29 UTC
It's possible for a weak variable K (either a static data member of a class template or a static variable in an inline function) to have a constant initializer in one translation unit (call it TU1) and a non-constant initializer in another translation unit (call it TU2), without an ODR violation. This can happen, for instance, if the initializer for an extern const int X whose value is used in K's initializer is visible in one TU but not the other.

In this case, in TU1, g++ uses static initialization and puts the variable in .rodata. In TU2, it uses dynamic initialization. If these TUs are linked together in the wrong order, the linker will put the variable in .rodata but the binary will still try to dynamically initialize it. This causes the program to segfault on startup (trying to write to read-only memory).

Testcase:

$ cat repro.cpp
struct S {
  static const int x;
};
template<typename T> struct U {
  static const int k;
};
#ifdef TU1
const int S::x = 42;
#endif
template<typename T> const int U<T>::k = T::x;

#ifdef TU1
extern const int *f();
const int *g() { return &U<S>::k; }
int main() {
  return *f() + U<S>::k;
}
#endif

#ifdef TU2
const int *f() { return &U<S>::k; }
#endif
$ g++ repro.cpp -DTU1 -c -o tu1.o
$ g++ repro.cpp -DTU2 -c -o tu2.o
$ g++ tu1.o tu2.o
$ ./a.out
Segmentation fault


clang has the same issue (which is how this was discovered), and the current proposed solution there is to never put weak constants in .rodata.
Comment 1 Andrew Pinski 2012-02-01 19:58:12 UTC
I think you are violating ODR here.
Comment 2 Richard Smith 2012-02-01 21:14:35 UTC
As noted in comment#0, I believe there is no ODR violation here. Each definition of U<S>::k consists of the same sequence of tokens, every name refers to the same entity in both definitions, and none of the other provisions of [basic.def.odr]p5 applies.

After some discussion, the proposed approach in clang is (roughly) to always generate a guard variable (initialized to 1 for a statically-initialized variable), and to put the variable and its guard variable in the same COMDAT group. Full details are in this thread:

http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-November/018816.html
Comment 3 Rafael Avila de Espindola 2014-10-23 13:11:31 UTC
In clang this was fixed by putting a .init_array section in the same comdat as the variable it is initializing. That way if the variable is dropped, so is the initialization code.