与gcc 4.7超级奇怪的段错误 - Bug?

kal*_*alj 5 c++ linux gcc4.7

这是我一直在尝试编译的一段代码:

#include <cstdio>

#define N 3

struct Data {
    int A[N][N];
    int B[N];
};

int foo(int uloc, const int A[N][N], const int B[N])
{
    for(unsigned int j = 0; j < N; j++) {
        for( int i = 0; i < N; i++) {
            for( int r = 0; r < N ; r++) {
                for( int q = 0; q < N ; q++) {
                   uloc += B[i]*A[r][j] + B[j];
                }
            }
        }
    }
    return uloc;
}

int apply(const Data *d)
{
    return foo(4,d->A,d->B);
}

int main(int, char **)
{
    Data d;
    for(int i = 0; i < N; ++i) {
        for(int j = 0; j < N; ++j) {
            d.A[i][j] = 0.0;
        }
        d.B[i] = 0.0;
    }

    int res = 11 + apply(&d);

    printf("%d\n",res);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是的,它看起来很奇怪,目前根本没有做任何有用的事情,但它是一个更大的程序最简洁的版本,我最初遇到了问题.

它使用GCC(G ++)4.4和4.6进行编译和运行,但如果我使用GCC 4.7,并启用第三级优化:

g++-4.7 -g -O3 prog.cpp -o prog
Run Code Online (Sandbox Code Playgroud)

运行时出现分段错误.Gdb并没有真正提供有关出错的信息:

(gdb) run
Starting program: /home/kalle/work/code/advect_diff/c++/strunt 

Program received signal SIGSEGV, Segmentation fault.
apply (d=d@entry=0x7fffffffe1a0) at src/strunt.cpp:25
25      int apply(const Data *d)
(gdb) bt
#0  apply (d=d@entry=0x7fffffffe1a0) at src/strunt.cpp:25
#1  0x00000000004004cc in main () at src/strunt.cpp:34
Run Code Online (Sandbox Code Playgroud)

我尝试以不同的方式调整代码,看看错误是否消失.似乎有必要在foo中拥有所有四个循环级别,并且我无法通过单级函数调用来重现它.哦,是的,最外层的循环必须使用无符号循环索引.

我开始怀疑这是编译器或运行时中的错误,因为它特定于版本4.7,我无法看到哪些内存访问无效.

任何有关正在发生的事情的见解都将非常感激.

可以通过稍微修改代码来获得GCC的C版本的相同情况.

我的系统是:

Debian wheezy Linux 3.2.0-4-amd64 GCC 4.7.2-5


好的,所以我查看了gdb提供的反汇编,但我担心这对我说不多:

Dump of assembler code for function apply(Data const*):
   0x0000000000400760 <+0>: push   %r13
   0x0000000000400762 <+2>: movabs $0x400000000,%r8
   0x000000000040076c <+12>:    push   %r12
   0x000000000040076e <+14>:    push   %rbp
   0x000000000040076f <+15>:    push   %rbx
   0x0000000000400770 <+16>:    mov    0x24(%rdi),%ecx
=> 0x0000000000400773 <+19>:    mov    (%rdi,%r8,1),%ebp
   0x0000000000400777 <+23>:    mov    0x18(%rdi),%r10d
   0x000000000040077b <+27>:    mov    $0x4,%r8b
   0x000000000040077e <+30>:    mov    0x28(%rdi),%edx
   0x0000000000400781 <+33>:    mov    0x2c(%rdi),%eax
   0x0000000000400784 <+36>:    mov    %ecx,%ebx
   0x0000000000400786 <+38>:    mov    (%rdi,%r8,1),%r11d
   0x000000000040078a <+42>:    mov    0x1c(%rdi),%r9d
   0x000000000040078e <+46>:    imul   %ebp,%ebx
   0x0000000000400791 <+49>:    mov    $0x8,%r8b
   0x0000000000400794 <+52>:    mov    0x20(%rdi),%esi
Run Code Online (Sandbox Code Playgroud)

当我看到这个时,我应该看到什么?


编辑2015-08-13:这似乎在g ++ 4.8及更高版本中得到修复.

use*_*ica 6

你从未初始化d.它的值是不确定的,并且尝试使用其内容进行数学运算是未定义的行为.(即使尝试读取它的值而不对它们做任何事情也是未定义的行为.)初始化d并看看会发生什么.


现在你已经初始化d并且它仍然失败,这看起来像一个真正的编译器错误.尝试更新到4.7.3或4.8.2; 如果问题仍然存在,请提交错误报告.(当前已知错误的列表似乎是空的,或者至少链接是仅列出非错误的链接.)

  • @PlasmaHH:编译器可以自由地观察到`d`永远不会被初始化,假设它永远不会被使用,并且不想在程序被允许读取的地方实际分配它.如果在初始化`d`时程序仍然是段错误,则*然后*它是编译器错误. (4认同)
  • 对大家:`d`是默认初始化的,是一个完全有效的对象.访问dA和dB也非常安全.但是这些数组中的值是垃圾,因为它是那个地方的内存中剩下的.根本没有未定义的行为!! (3认同)

Pla*_*aHH 2

不幸的是,它确实是 gcc 中的一个错误。我一点也不知道它在那里做什么,但是为 apply 函数生成的程序集是(我在没有 main 的情况下编译了它,并且它内联了 foo ):

_Z5applyPK4Data:
        pushq   %r13
        movabsq $17179869184, %r8
        pushq   %r12
        pushq   %rbp
        pushq   %rbx
        movl    36(%rdi), %ecx
        movl    (%rdi,%r8), %ebp
        movl    24(%rdi), %r10d
Run Code Online (Sandbox Code Playgroud)

恰恰在movl (%rdi,%r8), %ebp它会崩溃,因为它添加了一个无意义的0x400000000$rdi第一个参数,因此指向 的指针Data)并取消引用它。