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?
I would like to summarize the things I learned from the helpful comments above:
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:
Would like to thank David Schwartz and n.'pronouns'm for their guidance.