简体   繁体   English

gcc、段错误和静态变量地址变化的奥秘(跨堆栈帧)

[英]gcc, segfault, and the mystery of the changing address of a static variable (across stack frames)

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 分析回溯并注意到以下几点:

(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).上面有 2 个堆栈帧在同一个头文件中引用相同的const string C_STATIC_STRING ,但是一个帧正确地寻址了变量(帧 3),另一个(帧 2)有一个偏移地址(如果我计算正确,则为 4512 字节) .

  • The 0x66a660 one addresses the correct string 0x66a660 一地址正确的字符串
  • The 0x66b800 results in error if read: Cannot access memory at address 0xffffffffffffffe8如果读取 0x66b800 会导致错误:无法访问地址 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 constants.h - 包含宏和常量
#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 Test1 类 - 有一个私有字符串,它在构造过程中使用 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测试1.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测试类 - 拥有一个 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 test.cpp - 几乎是空的

using namespace std;

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

  • main.cpp -- has a static instance of Test class main.cpp -- 有一个 Test 类的静态实例

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

Disassembly seems to indicate that the issue lies with initialization order of static variables/constants.反汇编似乎表明问题在于静态变量/常量的初始化顺序。

I'm not too good at C++ and compilers.我不太擅长 C++ 和编译器。 Perhaps anyone has an idea on what is exactly going on?也许有人知道到底发生了什么? GCC bug or is it just me? GCC错误还是只是我?

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.由于称为Static Initialization Fiasco的现象,以我所做的方式使用静态变量依赖于“好运”,以便在使用变量之前对其进行初始化。 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.为了解决第 2 号问题,我将静态变量包装在一个方法(某种 getter)中,并使用该方法而不是变量。 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.感谢 David Schwartz 和 n.'pronouns'm 的指导。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM