首页 > 解决方案 > gcc、segfault和静态变量地址变化之谜(跨栈帧)

问题描述

我在我的应用程序中遇到了一个段错误,并且现在已经研究了几个小时。我正在使用 gdb 分析回溯并注意到以下内容:

(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.

上面有 2 个堆栈帧const string C_STATIC_STRING在同一个头文件中引用相同的内容,但是一个帧正确地寻址变量(第 3 帧),而另一个(第 2 帧)具有偏移地址(如果我计算正确,则为 4512 字节)。

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

附加信息:

我设法使用更简单的代码重现了该问题:

#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
#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

测试1.cpp

using namespace std;

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

Test1::Test1(): m_str(std::string("Extra ") + CONSTANT) 
{
     
};
#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 - 几乎是空的

using namespace std;

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


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";
}

现在构建过程

#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 被编译成一个静态库,然后用于编译其余部分。在 Cygwin 中,它按预期工作在 OEL 7 上,它会出现分段错误(无论优化级别如何)如果我省略静态链接库并只在 test1 中编译,那么它也适用于 OEL。

反汇编似乎表明问题在于静态变量/常量的初始化顺序。

我不太擅长 C++ 和编译器。也许有人知道到底发生了什么?GCC错误还是只有我?

标签: c++linuxgccsegmentation-faultg++4.8

解决方案


我想总结一下我从上面有用的评论中学到的东西:

  1. 静态变量在不同的翻译单元中有不同的地址是不可避免的。
  2. 由于称为“静态初始化惨败”的现象,以我的方式使用静态变量依赖于“好运”,以便在使用变量之前对其进行初始化。如果在编译时运气不在您身边,您将在尝试使用该变量时遇到分段错误。

为了解决问题 nr 2,我将我的静态变量包装在一个方法(某种 getter)中,并使用该方法而不是变量。它强制在正确的时间初始化其他静态变量。该方法看起来像这样:

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

感谢 David Schwartz 和 n.'pronouns'm 的指导。


推荐阅读