-1

I get a segfault in my application and have been poking at it for multiple hours now. I was analysing the backtrace using gdb and noticed the following:

(gdb) frame 3
(gdb) info address C_STATIC_STRING
Symbol "C_STATIC_STRING" is static storage at address 0x66a660.
(gdb) frame 2
(gdb) info address C_STATIC_STRING
Symbol "C_STATIC_STRING" is static storage at address 0x66b800.

Above there are 2 stack frames referring to the same const string C_STATIC_STRING in the same header file, but one frame correctly addresses the variable (frame 3) and the other (frame 2) has an offset address (by 4512 bytes if I calculated correctly).

  • The 0x66a660 one addresses the correct string
  • The 0x66b800 results in error if read: Cannot access memory at address 0xffffffffffffffe8

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39.0.3)

ADDITIONAL INFO:

I have managed to reproduce the issue using a simpler code:

  • constants.h - containing the macro and the constatnt
#ifndef CONSTANTS_H
#define CONSTANTS_H

using namespace std;

#include <iostream>
#include <string>


#ifndef C_MACRO
#define C_MACRO  "MACRO "
#endif

const std::string CONSTANT = C_MACRO "CONSTANT_STRING";


#endif
  • Test1 class - has a private string that it initializes during construction using the CONSTANT test1.h
#ifndef TEST1_H
#define TEST1_H

using namespace std;

#include <iostream>
#include <string>
#include "constants.h"

class Test1 {
 public:
 Test1();
 std::string getString() {
  return m_str;   
 }
 private:
 std::string m_str;
};

#endif

test1.cpp

using namespace std;

#include <iostream>
#include <string>
#include "test1.h"

Test1::Test1(): m_str(std::string("Extra ") + CONSTANT) 
{
     
};
  • Test class - owns an instance of Test1 test.h
#ifndef TEST_H
#define TEST_H

using namespace std;

#include <iostream>
#include <string>
#include "test1.h"
#include "constants.h"


class Test {
    public:
    Test1 getTest() {
        return m_test;   
    }

 private:
 Test1 m_test;    
};

#endif

test.cpp - pretty much empty

using namespace std;

#include <iostream>
#include <string>
#include "test.h"

  • main.cpp -- has a static instance of Test class

using namespace std;

#include <iostream>
#include <string>
#include "test.h"


namespace NOTSTD {
    
    Test variable;
}
using namespace NOTSTD;

int main()
{
  
  std::cout << variable.getTest().getString() << " printed";
}

Now the build process

  • Makefile
#Test makefile
CPP = g++ 
CPPFLAGS = -Wall -ggdb -O0

AR = ar
RANLIB = ranlib

OUTPUT = test

all:: $(OUTPUT)

for_static = test1.o
static_lib.a: $(for_static)
    $(AR) qc $@ $(for_static)
    $(RANLIB) $@
    
$(OUTPUT): static_lib.a test.o main.o
    $(CPP) ${CPPFLAGS} test.o main.o -o $(OUTPUT) static_lib.a
    
%.o : %.cpp
    $(CPP) $(CPPFLAGS) -c $< -o $@
    
clean:
    rm -f $(OUTPUT)
    rm -f *.o
    rm -f *.a

Test1 gets compiled into a static library and later used to compile the rest. In Cygwin, it works as expected On OEL 7 it gets a segmentation fault (no matter the optimization level) If I omit the statically linked library and just compile in test1, then it works on OEL too.

Disassembly seems to indicate that the issue lies with initialization order of static variables/constants.

I'm not too good at C++ and compilers. Perhaps anyone has an idea on what is exactly going on? GCC bug or is it just me?

Denis
  • 105
  • 1
  • 10
  • 3
    You don't have to disclose the actual source code when creating a [mcve]. – Ted Lyngmo Sep 23 '20 at 00:02
  • 1
    I'm afraid that the best you will likely get is our sympathy. It sucks to have a mystery bug, but it'll be dumb luck if anyone can infer the problem from equally mysterious code. See if you can produce a [mre]. It won't look anything like the code it represents and odds are good you'll figure out what the problem is while making it. – user4581301 Sep 23 '20 at 00:02
  • Your static is defined in a header? Are those two frames in different files that include the header? – Retired Ninja Sep 23 '20 at 00:02
  • Is that `C_STATIC_STRING` defined in your own code or in some library? might you have more than one version of that lib linked? – Vlad Feinstein Sep 23 '20 at 00:05
  • the string is in my code, indeed in a header file.and indeed frames are in different files. – Denis Sep 23 '20 at 00:07
  • @TedLyngmo I understand your point, but I am hoping for a "free lunch" ... was hoping that perhaps it is a known bug in gcc or something – Denis Sep 23 '20 at 00:09
  • 2
    At least show how the variable is defined. If it is defined in a header and included in multiple source files then it's quite possible for there to be multiple static variables of the same name in different frames. – kaylum Sep 23 '20 at 00:10
  • The variable is defined as follows const string C_STATIC_STRING = SOMEMACRO "some_text"; if i execute "info variables" in those different frames, they both list the same thing (same variable in the same header file) – Denis Sep 23 '20 at 00:17
  • Hmm ... i do see the variable listed 3 time though – Denis Sep 23 '20 at 00:20
  • @Denis It will probably take a lot fewer man-hours for you to create a [mcve] than it'll take all of us speculating and trying to create a [mcve] for ourselves. – Ted Lyngmo Sep 23 '20 at 00:20
  • I was able to reproduce the issue using a smaller code base (added in the question). Perhaps we can reopen the question? – Denis Sep 24 '20 at 15:09
  • 1
    `Test1::Test1(): m_str(std::string("Extra ") + CONSTANT)` This code, quite obviously, cannot be allowed to execute before you can ensure that `CONSTANT` has been constructed. Nothing in your code enforces this ordering requirement. So your code is broken. You are correct, the bug is static initialization order fiasco and the usual solutions to that problem will work. In the example code, putting `variable` inside `main` will solve the problem. – David Schwartz Sep 24 '20 at 15:56
  • In the actual code, I do not have the main method in the library. I do have other executables that use that declared variable via extern keyword. Any idea why it used to work (and still works in windows)? And if updating to a later gcc (OEL 8 ... I think it is gcc 9.2.1) would help? – Denis Sep 24 '20 at 16:14
  • 1
    @DavidSchwartz thanks for the pointer towards the bug name. Should have quoted it - **static initialization fiasco**. did not know that it was a thing. here is an explanation if someone else is wondering https://isocpp.org/wiki/faq/ctors#static-init-order – Denis Sep 24 '20 at 21:20
  • 2
    Being in the same header file bears no relevance whatsoever. Static variables in different translation units have different addresses. If you need the address to be the same, don't make the variable static. – n. m. could be an AI Sep 24 '20 at 22:56
  • Yes, that confirms my suspicion... and explain why I see 3 CONSTANT static variables at run time – Denis Sep 25 '20 at 23:17

1 Answers1

1

I would like to summarize the things I learned from the helpful comments above:

  1. It is inevitable for static variables to have different addresses in different Translation Units.
  2. Due to the phenomenon known as Static Initialization Fiasco, using the static variables in the way that I did, relies on "good luck" for the variable to be initialized before they are used. If the fortune is not on your side at compile time, you will get a segmentation fault while attempting to use the variable.

To work around the issue nr 2, I have wrapped my static variable in a method (a getter of sorts) and used the method instead of the variable. It forces the initialization of other static variables at the correct time. the method looks something like that:

Test getTest(){
    static Test test;
    return test;
}

Would like to thank David Schwartz and n.'pronouns'm for their guidance.

Denis
  • 105
  • 1
  • 10